"Fossies" - the Fresh Open Source Software Archive

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