"Fossies" - the Fresh Open Source Software Archive

Member "xfe-1.43.2/src/SearchPanel.cpp" (6 Jun 2019, 145679 Bytes) of package /linux/privat/xfe-1.43.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "SearchPanel.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.43.1_vs_1.43.2.

    1 // Search panel
    2 #include "config.h"
    3 #include "i18n.h"
    4 
    5 #include <fx.h>
    6 #include <fxkeys.h>
    7 
    8 
    9 #include <FXPNGIcon.h>
   10 #include <FXJPGIcon.h>
   11 #include <FXTIFIcon.h>
   12 
   13 
   14 #include "xfedefs.h"
   15 #include "startupnotification.h"
   16 #include "icons.h"
   17 #include "File.h"
   18 #include "FileDict.h"
   19 #include "FileDialog.h"
   20 #include "FileList.h"
   21 #include "MessageBox.h"
   22 #include "ArchInputDialog.h"
   23 #include "HistInputDialog.h"
   24 #include "BrowseInputDialog.h"
   25 #include "OverwriteBox.h"
   26 #include "CommandWindow.h"
   27 #include "ExecuteBox.h"
   28 #include "XFileExplorer.h"
   29 #include "SearchPanel.h"
   30 
   31 
   32 
   33 #if defined(linux)
   34 extern FXStringDict* fsdevices; // Devices from fstab
   35 #endif
   36 
   37 // Global Variables
   38 extern FXMainWindow* mainWindow;
   39 extern FXString      homedir;
   40 extern FXString      xdgdatahome;
   41 extern FXbool        allowPopupScroll;
   42 extern FXuint        single_click;
   43 
   44 // Clipboard
   45 extern FXString clipboard;
   46 extern FXuint   clipboard_type;
   47 
   48 extern char OpenHistory[OPEN_HIST_SIZE][MAX_COMMAND_SIZE];
   49 extern int  OpenNum;
   50 
   51 #if defined(linux)
   52 extern FXbool pkg_format;
   53 #endif
   54 
   55 // Button separator margins and height
   56 #define SEP_SPACE     5
   57 #define SEP_HEIGHT    20
   58 
   59 
   60 // Map
   61 FXDEFMAP(SearchPanel) SearchPanelMap[] =
   62 {
   63     FXMAPFUNC(SEL_CLIPBOARD_LOST, 0, SearchPanel::onClipboardLost),
   64     FXMAPFUNC(SEL_CLIPBOARD_GAINED, 0, SearchPanel::onClipboardGained),
   65     FXMAPFUNC(SEL_CLIPBOARD_REQUEST, 0, SearchPanel::onClipboardRequest),
   66     FXMAPFUNC(SEL_CLICKED, SearchPanel::ID_FILELIST, SearchPanel::onCmdItemClicked),
   67     FXMAPFUNC(SEL_DOUBLECLICKED, SearchPanel::ID_FILELIST, SearchPanel::onCmdItemDoubleClicked),
   68     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_GOTO_PARENTDIR, SearchPanel::onCmdGotoParentdir),
   69     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_OPEN_WITH, SearchPanel::onCmdOpenWith),
   70     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_OPEN, SearchPanel::onCmdOpen),
   71     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_VIEW, SearchPanel::onCmdEdit),
   72     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_EDIT, SearchPanel::onCmdEdit),
   73     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_COMPARE, SearchPanel::onCmdCompare),
   74     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_REFRESH, SearchPanel::onCmdRefresh),
   75     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_PROPERTIES, SearchPanel::onCmdProperties),
   76     FXMAPFUNC(SEL_MIDDLEBUTTONPRESS, SearchPanel::ID_FILELIST, SearchPanel::onCmdEdit),
   77     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_SELECT_ALL, SearchPanel::onCmdSelect),
   78     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_DESELECT_ALL, SearchPanel::onCmdSelect),
   79     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_SELECT_INVERSE, SearchPanel::onCmdSelect),
   80     FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, SearchPanel::ID_FILELIST, SearchPanel::onCmdPopupMenu),
   81     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_POPUP_MENU, SearchPanel::onCmdPopupMenu),
   82     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_COPY_CLIPBOARD, SearchPanel::onCmdCopyCut),
   83     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_CUT_CLIPBOARD, SearchPanel::onCmdCopyCut),
   84     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_COPYTO, SearchPanel::onCmdFileMan),
   85     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_MOVETO, SearchPanel::onCmdFileMan),
   86     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_RENAME, SearchPanel::onCmdFileMan),
   87     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_SYMLINK, SearchPanel::onCmdFileMan),
   88     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_ADD_TO_ARCH, SearchPanel::onCmdAddToArch),
   89     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_EXTRACT, SearchPanel::onCmdExtract),
   90     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_TRASH, SearchPanel::onCmdFileTrash),
   91     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_FILE_DELETE, SearchPanel::onCmdFileDelete),
   92     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_GO_SCRIPTDIR, SearchPanel::onCmdGoScriptDir),
   93     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_DIR_USAGE, SearchPanel::onCmdDirUsage),
   94     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_STATUS, SearchPanel::onUpdStatus),
   95     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_FILE_RENAME, SearchPanel::onUpdSelMult),
   96     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_GOTO_PARENTDIR, SearchPanel::onUpdSelMult),
   97     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_COMPARE, SearchPanel::onUpdCompare),
   98     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_COPY_CLIPBOARD, SearchPanel::onUpdMenu),
   99     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_CUT_CLIPBOARD, SearchPanel::onUpdMenu),
  100     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_PROPERTIES, SearchPanel::onUpdMenu),
  101     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_FILE_TRASH, SearchPanel::onUpdMenu),
  102     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_FILE_DELETE, SearchPanel::onUpdMenu),
  103     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_DIR_USAGE, SearchPanel::onUpdDirUsage),
  104 #if defined(linux)
  105     FXMAPFUNC(SEL_COMMAND, SearchPanel::ID_PKG_QUERY, SearchPanel::onCmdPkgQuery),
  106     FXMAPFUNC(SEL_UPDATE, SearchPanel::ID_PKG_QUERY, SearchPanel::onUpdPkgQuery),
  107 #endif
  108 };
  109 
  110 
  111 // Object implementation
  112 FXIMPLEMENT(SearchPanel, FXVerticalFrame, SearchPanelMap, ARRAYNUMBER(SearchPanelMap))
  113 
  114 
  115 
  116 // Contruct Search Panel
  117 SearchPanel::SearchPanel(FXComposite* p, FXuint name_size, FXuint dir_size, FXuint size_size, FXuint type_size, FXuint ext_size,
  118                          FXuint modd_size, FXuint user_size, FXuint grou_size, FXuint attr_size, FXColor listbackcolor, FXColor listforecolor,
  119                          FXuint opts, int x, int y, int w, int h) :
  120     FXVerticalFrame(p, opts, x, y, w, h, 0, 0, 0, 0)
  121 {
  122     // Global container
  123     FXVerticalFrame* cont = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1);
  124 
  125     // Container for the action toolbar
  126     FXHorizontalFrame* toolbar = new FXHorizontalFrame(cont, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, DEFAULT_SPACING, DEFAULT_SPACING, DEFAULT_SPACING, DEFAULT_SPACING, 0, 0);
  127 
  128     // File list
  129     FXVerticalFrame* cont2 = new FXVerticalFrame(cont, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_SUNKEN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  130 
  131     list = new FileList(this, cont2, this, ID_FILELIST, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y|_ICONLIST_DETAILED|_FILELIST_SEARCH);
  132     list->setHeaderSize(0, name_size);
  133     list->setHeaderSize(1, dir_size);
  134     list->setHeaderSize(2, size_size);
  135     list->setHeaderSize(3, type_size);
  136     list->setHeaderSize(4, ext_size);
  137     list->setHeaderSize(5, modd_size);
  138     list->setHeaderSize(6, user_size);
  139     list->setHeaderSize(7, grou_size);
  140     list->setHeaderSize(8, attr_size);
  141     list->setTextColor(listforecolor);
  142     list->setBackColor(listbackcolor);
  143 
  144     // Set list style
  145     FXuint liststyle = getApp()->reg().readUnsignedEntry("SEARCH PANEL", "liststyle", _ICONLIST_DETAILED);
  146     list->setListStyle(liststyle);
  147     
  148     // Set dirs first
  149     FXuint dirsfirst = getApp()->reg().readUnsignedEntry("SEARCH PANEL", "dirs_first", 1);
  150     list->setDirsFirst(dirsfirst);
  151 
  152     // Set ignore case
  153     FXuint ignorecase = getApp()->reg().readUnsignedEntry("SEARCH PANEL", "ignore_case", 1);
  154     list->setIgnoreCase(ignorecase);
  155 
  156     // Toolbar buttons
  157     FXHotKey hotkey;
  158     FXString key;
  159 
  160     // Refresh panel toolbar button
  161     key = getApp()->reg().readStringEntry("KEYBINDINGS", "refresh", "Ctrl-R");
  162     refreshbtn = new FXButton(toolbar, TAB+_("Refresh panel")+PARS(key), reloadicon, this, SearchPanel::ID_REFRESH, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  163     hotkey = _parseAccel(key);
  164     refreshbtn->addHotKey(hotkey);
  165 
  166     // Goto dir toolbar button
  167     key = getApp()->reg().readStringEntry("KEYBINDINGS", "go_up", "Backspace");
  168     gotodirbtn = new FXButton(toolbar, TAB+_("Go to parent folder")+PARS(key), gotodiricon, this, SearchPanel::ID_GOTO_PARENTDIR, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  169     hotkey = _parseAccel(key);
  170     gotodirbtn->addHotKey(hotkey);
  171 
  172     // Copy / cut / properties toolbar buttons
  173     key = getApp()->reg().readStringEntry("KEYBINDINGS", "copy", "Ctrl-C");
  174     copybtn = new FXButton(toolbar, TAB+_("Copy selected files to clipboard")+PARS(key), copy_clpicon, this, SearchPanel::ID_COPY_CLIPBOARD, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  175     hotkey = _parseAccel(key);
  176     copybtn->addHotKey(hotkey);
  177 
  178     key = getApp()->reg().readStringEntry("KEYBINDINGS", "cut", "Ctrl-X");
  179     cutbtn = new FXButton(toolbar, TAB+_("Cut selected files to clipboard")+PARS(key), cut_clpicon, this, SearchPanel::ID_CUT_CLIPBOARD, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  180     hotkey = _parseAccel(key);
  181     cutbtn->addHotKey(hotkey);
  182 
  183     key = getApp()->reg().readStringEntry("KEYBINDINGS", "properties", "F9");
  184     propbtn = new FXButton(toolbar, TAB+_("Show properties of selected files")+PARS(key), attribicon, this, SearchPanel::ID_PROPERTIES, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  185     hotkey = _parseAccel(key);
  186     propbtn->addHotKey(hotkey);
  187 
  188     // Separator
  189     new FXFrame(toolbar, LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, SEP_SPACE);
  190     new FXVerticalSeparator(toolbar, LAYOUT_SIDE_TOP|LAYOUT_CENTER_Y|SEPARATOR_GROOVE|LAYOUT_FIX_HEIGHT, 0, 0, 0, SEP_HEIGHT);
  191     new FXFrame(toolbar, LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, SEP_SPACE);
  192 
  193     // Move to trash / delete toolbar buttons
  194     key = getApp()->reg().readStringEntry("KEYBINDINGS", "move_to_trash", "Del");
  195     trashbtn = new FXButton(toolbar, TAB+_("Move selected files to trash can")+PARS(key), filedeleteicon, this, SearchPanel::ID_FILE_TRASH, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  196     hotkey = _parseAccel(key);
  197     trashbtn->addHotKey(hotkey);
  198 
  199     key = getApp()->reg().readStringEntry("KEYBINDINGS", "delete", "Shift-Del");
  200     delbtn = new FXButton(toolbar, TAB+_("Delete selected files")+PARS(key), filedelete_permicon, this, SearchPanel::ID_FILE_DELETE, BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT);
  201     hotkey = _parseAccel(key);
  202     delbtn->addHotKey(hotkey);
  203 
  204     // Separator
  205     new FXFrame(toolbar, LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, SEP_SPACE);
  206     new FXVerticalSeparator(toolbar, LAYOUT_SIDE_TOP|LAYOUT_CENTER_Y|SEPARATOR_GROOVE|LAYOUT_FIX_HEIGHT, 0, 0, 0, SEP_HEIGHT);
  207     new FXFrame(toolbar, LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, SEP_SPACE);
  208 
  209     // Icon view toolbar buttons
  210     key = getApp()->reg().readStringEntry("KEYBINDINGS", "big_icons", "F10");
  211     bigiconsbtn = new FXButton(toolbar, TAB+_("Big icon list")+PARS(key), bigiconsicon, list, IconList::ID_SHOW_BIG_ICONS, BUTTON_TOOLBAR|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
  212     hotkey = _parseAccel(key);
  213     bigiconsbtn->addHotKey(hotkey);
  214 
  215     key = getApp()->reg().readStringEntry("KEYBINDINGS", "small_icons", "F11");
  216     smalliconsbtn = new FXButton(toolbar, TAB+_("Small icon list")+PARS(key), smalliconsicon, list, IconList::ID_SHOW_MINI_ICONS, BUTTON_TOOLBAR|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
  217     hotkey = _parseAccel(key);
  218     smalliconsbtn->addHotKey(hotkey);
  219 
  220     key = getApp()->reg().readStringEntry("KEYBINDINGS", "detailed_file_list", "F12");
  221     detailsbtn = new FXButton(toolbar, TAB+_("Detailed file list")+PARS(key), detailsicon, list, IconList::ID_SHOW_DETAILS, BUTTON_TOOLBAR|LAYOUT_TOP|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
  222     hotkey = _parseAccel(key);
  223     detailsbtn->addHotKey(hotkey);
  224 
  225     // Status bar
  226     statusbar = new FXHorizontalFrame(cont, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 3, 3, 3, 3);
  227     statusbar->setTarget(this);
  228     statusbar->setSelector(FXSEL(SEL_UPDATE, SearchPanel::ID_STATUS));
  229 
  230     key = getApp()->reg().readStringEntry("KEYBINDINGS", "thumbnails", "Ctrl-F7");
  231     thumbbtn = new FXToggleButton(statusbar, TAB+_("Show thumbnails")+PARS(key), TAB+_("Hide thumbnails")+PARS(key), showthumbicon, hidethumbicon, this->list,
  232                                   FileList::ID_TOGGLE_THUMBNAILS, TOGGLEBUTTON_TOOLBAR|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
  233     hotkey = _parseAccel(key);
  234     thumbbtn->addHotKey(hotkey);
  235 
  236     new FXHorizontalFrame(statusbar, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0);
  237     status = new FXLabel(statusbar, _("Status"), NULL, JUSTIFY_LEFT|LAYOUT_LEFT|LAYOUT_FILL_X);
  238 
  239     corner = new FXDragCorner(statusbar);
  240 
  241     // File associations
  242     associations = NULL;
  243     associations = new FileDict(getApp());
  244 
  245     // Dialogs
  246     archdialog = NULL;
  247     opendialog = NULL;
  248     operationdialogsingle = NULL;
  249     operationdialogrename = NULL;
  250     operationdialogmultiple = NULL;
  251     comparedialog = NULL;
  252 
  253     // Trahscan locations
  254     trashfileslocation = xdgdatahome + PATHSEPSTRING TRASHFILESPATH;
  255     trashinfolocation = xdgdatahome + PATHSEPSTRING TRASHINFOPATH;
  256 
  257     // Default programs identifiers
  258     progs["<txtviewer>"] = TXTVIEWER;
  259     progs["<txteditor>"] = TXTEDITOR;
  260     progs["<imgviewer>"] = IMGVIEWER;
  261     progs["<imgeditor>"] = IMGEDITOR;
  262     progs["<pdfviewer>"] = PDFVIEWER;
  263     progs["<audioplayer>"] = AUDIOPLAYER;
  264     progs["<videoplayer>"] = VIDEOPLAYER;
  265     progs["<archiver>"] = ARCHIVER;
  266 
  267     // Class variable initializations
  268     ctrlflag = false;
  269     shiftf10 = false;
  270 }
  271 
  272 
  273 // Create window
  274 void SearchPanel::create()
  275 {
  276     // Register standard uri-list type
  277     urilistType = getApp()->registerDragType("text/uri-list");
  278 
  279     // Register special uri-list type used for Gnome, XFCE and Xfe
  280     xfelistType = getApp()->registerDragType("x-special/gnome-copied-files");
  281 
  282     // Register special uri-list type used for KDE
  283     kdelistType = getApp()->registerDragType("application/x-kde-cutselection");
  284 
  285     // Register standard UTF-8 text type used for file dialogs
  286     utf8Type = getApp()->registerDragType("UTF8_STRING");
  287 
  288     FXVerticalFrame::create();
  289 
  290     // Single click navigation
  291     if (single_click == SINGLE_CLICK_DIR_FILE)
  292     {
  293         list->setDefaultCursor(getApp()->getDefaultCursor(DEF_HAND_CURSOR));
  294     }
  295     else
  296     {
  297         list->setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
  298     }
  299 }
  300 
  301 
  302 // Clean up
  303 SearchPanel::~SearchPanel()
  304 {
  305     delete list;
  306     if (opendialog != NULL)
  307     {
  308         delete opendialog;
  309     }
  310     if (archdialog != NULL)
  311     {
  312         delete archdialog;
  313     }
  314     if (operationdialogsingle != NULL)
  315     {
  316         delete operationdialogsingle;
  317     }
  318     if (operationdialogrename != NULL)
  319     {
  320         delete operationdialogrename;
  321     }
  322     if (operationdialogmultiple != NULL)
  323     {
  324         delete operationdialogmultiple;
  325     }
  326     if (comparedialog != NULL)
  327     {
  328         delete comparedialog;
  329     }
  330 }
  331 
  332 
  333 // Double Click on File Item
  334 long SearchPanel::onCmdItemDoubleClicked(FXObject* sender, FXSelector sel, void* ptr)
  335 {
  336     // Wait cursor
  337     getApp()->beginWaitCursor();
  338 
  339     FXlong item = (FXlong)ptr;
  340     if (item > -1)
  341     {
  342 #ifdef STARTUP_NOTIFICATION
  343         // Startup notification option and exceptions (if any)
  344         FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
  345         FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
  346 #endif
  347 
  348         // Default programs
  349         FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
  350         FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
  351         FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
  352         FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
  353         FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
  354         FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
  355         FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
  356         FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
  357 
  358         FXString cmd, cmdname, filename, pathname, parentdir;
  359 
  360         // File name and path
  361         filename = list->getItemFilename(item);
  362         pathname = list->getItemFullPathname(item);
  363 
  364         // If directory, open the directory
  365         if (list->isItemDirectory(item))
  366         {
  367             // Does not have access
  368             if (!::isReadExecutable(pathname))
  369             {
  370                 MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), pathname.text());
  371                 getApp()->endWaitCursor();
  372                 return(0);
  373             }
  374 
  375             // Change directory in Xfe
  376             ((XFileExplorer*)mainWindow)->setDirectory(pathname);
  377 
  378             // Raise the Xfe window
  379             ((XFileExplorer*)mainWindow)->raise();
  380             ((XFileExplorer*)mainWindow)->setFocus();
  381 
  382             // Warning message when setting current folder in Xfe
  383             FXbool warn = getApp()->reg().readUnsignedEntry("OPTIONS", "folder_warn", true);
  384             if (warn)
  385             {
  386                 MessageBox::information(((XFileExplorer*)mainWindow), BOX_OK, _("Information"), _("Current folder has been set to '%s'"), pathname.text());
  387             }
  388         }
  389         else if (list->isItemFile(item))
  390         {
  391             // Parent directory
  392             parentdir = FXPath::directory(pathname);
  393 
  394             // Update associations dictionary
  395             FileDict*  assocdict = new FileDict(getApp());
  396             FileAssoc* association = assocdict->findFileBinding(pathname.text());
  397 
  398             // If there is an association
  399             if (association)
  400             {
  401                 // Use it to open the file
  402                 if (association->command.section(',', 0) != "")
  403                 {
  404                     cmdname = association->command.section(',', 0);
  405 
  406                     // Use a default program if possible
  407                     switch (progs[cmdname])
  408                     {
  409                     case TXTVIEWER:
  410                         cmdname = txtviewer;
  411                         break;
  412 
  413                     case TXTEDITOR:
  414                         cmdname = txteditor;
  415                         break;
  416 
  417                     case IMGVIEWER:
  418                         cmdname = imgviewer;
  419                         break;
  420 
  421                     case IMGEDITOR:
  422                         cmdname = imgeditor;
  423                         break;
  424 
  425                     case PDFVIEWER:
  426                         cmdname = pdfviewer;
  427                         break;
  428 
  429                     case AUDIOPLAYER:
  430                         cmdname = audioplayer;
  431                         break;
  432 
  433                     case VIDEOPLAYER:
  434                         cmdname = videoplayer;
  435                         break;
  436 
  437                     case ARCHIVER:
  438                         cmdname = archiver;
  439                         break;
  440 
  441                     case NONE: // No program found
  442                         ;
  443                         break;
  444                     }
  445 
  446                     // If command exists, run it
  447                     if (::existCommand(cmdname))
  448                     {
  449                         cmd = cmdname+" "+::quote(pathname);
  450 #ifdef STARTUP_NOTIFICATION
  451                         runcmd(cmd, cmdname, parentdir, searchdir, usesn, snexcepts);
  452 #else
  453                         runcmd(cmd, parentdir, searchdir);
  454 #endif
  455                     }
  456 
  457                     // If command does not exist, call the "Open with..." dialog
  458                     else
  459                     {
  460                         getApp()->endWaitCursor();
  461                         this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  462                     }
  463                 }
  464 
  465                 // Or execute the file
  466                 else if (list->isItemExecutable(item))
  467                 {
  468                     execFile(pathname);
  469                 }
  470 
  471                 // Or call the "Open with..." dialog
  472                 else
  473                 {
  474                     getApp()->endWaitCursor();
  475                     this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  476                 }
  477             }
  478 
  479             // If no association but executable
  480             else if (list->isItemExecutable(item))
  481             {
  482                 execFile(pathname);
  483             }
  484 
  485             // Other cases
  486             else
  487             {
  488                 getApp()->endWaitCursor();
  489                 this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  490             }
  491         }
  492     }
  493 
  494     getApp()->endWaitCursor();
  495 
  496     return(1);
  497 }
  498 
  499 
  500 // Execute file with an optional confirm dialog
  501 void SearchPanel::execFile(FXString pathname)
  502 {
  503     int      ret;
  504     FXString cmd, cmdname, parentdir;
  505 
  506 #ifdef STARTUP_NOTIFICATION
  507     // Startup notification option and exceptions (if any)
  508     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
  509     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
  510 #endif
  511 
  512     // Parent directory
  513     parentdir = FXPath::directory(pathname);
  514 
  515     // File is executable, but is it a text file?
  516     FXString str = mimetype(pathname);
  517     FXbool   isTextFile = true;
  518     if (strstr(str.text(), "charset=binary"))
  519     {
  520         isTextFile = false;
  521     }
  522 
  523     // With confirmation dialog
  524     FXbool confirm_execute = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_execute", true);
  525     if (isTextFile && (confirm_execute == 1))
  526     {
  527         FXString msg;
  528         msg.format(_("File %s is an executable text file, what do you want to do?"), pathname.text());
  529         ExecuteBox* dlg = new ExecuteBox(this, _("Confirm Execute"), msg);
  530         FXuint      answer = dlg->execute(PLACEMENT_CURSOR);
  531         delete dlg;
  532 
  533         // Execute
  534         if (answer == EXECBOX_CLICKED_EXECUTE)
  535         {
  536             cmdname = FXPath::name(pathname);
  537             cmd = ::quote(pathname);
  538 #ifdef STARTUP_NOTIFICATION
  539             runcmd(cmd, cmdname, parentdir, searchdir, usesn, snexcepts);
  540 #else
  541             runcmd(cmd, parentdir, searchdir);
  542 #endif
  543         }
  544 
  545         // Execute in console mode
  546         if (answer == EXECBOX_CLICKED_CONSOLE)
  547         {
  548             ret = chdir(parentdir.text());
  549             if (ret < 0)
  550             {
  551                 int errcode = errno;
  552                 if (errcode)
  553                 {
  554                     MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), parentdir.text(), strerror(errcode));
  555                 }
  556                 else
  557                 {
  558                     MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), parentdir.text());
  559                 }
  560             }
  561 
  562             cmdname = FXPath::name(pathname);
  563             cmd = ::quote(pathname);
  564 
  565             // Make and show command window
  566             // The CommandWindow object will delete itself when closed!
  567             CommandWindow* cmdwin = new CommandWindow(getApp(), _("Command log"), cmd, 30, 80);
  568             cmdwin->create();
  569             cmdwin->setIcon(runicon);
  570         }
  571 
  572         // Edit
  573         if (answer == EXECBOX_CLICKED_EDIT)
  574         {
  575             FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
  576             cmd = txteditor;
  577             cmdname = cmd;
  578 
  579             // If command exists, run it
  580             if (::existCommand(cmdname))
  581             {
  582                 cmd = cmdname+" "+::quote(pathname);
  583 #ifdef STARTUP_NOTIFICATION
  584                 runcmd(cmd, cmdname, parentdir, searchdir, usesn, snexcepts);
  585 #else
  586                 runcmd(cmd, parentdir, searchdir);
  587 #endif
  588             }
  589 
  590             // If command does not exist, call the "Open with..." dialog
  591             else
  592             {
  593                 this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  594             }
  595         }
  596     }
  597 
  598     // Without confirmation dialog
  599     else
  600     {
  601         cmdname = FXPath::name(pathname);
  602         cmd = ::quote(pathname);
  603 #ifdef STARTUP_NOTIFICATION
  604         runcmd(cmd, cmdname, parentdir, searchdir, usesn, snexcepts);
  605 #else
  606         runcmd(cmd, parentdir, searchdir);
  607 #endif
  608     }
  609 }
  610 
  611 
  612 // Single click on File Item
  613 long SearchPanel::onCmdItemClicked(FXObject* sender, FXSelector sel, void* ptr)
  614 {
  615     if (single_click != SINGLE_CLICK_NONE)
  616     {
  617         // Default programs
  618         FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
  619         FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
  620         FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
  621         FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
  622         FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
  623         FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
  624         FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
  625         FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
  626 
  627         // In detailed mode, avoid single click when cursor is not over the first column
  628         int    x, y;
  629         FXuint state;
  630         getCursorPosition(x, y, state);
  631         FXbool allow = true;
  632         if (!(list->getListStyle()&(_ICONLIST_BIG_ICONS|_ICONLIST_MINI_ICONS)) && ((x-list->getXPosition()) > list->getHeaderSize(0)))
  633         {
  634             allow = false;
  635         }
  636 
  637         // Single click with control or shift
  638         if (state&(CONTROLMASK|SHIFTMASK))
  639         {
  640             return(1);
  641         }
  642 
  643         // Single click without control or shift
  644         else
  645         {
  646             FXString cmd, cmdname, filename, pathname, parentdir;
  647 
  648             // Wait cursor
  649             getApp()->beginWaitCursor();
  650 
  651 #ifdef STARTUP_NOTIFICATION
  652             // Startup notification option and exceptions (if any)
  653             FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
  654             FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
  655 #endif
  656 
  657             FXlong item = (FXlong)ptr;
  658             if (item > -1)
  659             {
  660                 // File name and path
  661                 filename = list->getItemFilename(item);
  662                 pathname = list->getItemFullPathname(item);
  663 
  664                 // If directory, open the directory
  665                 if ((single_click != SINGLE_CLICK_NONE) && list->isItemDirectory(item) && allow)
  666                 {
  667                     // Does not have access
  668                     if (!::isReadExecutable(pathname))
  669                     {
  670                         MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), pathname.text());
  671                         getApp()->endWaitCursor();
  672                         return(0);
  673                     }
  674 
  675                     // Change directory in Xfe
  676                     ((XFileExplorer*)mainWindow)->setDirectory(pathname);
  677 
  678                     // Raise the Xfe window
  679                     ((XFileExplorer*)mainWindow)->raise();
  680                     ((XFileExplorer*)mainWindow)->setFocus();
  681 
  682                     // Warning message when setting current folder in Xfe
  683                     FXbool warn = getApp()->reg().readUnsignedEntry("OPTIONS", "folder_warn", true);
  684                     if (warn)
  685                     {
  686                         MessageBox::information(((XFileExplorer*)mainWindow), BOX_OK, _("Information"), _("Current folder has been set to '%s'"), pathname.text());
  687                     }
  688                 }
  689 
  690                 // If file, use the association if any
  691                 else if ((single_click == SINGLE_CLICK_DIR_FILE) && list->isItemFile(item) && allow)
  692                 {
  693                     // Parent directory
  694                     parentdir = FXPath::directory(pathname);
  695 
  696                     // Update associations dictionary
  697                     FileDict*  assocdict = new FileDict(getApp());
  698                     FileAssoc* association = assocdict->findFileBinding(pathname.text());
  699 
  700                     // If there is an association
  701                     if (association)
  702                     {
  703                         // Use it to open the file
  704                         if (association->command.section(',', 0) != "")
  705                         {
  706                             cmdname = association->command.section(',', 0);
  707 
  708                             // Use a default program if possible
  709                             switch (progs[cmdname])
  710                             {
  711                             case TXTVIEWER:
  712                                 cmdname = txtviewer;
  713                                 break;
  714 
  715                             case TXTEDITOR:
  716                                 cmdname = txteditor;
  717                                 break;
  718 
  719                             case IMGVIEWER:
  720                                 cmdname = imgviewer;
  721                                 break;
  722 
  723                             case IMGEDITOR:
  724                                 cmdname = imgeditor;
  725                                 break;
  726 
  727                             case PDFVIEWER:
  728                                 cmdname = pdfviewer;
  729                                 break;
  730 
  731                             case AUDIOPLAYER:
  732                                 cmdname = audioplayer;
  733                                 break;
  734 
  735                             case VIDEOPLAYER:
  736                                 cmdname = videoplayer;
  737                                 break;
  738 
  739                             case ARCHIVER:
  740                                 cmdname = archiver;
  741                                 break;
  742 
  743                             case NONE: // No program found
  744                                 ;
  745                                 break;
  746                             }
  747 
  748                             // If command exists, run it
  749                             if (::existCommand(cmdname))
  750                             {
  751                                 cmd = cmdname+" "+::quote(pathname);
  752 #ifdef STARTUP_NOTIFICATION
  753                                 runcmd(cmd, cmdname, parentdir, searchdir, usesn, snexcepts);
  754 #else
  755                                 runcmd(cmd, parentdir, searchdir);
  756 #endif
  757                             }
  758 
  759                             // If command does not exist, call the "Open with..." dialog
  760                             else
  761                             {
  762                                 getApp()->endWaitCursor();
  763                                 this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  764                             }
  765                         }
  766 
  767                         // Or execute the file
  768                         else if (list->isItemExecutable(item))
  769                         {
  770                             execFile(pathname);
  771                         }
  772 
  773                         // Or call the "Open with..." dialog
  774                         else
  775                         {
  776                             getApp()->endWaitCursor();
  777                             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  778                         }
  779                     }
  780 
  781                     // If no association but executable
  782                     else if (list->isItemExecutable(item))
  783                     {
  784                         execFile(pathname);
  785                     }
  786 
  787                     // Other cases
  788                     else
  789                     {
  790                         getApp()->endWaitCursor();
  791                         this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  792                     }
  793                 }
  794             }
  795             getApp()->endWaitCursor();
  796         }
  797     }
  798     return(1);
  799 }
  800 
  801 
  802 // Open with
  803 long SearchPanel::onCmdOpenWith(FXObject*, FXSelector, void*)
  804 {
  805     char** str = NULL;
  806 
  807     if (list->getNumSelectedItems() == 0)
  808     {
  809         return(0);
  810     }
  811 
  812     FXString cmd = "", cmdname;
  813     if (opendialog == NULL)
  814     {
  815         opendialog = new HistInputDialog(this, "", _("Open selected file(s) with:"), _("Open With"), "", bigfileopenicon, HIST_INPUT_EXECUTABLE_FILE, true, _("A&ssociate"));
  816     }
  817     opendialog->setText(cmd);
  818 
  819     // Dialog with history list and associate checkbox
  820     opendialog->CursorEnd();
  821     opendialog->selectAll();
  822     opendialog->clearItems();
  823     for (int i = 0; i < OpenNum; i++)
  824     {
  825         opendialog->appendItem(OpenHistory[i]);
  826     }
  827     opendialog->sortItems();
  828     opendialog->setDirectory(ROOTDIR);
  829     if (opendialog->execute())
  830     {
  831         cmd = opendialog->getText();
  832         if (cmd == "")
  833         {
  834             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
  835             return(0);
  836         }
  837 
  838         for (int u = 0; u < list->getNumItems(); u++)
  839         {
  840             if (list->isItemSelected(u))
  841             {
  842                 // Handles "associate" checkbox for "open with..." dialog
  843                 if (opendialog->getOption())
  844                 {
  845                     FXString filename = list->getItemFilename(u);
  846                     FXString ext = filename.rafter('.', 2).lower();
  847 
  848                     if ((ext == "tar.gz") || (ext == "tar.bz2") || (ext == "tar.xz") || (ext == "tar.z")) // Special cases
  849                     {
  850                     }
  851                     else
  852                     {
  853                         ext = FXPath::extension(filename).lower();
  854                     }
  855 
  856                     if (ext == "")
  857                     {
  858                         ext = FXPath::name(filename);
  859                     }
  860 
  861                     FileAssoc* association = list->getItemAssoc(u);
  862 
  863                     if (association)
  864                     {
  865                         // Update existing association
  866                         FXString oldfileassoc = getApp()->reg().readStringEntry("FILETYPES", ext.text(), "");
  867                         oldfileassoc.erase(0, oldfileassoc.section(';', 0).section(',', 0).length());
  868                         oldfileassoc.prepend(opendialog->getText());
  869                         getApp()->reg().writeStringEntry("FILETYPES", ext.text(), oldfileassoc.text());
  870 
  871                         // Handle file association
  872                         str = new char* [2];
  873                         str[0] = new char[strlen(ext.text())+1];
  874                         str[1] = new char[strlen(oldfileassoc.text())+1];
  875                         strlcpy(str[0], ext.text(), ext.length()+1);
  876                         strlcpy(str[1], oldfileassoc.text(), oldfileassoc.length()+1);
  877                         mainWindow->handle(this, FXSEL(SEL_COMMAND, XFileExplorer::ID_FILE_ASSOC), str);
  878                     }
  879                     else
  880                     {
  881                         // New association
  882                         FXString newcmd = opendialog->getText().append(";Document;;;;");
  883                         getApp()->reg().writeStringEntry("FILETYPES", ext.text(), newcmd.text());
  884 
  885                         // Handle file association
  886                         str = new char* [2];
  887                         str[0] = new char[strlen(ext.text())+1];
  888                         str[1] = new char[strlen(newcmd.text())+1];
  889                         strlcpy(str[0], ext.text(), ext.length()+1);
  890                         strlcpy(str[1], newcmd.text(), newcmd.length()+1);
  891                         mainWindow->handle(this, FXSEL(SEL_COMMAND, XFileExplorer::ID_FILE_ASSOC), str);
  892                     }
  893                 }
  894                 // End
  895 
  896                 FXString pathname = list->getItemFullPathname(u);
  897                 cmdname = cmd;
  898                 cmd += " ";
  899                 cmd = cmd+::quote(pathname);
  900             }
  901         }
  902 
  903         // Run command if it exists
  904         getApp()->beginWaitCursor();
  905 
  906 #ifdef STARTUP_NOTIFICATION
  907         // Startup notification option and exceptions (if any)
  908         FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
  909         FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
  910 #endif
  911 
  912         // If command exists, run it
  913         if (::existCommand(cmdname))
  914 #ifdef STARTUP_NOTIFICATION
  915         {
  916             runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
  917         }
  918 #else
  919         {
  920             runcmd(cmd, searchdir, searchdir);
  921         }
  922 #endif
  923         // If command does not exist, call the "Open with..." dialog
  924         else
  925         {
  926             getApp()->endWaitCursor();
  927             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
  928             return(1);
  929         }
  930 
  931         // Update history list
  932         OpenNum = opendialog->getHistorySize();
  933         cmd = opendialog->getText();
  934 
  935         // Check if cmd is a new string, i.e. is not already in history
  936         FXbool newstr = true;
  937         for (int i = 0; i < OpenNum-1; i++)
  938         {
  939             if (streq(OpenHistory[i], cmd.text()))
  940             {
  941                 newstr = false;
  942                 break;
  943             }
  944         }
  945 
  946         // Restore original history order
  947         opendialog->clearItems();
  948         for (int i = 0; i < OpenNum; i++)
  949         {
  950             opendialog->appendItem(OpenHistory[i]);
  951         }
  952 
  953         // History limit reached
  954         if (OpenNum > OPEN_HIST_SIZE)
  955         {
  956             OpenNum--;
  957         }
  958 
  959         // New string
  960         if (newstr)
  961         {
  962             // FIFO
  963             strlcpy(OpenHistory[0], cmd.text(), cmd.length()+1);
  964             for (int i = 1; i < OpenNum; i++)
  965             {
  966                 strlcpy(OpenHistory[i], opendialog->getHistoryItem(i-1).text(), opendialog->getHistoryItem(i-1).length()+1);
  967             }
  968         }
  969 
  970         getApp()->endWaitCursor();
  971     }
  972 
  973     return(1);
  974 }
  975 
  976 
  977 // Open single or multiple files
  978 long SearchPanel::onCmdOpen(FXObject*, FXSelector, void*)
  979 {
  980     // Wait cursor
  981     getApp()->beginWaitCursor();
  982 
  983     FXString   pathname, samecmd, cmd, cmdname, itemslist = " ";
  984     FileAssoc* association;
  985     FXbool     same = true;
  986     FXbool     first = true;
  987 
  988     if (list->getNumSelectedItems() == 0)
  989     {
  990         getApp()->endWaitCursor();
  991         return(0);
  992     }
  993 
  994     // Default programs
  995     FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
  996     FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
  997     FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
  998     FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
  999     FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
 1000     FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
 1001     FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
 1002     FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
 1003 
 1004     // Update associations dictionary
 1005     FileDict* assocdict = new FileDict(getApp());
 1006 
 1007     // Check if all files have the same association
 1008     for (int u = 0; u < list->getNumItems(); u++)
 1009     {
 1010         if (list->isItemSelected(u))
 1011         {
 1012             // Increment number of selected items
 1013             pathname = list->getItemFullPathname(u);
 1014             association = assocdict->findFileBinding(pathname.text());
 1015 
 1016             if (association)
 1017             {
 1018                 cmd = association->command.section(',', 0);
 1019 
 1020                 // Use a default program if possible
 1021                 switch (progs[cmd])
 1022                 {
 1023                 case TXTVIEWER:
 1024                     cmd = txtviewer;
 1025                     break;
 1026 
 1027                 case TXTEDITOR:
 1028                     cmd = txteditor;
 1029                     break;
 1030 
 1031                 case IMGVIEWER:
 1032                     cmd = imgviewer;
 1033                     break;
 1034 
 1035                 case IMGEDITOR:
 1036                     cmd = imgeditor;
 1037                     break;
 1038 
 1039                 case PDFVIEWER:
 1040                     cmd = pdfviewer;
 1041                     break;
 1042 
 1043                 case AUDIOPLAYER:
 1044                     cmd = audioplayer;
 1045                     break;
 1046 
 1047                 case VIDEOPLAYER:
 1048                     cmd = videoplayer;
 1049                     break;
 1050 
 1051                 case ARCHIVER:
 1052                     cmd = archiver;
 1053                     break;
 1054 
 1055                 case NONE: // No program found
 1056                     ;
 1057                     break;
 1058                 }
 1059 
 1060                 if (cmd != "")
 1061                 {
 1062                     // First selected item
 1063                     if (first)
 1064                     {
 1065                         samecmd = cmd;
 1066                         first = false;
 1067                     }
 1068 
 1069                     if (samecmd != cmd)
 1070                     {
 1071                         same = false;
 1072                         break;
 1073                     }
 1074 
 1075                     // List of selected items
 1076                     itemslist += ::quote(pathname) + " ";
 1077                 }
 1078                 else
 1079                 {
 1080                     same = false;
 1081                     break;
 1082                 }
 1083             }
 1084             else
 1085             {
 1086                 same = false;
 1087                 break;
 1088             }
 1089         }
 1090     }
 1091 
 1092 #ifdef STARTUP_NOTIFICATION
 1093     // Startup notification option and exceptions (if any)
 1094     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
 1095     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
 1096 #endif
 1097 
 1098     // Same command for all files : open them
 1099     if (same)
 1100     {
 1101         cmdname = samecmd;
 1102 
 1103         // If command exists, run it
 1104         if (::existCommand(cmdname))
 1105         {
 1106             cmd = samecmd+itemslist;
 1107 #ifdef STARTUP_NOTIFICATION
 1108             runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1109 #else
 1110             runcmd(cmd, searchdir, searchdir);
 1111 #endif
 1112         }
 1113 
 1114         // If command does not exist, call the "Open with..." dialog
 1115         else
 1116         {
 1117             getApp()->endWaitCursor();
 1118             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1119         }
 1120     }
 1121 
 1122     // Files have different commands : handle them separately
 1123     else
 1124     {
 1125         for (int u = 0; u < list->getNumItems(); u++)
 1126         {
 1127             if (list->isItemSelected(u))
 1128             {
 1129                 pathname = list->getItemFullPathname(u);
 1130                 association = assocdict->findFileBinding(pathname.text());
 1131 
 1132                 // If there is an association
 1133                 if (association)
 1134                 {
 1135                     // Use it to open the file
 1136                     cmd = association->command.section(',', 0);
 1137 
 1138                     // Use a default program if possible
 1139                     switch (progs[cmd])
 1140                     {
 1141                     case TXTVIEWER:
 1142                         cmd = txtviewer;
 1143                         break;
 1144 
 1145                     case TXTEDITOR:
 1146                         cmd = txteditor;
 1147                         break;
 1148 
 1149                     case IMGVIEWER:
 1150                         cmd = imgviewer;
 1151                         break;
 1152 
 1153                     case IMGEDITOR:
 1154                         cmd = imgeditor;
 1155                         break;
 1156 
 1157                     case PDFVIEWER:
 1158                         cmd = pdfviewer;
 1159                         break;
 1160 
 1161                     case AUDIOPLAYER:
 1162                         cmd = audioplayer;
 1163                         break;
 1164 
 1165                     case VIDEOPLAYER:
 1166                         cmd = videoplayer;
 1167                         break;
 1168 
 1169                     case ARCHIVER:
 1170                         cmd = archiver;
 1171                         break;
 1172 
 1173                     case NONE: // No program found
 1174                         ;
 1175                         break;
 1176                     }
 1177 
 1178                     if (cmd != "")
 1179                     {
 1180                         cmdname = cmd;
 1181 
 1182                         // If command exists, run it
 1183                         if (::existCommand(cmdname))
 1184                         {
 1185                             cmd = cmdname+" "+::quote(pathname);
 1186 #ifdef STARTUP_NOTIFICATION
 1187                             runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1188 #else
 1189                             runcmd(cmd, searchdir, searchdir);
 1190 #endif
 1191                         }
 1192 
 1193                         // If command does not exist, call the "Open with..." dialog
 1194                         else
 1195                         {
 1196                             getApp()->endWaitCursor();
 1197                             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1198                         }
 1199                     }
 1200 
 1201                     // or execute the file
 1202                     else if (list->isItemExecutable(u))
 1203                     {
 1204                         execFile(pathname);
 1205                     }
 1206 
 1207                     // or call the "Open with..." dialog
 1208                     else
 1209                     {
 1210                         getApp()->endWaitCursor();
 1211                         this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1212                     }
 1213                 }
 1214 
 1215                 // If no association but executable
 1216                 else if (list->isItemExecutable(u))
 1217                 {
 1218                     execFile(pathname);
 1219                 }
 1220 
 1221                 // Other cases
 1222                 else
 1223                 {
 1224                     getApp()->endWaitCursor();
 1225                     this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1226                 }
 1227             }
 1228         }
 1229     }
 1230 
 1231     getApp()->endWaitCursor();
 1232 
 1233     return(1);
 1234 }
 1235 
 1236 
 1237 // View/Edit files
 1238 long SearchPanel::onCmdEdit(FXObject*, FXSelector s, void*)
 1239 {
 1240     // Wait cursor
 1241     getApp()->beginWaitCursor();
 1242 
 1243     FXString   pathname, samecmd, cmd, cmdname, itemslist = " ";
 1244     FileAssoc* association;
 1245     FXbool     same = true;
 1246     FXbool     first = true;
 1247 
 1248     if (list->getNumSelectedItems() == 0)
 1249     {
 1250         getApp()->endWaitCursor();
 1251         return(0);
 1252     }
 1253 
 1254     FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
 1255     FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
 1256     FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
 1257     FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
 1258     FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
 1259     FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
 1260     FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
 1261     FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
 1262 
 1263     // Update associations dictionary
 1264     FileDict* assocdict = new FileDict(getApp());
 1265 
 1266     // Check if all files have the same association
 1267     for (int u = 0; u < list->getNumItems(); u++)
 1268     {
 1269         if (list->isItemSelected(u))
 1270         {
 1271             // Increment number of selected items
 1272             pathname = list->getItemFullPathname(u);
 1273             association = assocdict->findFileBinding(pathname.text());
 1274 
 1275             // If there is an association
 1276             if (association)
 1277             {
 1278                 // Use it to edit/view the files
 1279                 if (FXSELID(s) == ID_EDIT) // Edit
 1280                 {
 1281                     cmd = association->command.section(',', 2);
 1282 
 1283                     // Use a default editor if possible
 1284                     switch (progs[cmd])
 1285                     {
 1286                     case TXTEDITOR:
 1287                         cmd = txteditor;
 1288                         break;
 1289 
 1290                     case IMGEDITOR:
 1291                         cmd = imgeditor;
 1292                         break;
 1293 
 1294                     case ARCHIVER:
 1295                         cmd = archiver;
 1296                         break;
 1297 
 1298                     case NONE: // No default editor found
 1299                         ;
 1300                         break;
 1301                     }
 1302 
 1303                     if (cmd.length() == 0)
 1304                     {
 1305                         cmd = txteditor;
 1306                     }
 1307                 }
 1308                 else // Any other is View
 1309                 {
 1310                     cmd = association->command.section(',', 1);
 1311 
 1312                     // Use a default viewer if possible
 1313                     switch (progs[cmd])
 1314                     {
 1315                     case TXTVIEWER:
 1316                         cmd = txtviewer;
 1317                         break;
 1318 
 1319                     case IMGVIEWER:
 1320                         cmd = imgviewer;
 1321                         break;
 1322 
 1323                     case PDFVIEWER:
 1324                         cmd = pdfviewer;
 1325                         break;
 1326 
 1327                     case AUDIOPLAYER:
 1328                         cmd = audioplayer;
 1329                         break;
 1330 
 1331                     case VIDEOPLAYER:
 1332                         cmd = videoplayer;
 1333                         break;
 1334 
 1335                     case ARCHIVER:
 1336                         cmd = archiver;
 1337                         break;
 1338 
 1339                     case NONE: // No default viewer found
 1340                         ;
 1341                         break;
 1342                     }
 1343 
 1344                     if (cmd.length() == 0)
 1345                     {
 1346                         cmd = txtviewer;
 1347                     }
 1348                 }
 1349                 if (cmd.text() != NULL)
 1350                 {
 1351                     // First selected item
 1352                     if (first)
 1353                     {
 1354                         samecmd = cmd;
 1355                         first = false;
 1356                     }
 1357 
 1358                     if (samecmd != cmd)
 1359                     {
 1360                         same = false;
 1361                         break;
 1362                     }
 1363 
 1364                     // List of selected items
 1365                     itemslist += ::quote(pathname) + " ";
 1366                 }
 1367                 else
 1368                 {
 1369                     same = false;
 1370                     break;
 1371                 }
 1372             }
 1373 
 1374             // No association
 1375             else
 1376             {
 1377                 same = false;
 1378                 break;
 1379             }
 1380         }
 1381     }
 1382 
 1383 #ifdef STARTUP_NOTIFICATION
 1384     // Startup notification option and exceptions (if any)
 1385     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
 1386     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
 1387 #endif
 1388 
 1389     // Same association for all files : execute the associated or default editor or viewer
 1390     if (same)
 1391     {
 1392         cmdname = samecmd;
 1393 
 1394         // If command exists, run it
 1395         if (::existCommand(cmdname))
 1396         {
 1397             cmd = cmdname+itemslist;
 1398 #ifdef STARTUP_NOTIFICATION
 1399             runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1400 #else
 1401             runcmd(cmd, searchdir, searchdir);
 1402 #endif
 1403         }
 1404 
 1405         // If command does not exist, call the "Open with..." dialog
 1406         else
 1407         {
 1408             getApp()->endWaitCursor();
 1409             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1410         }
 1411     }
 1412 
 1413     // Files have different associations : handle them separately
 1414     else
 1415     {
 1416         for (int u = 0; u < list->getNumItems(); u++)
 1417         {
 1418             if (list->isItemSelected(u))
 1419             {
 1420                 pathname = list->getItemFullPathname(u);
 1421 
 1422                 // Only View / Edit regular files (not directories)
 1423                 if (::isFile(pathname))
 1424                 {
 1425                     association = assocdict->findFileBinding(pathname.text());
 1426 
 1427                     // If there is an association
 1428                     if (association)
 1429                     {
 1430                         // Use it to edit/view the file
 1431                         if (FXSELID(s) == ID_EDIT) // Edit
 1432                         {
 1433                             cmd = association->command.section(',', 2);
 1434 
 1435                             // Use a default editor if possible
 1436                             switch (progs[cmd])
 1437                             {
 1438                             case TXTEDITOR:
 1439                                 cmd = txteditor;
 1440                                 break;
 1441 
 1442                             case IMGEDITOR:
 1443                                 cmd = imgeditor;
 1444                                 break;
 1445 
 1446                             case ARCHIVER:
 1447                                 cmd = archiver;
 1448                                 break;
 1449                             }
 1450 
 1451                             if (cmd.length() == 0)
 1452                             {
 1453                                 cmd = txteditor;
 1454                             }
 1455                         }
 1456                         else // Any other is View
 1457                         {
 1458                             cmd = association->command.section(',', 1);
 1459 
 1460                             // Use a default viewer if possible
 1461                             switch (progs[cmd])
 1462                             {
 1463                             case TXTVIEWER:
 1464                                 cmd = txtviewer;
 1465                                 break;
 1466 
 1467                             case IMGVIEWER:
 1468                                 cmd = imgviewer;
 1469                                 break;
 1470 
 1471                             case PDFVIEWER:
 1472                                 cmd = pdfviewer;
 1473                                 break;
 1474 
 1475                             case AUDIOPLAYER:
 1476                                 cmd = audioplayer;
 1477                                 break;
 1478 
 1479                             case VIDEOPLAYER:
 1480                                 cmd = videoplayer;
 1481                                 break;
 1482 
 1483                             case ARCHIVER:
 1484                                 cmd = archiver;
 1485                                 break;
 1486 
 1487                             case NONE: // No default viewer found
 1488                                 ;
 1489                                 break;
 1490                             }
 1491 
 1492                             if (cmd.length() == 0)
 1493                             {
 1494                                 cmd = txtviewer;
 1495                             }
 1496                         }
 1497                         if (cmd.text() != NULL)
 1498                         {
 1499                             cmdname = cmd;
 1500 
 1501                             // If command exists, run it
 1502                             if (::existCommand(cmdname))
 1503                             {
 1504                                 cmd = cmdname+" "+::quote(pathname);
 1505 #ifdef STARTUP_NOTIFICATION
 1506                                 runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1507 #else
 1508                                 runcmd(cmd, searchdir, searchdir);
 1509 #endif
 1510                             }
 1511 
 1512                             // If command does not exist, call the "Open with..." dialog
 1513                             else
 1514                             {
 1515                                 getApp()->endWaitCursor();
 1516                                 this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1517                             }
 1518                         }
 1519                     }
 1520 
 1521                     // No association
 1522                     else
 1523                     {
 1524                         if (FXSELID(s) == ID_EDIT)
 1525                         {
 1526                             cmd = txteditor;
 1527                         }
 1528                         else
 1529                         {
 1530                             cmd = txtviewer;
 1531                         }
 1532 
 1533                         cmdname = cmd;
 1534 
 1535                         // If command exists, run it
 1536                         if (::existCommand(cmdname))
 1537                         {
 1538                             cmd = cmdname+" "+::quote(pathname);
 1539 #ifdef STARTUP_NOTIFICATION
 1540                             runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1541 #else
 1542                             runcmd(cmd, searchdir, searchdir);
 1543 #endif
 1544                         }
 1545 
 1546                         // If command does not exist, call the "Open with..." dialog
 1547                         else
 1548                         {
 1549                             getApp()->endWaitCursor();
 1550                             this->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
 1551                         }
 1552                     }
 1553                 }
 1554             }
 1555         }
 1556     }
 1557 
 1558     getApp()->endWaitCursor();
 1559 
 1560     return(1);
 1561 }
 1562 
 1563 
 1564 // Compare two files
 1565 long SearchPanel::onCmdCompare(FXObject*, FXSelector s, void*)
 1566 {
 1567     list->setFocus();
 1568     int num = list->getNumSelectedItems();
 1569 
 1570     // Only one or two selected items can be handled
 1571     if ((num != 1) && (num != 2))
 1572     {
 1573         getApp()->endWaitCursor();
 1574         return(0);
 1575     }
 1576 
 1577 #ifdef STARTUP_NOTIFICATION
 1578     // Startup notification option and exceptions (if any)
 1579     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
 1580     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
 1581 #endif
 1582 
 1583     FXString filecomparator = getApp()->reg().readStringEntry("PROGS", "filecomparator", DEFAULT_FILECOMPARATOR);
 1584     FXString pathname, cmd, cmdname, itemslist = " ";
 1585 
 1586     // One selected item
 1587     if (num == 1)
 1588     {
 1589         // Get the selected item
 1590         for (int u = 0; u < list->getNumItems(); u++)
 1591         {
 1592             if (list->isItemSelected(u))
 1593             {
 1594                 pathname = list->getItemFullPathname(u);
 1595                 itemslist += ::quote(pathname) + " ";
 1596             }
 1597         }
 1598 
 1599         // Open a dialog to select the other item to be compared
 1600         if (comparedialog == NULL)
 1601         {
 1602             comparedialog = new BrowseInputDialog(this, "", "", _("Compare"), _("With:"), bigcompareicon, BROWSE_INPUT_FILE);
 1603         }
 1604         comparedialog->setIcon(bigcompareicon);
 1605         comparedialog->setMessage(pathname);
 1606         comparedialog->setText("");
 1607         int rc = 1;
 1608         rc = comparedialog->execute(PLACEMENT_CURSOR);
 1609 
 1610         // Get item path and add it to the list
 1611         FXString str = comparedialog->getText();
 1612         itemslist += ::quote(str);
 1613         if (!rc || (str == ""))
 1614         {
 1615             return(0);
 1616         }
 1617     }
 1618 
 1619     // Two selected items
 1620     else if (num == 2)
 1621     {
 1622         // Get the two selected items
 1623         for (int u = 0; u < list->getNumItems(); u++)
 1624         {
 1625             if (list->isItemSelected(u))
 1626             {
 1627                 pathname = list->getItemFullPathname(u);
 1628                 itemslist += ::quote(pathname) + " ";
 1629             }
 1630         }
 1631     }
 1632 
 1633     // Wait cursor
 1634     getApp()->beginWaitCursor();
 1635 
 1636     // If command exists, run it
 1637     cmdname = filecomparator;
 1638     if (::existCommand(cmdname))
 1639     {
 1640         cmd = cmdname+itemslist;
 1641 #ifdef STARTUP_NOTIFICATION
 1642         runcmd(cmd, cmdname, searchdir, searchdir, usesn, snexcepts);
 1643 #else
 1644         runcmd(cmd, searchdir, searchdir);
 1645 #endif
 1646     }
 1647 
 1648     // If command does not exist, issue an error message
 1649     else
 1650     {
 1651         getApp()->endWaitCursor();
 1652         MessageBox::error(this, BOX_OK, _("Error"), _("Program %s not found. Please define a file comparator program in the Preferences dialog!"), cmdname.text());
 1653     }
 1654 
 1655     getApp()->endWaitCursor();
 1656 
 1657     return(1);
 1658 }
 1659 
 1660 
 1661 // Force panel refresh
 1662 long SearchPanel::onCmdRefresh(FXObject* sender, FXSelector, void*)
 1663 {
 1664     list->onCmdRefresh(0, 0, 0);
 1665 
 1666     return(1);
 1667 }
 1668 
 1669 
 1670 // Go to parent directory
 1671 long SearchPanel::onCmdGotoParentdir(FXObject*, FXSelector, void*)
 1672 {
 1673     if (list->getNumSelectedItems() != 1)
 1674     {
 1675         return(0);
 1676     }
 1677 
 1678     // Get selected item path name
 1679     FXString pathname;
 1680     for (int u = 0; u < list->getNumItems(); u++)
 1681     {
 1682         if (list->isItemSelected(u))
 1683         {
 1684             pathname = list->getItemFullPathname(u);
 1685             break;
 1686         }
 1687     }
 1688 
 1689     // Parent directory name
 1690     FXString parentdir = FXPath::directory(pathname);
 1691 
 1692     // Does not have access
 1693     if (!::isReadExecutable(parentdir))
 1694     {
 1695         MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), parentdir.text());
 1696         getApp()->endWaitCursor();
 1697         return(0);
 1698     }
 1699 
 1700     // Change directory in Xfe
 1701     ((XFileExplorer*)mainWindow)->setDirectory(parentdir);
 1702 
 1703     // Raise the Xfe window
 1704     ((XFileExplorer*)mainWindow)->raise();
 1705     ((XFileExplorer*)mainWindow)->setFocus();
 1706 
 1707     // Warning message when setting current folder in Xfe
 1708     FXbool warn = getApp()->reg().readUnsignedEntry("OPTIONS", "folder_warn", true);
 1709     if (warn)
 1710     {
 1711         MessageBox::information(((XFileExplorer*)mainWindow), BOX_OK, _("Information"), _("Current folder has been set to '%s'"), parentdir.text());
 1712     }
 1713 
 1714     return(1);
 1715 }
 1716 
 1717 
 1718 // File or directory properties
 1719 long SearchPanel::onCmdProperties(FXObject* sender, FXSelector, void*)
 1720 {
 1721     int num, itm;
 1722 
 1723     // There is one selected file in the file list
 1724     num = list->getNumSelectedItems(&itm);
 1725     if (num == 1)
 1726     {
 1727         FXString filename = list->getItemFilename(itm);
 1728         FXString pathname = FXPath::directory(list->getItemFullPathname(itm));
 1729 
 1730         PropertiesBox* attrdlg = new PropertiesBox(this, filename, pathname);
 1731         attrdlg->create();
 1732         attrdlg->show(PLACEMENT_OWNER);
 1733     }
 1734 
 1735     // There are multiple selected files in the file list
 1736     else if (num > 1)
 1737     {
 1738         FXString* files = new FXString[num];
 1739         FXString* paths = new FXString[num];
 1740 
 1741         int i = 0;
 1742         for (int u = 0; u < list->getNumItems(); u++)
 1743         {
 1744             if (list->isItemSelected(u))
 1745             {
 1746                 files[i] = list->getItemText(u).section('\t', 0);
 1747                 paths[i] = FXPath::directory(list->getItemFullPathname(u));
 1748                 i++;
 1749             }
 1750         }
 1751 
 1752         PropertiesBox* attrdlg = new PropertiesBox(this, files, num, paths);
 1753         attrdlg->create();
 1754         attrdlg->show(PLACEMENT_OWNER);
 1755     }
 1756     return(1);
 1757 }
 1758 
 1759 
 1760 // Handle item selection
 1761 long SearchPanel::onCmdSelect(FXObject* sender, FXSelector sel, void* ptr)
 1762 {
 1763     switch (FXSELID(sel))
 1764     {
 1765     case ID_SELECT_ALL:
 1766         list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SELECT_ALL), ptr);
 1767         return(1);
 1768 
 1769     case ID_DESELECT_ALL:
 1770         list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_DESELECT_ALL), ptr);
 1771         return(1);
 1772 
 1773     case ID_SELECT_INVERSE:
 1774         list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SELECT_INVERSE), ptr);
 1775         return(1);
 1776     }
 1777     return(1);
 1778 }
 1779 
 1780 
 1781 // Set search root path
 1782 void SearchPanel::setSearchPath(FXString path)
 1783 {
 1784     searchdir = path;
 1785     list->setDirectory(path, false);
 1786 }
 1787 
 1788 
 1789 // Append an item to the file list
 1790 // Note that thumbnails are not displayed here
 1791 long SearchPanel::appendItem(FXString& pathname)
 1792 {
 1793     FXString    filename, dirname;
 1794     FXString    grpid, usrid, atts, mod, ext, del;
 1795     FileAssoc*  fileassoc;
 1796     FXString    filetype, lowext;
 1797     FXIcon*     big, *mini;
 1798     time_t      filemtime, filectime;
 1799     struct stat info, linfo;
 1800     FXbool      isLink, isBrokenLink, isDir;
 1801 
 1802     // Only process valid file paths and paths different from the search directory
 1803     if (lstatrep(pathname.text(), &linfo) == 0)
 1804     {
 1805         filename = FXPath::name(pathname);
 1806         dirname = FXPath::directory(pathname);
 1807 
 1808         // Get file/link info and indicate if it is a link
 1809         isLink = S_ISLNK(linfo.st_mode);
 1810         isBrokenLink = false;
 1811 
 1812         // Find if it is a broken link
 1813         if (isLink && (statrep(pathname.text(), &info) != 0))
 1814         {
 1815             isBrokenLink = true;
 1816         }
 1817 
 1818         // File times
 1819         filemtime = linfo.st_mtime;
 1820         filectime = linfo.st_ctime;
 1821 
 1822         // Find if it is a folder
 1823         isDir = false;
 1824         if (S_ISDIR(linfo.st_mode))
 1825         {
 1826             isDir = true;
 1827         }
 1828 
 1829         // Extension
 1830         ext = FXPath::extension(pathname);
 1831 
 1832         // User name
 1833         usrid = FXSystem::userName(linfo.st_uid);
 1834 
 1835         // Group name
 1836         grpid = FXSystem::groupName(linfo.st_gid);
 1837 
 1838         // Permissions (caution : we don't use the FXSystem::modeString() function because
 1839         // it seems to be incompatible with the info.st_mode format)
 1840         atts = ::permissions(linfo.st_mode);
 1841 
 1842         // Mod time
 1843         mod = FXSystem::time("%x %X", linfo.st_mtime);
 1844         del = "";
 1845         ext = "";
 1846 
 1847         // Obtain the extension for files only
 1848         if (!isDir)
 1849         {
 1850             ext = FXPath::extension(pathname);
 1851         }
 1852 
 1853         // Obtain the stat info on the file itself
 1854         if (statrep(pathname.text(), &info) != 0)
 1855         {
 1856             // Except in the case of a broken link
 1857             if (isBrokenLink)
 1858             {
 1859                 lstatrep(pathname.text(), &info);
 1860             }
 1861             else
 1862             {
 1863                 goto end;
 1864             }
 1865         }
 1866 
 1867         // Assume no associations
 1868         fileassoc = NULL;
 1869 
 1870         // Determine icons and type
 1871         if (isDir)
 1872         {
 1873             if (!::isReadExecutable(pathname))
 1874             {
 1875                 big = bigfolderlockedicon;
 1876                 mini = minifolderlockedicon;
 1877                 filetype = _("Folder");
 1878             }
 1879             else
 1880             {
 1881                 big = bigfoldericon;
 1882                 mini = minifoldericon;
 1883                 filetype = _("Folder");
 1884             }
 1885         }
 1886         else if (S_ISCHR(info.st_mode))
 1887         {
 1888             big = bigchardevicon;
 1889             mini = minichardevicon;
 1890             filetype = _("Character Device");
 1891         }
 1892         else if (S_ISBLK(info.st_mode))
 1893         {
 1894             big = bigblockdevicon;
 1895             mini = miniblockdevicon;
 1896             filetype = _("Block Device");
 1897         }
 1898         else if (S_ISFIFO(info.st_mode))
 1899         {
 1900             big = bigpipeicon;
 1901             mini = minipipeicon;
 1902             filetype = _("Named Pipe");
 1903         }
 1904         else if (S_ISSOCK(info.st_mode))
 1905         {
 1906             big = bigsocketicon;
 1907             mini = minisocketicon;
 1908             filetype = _("Socket");
 1909         }
 1910         else if ((info.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH)) && !(S_ISDIR(info.st_mode) || S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode) || S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode)))
 1911         {
 1912             big = bigexecicon;
 1913             mini = miniexecicon;
 1914             filetype = _("Executable");
 1915             if (associations)
 1916             {
 1917                 fileassoc = associations->findFileBinding(pathname.text());
 1918             }
 1919         }
 1920         else
 1921         {
 1922             big = bigdocicon;
 1923             mini = minidocicon;
 1924             filetype = _("Document");
 1925             if (associations)
 1926             {
 1927                 fileassoc = associations->findFileBinding(pathname.text());
 1928             }
 1929         }
 1930 
 1931         // If association is found, use it
 1932         if (fileassoc)
 1933         {
 1934             filetype = fileassoc->extension.text();
 1935 
 1936             if (fileassoc->bigicon)
 1937             {
 1938                 big = fileassoc->bigicon;
 1939             }
 1940             if (fileassoc->miniicon)
 1941             {
 1942                 mini = fileassoc->miniicon;
 1943             }
 1944         }
 1945 
 1946         // Symbolic links have a specific type
 1947         if (isBrokenLink)
 1948         {
 1949             filetype = _("Broken link");
 1950         }
 1951 
 1952         else if (isLink)
 1953         {
 1954             if (associations)
 1955             {
 1956                 // Don't forget to remove trailing '/' here!
 1957                 fileassoc = associations->findFileBinding(::cleanPath(::readLink(pathname)).text());
 1958                 if (fileassoc)
 1959                 {
 1960                     filetype = _("Link to ")+fileassoc->extension;
 1961                 }
 1962                 else
 1963                 {
 1964                     filetype = _("Link to ")+filetype;
 1965                 }
 1966             }
 1967         }
 1968 
 1969         // Don't display the file size for directories
 1970         FXString hsize;
 1971         if (isDir)
 1972         {
 1973             hsize = "";
 1974         }
 1975         else
 1976         {
 1977             char size[64];
 1978 #if __WORDSIZE == 64
 1979             snprintf(size, sizeof(size)-1, "%lu", (FXulong)linfo.st_size);
 1980 #else
 1981             snprintf(size, sizeof(size)-1, "%llu", (FXulong)linfo.st_size);
 1982 #endif
 1983             hsize = ::hSize(size);
 1984         }
 1985 
 1986 #if defined(linux)
 1987         // Devices have a specific icon
 1988         if (fsdevices->find(pathname.text()))
 1989         {
 1990             filetype = _("Mount point");
 1991 
 1992             if (::streq(fsdevices->find(pathname.text()), "harddisk"))
 1993             {
 1994                 big = bigharddiskicon;
 1995                 mini = harddiskicon;
 1996             }
 1997             else if (::streq(fsdevices->find(pathname.text()), "nfsdisk"))
 1998             {
 1999                 big = bignfsdriveicon;
 2000                 mini = nfsdriveicon;
 2001             }
 2002             else if (::streq(fsdevices->find(pathname.text()), "smbdisk"))
 2003             {
 2004                 big = bignfsdriveicon;
 2005                 mini = nfsdriveicon;
 2006             }
 2007             else if (::streq(fsdevices->find(pathname.text()), "floppy"))
 2008             {
 2009                 big = bigfloppyicon;
 2010                 mini = floppyicon;
 2011             }
 2012             else if (::streq(fsdevices->find(pathname.text()), "cdrom"))
 2013             {
 2014                 big = bigcdromicon;
 2015                 mini = cdromicon;
 2016             }
 2017             else if (::streq(fsdevices->find(pathname.text()), "zip"))
 2018             {
 2019                 big = bigzipicon;
 2020                 mini = zipicon;
 2021             }
 2022         }
 2023 #endif
 2024 
 2025         // Symbolic links have a specific icon
 2026         if (isLink)
 2027         {
 2028             // Broken link
 2029             if (isBrokenLink)
 2030             {
 2031                 big = bigbrokenlinkicon;
 2032                 mini = minibrokenlinkicon;
 2033             }
 2034             else
 2035             {
 2036                 big = biglinkicon;
 2037                 mini = minilinkicon;
 2038             }
 2039         }
 2040 
 2041         // Add item to the file list
 2042         FXString str = filename + "\t" + dirname + "\t" + hsize + "\t" + filetype + "\t" + ext + "\t" + mod + "\t" + usrid +"\t" + grpid + "\t" + atts + "\t" + del + "\t" + pathname;
 2043 
 2044         // Append item to the list
 2045         list->appendItem(str, big, mini);
 2046 
 2047         // Get last item
 2048         int       count = list->getNumItems();
 2049         FileItem* item = (FileItem*)list->getItem(count-1);
 2050 
 2051         if (item == NULL)
 2052         {
 2053             fprintf(stderr, "%s::appendItem: NULL item specified.\n", getClassName());
 2054             exit(EXIT_FAILURE);
 2055         }
 2056 
 2057         // Set icons
 2058         item->setBigIcon(big, false);
 2059         item->setMiniIcon(mini, false);
 2060 
 2061         // Set item flags from the obtained info
 2062         if (S_ISDIR(info.st_mode))
 2063         {
 2064             item->state |= FileItem::FOLDER;
 2065         }
 2066         else
 2067         {
 2068             item->state &= ~FileItem::FOLDER;
 2069         }
 2070         if (S_ISLNK(info.st_mode))
 2071         {
 2072             item->state |= FileItem::SYMLINK;
 2073         }
 2074         else
 2075         {
 2076             item->state &= ~FileItem::SYMLINK;
 2077         }
 2078         if (S_ISCHR(info.st_mode))
 2079         {
 2080             item->state |= FileItem::CHARDEV;
 2081         }
 2082         else
 2083         {
 2084             item->state &= ~FileItem::CHARDEV;
 2085         }
 2086         if (S_ISBLK(info.st_mode))
 2087         {
 2088             item->state |= FileItem::BLOCKDEV;
 2089         }
 2090         else
 2091         {
 2092             item->state &= ~FileItem::BLOCKDEV;
 2093         }
 2094         if (S_ISFIFO(info.st_mode))
 2095         {
 2096             item->state |= FileItem::FIFO;
 2097         }
 2098         else
 2099         {
 2100             item->state &= ~FileItem::FIFO;
 2101         }
 2102         if (S_ISSOCK(info.st_mode))
 2103         {
 2104             item->state |= FileItem::SOCK;
 2105         }
 2106         else
 2107         {
 2108             item->state &= ~FileItem::SOCK;
 2109         }
 2110         if ((info.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH)) && !(S_ISDIR(info.st_mode) || S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode) || S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode)))
 2111         {
 2112             item->state |= FileItem::EXECUTABLE;
 2113         }
 2114         else
 2115         {
 2116             item->state &= ~FileItem::EXECUTABLE;
 2117         }
 2118 
 2119         // We can drag items
 2120         item->state |= FileItem::DRAGGABLE;
 2121 
 2122         // Set item attributes
 2123         item->size = (FXulong)linfo.st_size;
 2124         item->assoc = fileassoc;
 2125         item->date = filemtime;
 2126         item->cdate = filectime;
 2127 
 2128         // And finally, don't forget to create the appended item!
 2129         item->create();
 2130     }
 2131     else
 2132     {
 2133         return(0);
 2134     }
 2135 end:
 2136 
 2137     return(1);
 2138 }
 2139 
 2140 
 2141 // File list context menu
 2142 long SearchPanel::onCmdPopupMenu(FXObject* o, FXSelector s, void* p)
 2143 {
 2144     // No item in list
 2145     if (list->getNumItems() == 0)
 2146     {
 2147         return(0);
 2148     }
 2149 
 2150     list->setAllowRefresh(false);
 2151 
 2152     // Check if control key was pressed
 2153     ctrlflag = false;
 2154     shiftf10 = false;
 2155     if (p != NULL)
 2156     {
 2157         FXEvent* event = (FXEvent*)p;
 2158         if (event->state&CONTROLMASK)
 2159         {
 2160             ctrlflag = true;
 2161         }
 2162         if (event->state&SHIFTMASK && (event->code == KEY_F10))
 2163         {
 2164             shiftf10 = true;
 2165         }
 2166     }
 2167 
 2168     // Use to select the item under cursor when right clicking
 2169     // Only when Shift-F10 was not pressed
 2170     if (!shiftf10 && (list->getNumSelectedItems() <= 1))
 2171     {
 2172         int    x, y;
 2173         FXuint state;
 2174         list->getCursorPosition(x, y, state);
 2175 
 2176         int item = list->getItemAt(x, y);
 2177 
 2178         if (list->getCurrentItem() >= 0)
 2179         {
 2180             list->deselectItem(list->getCurrentItem());
 2181         }
 2182         if (item >= 0)
 2183         {
 2184             list->setCurrentItem(item);
 2185             list->selectItem(item);
 2186         }
 2187     }
 2188 
 2189     // If control flag is set, deselect all items
 2190     if (ctrlflag)
 2191     {
 2192         list->handle(o, FXSEL(SEL_COMMAND, FileList::ID_DESELECT_ALL), p);
 2193     }
 2194 
 2195     // Popup menu pane
 2196     FXMenuPane* menu = new FXMenuPane(this);
 2197     int         x, y;
 2198     FXuint      state;
 2199     getRoot()->getCursorPosition(x, y, state);
 2200 
 2201     int num, itm;
 2202     num = list->getNumSelectedItems(&itm);
 2203 
 2204     // No selection or control key was pressed
 2205     if ((num == 0) || ctrlflag)
 2206     {
 2207         // Reset the control flag
 2208         ctrlflag = false;
 2209 
 2210         new FXMenuCheck(menu, _("Thum&bnails"), list, FileList::ID_TOGGLE_THUMBNAILS);
 2211         new FXMenuSeparator(menu);
 2212         new FXMenuRadio(menu, _("B&ig icons"), list, IconList::ID_SHOW_BIG_ICONS);
 2213         new FXMenuRadio(menu, _("&Small icons"), list, IconList::ID_SHOW_MINI_ICONS);
 2214         new FXMenuRadio(menu, _("F&ull file list"), list, IconList::ID_SHOW_DETAILS);
 2215         new FXMenuSeparator(menu);
 2216         new FXMenuRadio(menu, _("&Rows"), list, FileList::ID_ARRANGE_BY_ROWS);
 2217         new FXMenuRadio(menu, _("&Columns"), list, FileList::ID_ARRANGE_BY_COLUMNS);
 2218         new FXMenuCheck(menu, _("Autosize"), list, FileList::ID_AUTOSIZE);
 2219         new FXMenuSeparator(menu);
 2220         new FXMenuRadio(menu, _("&Name"), list, FileList::ID_SORT_BY_NAME);
 2221         new FXMenuRadio(menu, _("Si&ze"), list, FileList::ID_SORT_BY_SIZE);
 2222         new FXMenuRadio(menu, _("&Type"), list, FileList::ID_SORT_BY_TYPE);
 2223         new FXMenuRadio(menu, _("E&xtension"), list, FileList::ID_SORT_BY_EXT);
 2224         new FXMenuRadio(menu, _("&Date"), list, FileList::ID_SORT_BY_TIME);
 2225         new FXMenuRadio(menu, _("&User"), list, FileList::ID_SORT_BY_USER);
 2226         new FXMenuRadio(menu, _("&Group"), list, FileList::ID_SORT_BY_GROUP);
 2227         new FXMenuRadio(menu, _("Per&missions"), list, FileList::ID_SORT_BY_PERM);
 2228         new FXMenuSeparator(menu);
 2229         new FXMenuCheck(menu, _("I&gnore case"), list, FileList::ID_SORT_CASE);
 2230         new FXMenuCheck(menu, _("Fold&ers first"), list, FileList::ID_DIRS_FIRST);
 2231         new FXMenuCheck(menu, _("Re&verse order"), list, FileList::ID_SORT_REVERSE);
 2232     }
 2233     // Non empty selection
 2234     else
 2235     {
 2236         // Submenu items
 2237         FXMenuPane* submenu = new FXMenuPane(this);
 2238         new FXMenuCheck(submenu, _("Thum&bnails"), list, FileList::ID_TOGGLE_THUMBNAILS);
 2239         new FXMenuSeparator(submenu);
 2240         new FXMenuRadio(submenu, _("B&ig icons"), list, IconList::ID_SHOW_BIG_ICONS);
 2241         new FXMenuRadio(submenu, _("&Small icons"), list, IconList::ID_SHOW_MINI_ICONS);
 2242         new FXMenuRadio(submenu, _("&Full file list"), list, IconList::ID_SHOW_DETAILS);
 2243         new FXMenuSeparator(submenu);
 2244         new FXMenuRadio(submenu, _("&Rows"), list, FileList::ID_ARRANGE_BY_ROWS);
 2245         new FXMenuRadio(submenu, _("&Columns"), list, FileList::ID_ARRANGE_BY_COLUMNS);
 2246         new FXMenuCheck(submenu, _("&Autosize"), list, FileList::ID_AUTOSIZE);
 2247         new FXMenuSeparator(submenu);
 2248         new FXMenuRadio(submenu, _("&Name"), list, FileList::ID_SORT_BY_NAME);
 2249         new FXMenuRadio(submenu, _("Si&ze"), list, FileList::ID_SORT_BY_SIZE);
 2250         new FXMenuRadio(submenu, _("&Type"), list, FileList::ID_SORT_BY_TYPE);
 2251         new FXMenuRadio(submenu, _("E&xtension"), list, FileList::ID_SORT_BY_EXT);
 2252         new FXMenuRadio(submenu, _("&Date"), list, FileList::ID_SORT_BY_TIME);
 2253         new FXMenuRadio(submenu, _("&User"), list, FileList::ID_SORT_BY_USER);
 2254         new FXMenuRadio(submenu, _("&Group"), list, FileList::ID_SORT_BY_GROUP);
 2255         new FXMenuRadio(submenu, _("Per&missions"), list, FileList::ID_SORT_BY_PERM);
 2256         new FXMenuSeparator(submenu);
 2257         new FXMenuCheck(submenu, _("Ignore c&ase"), list, FileList::ID_SORT_CASE);
 2258         new FXMenuCheck(submenu, _("Fold&ers first"), list, FileList::ID_DIRS_FIRST);
 2259         new FXMenuCheck(submenu, _("Re&verse order"), list, FileList::ID_SORT_REVERSE);
 2260         new FXMenuCascade(menu, _("Pane&l"), NULL, submenu);
 2261         new FXMenuSeparator(menu);
 2262 
 2263 
 2264         FXbool ar = false;
 2265         if (list->getItem(itm) && list->isItemFile(itm))
 2266         {
 2267             new FXMenuCommand(menu, _("Open &with..."), fileopenicon, this, SearchPanel::ID_OPEN_WITH);
 2268             new FXMenuCommand(menu, _("&Open"), fileopenicon, this, SearchPanel::ID_OPEN);
 2269             FXString name = this->list->getItemText(itm).section('\t', 0);
 2270 
 2271             // Last and before last file extensions
 2272             FXString ext1 = name.rafter('.', 1).lower();
 2273             FXString ext2 = name.rafter('.', 2).lower();
 2274 
 2275             // Display the extract and package menus according to the archive extensions
 2276             if ((num == 1) && ((ext2 == "tar.gz") || (ext2 == "tar.bz2") || (ext2 == "tar.xz") || (ext2 == "tar.z")))
 2277             {
 2278                 ar = true;
 2279                 new FXMenuCommand(menu, _("E&xtract to..."), archexticon, this, SearchPanel::ID_EXTRACT);
 2280             }
 2281             else if ((num == 1) && ((ext1 == "gz") || (ext1 == "bz2") || (ext1 == "xz") || (ext1 == "z")))
 2282             {
 2283                 ar = true;
 2284                 new FXMenuCommand(menu, _("&Extract here"), archexticon, this, SearchPanel::ID_EXTRACT);
 2285             }
 2286             else if ((num == 1) && ((ext1 == "tar") || (ext1 == "tgz") || (ext1 == "tbz2") || (ext1 == "tbz") || (ext1 == "taz") || (ext1 == "txz") || (ext1 == "zip") || (ext1 == "7z") || (ext1 == "lzh") || (ext1 == "rar") || (ext1 == "ace") || (ext1 == "arj")))
 2287             {
 2288                 ar = true;
 2289                 new FXMenuCommand(menu, _("E&xtract to..."), archexticon, this, SearchPanel::ID_EXTRACT);
 2290             }
 2291 #if defined(linux)
 2292             else if ((num == 1) && ((ext1 == "rpm") || (ext1 == "deb")))
 2293             {
 2294                 ar = true;
 2295                 new FXMenuCommand(menu, _("&View"), packageicon, this, SearchPanel::ID_VIEW);
 2296             }
 2297 #endif
 2298             // Not archive nor package
 2299             if (!ar)
 2300             {
 2301                 new FXMenuCommand(menu, _("&View"), viewicon, this, SearchPanel::ID_VIEW);
 2302                 new FXMenuCommand(menu, _("&Edit"), editicon, this, SearchPanel::ID_EDIT);
 2303                 if (num == 1)
 2304                 {
 2305                     new FXMenuCommand(menu, _("Com&pare..."), compareicon, this, SearchPanel::ID_COMPARE);
 2306                 }
 2307                 else
 2308                 {
 2309                     new FXMenuCommand(menu, _("Com&pare"), compareicon, this, SearchPanel::ID_COMPARE);
 2310                 }
 2311             }
 2312         }
 2313         if (!ar)
 2314         {
 2315             new FXMenuCommand(menu, _("&Add to archive..."), archaddicon, this, SearchPanel::ID_ADD_TO_ARCH);
 2316         }
 2317 #if defined(linux)
 2318         if ((num == 1) && !ar)
 2319         {
 2320             new FXMenuCommand(menu, _("&Packages query "), packageicon, this, SearchPanel::ID_PKG_QUERY);
 2321         }
 2322 #endif
 2323 
 2324         // Build scripts menu
 2325         new FXMenuSeparator(menu);
 2326         FXString    scriptpath = homedir + PATHSEPSTRING CONFIGPATH PATHSEPSTRING XFECONFIGPATH PATHSEPSTRING SCRIPTPATH;
 2327         FXMenuPane* scriptsmenu = new FXMenuPane(this);
 2328         new FXMenuCascade(menu, _("Scripts"), runicon, scriptsmenu);
 2329         readScriptDir(scriptsmenu, scriptpath);
 2330         new FXMenuSeparator(scriptsmenu);
 2331         new FXMenuCommand(scriptsmenu, _("&Go to script folder"), gotodiricon, this, SearchPanel::ID_GO_SCRIPTDIR);
 2332 
 2333         new FXMenuSeparator(menu);
 2334         new FXMenuCommand(menu, _("&Go to parent folder"), gotodiricon, this, SearchPanel::ID_GOTO_PARENTDIR);
 2335         new FXMenuCommand(menu, _("&Copy"), copy_clpicon, this, SearchPanel::ID_COPY_CLIPBOARD);
 2336         new FXMenuCommand(menu, _("C&ut"), cut_clpicon, this, SearchPanel::ID_CUT_CLIPBOARD);
 2337         new FXMenuSeparator(menu);
 2338         new FXMenuCommand(menu, _("Re&name..."), renameiticon, this, SearchPanel::ID_FILE_RENAME);
 2339         new FXMenuCommand(menu, _("Copy &to..."), copy_clpicon, this, SearchPanel::ID_FILE_COPYTO);
 2340         new FXMenuCommand(menu, _("&Move to..."), moveiticon, this, SearchPanel::ID_FILE_MOVETO);
 2341         new FXMenuCommand(menu, _("Symlin&k to..."), minilinkicon, this, SearchPanel::ID_FILE_SYMLINK);
 2342         new FXMenuCommand(menu, _("M&ove to trash"), filedeleteicon, this, SearchPanel::ID_FILE_TRASH);
 2343         new FXMenuCommand(menu, _("&Delete"), filedelete_permicon, this, SearchPanel::ID_FILE_DELETE);
 2344         new FXMenuSeparator(menu);
 2345         new FXMenuCommand(menu, _("Compare &sizes"), charticon, this, SearchPanel::ID_DIR_USAGE);
 2346         new FXMenuCommand(menu, _("P&roperties"), attribicon, this, SearchPanel::ID_PROPERTIES);
 2347     }
 2348     menu->create();
 2349     allowPopupScroll = true;  // Allow keyboard scrolling
 2350     menu->popup(NULL, x, y);
 2351     getApp()->runModalWhileShown(menu);
 2352     allowPopupScroll = false;
 2353     list->setAllowRefresh(true);
 2354 
 2355     return(1);
 2356 }
 2357 
 2358 
 2359 // Read all executable file names that are located into the script directory
 2360 // Sort entries alphabetically
 2361 int SearchPanel::readScriptDir(FXMenuPane* scriptsmenu, FXString dir)
 2362 {
 2363     DIR*            dp;
 2364     struct dirent** namelist;
 2365 
 2366     // Open directory
 2367     if ((dp = opendir(dir.text())) == NULL)
 2368     {
 2369         return(0);
 2370     }
 2371 
 2372     // Eventually add a / at the end of the directory name
 2373     if (dir[dir.length()-1] != '/')
 2374     {
 2375         dir = dir+"/";
 2376     }
 2377 
 2378     // Read directory and sort entries alphabetically
 2379     int n = scandir(dir.text(), &namelist, NULL, alphasort);
 2380     if (n < 0)
 2381     {
 2382         perror("scandir");
 2383     }
 2384     else
 2385     {
 2386         for (int k = 0; k < n; k++)
 2387         {
 2388             // Avoid hidden directories and '.' and '..'
 2389             if (namelist[k]->d_name[0] != '.')
 2390             {
 2391                 FXString pathname = dir + namelist[k]->d_name;
 2392 
 2393                 // Recurse if non empty directory
 2394                 if (::isDirectory(pathname))
 2395                 {
 2396                     if (!::isEmptyDir(pathname))
 2397                     {
 2398                         FXMenuPane* submenu = new FXMenuPane(this);
 2399                         new FXMenuCascade(scriptsmenu, namelist[k]->d_name, NULL, submenu);
 2400                         readScriptDir(submenu, pathname);
 2401                     }
 2402                 }
 2403 
 2404                 // Add only executable files to the list
 2405                 else if (isReadExecutable(pathname))
 2406                 {
 2407                     new FXMenuCommand(scriptsmenu, namelist[k]->d_name + FXString("\t\t") + pathname, miniexecicon, this, FilePanel::ID_RUN_SCRIPT);
 2408                 }
 2409             }
 2410             free(namelist[k]);
 2411         }
 2412         free(namelist);
 2413     }
 2414 
 2415     // Close directory
 2416     (void)closedir(dp);
 2417 
 2418     return(1);
 2419 }
 2420 
 2421 
 2422 // Add files or directory to an archive
 2423 long SearchPanel::onCmdAddToArch(FXObject* o, FXSelector, void*)
 2424 {
 2425     int      ret;
 2426     FXString name, ext1, ext2, cmd, archive = "";
 2427     File*    f;
 2428 
 2429     // Enter search directory
 2430     ret = chdir(searchdir.text());
 2431     if (ret < 0)
 2432     {
 2433         int errcode = errno;
 2434         if (errcode)
 2435         {
 2436             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), searchdir.text(), strerror(errcode));
 2437         }
 2438         else
 2439         {
 2440             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), searchdir.text());
 2441         }
 2442 
 2443         return(0);
 2444     }
 2445 
 2446     // If only one item is selected, use its name as a starting guess for the archive name
 2447     if (list->getNumSelectedItems() == 1)
 2448     {
 2449         for (int u = 0; u < list->getNumItems(); u++)
 2450         {
 2451             if (list->isItemSelected(u))
 2452             {
 2453                 name = list->getItemFilename(u);
 2454                 break;
 2455             }
 2456         }
 2457         archive = name;
 2458     }
 2459 
 2460     // Initial archive name with full path and default extension
 2461     archive = homedir+PATHSEPSTRING+archive+".tar.gz";
 2462 
 2463     // Archive dialog
 2464     if (archdialog == NULL)
 2465     {
 2466         archdialog = new ArchInputDialog(this, "");
 2467     }
 2468     archdialog->setText(archive);
 2469     archdialog->CursorEnd();
 2470 
 2471     if (archdialog->execute())
 2472     {
 2473         if (archdialog->getText() == "")
 2474         {
 2475             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
 2476             return(0);
 2477         }
 2478 
 2479         // Get string and preserve escape characters
 2480         archive = ::quote(archdialog->getText());
 2481 
 2482         // Get extensions of the archive name
 2483         ext1 = archdialog->getText().rafter('.', 1).lower();
 2484         ext2 = archdialog->getText().rafter('.', 2).lower();
 2485 
 2486         // Handle different archive formats
 2487         if (ext2 == "tar.gz")
 2488         {
 2489             cmd = "tar -zcvf "+archive+" ";
 2490         }
 2491         else if (ext2 == "tar.bz2")
 2492         {
 2493             cmd = "tar -jcvf "+archive+" ";
 2494         }
 2495         else if (ext2 == "tar.xz")
 2496         {
 2497             cmd = "tar -Jcvf "+archive+" ";
 2498         }
 2499         else if (ext2 == "tar.z")
 2500         {
 2501             cmd = "tar -Zcvf "+archive+" ";
 2502         }
 2503         else if (ext1 == "tar")
 2504         {
 2505             cmd = "tar -cvf "+archive+" ";
 2506         }
 2507         else if (ext1 == "gz")
 2508         {
 2509             cmd = "gzip -v ";
 2510         }
 2511         else if (ext1 == "tgz")
 2512         {
 2513             cmd = "tar -zcvf "+archive+" ";
 2514         }
 2515         else if (ext1 == "taz")
 2516         {
 2517             cmd = "tar -Zcvf "+archive+" ";
 2518         }
 2519         else if (ext1 == "bz2")
 2520         {
 2521             cmd = "bzip2 -v ";
 2522         }
 2523         else if (ext1 == "xz")
 2524         {
 2525             cmd = "xz -v ";
 2526         }
 2527         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
 2528         {
 2529             cmd = "tar -jcvf "+archive+" ";
 2530         }
 2531         else if (ext1 == "txz")
 2532         {
 2533             cmd = "tar -Jcvf "+archive+" ";
 2534         }
 2535         else if (ext1 == "z")
 2536         {
 2537             cmd = "compress -v ";
 2538         }
 2539         else if (ext1 == "zip")
 2540         {
 2541             cmd = "zip -r "+archive+" ";
 2542         }
 2543         else if (ext1 == "7z")
 2544         {
 2545             cmd = "7z a "+archive+" ";
 2546         }
 2547 
 2548         // Default archive format
 2549         else
 2550         {
 2551             archive += ".tar.gz";
 2552             cmd = "tar -zcvf "+archive+" ";
 2553         }
 2554 
 2555         for (int u = 0; u < list->getNumItems(); u++)
 2556         {
 2557             if (list->isItemSelected(u))
 2558             {
 2559                 name = FXPath::relative(searchdir, list->getItemFullPathname(u));
 2560                 cmd += " ";
 2561                 cmd = cmd+::quote(name);
 2562                 cmd += " ";
 2563             }
 2564         }
 2565 
 2566         // Wait cursor
 2567         getApp()->beginWaitCursor();
 2568 
 2569         // File object
 2570         f = new File(this, _("Create archive"), ARCHIVE);
 2571         f->create();
 2572 
 2573         // Create archive
 2574         f->archive(archive, cmd);
 2575 
 2576         getApp()->endWaitCursor();
 2577         delete f;
 2578     }
 2579     return(1);
 2580 }
 2581 
 2582 
 2583 // Extract archive
 2584 long SearchPanel::onCmdExtract(FXObject*, FXSelector, void*)
 2585 {
 2586     FXString name, ext1, ext2, cmd, dir;
 2587     File*    f;
 2588 
 2589     // File selection dialog
 2590     FileDialog  browse(this, _("Select a destination folder"));
 2591     const char* patterns[] =
 2592     {
 2593         _("All Files"), "*", NULL
 2594     };
 2595 
 2596     browse.setDirectory(homedir);
 2597     browse.setPatternList(patterns);
 2598     browse.setSelectMode(SELECT_FILE_DIRECTORY);
 2599 
 2600     int item;
 2601     list->getNumSelectedItems(&item);
 2602     if (list->getItem(item))
 2603     {
 2604         // Path
 2605         FXString path = FXPath::directory(list->getItemFullPathname(item));
 2606 
 2607         // Archive name and extensions
 2608         name = list->getItemText(item).text();
 2609         ext1 = name.section('\t', 0).rafter('.', 1).lower();
 2610         ext2 = name.section('\t', 0).rafter('.', 2).lower();
 2611         name = ::quote(path + PATHSEPSTRING + name.section('\t', 0));
 2612 
 2613         // Handle different archive formats
 2614         FXbool dialog = true;
 2615 
 2616         if (ext2 == "tar.gz")
 2617         {
 2618             cmd = "tar -zxvf ";
 2619         }
 2620         else if (ext2 == "tar.bz2")
 2621         {
 2622             cmd = "tar -jxvf ";
 2623         }
 2624         else if (ext2 == "tar.xz")
 2625         {
 2626             cmd = "tar -Jxvf ";
 2627         }
 2628         else if (ext2 == "tar.z")
 2629         {
 2630             cmd = "tar -Zxvf ";
 2631         }
 2632         else if (ext1 == "tar")
 2633         {
 2634             cmd = "tar -xvf ";
 2635         }
 2636         else if (ext1 == "gz")
 2637         {
 2638             cmd = "gunzip -v ";
 2639             dialog = false;
 2640         }
 2641         else if (ext1 == "tgz")
 2642         {
 2643             cmd = "tar -zxvf ";
 2644         }
 2645         else if (ext1 == "taz")
 2646         {
 2647             cmd = "tar -Zxvf ";
 2648         }
 2649         else if (ext1 == "bz2")
 2650         {
 2651             cmd = "bunzip2 -v ";
 2652             dialog = false;
 2653         }
 2654         else if (ext1 == "xz")
 2655         {
 2656             cmd = "unxz -v ";
 2657             dialog = false;
 2658         }
 2659         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
 2660         {
 2661             cmd = "tar -jxvf ";
 2662         }
 2663         else if (ext1 == "txz")
 2664         {
 2665             cmd = "tar -Jxvf ";
 2666         }
 2667         else if (ext1 == "z")
 2668         {
 2669             cmd = "uncompress -v ";
 2670             dialog = false;
 2671         }
 2672         else if (ext1 == "zip")
 2673         {
 2674             cmd = "unzip -o ";
 2675         }
 2676         else if (ext1 == "7z")
 2677         {
 2678             cmd = "7z x -y ";
 2679         }
 2680         else if (ext1 == "rar")
 2681         {
 2682             cmd = "unrar x -o+ ";
 2683         }
 2684         else if (ext1 == "lzh")
 2685         {
 2686             cmd = "lha -xf ";
 2687         }
 2688         else if (ext1 == "ace")
 2689         {
 2690             cmd = "unace x ";
 2691         }
 2692         else if (ext1 == "arj")
 2693         {
 2694             cmd = "arj x -y ";
 2695         }
 2696         else
 2697         {
 2698             cmd = "tar -zxvf ";
 2699         }
 2700 
 2701         // Final extract command
 2702         cmd += name+" ";
 2703 
 2704         // Extract with file dialog
 2705         if (dialog)
 2706         {
 2707             // Extract archive
 2708             if (browse.execute())
 2709             {
 2710                 dir = browse.getFilename();
 2711 
 2712                 if (isWritable(dir))
 2713                 {
 2714                     // Wait cursor
 2715                     getApp()->beginWaitCursor();
 2716 
 2717                     // File object
 2718                     f = new File(this, _("Extract archive"), EXTRACT);
 2719                     f->create();
 2720 
 2721                     // Extract archive
 2722                     f->extract(name, dir, cmd);
 2723 
 2724                     getApp()->endWaitCursor();
 2725                     delete f;
 2726                 }
 2727                 else
 2728                 {
 2729                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), dir.text());
 2730                 }
 2731             }
 2732         }
 2733 
 2734         // Extract here (without file dialog)
 2735         else
 2736         {
 2737             if (isWritable(path))
 2738             {
 2739                 // Wait cursor
 2740                 getApp()->beginWaitCursor();
 2741 
 2742                 // File object
 2743                 f = new File(this, _("Extract archive"), EXTRACT);
 2744                 f->create();
 2745 
 2746                 // Extract archive
 2747                 f->extract(name, path, cmd);
 2748 
 2749                 getApp()->endWaitCursor();
 2750                 delete f;
 2751             }
 2752             else
 2753             {
 2754                 MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), path.text());
 2755             }
 2756         }
 2757     }
 2758 
 2759     return(1);
 2760 }
 2761 
 2762 
 2763 // Directory usage on file selection
 2764 long SearchPanel::onCmdDirUsage(FXObject* o, FXSelector, void*)
 2765 {
 2766     FXString pathname, command, itemslist = " ";
 2767     FXString cmd1 = "/usr/bin/du --apparent-size -k -s ";
 2768     FXString cmd2 = " 2> /dev/null | /usr/bin/sort -rn | /usr/bin/cut -f2 | /usr/bin/xargs -d '\n' /usr/bin/du --apparent-size --total --si -s 2> /dev/null";
 2769 
 2770     // Construct selected files list
 2771     for (int u = 0; u < list->getNumItems(); u++)
 2772     {
 2773         if (list->isItemSelected(u))
 2774         {
 2775             pathname = list->getItemFullPathname(u);
 2776 
 2777             // List of selected items
 2778             itemslist += ::quote(pathname) + " ";
 2779         }
 2780     }
 2781 
 2782     // Command to be executed
 2783     command = cmd1 + itemslist + cmd2;
 2784 
 2785     // Make and show command window
 2786     CommandWindow* cmdwin=new CommandWindow(getApp(),_("Sizes of Selected Items"),command,25,50);
 2787     cmdwin->create();
 2788     cmdwin->setIcon(charticon);
 2789 
 2790     return(1);
 2791 }
 2792 
 2793 
 2794 // Trash files from the file list
 2795 long SearchPanel::onCmdFileTrash(FXObject*, FXSelector, void*)
 2796 {
 2797     int   firstitem = 0;
 2798     File* f = NULL;
 2799 
 2800     FXbool confirm_trash = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_trash", true);
 2801 
 2802     // If we don't have permission to write to the trash directory
 2803     if (!::isWritable(trashfileslocation))
 2804     {
 2805         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to trash location %s: Permission denied"), trashfileslocation.text());
 2806         return(0);
 2807     }
 2808 
 2809     // Items number in the file list
 2810     int num = list->getNumSelectedItems();
 2811     if (num < 1)
 2812     {
 2813         return(0);
 2814     }
 2815 
 2816     if (confirm_trash)
 2817     {
 2818         FXString message;
 2819         if (num == 1)
 2820         {
 2821             FXString pathname;
 2822             for (int u = 0; u < list->getNumItems(); u++)
 2823             {
 2824                 if (list->isItemSelected(u))
 2825                 {
 2826                     pathname = list->getItemFullPathname(u);
 2827                 }
 2828             }
 2829             if (::isDirectory(pathname))
 2830             {
 2831                 message.format(_("Move folder %s to trash can?"), pathname.text());
 2832             }
 2833             else
 2834             {
 2835                 message.format(_("Move file %s to trash can?"), pathname.text());
 2836             }
 2837         }
 2838         else
 2839         {
 2840             message.format(_("Move %s selected items to trash can?"), FXStringVal(num).text());
 2841         }
 2842 
 2843         MessageBox box(this, _("Confirm Trash"), message, delete_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
 2844         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
 2845         {
 2846             return(0);
 2847         }
 2848     }
 2849 
 2850     // Wait cursor
 2851     getApp()->beginWaitCursor();
 2852 
 2853     // File object
 2854     f = new File(this, _("Move to trash"), DELETE, num);
 2855     f->create();
 2856     list->setAllowRefresh(false);
 2857 
 2858     // Overwrite initialisations
 2859     FXbool overwrite = false;
 2860     FXbool overwrite_all = false;
 2861     FXbool skip_all = false;
 2862 
 2863     // Delete selected files
 2864     FXString filename, pathname;
 2865     for (int u = 0; u < list->getNumItems(); u++)
 2866     {
 2867         if (list->isItemSelected(u))
 2868         {
 2869             // Get index of first selected item
 2870             if (firstitem == 0)
 2871             {
 2872                 firstitem = u;
 2873             }
 2874 
 2875             // Get file name and path
 2876             filename = list->getItemFilename(u);
 2877             pathname = list->getItemFullPathname(u);
 2878 
 2879             // File could have already been trashed above in the tree
 2880             if (!::exists(pathname))
 2881             {
 2882                 continue;
 2883             }
 2884 
 2885             // If we don't have permission to write to the file
 2886             if (!::isWritable(pathname))
 2887             {
 2888                 // Overwrite dialog if necessary
 2889                 if (!(overwrite_all | skip_all))
 2890                 {
 2891                     f->hideProgressDialog();
 2892                     FXString msg;
 2893                     msg.format(_("File %s is write-protected, move it anyway to trash can?"), pathname.text());
 2894   
 2895                     if (num ==1)
 2896                     {
 2897                         OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Trash"), msg, OVWBOX_SINGLE_FILE);
 2898                         FXuint answer = dlg->execute(PLACEMENT_OWNER);
 2899                         delete dlg; 
 2900                         if (answer == 1)
 2901                         {
 2902                             overwrite = true;
 2903                         }
 2904                         else
 2905                         {
 2906                             goto end;
 2907                         }
 2908                     }  
 2909                     else
 2910                     {
 2911                         OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Trash"), msg);
 2912                         FXuint        answer = dlg->execute(PLACEMENT_OWNER);
 2913                         delete dlg;
 2914                         switch (answer)
 2915                         {
 2916                         // Cancel
 2917                         case 0:
 2918                             goto end;
 2919                             break;
 2920 
 2921                         // Overwrite
 2922                         case 1:
 2923                             overwrite = true;
 2924                             break;
 2925 
 2926                         // Overwrite all
 2927                         case 2:
 2928                             overwrite_all = true;
 2929                             break;
 2930 
 2931                         // Skip
 2932                         case 3:
 2933                             overwrite = false;
 2934                             break;
 2935 
 2936                         // Skip all
 2937                         case 4:
 2938                             skip_all = true;
 2939                             break;
 2940                         }
 2941                     }
 2942                 }
 2943                 if ((overwrite | overwrite_all) & !skip_all)
 2944                 {
 2945                     // Trash files path name
 2946                     FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
 2947 
 2948                     // Create trashinfo file
 2949                     createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
 2950 
 2951                     // Move file to trash files location
 2952                     int ret = f->move(pathname, trashpathname);
 2953 
 2954                     // An error has occurred
 2955                     if ((ret == 0) && !f->isCancelled())
 2956                     {
 2957                         f->hideProgressDialog();
 2958                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
 2959                         break;
 2960                     }
 2961                 }
 2962                 f->showProgressDialog();
 2963             }
 2964 
 2965             // If we have permission to write
 2966             else
 2967             {
 2968                 // Trash files path name
 2969                 FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
 2970 
 2971                 // Create trashinfo file
 2972                 createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
 2973 
 2974                 // Move file to trash files location
 2975                 int ret = f->move(pathname, trashpathname);
 2976 
 2977                 // An error has occurred
 2978                 if ((ret == 0) && !f->isCancelled())
 2979                 {
 2980                     f->hideProgressDialog();
 2981                     MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
 2982                     break;
 2983                 }
 2984 
 2985                 // If action is cancelled in progress dialog
 2986                 if (f->isCancelled())
 2987                 {
 2988                     f->hideProgressDialog();
 2989                     MessageBox::error(this, BOX_OK, _("Warning"), _("Move to trash file operation cancelled!"));
 2990                     break;
 2991                 }
 2992             }
 2993         }
 2994     }
 2995 end:
 2996     getApp()->endWaitCursor();
 2997     delete f;
 2998 
 2999     list->setAllowRefresh(true);
 3000     list->onCmdRefresh(0, 0, 0);
 3001 
 3002     return(1);
 3003 }
 3004 
 3005 
 3006 // Definitively delete files from the file list or the tree list (no trash can)
 3007 long SearchPanel::onCmdFileDelete(FXObject*, FXSelector, void*)
 3008 {
 3009     int   firstitem = 0;
 3010     File* f = NULL;
 3011 
 3012     FXbool confirm_del = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete", true);
 3013     FXbool confirm_del_emptydir = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete_emptydir", true);
 3014 
 3015 
 3016     // Items number in the file list
 3017     int num = list->getNumSelectedItems();
 3018 
 3019     if (num == 0)
 3020     {
 3021         return(0);
 3022     }
 3023 
 3024     // If exist selected files, use them
 3025     if (num >= 1)
 3026     {
 3027         if (confirm_del)
 3028         {
 3029             FXString message;
 3030             if (num == 1)
 3031             {
 3032                 FXString pathname;
 3033                 for (int u = 0; u < list->getNumItems(); u++)
 3034                 {
 3035                     if (list->isItemSelected(u))
 3036                     {
 3037                         pathname = list->getItemFullPathname(u);
 3038                     }
 3039                 }
 3040                 if (::isDirectory(pathname))
 3041                 {
 3042                     message.format(_("Definitively delete folder %s ?"), pathname.text());
 3043                 }
 3044                 else
 3045                 {
 3046                     message.format(_("Definitively delete file %s ?"), pathname.text());
 3047                 }
 3048             }
 3049             else
 3050             {
 3051                 message.format(_("Definitively delete %s selected items?"), FXStringVal(num).text());
 3052             }
 3053             MessageBox box(this, _("Confirm Delete"), message, delete_big_permicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
 3054             if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
 3055             {
 3056                 return(0);
 3057             }
 3058         }
 3059         // Wait cursor
 3060         getApp()->beginWaitCursor();
 3061 
 3062         // File object
 3063         f = new File(this, _("File delete"), DELETE, num);
 3064         f->create();
 3065         list->setAllowRefresh(false);
 3066 
 3067         // Overwrite initialisations
 3068         FXbool overwrite = false;
 3069         FXbool overwrite_all = false;
 3070         FXbool skip_all = false;
 3071         FXbool ask_del_empty = true;
 3072         FXbool skip_all_del_emptydir = false;
 3073 
 3074         // Delete selected files
 3075         FXString filename, pathname;
 3076         for (int u = 0; u < list->getNumItems(); u++)
 3077         {
 3078             if (list->isItemSelected(u))
 3079             {
 3080                 // Get index of first selected item
 3081                 if (firstitem == 0)
 3082                 {
 3083                     firstitem = u;
 3084                 }
 3085 
 3086                 // Get file name and path
 3087                 filename = list->getItemFilename(u);
 3088                 pathname = list->getItemFullPathname(u);
 3089 
 3090                 // File could have already been deleted above in the tree
 3091                 if (!::exists(pathname))
 3092                 {
 3093                     continue;
 3094                 }
 3095 
 3096                 // Confirm empty directory deletion
 3097                 if (confirm_del & confirm_del_emptydir & ask_del_empty)
 3098                 {
 3099                     if ((::isEmptyDir(pathname) == 0) && !::isLink(pathname))
 3100                     {
 3101                         if (skip_all_del_emptydir)
 3102                         {
 3103                             continue;
 3104                         }
 3105 
 3106                         f->hideProgressDialog();
 3107                         FXString msg;
 3108                         msg.format(_("Folder %s is not empty, delete it anyway?"), pathname.text());
 3109                         OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg);
 3110                         FXuint answer = dlg->execute(PLACEMENT_OWNER);
 3111                         delete dlg;
 3112                         switch (answer)
 3113                         {
 3114                         // Cancel
 3115                         case 0:
 3116                             goto end;
 3117                             break;
 3118 
 3119                         // Yes
 3120                         case 1:
 3121                             break;
 3122 
 3123                         // Yes for all
 3124                         case 2:
 3125                             ask_del_empty = false;
 3126                             break;
 3127 
 3128                         // Skip
 3129                         case 3:
 3130                             continue;
 3131                             break;
 3132 
 3133                         // Skip all
 3134                         case 4:
 3135                             skip_all_del_emptydir = true;
 3136                             continue;
 3137                             break;
 3138                         }
 3139                         f->showProgressDialog();
 3140                     }
 3141                 }
 3142 
 3143                 // If we don't have permission to write to the file
 3144                 if (!::isWritable(pathname))
 3145                 {
 3146                     // Overwrite dialog if necessary
 3147                     if (!(overwrite_all | skip_all))
 3148                     {
 3149                         f->hideProgressDialog();
 3150                         FXString msg;
 3151                         msg.format(_("File %s is write-protected, delete it anyway?"), pathname.text());
 3152 
 3153                         if (num ==1)
 3154                         {
 3155                             OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg, OVWBOX_SINGLE_FILE);
 3156                             FXuint answer = dlg->execute(PLACEMENT_OWNER);
 3157                             delete dlg;
 3158                             if (answer == 1)
 3159                             {
 3160                                 overwrite = true;
 3161                             }
 3162                             else
 3163                             {
 3164                                 goto end;
 3165                             }                           
 3166                         }
 3167                         
 3168                         else
 3169                         {
 3170                             OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg);
 3171                             FXuint answer = dlg->execute(PLACEMENT_OWNER);
 3172                             delete dlg;
 3173                             switch (answer)
 3174                             {
 3175                             // Cancel
 3176                             case 0:
 3177                                 goto end;
 3178                                 break;
 3179 
 3180                             // Yes
 3181                             case 1:
 3182                                 overwrite = true;
 3183                                 break;
 3184 
 3185                             // Yes for all
 3186                             case 2:
 3187                                 overwrite_all = true;
 3188                                 break;
 3189 
 3190                             // Skip
 3191                             case 3:
 3192                                 overwrite = false;
 3193                                 break;
 3194 
 3195                             // Skip all
 3196                             case 4:
 3197                                 skip_all = true;
 3198                                 break;
 3199                             }
 3200                         }
 3201                     }
 3202                     if ((overwrite | overwrite_all) & !skip_all)
 3203                     {
 3204                         // Definitively remove file or folder
 3205                         f->remove(pathname);
 3206                     }
 3207                     f->showProgressDialog();
 3208                 }
 3209 
 3210                 // If we have permission to write
 3211                 else
 3212                 {
 3213                     // Definitively remove file or folder
 3214                     f->remove(pathname);
 3215 
 3216                     // If is located at trash location, try to also remove the corresponding trashinfo file if it exists
 3217                     // Do it silently and don't report any error if it fails
 3218                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 3219                     if (use_trash_can && (pathname.left(trashfileslocation.length()) == trashfileslocation))
 3220                     {
 3221                         FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+filename+".trashinfo";
 3222                         ::unlink(trashinfopathname.text());
 3223                     }
 3224 
 3225                     // If action is cancelled in progress dialog
 3226                     if (f->isCancelled())
 3227                     {
 3228                         f->hideProgressDialog();
 3229                         MessageBox::error(this, BOX_OK, _("Warning"), _("Delete file operation cancelled!"));
 3230                         break;
 3231                     }
 3232                 }
 3233             }
 3234         }
 3235 end:
 3236         getApp()->endWaitCursor();
 3237         delete f;
 3238     }
 3239 
 3240     list->setAllowRefresh(true);
 3241     list->onCmdRefresh(0, 0, 0);
 3242 
 3243     return(1);
 3244 }
 3245 
 3246 
 3247 // We now really do have the clipboard, keep clipboard content
 3248 long SearchPanel::onClipboardGained(FXObject* sender, FXSelector sel, void* ptr)
 3249 {
 3250     FXVerticalFrame::onClipboardGained(sender, sel, ptr);
 3251     return(1);
 3252 }
 3253 
 3254 
 3255 // We lost the clipboard
 3256 long SearchPanel::onClipboardLost(FXObject* sender, FXSelector sel, void* ptr)
 3257 {
 3258     FXVerticalFrame::onClipboardLost(sender, sel, ptr);
 3259     return(1);
 3260 }
 3261 
 3262 
 3263 // Somebody wants our clipboard content
 3264 long SearchPanel::onClipboardRequest(FXObject* sender, FXSelector sel, void* ptr)
 3265 {
 3266     FXEvent* event = (FXEvent*)ptr;
 3267     FXuchar* data;
 3268     FXuint   len;
 3269 
 3270     // Perhaps the target wants to supply its own data for the clipboard
 3271     if (FXVerticalFrame::onClipboardRequest(sender, sel, ptr))
 3272     {
 3273         return(1);
 3274     }
 3275 
 3276     // Clipboard target is xfelistType (Xfe, Gnome or XFCE)
 3277     if (event->target == xfelistType)
 3278     {
 3279         // Prepend "copy" or "cut" as in the Gnome way and avoid duplicating these strings
 3280         if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
 3281         {
 3282             if (clipboard_type == CUT_CLIPBOARD)
 3283             {
 3284                 clipboard = "cut\n" + clipboard;
 3285             }
 3286             else
 3287             {
 3288                 clipboard = "copy\n" + clipboard;
 3289             }
 3290         }
 3291 
 3292         // Return clipboard content
 3293         if (event->target == xfelistType)
 3294         {
 3295             if (!clipboard.empty())
 3296             {
 3297                 len = clipboard.length();
 3298                 FXMEMDUP(&data, clipboard.text(), FXuchar, len);
 3299                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
 3300 
 3301                 // Return because xfelistType is not compatible with other types
 3302                 return(1);
 3303             }
 3304         }
 3305     }
 3306 
 3307     // Clipboard target is kdelisType (KDE)
 3308     if (event->target == kdelistType)
 3309     {
 3310         // The only data to be passed in this case is "0" for copy and "1" for cut
 3311         // The uri data are passed using the standard uri-list type
 3312         FXString flag;
 3313         if (clipboard_type == CUT_CLIPBOARD)
 3314         {
 3315             flag = "1";
 3316         }
 3317         else
 3318         {
 3319             flag = "0";
 3320         }
 3321 
 3322         // Return clipboard content
 3323         if (event->target == kdelistType)
 3324         {
 3325             FXMEMDUP(&data, flag.text(), FXuchar, 1);
 3326             setDNDData(FROM_CLIPBOARD, event->target, data, 1);
 3327         }
 3328     }
 3329 
 3330     // Clipboard target is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
 3331     if (event->target == urilistType)
 3332     {
 3333         if (!clipboard.empty())
 3334         {
 3335             len = clipboard.length();
 3336             FXMEMDUP(&data, clipboard.text(), FXuchar, len);
 3337             setDNDData(FROM_CLIPBOARD, event->target, data, len);
 3338 
 3339             return(1);
 3340         }
 3341     }
 3342 
 3343     // Clipboard target is utf8Type (to paste file pathes as text to other applications)
 3344     if (event->target == utf8Type)
 3345     {
 3346         if (!clipboard.empty())
 3347         {
 3348             int      beg = 0, end = 0;
 3349             FXString str = "";
 3350             FXString pathname, url;
 3351 
 3352             // Clipboard don't contain 'copy\n' or 'cut\n' as first line
 3353             if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
 3354             {
 3355                 // Remove the 'file:' prefix for each file path
 3356                 while (1)
 3357                 {
 3358                     end = clipboard.find('\n', end);
 3359                     if (end < 0) // Last line
 3360                     {
 3361                         end = clipboard.length();
 3362                         url = clipboard.mid(beg, end-beg+1);
 3363                         pathname = FXURL::decode(FXURL::fileFromURL(url));
 3364                         str += pathname;
 3365                         break;
 3366                     }
 3367                     url = clipboard.mid(beg, end-beg+1);
 3368                     pathname = FXURL::decode(FXURL::fileFromURL(url));
 3369                     str += pathname;
 3370                     end++;
 3371                     beg = end;
 3372                 }
 3373                 end = str.length();
 3374             }
 3375 
 3376             // Clipboard contains 'copy\n' or 'cut\n' as first line, thus skip it
 3377             else
 3378             {
 3379                 // Start after the 'copy\n' or 'cut\n' prefix
 3380                 end = clipboard.find('\n', 0);
 3381                 end++;
 3382                 beg = end;
 3383 
 3384                 // Remove the 'file:' prefix for each file path
 3385                 while (1)
 3386                 {
 3387                     end = clipboard.find('\n', end);
 3388                     if (end < 0) // Last line
 3389                     {
 3390                         end = clipboard.length();
 3391                         url = clipboard.mid(beg, end-beg+1);
 3392                         pathname = FXURL::decode(FXURL::fileFromURL(url));
 3393                         str += pathname;
 3394                         break;
 3395                     }
 3396                     url = clipboard.mid(beg, end-beg+1);
 3397                     pathname = FXURL::decode(FXURL::fileFromURL(url));
 3398                     str += pathname;
 3399                     end++;
 3400                     beg = end;
 3401                 }
 3402                 end = str.length();
 3403             }
 3404 
 3405             if (!str.empty())
 3406             {
 3407                 len = str.length();
 3408                 FXMEMDUP(&data, str.text(), FXuchar, len);
 3409                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
 3410 
 3411                 return(1);
 3412             }
 3413         }
 3414     }
 3415     return(0);
 3416 }
 3417 
 3418 
 3419 // Copy or cut to clipboard
 3420 long SearchPanel::onCmdCopyCut(FXObject*, FXSelector sel, void*)
 3421 {
 3422     // Clear clipboard
 3423     clipboard.clear();
 3424 
 3425     // Clipboard type
 3426     if (FXSELID(sel) == ID_CUT_CLIPBOARD)
 3427     {
 3428         clipboard_type = CUT_CLIPBOARD;
 3429     }
 3430     else
 3431     {
 3432         clipboard_type = COPY_CLIPBOARD;
 3433     }
 3434 
 3435     // Items number in the file list
 3436     int num = list->getNumSelectedItems();
 3437 
 3438     if (num == 0)
 3439     {
 3440         return(0);
 3441     }
 3442 
 3443     // If exist selected files, use them
 3444     if (num >= 1)
 3445     {
 3446         for (int u = 0; u < list->getNumItems(); u++)
 3447         {
 3448             if (list->isItemSelected(u))
 3449             {
 3450                 FXString pathname = list->getItemFullPathname(u);
 3451                 clipboard += FXURL::encode(::fileToURI(pathname))+"\n";
 3452             }
 3453         }
 3454     }
 3455 
 3456     // Remove the last \n of the list, for compatibility with some file managers (e.g. nautilus 2.30.1)
 3457     clipboard.erase(clipboard.length()-1);
 3458 
 3459     // Acquire the clipboard
 3460     FXDragType types[4];
 3461     types[0] = xfelistType;
 3462     types[1] = kdelistType;
 3463     types[2] = urilistType;
 3464     types[3] = utf8Type;
 3465 
 3466     if (acquireClipboard(types, 4))
 3467     {
 3468         return(0);
 3469     }
 3470 
 3471     return(1);
 3472 }
 3473 
 3474 
 3475 // Copy/Move/Rename/Symlink file(s)
 3476 long SearchPanel::onCmdFileMan(FXObject* sender, FXSelector sel, void*)
 3477 {
 3478     int      num;
 3479     FXString src, targetdir, target, name, source;
 3480 
 3481     // Confirmation dialog?
 3482     FXbool ask_before_copy = getApp()->reg().readUnsignedEntry("OPTIONS", "ask_before_copy", true);
 3483 
 3484     // Number of selected items
 3485     num = list->getNumSelectedItems();
 3486 
 3487     // If no item, return
 3488     if (num <= 0)
 3489     {
 3490         return(0);
 3491     }
 3492 
 3493     // Obtain the list of source files
 3494     for (int u = 0; u < list->getNumItems(); u++)
 3495     {
 3496         if (list->isItemSelected(u))
 3497         {
 3498             src += list->getItemFullPathname(u)+"\n";
 3499         }
 3500     }
 3501 
 3502     // Name and directory of the first source file
 3503     source = src.section('\n', 0);
 3504     name = FXPath::name(source);
 3505     FXString dir = FXPath::directory(source);
 3506 
 3507     // Initialise target dir name
 3508 x:
 3509     targetdir = homedir;
 3510     if (targetdir != ROOTDIR)
 3511     {
 3512         target = targetdir+PATHSEPSTRING;
 3513     }
 3514     else
 3515     {
 3516         target = targetdir;
 3517     }
 3518 
 3519     // Target dir for the rename command
 3520     if (FXSELID(sel) == ID_FILE_RENAME)
 3521     {
 3522         targetdir = dir;
 3523     }
 3524 
 3525     // Configure the command, title, message, etc.
 3526     FXIcon*  icon = NULL;
 3527     FXString command, title, message;
 3528     if (FXSELID(sel) == ID_FILE_RENAME)
 3529     {
 3530         command = "rename";
 3531         title = _("Rename");
 3532         icon = move_bigicon;
 3533         if (num == 1)
 3534         {
 3535             message = _("Rename ");
 3536             message += name;
 3537             target = name;
 3538             title = _("Rename");
 3539         }
 3540         else
 3541         {
 3542             return(0);
 3543         }
 3544     }
 3545     if (FXSELID(sel) == ID_FILE_COPYTO)
 3546     {
 3547         command = "copy";
 3548         title = _("Copy");
 3549         icon = copy_bigicon;
 3550         if (num == 1)
 3551         {
 3552             message = _("Copy ");
 3553             message += source;
 3554         }
 3555         else
 3556         {
 3557             message.format(_("Copy %s items"), FXStringVal(num).text());
 3558         }
 3559     }
 3560     if (FXSELID(sel) == ID_FILE_MOVETO)
 3561     {
 3562         command = "move";
 3563         title = _("Move");
 3564         icon = move_bigicon;
 3565         if (num == 1)
 3566         {
 3567             message = _("Move ");
 3568             message += source;
 3569             title = _("Move");
 3570         }
 3571         else
 3572         {
 3573             message.format(_("Move %s items"), FXStringVal(num).text());
 3574         }
 3575     }
 3576     if (FXSELID(sel) == ID_FILE_SYMLINK)
 3577     {
 3578         command = "symlink";
 3579         title = _("Symlink");
 3580         icon = link_bigicon;
 3581         if (num == 1)
 3582         {
 3583             message = _("Symlink ");
 3584             message += source;
 3585             target += name;
 3586         }
 3587         else
 3588         {
 3589             message.format(_("Symlink %s items"), FXStringVal(num).text());
 3590         }
 3591     }
 3592 
 3593     // File operation dialog, if needed
 3594     if (ask_before_copy || (source == target) || (FXSELID(sel) == ID_FILE_COPYTO) || (FXSELID(sel) == ID_FILE_MOVETO) || (FXSELID(sel) == ID_FILE_RENAME) || (FXSELID(sel) == ID_FILE_SYMLINK))
 3595     {
 3596         if (num == 1)
 3597         {
 3598             if (FXSELID(sel) == ID_FILE_RENAME)
 3599             {
 3600                 if (operationdialogrename == NULL)
 3601                 {
 3602                     operationdialogrename = new InputDialog(this, "", "", title, _("To:"), icon);
 3603                 }
 3604                 operationdialogrename->setTitle(title);
 3605                 operationdialogrename->setIcon(icon);
 3606                 operationdialogrename->setMessage(message);
 3607                 operationdialogrename->setText(target);
 3608 
 3609                 if (::isDirectory(source))  // directory
 3610                 {
 3611                     operationdialogrename->selectAll();
 3612                 }
 3613                 else
 3614                 {
 3615                     int pos = target.rfind('.');
 3616                     if (pos <= 0)
 3617                     {
 3618                         operationdialogrename->selectAll(); // no extension or dot file
 3619                     }
 3620                     else
 3621                     {
 3622                         operationdialogrename->setSelection(0, pos);
 3623                     }
 3624                 }
 3625 
 3626                 int rc = 1;
 3627                 rc = operationdialogrename->execute(PLACEMENT_CURSOR);
 3628                 target = operationdialogrename->getText();
 3629 
 3630                 // Target name contains '/'
 3631                 if (target.contains(PATHSEPCHAR))
 3632                 {
 3633                     MessageBox::error(this, BOX_OK, _("Error"), _("Character '/' is not allowed in file or folder names, operation cancelled"));
 3634                     return(0);
 3635                 }
 3636 
 3637                 if (!rc)
 3638                 {
 3639                     return(0);
 3640                 }
 3641             }
 3642             else
 3643             {
 3644                 if (operationdialogsingle == NULL)
 3645                 {
 3646                     operationdialogsingle = new BrowseInputDialog(this, "", "", title, _("To:"), icon, BROWSE_INPUT_MIXED);
 3647                 }
 3648                 operationdialogsingle->setTitle(title);
 3649                 operationdialogsingle->setIcon(icon);
 3650                 operationdialogsingle->setMessage(message);
 3651                 operationdialogsingle->setText(target);
 3652 
 3653 
 3654                 // Select file name without path
 3655                 if (FXSELID(sel) == ID_FILE_SYMLINK)
 3656                 {
 3657                     int pos = target.rfind(PATHSEPSTRING);
 3658                     if (pos >= 0)
 3659                     {
 3660                         operationdialogsingle->setSelection(pos+1, target.length());
 3661                     }
 3662                 }
 3663 
 3664                 operationdialogsingle->setDirectory(targetdir);
 3665                 int rc = 1;
 3666                 rc = operationdialogsingle->execute(PLACEMENT_CURSOR);
 3667                 target = operationdialogsingle->getText();
 3668                 if (!rc)
 3669                 {
 3670                     return(0);
 3671                 }
 3672             }
 3673         }
 3674         else
 3675         {
 3676             if (operationdialogmultiple == NULL)
 3677             {
 3678                 operationdialogmultiple = new BrowseInputDialog(this, "", "", title, _("To folder:"), icon, BROWSE_INPUT_FOLDER);
 3679             }
 3680             operationdialogmultiple->setTitle(title);
 3681             operationdialogmultiple->setIcon(icon);
 3682             operationdialogmultiple->setMessage(message);
 3683             operationdialogmultiple->setText(target);
 3684             operationdialogmultiple->CursorEnd();
 3685             operationdialogmultiple->setDirectory(targetdir);
 3686             int rc = 1;
 3687             rc = operationdialogmultiple->execute(PLACEMENT_CURSOR);
 3688             target = operationdialogmultiple->getText();
 3689             if (!rc)
 3690             {
 3691                 return(0);
 3692             }
 3693         }
 3694     }
 3695 
 3696     // Nothing entered
 3697     if (target == "")
 3698     {
 3699         MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
 3700         return(0);
 3701     }
 3702 
 3703     // Except for rename, an absolute path is required
 3704     if ((FXSELID(sel) != ID_FILE_RENAME) && !ISPATHSEP(target[0]))
 3705     {
 3706         MessageBox::warning(this, BOX_OK, _("Warning"), _("You must enter an absolute path!"));
 3707         goto x;
 3708     }
 3709 
 3710     // Update target and target parent directory
 3711     target=::filePath(target,targetdir);
 3712     if (::isDirectory(target))
 3713     {
 3714         targetdir = target;
 3715     }
 3716     else
 3717     {
 3718         targetdir = FXPath::directory(target);
 3719     }
 3720  
 3721     // Target directory not writable
 3722     if (!::isWritable(targetdir))
 3723     {
 3724         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
 3725         return(0);
 3726     }
 3727 
 3728     // Multiple sources and non existent destination
 3729     if ((num > 1) && !::exists(target))
 3730     {
 3731         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), target.text());
 3732         return(0);
 3733     }
 3734 
 3735     // Multiple sources and target is a file
 3736     if ((num > 1) && ::isFile(target))
 3737     {
 3738         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), target.text());
 3739         return(0);
 3740     }
 3741 
 3742     // Target is a directory and is not writable
 3743     if (::isDirectory(target) && !::isWritable(target))
 3744     {
 3745         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), target.text());
 3746         return(0);
 3747     }
 3748 
 3749     // Target is a file and its parent directory is not writable
 3750     if (::isFile(target) && !::isWritable(targetdir))
 3751     {
 3752         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
 3753         return(0);
 3754     }
 3755 
 3756     // Target parent directory doesn't exist
 3757     if (!::exists(targetdir))
 3758     {
 3759         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), targetdir.text());
 3760         return(0);
 3761     }
 3762 
 3763     // Target parent directory is not a directory
 3764     if (!::isDirectory(targetdir))
 3765     {
 3766         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), targetdir.text());
 3767         return(0);
 3768     }
 3769 
 3770     // One source
 3771     File* f = NULL;
 3772     int   ret;
 3773     if (num == 1)
 3774     {
 3775         // An empty source file name corresponds to the ".." file
 3776         // Don't perform any file operation on it!
 3777         if (source == "")
 3778         {
 3779             return(0);
 3780         }
 3781 
 3782         // Wait cursor
 3783         getApp()->beginWaitCursor();
 3784 
 3785         // File object
 3786         if (command == "copy")
 3787         {
 3788             f = new File(this, _("File copy"), COPY, num);
 3789             f->create();
 3790 
 3791             // If target file is located at trash location, also create the corresponding trashinfo file
 3792             // Do it silently and don't report any error if it fails
 3793             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 3794             if (use_trash_can && (target == trashfileslocation))
 3795             {
 3796                 // Trash files path name
 3797                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
 3798 
 3799                 // Adjust target name to get the _N suffix if any
 3800                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
 3801 
 3802                 // Create trashinfo file
 3803                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
 3804 
 3805                 // Copy source to trash target
 3806                 ret = f->copy(source, trashtarget);
 3807             }
 3808 
 3809             // Copy source to target
 3810             else
 3811             {
 3812                 ret = f->copy(source, target);
 3813             }
 3814 
 3815             // An error has occurred
 3816             if ((ret == 0) && !f->isCancelled())
 3817             {
 3818                 f->hideProgressDialog();
 3819                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
 3820             }
 3821 
 3822             // If action is cancelled in progress dialog
 3823             if (f->isCancelled())
 3824             {
 3825                 f->hideProgressDialog();
 3826                 MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
 3827             }
 3828         }
 3829         else if (command == "rename")
 3830         {
 3831             f = new File(this, _("File rename"), RENAME, num);
 3832             f->create();
 3833             ret = f->rename(source, target);
 3834 
 3835             // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
 3836             // Do it silently and don't report any error if it fails
 3837             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 3838             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
 3839             {
 3840                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
 3841                 ::unlink(trashinfopathname.text());
 3842             }
 3843         }
 3844         else if (command == "move")
 3845         {
 3846             f = new File(this, _("File move"), MOVE, num);
 3847             f->create();
 3848 
 3849             // If target file is located at trash location, also create the corresponding trashinfo file
 3850             // Do it silently and don't report any error if it fails
 3851             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 3852             if (use_trash_can && (target == trashfileslocation))
 3853             {
 3854                 // Trash files path name
 3855                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
 3856 
 3857                 // Adjust target name to get the _N suffix if any
 3858                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
 3859 
 3860                 // Create trashinfo file
 3861                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
 3862 
 3863                 // Move source to trash target
 3864                 ret = f->move(source, trashtarget);
 3865             }
 3866 
 3867             // Move source to target
 3868             else
 3869             {
 3870                 ret = f->move(source, target);
 3871             }
 3872 
 3873             // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
 3874             // Do it silently and don't report any error if it fails
 3875             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
 3876             {
 3877                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
 3878                 ::unlink(trashinfopathname.text());
 3879             }
 3880 
 3881             // An error has occurred
 3882             if ((ret == 0) && !f->isCancelled())
 3883             {
 3884                 f->hideProgressDialog();
 3885                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
 3886             }
 3887 
 3888             // If action is cancelled in progress dialog
 3889             if (f->isCancelled())
 3890             {
 3891                 f->hideProgressDialog();
 3892                 MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
 3893             }
 3894         }
 3895         else if (command == "symlink")
 3896         {
 3897             f = new File(this, _("Symlink"), SYMLINK, num);
 3898             f->create();
 3899             f->symlink(source, target);
 3900         }
 3901         // Shouldn't happen
 3902         else
 3903         {
 3904             exit(EXIT_FAILURE);
 3905         }
 3906 
 3907         getApp()->endWaitCursor();
 3908         delete f;
 3909     }
 3910 
 3911     // Multiple sources
 3912     // Note : rename cannot be used in this case!
 3913     else if (num > 1)
 3914     {
 3915         // Wait cursor
 3916         getApp()->beginWaitCursor();
 3917 
 3918         // File object
 3919         if (command == "copy")
 3920         {
 3921             f = new File(this, _("File copy"), COPY, num);
 3922         }
 3923         else if (command == "move")
 3924         {
 3925             f = new File(this, _("File move"), MOVE, num);
 3926         }
 3927         else if (command == "symlink")
 3928         {
 3929             f = new File(this, _("Symlink"), SYMLINK, num);
 3930         }
 3931         // Shouldn't happen
 3932         else
 3933         {
 3934             exit(EXIT_FAILURE);
 3935         }
 3936         f->create();
 3937 
 3938         list->setAllowRefresh(false);
 3939 
 3940         // Loop on the multiple files
 3941         for (int i = 0; i < num; i++)
 3942         {
 3943             // Individual source file
 3944             source = src.section('\n', i);
 3945 
 3946             // File could have already been moved above in the tree
 3947             if (!::exists(source))
 3948             {
 3949                 continue;
 3950             }
 3951 
 3952             // An empty file name corresponds to the ".." file (why?)
 3953             // Don't perform any file operation on it!
 3954             if (source != "")
 3955             {
 3956                 if (command == "copy")
 3957                 {
 3958                     // If target file is located at trash location, also create the corresponding trashinfo file
 3959                     // Do it silently and don't report any error if it fails
 3960                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 3961                     if (use_trash_can && (target == trashfileslocation))
 3962                     {
 3963                         // Trash files path name
 3964                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
 3965 
 3966                         // Adjust target name to get the _N suffix if any
 3967                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
 3968 
 3969                         // Create trashinfo file
 3970                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
 3971 
 3972                         // Copy source to trash target
 3973                         ret = f->copy(source, trashtarget);
 3974                     }
 3975 
 3976                     // Copy source to target
 3977                     else
 3978                     {
 3979                         ret = f->copy(source, target);
 3980                     }
 3981 
 3982                     // An error has occurred
 3983                     if ((ret == 0) && !f->isCancelled())
 3984                     {
 3985                         f->hideProgressDialog();
 3986                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
 3987                         break;
 3988                     }
 3989 
 3990                     // If action is cancelled in progress dialog
 3991                     if (f->isCancelled())
 3992                     {
 3993                         f->hideProgressDialog();
 3994                         MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
 3995                         break;
 3996                     }
 3997                 }
 3998                 else if (command == "move")
 3999                 {
 4000                     // If target file is located at trash location, also create the corresponding trashinfo file
 4001                     // Do it silently and don't report any error if it fails
 4002                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
 4003                     if (use_trash_can && (target == trashfileslocation))
 4004                     {
 4005                         // Trash files path name
 4006                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
 4007 
 4008                         // Adjust target name to get the _N suffix if any
 4009                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
 4010 
 4011                         // Create trashinfo file
 4012                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
 4013 
 4014                         // Move source to trash target
 4015                         ret = f->move(source, trashtarget);
 4016                     }
 4017 
 4018                     // Move source to target
 4019                     else
 4020                     {
 4021                         ret = f->move(source, target);
 4022                     }
 4023 
 4024                     // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
 4025                     // Do it silently and don't report any error if it fails
 4026                     if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
 4027                     {
 4028                         FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
 4029                         ::unlink(trashinfopathname.text());
 4030                     }
 4031 
 4032                     // An error has occurred
 4033                     if ((ret == 0) && !f->isCancelled())
 4034                     {
 4035                         f->hideProgressDialog();
 4036                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
 4037                         break;
 4038                     }
 4039 
 4040                     // If action is cancelled in progress dialog
 4041                     if (f->isCancelled())
 4042                     {
 4043                         f->hideProgressDialog();
 4044                         MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
 4045                         break;
 4046                     }
 4047                 }
 4048                 else if (command == "symlink")
 4049                 {
 4050                     ret = f->symlink(source, target);
 4051 
 4052                     // An error has occurred
 4053                     if ((ret == 0) && !f->isCancelled())
 4054                     {
 4055                         f->hideProgressDialog();
 4056                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the symlink operation!"));
 4057                         break;
 4058                     }
 4059 
 4060                     // If action is cancelled in progress dialog
 4061                     if (f->isCancelled())
 4062                     {
 4063                         f->hideProgressDialog();
 4064                         MessageBox::error(this, BOX_OK, _("Warning"), _("Symlink operation cancelled!"));
 4065                         break;
 4066                     }
 4067                 }
 4068                 // Shouldn't happen
 4069                 else
 4070                 {
 4071                     exit(EXIT_FAILURE);
 4072                 }
 4073             }
 4074         }
 4075 
 4076         getApp()->endWaitCursor();
 4077         delete f;
 4078     }
 4079 
 4080     // Force list refresh
 4081     list->setAllowRefresh(true);
 4082     list->onCmdRefresh(0, 0, 0);
 4083 
 4084     return(1);
 4085 }
 4086 
 4087 
 4088 // Go to script directory
 4089 long SearchPanel::onCmdGoScriptDir(FXObject* o, FXSelector sel, void*)
 4090 {
 4091     FXString scriptpath = homedir + PATHSEPSTRING CONFIGPATH PATHSEPSTRING XFECONFIGPATH PATHSEPSTRING SCRIPTPATH;
 4092 
 4093     if (!::exists(scriptpath))
 4094     {
 4095         // Create the script directory according to the umask
 4096         int mask = umask(0);
 4097         umask(mask);
 4098         errno = 0;
 4099         int ret = mkpath(scriptpath.text(), 511 & ~mask);
 4100         int errcode = errno;
 4101         if (ret == -1)
 4102         {
 4103             if (errcode)
 4104             {
 4105                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't create script folder %s: %s"), scriptpath.text(), strerror(errcode));
 4106             }
 4107             else
 4108             {
 4109                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't create script folder %s"), scriptpath.text());
 4110             }
 4111 
 4112             return(0);
 4113         }
 4114     }
 4115 
 4116     // Change directory in Xfe
 4117     ((XFileExplorer*)mainWindow)->setDirectory(scriptpath);
 4118 
 4119     // Raise the Xfe window
 4120     ((XFileExplorer*)mainWindow)->raise();
 4121     ((XFileExplorer*)mainWindow)->setFocus();
 4122 
 4123     return(1);
 4124 }
 4125 
 4126 
 4127 // Clear file list and reset panel status
 4128 void SearchPanel::clearItems(void)
 4129 {
 4130     status->setText(_("0 item"));
 4131     list->clearItems();
 4132 }
 4133 
 4134 
 4135 // Update the status bar
 4136 long SearchPanel::onUpdStatus(FXObject* sender, FXSelector, void*)
 4137 {
 4138     // Update the status bar
 4139     int      item = -1;
 4140     FXString str, linkto;
 4141     char     size[64];
 4142     FXulong  sz = 0;
 4143 
 4144     FXString hsize = _("0 bytes");
 4145     FXString path = list->getDirectory();
 4146     int      num = list->getNumSelectedItems();
 4147 
 4148     item = list->getCurrentItem();
 4149 
 4150     if (num > 1)
 4151     {
 4152         int nbdirs = 0;
 4153         for (int u = 0; u < list->getNumItems(); u++)
 4154         {
 4155             if (list->isItemSelected(u) && !list->isItemDirectory(u))
 4156             {
 4157                 sz += list->getItemFileSize(u);
 4158 #if __WORDSIZE == 64
 4159                 snprintf(size, sizeof(size)-1, "%lu", sz);
 4160 #else
 4161                 snprintf(size, sizeof(size)-1, "%llu", sz);
 4162 #endif
 4163                 hsize = ::hSize(size);
 4164             }
 4165             
 4166             if (list->isItemDirectory(u))
 4167             {
 4168                 nbdirs++;
 4169             }
 4170         }
 4171 
 4172         // Don't count the '..' directory
 4173         if (nbdirs >= 1)
 4174         {
 4175             nbdirs--;
 4176         }
 4177         
 4178         int nbfiles = num - nbdirs;
 4179         if (nbdirs <= 1 && nbfiles <= 1)
 4180         {
 4181             str.format(_("%s in %s selected items (%s folder, %s file)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());      
 4182         }
 4183         else if (nbdirs <=1 && nbfiles > 1)
 4184         {
 4185             str.format(_("%s in %s selected items (%s folder, %s files)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());     
 4186         }
 4187         else if (nbdirs > 1 && nbfiles <= 1)
 4188         {
 4189             str.format(_("%s in %s selected items (%s folders, %s file)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());     
 4190         }
 4191         else
 4192         {
 4193             str.format(_("%s in %s selected items (%s folders, %s files)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());        
 4194         }
 4195     }
 4196     else
 4197     {
 4198         // Nothing selected
 4199         if ((num == 0) || (item < 0))
 4200         {
 4201             num = list->getNumItems();
 4202             if (num == 1)
 4203             {
 4204                 str = _("1 item");
 4205             }
 4206             else
 4207             {
 4208                 int nbdirs = 0;
 4209                 for (int u = 0; u < num; u++)
 4210                 {
 4211                     if (list->isItemDirectory(u))
 4212                     {
 4213                         nbdirs++;
 4214                     }
 4215                 }       
 4216 
 4217                 int nbfiles = num - nbdirs;
 4218                 str.format(_("%s items (%s folders, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
 4219                 if (nbdirs <= 1 && nbfiles <= 1)
 4220                 {
 4221                     str.format(_("%s items (%s folder, %s file)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());       
 4222                 }
 4223                 else if (nbdirs <=1 && nbfiles > 1)
 4224                 {
 4225                     str.format(_("%s items (%s folder, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());      
 4226                 }
 4227                 else if (nbdirs > 1 && nbfiles <= 1)
 4228                 {
 4229                     str.format(_("%s items (%s folders, %s file)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());      
 4230                 }
 4231                 else
 4232                 {
 4233                     str.format(_("%s items (%s folders, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());     
 4234                 }
 4235             }
 4236         }
 4237         else // num=1
 4238         {
 4239             FXString string = list->getItemText(item);
 4240             FXString name = string.section('\t', 0);
 4241             FXString type = string.section('\t', 3);
 4242 
 4243             FXString date = string.section('\t', 5);
 4244             FXString usr = string.section('\t', 6);
 4245             FXString grp = string.section('\t', 7);
 4246             FXString perm = string.section('\t', 8);
 4247 
 4248             if (type.contains(_("Broken link")))
 4249             {
 4250                 linkto = ::readLink(path+PATHSEPSTRING+name);
 4251                 str = name + "->" + linkto.text() + " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
 4252             }
 4253             else if (type.contains(_("Link")))
 4254             {
 4255                 linkto = ::readLink(path+PATHSEPSTRING+name);
 4256                 str = name + "->" + linkto.text() + " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
 4257             }
 4258             else
 4259             {
 4260                 for (int u = 0; u < list->getNumItems(); u++)
 4261                 {
 4262                     if (list->isItemSelected(u) && !list->isItemDirectory(u))
 4263                     {
 4264                         sz = list->getItemFileSize(u);
 4265 #if __WORDSIZE == 64
 4266                         snprintf(size, sizeof(size)-1, "%lu", sz);
 4267 #else
 4268                         snprintf(size, sizeof(size)-1, "%llu", sz);
 4269 #endif
 4270                         hsize = ::hSize(size);
 4271                         break;
 4272                     }
 4273                 }
 4274                 str = hsize+ " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
 4275             }
 4276         }
 4277     }
 4278 
 4279     status->setText(str);
 4280 
 4281     return(1);
 4282 }
 4283 
 4284 
 4285 // Update the status of the menu items that should be disabled
 4286 // when the number of selected items is not one
 4287 long SearchPanel::onUpdSelMult(FXObject* o, FXSelector sel, void*)
 4288 {
 4289     int num;
 4290 
 4291     num = list->getNumSelectedItems();
 4292 
 4293     if (num == 1)
 4294     {
 4295         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
 4296     }
 4297     else
 4298     {
 4299         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4300     }
 4301 
 4302     return(1);
 4303 }
 4304 
 4305 
 4306 // Update the file compare menu item
 4307 long SearchPanel::onUpdCompare(FXObject* o, FXSelector sel, void*)
 4308 {
 4309     // Menu item is enabled only when two files are selected
 4310     int num;
 4311 
 4312     num = list->getNumSelectedItems();
 4313 
 4314     if ((num == 1) || (num == 2))
 4315     {
 4316         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
 4317     }
 4318     else
 4319     {
 4320         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4321     }
 4322 
 4323     return(1);
 4324 }
 4325 
 4326 
 4327 // Update menu items and toolbar buttons that are related to file operations
 4328 long SearchPanel::onUpdMenu(FXObject* o, FXSelector sel, void*)
 4329 {
 4330     // Menu item is disabled when nothing is selected
 4331     int num;
 4332 
 4333     num = list->getNumSelectedItems();
 4334 
 4335     if (num == 0)
 4336     {
 4337         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4338     }
 4339     else
 4340     {
 4341         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
 4342     }
 4343 
 4344     return(1);
 4345 }
 4346 
 4347 
 4348 // Update directory usage menu item
 4349 long SearchPanel::onUpdDirUsage(FXObject* o, FXSelector, void*)
 4350 {
 4351     // Menu item is enabled only when at least two items are selected
 4352     int num, item;
 4353 
 4354     num = list->getNumSelectedItems(&item);
 4355     if (num > 1)
 4356     {
 4357         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
 4358     }
 4359     else
 4360     {
 4361         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4362     }
 4363     return(1);
 4364 }
 4365 
 4366 
 4367 #if defined(linux)
 4368 
 4369 // Query packages data base
 4370 long SearchPanel::onCmdPkgQuery(FXObject* o, FXSelector sel, void*)
 4371 {
 4372     FXString cmd;
 4373 
 4374     // Name of the current selected file
 4375     FXString file = list->getCurrentFile();
 4376 
 4377     // Command to perform
 4378     if (pkg_format == DEB_PKG)
 4379     {
 4380         cmd = "dpkg -S " + ::quote(file);
 4381     }
 4382     else if (pkg_format == RPM_PKG)
 4383     {
 4384         cmd = "rpm -qf " + ::quote(file);
 4385     }
 4386     else
 4387     {
 4388         MessageBox::error(this, BOX_OK, _("Error"), _("No compatible package manager (rpm or dpkg) found!"));
 4389         return(0);
 4390     }
 4391 
 4392     // Query command
 4393     cmd += " 2>&1";
 4394 
 4395     // Wait cursor
 4396     getApp()->beginWaitCursor();
 4397 
 4398     // Perform the command
 4399     FILE* pcmd = popen(cmd.text(), "r");
 4400     if (!pcmd)
 4401     {
 4402         MessageBox::error(this, BOX_OK, _("Error"), _("Failed command: %s"), cmd.text());
 4403         return(0);
 4404     }
 4405 
 4406     // Get command output
 4407     char     text[10000] = { 0 };
 4408     FXString buf;
 4409     while (fgets(text, sizeof(text), pcmd))
 4410     {
 4411         buf += text;
 4412     }
 4413     snprintf(text, sizeof(text)-1, "%s", buf.text());
 4414 
 4415     // Close the stream and display error message if any
 4416     if ((pclose(pcmd) == -1) && (errno != ECHILD))   // ECHILD can be set if the child was caught by sigHarvest
 4417     {
 4418         getApp()->endWaitCursor();
 4419         MessageBox::error(this, BOX_OK, _("Error"), "%s", text);
 4420         return(0);
 4421     }
 4422     getApp()->endWaitCursor();
 4423 
 4424     // Get package name, or detect when the file isn't in a package
 4425     FXString str = text;
 4426     if (pkg_format == DEB_PKG)  // DEB based distribution
 4427     {
 4428         int idx = str.find(" ");               // Split output at first whitespace
 4429         FXString pkgname = str.left(idx-1);    // Remove trailing colon
 4430         FXString fname = str.right(str.length()-idx);
 4431         fname.trim();                          // Remove leading space and trailing newline
 4432         if (streq(fname.text(), file.text()))  // No other word than the file name
 4433         {
 4434             str = pkgname.text();
 4435         }
 4436         else
 4437         {
 4438             str = "";
 4439         }
 4440     }
 4441     if (pkg_format == RPM_PKG)   // RPM based distribution
 4442     {
 4443         if (str.find(' ') != -1) // Space character exists in the string
 4444         {
 4445             str = "";
 4446         }
 4447     }
 4448 
 4449     // Display the related output message
 4450     FXString message;
 4451     if (str == "")
 4452     {
 4453         message.format(_("File %s does not belong to any package."), file.text());
 4454         MessageBox::information(this, BOX_OK, _("Information"), "%s", message.text());
 4455     }
 4456     else
 4457     {
 4458         message.format(_("File %s belongs to the package: %s"), file.text(), str.text());
 4459         MessageBox::information(this, BOX_OK, _("Information"), "%s", message.text());
 4460     }
 4461 
 4462     return(1);
 4463 }
 4464 
 4465 
 4466 // Update the package query menu
 4467 long SearchPanel::onUpdPkgQuery(FXObject* o, FXSelector sel, void*)
 4468 {
 4469     // Menu item is disabled when multiple selection
 4470     // or when unique selection and the selected item is a directory
 4471 
 4472     int num;
 4473 
 4474     num = list->getNumSelectedItems();
 4475 
 4476     if (num > 1)
 4477     {
 4478         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4479     }
 4480     else // num=1
 4481     {
 4482         int item = list->getCurrentItem();
 4483         if ((item >= 0) && list->isItemDirectory(item))
 4484         {
 4485             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
 4486         }
 4487         else
 4488         {
 4489             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
 4490         }
 4491     }
 4492 
 4493     return(1);
 4494 }
 4495 
 4496 
 4497 #endif