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 // Flag for FilePanel::onCmdItemDoubleClicked
74 extern FXbool called_from_iconlist;
75 
76 
77 // Map
78 FXDEFMAP(FilePanel) FilePanelMap[] =
79 {
80     FXMAPFUNC(SEL_CLIPBOARD_LOST, 0, FilePanel::onClipboardLost),
81     FXMAPFUNC(SEL_CLIPBOARD_GAINED, 0, FilePanel::onClipboardGained),
82     FXMAPFUNC(SEL_CLIPBOARD_REQUEST, 0, FilePanel::onClipboardRequest),
83     FXMAPFUNC(SEL_TIMEOUT, FilePanel::ID_STOP_LIST_REFRESH_TIMER, FilePanel::onCmdStopListRefreshTimer),
84     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_DIRECTORY_UP, FilePanel::onCmdDirectoryUp),
85     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILTER, FilePanel::onCmdItemFilter),
86     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILTER_CURRENT, FilePanel::onCmdItemFilter),
87     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_GO_HOME, FilePanel::onCmdGoHome),
88     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_GO_TRASH, FilePanel::onCmdGoTrash),
89     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_VIEW, FilePanel::onCmdEdit),
90     FXMAPFUNC(SEL_MIDDLEBUTTONPRESS, FilePanel::ID_FILELIST, FilePanel::onCmdEdit),
91     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_EDIT, FilePanel::onCmdEdit),
92     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_COMPARE, FilePanel::onCmdCompare),
93     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_PROPERTIES, FilePanel::onCmdProperties),
94     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_XTERM, FilePanel::onCmdXTerm),
95     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_NEW_DIR, FilePanel::onCmdNewDir),
96     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_NEW_FILE, FilePanel::onCmdNewFile),
97     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_NEW_SYMLINK, FilePanel::onCmdNewSymlink),
98     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_COPY, FilePanel::onCmdFileMan),
99     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_CUT, FilePanel::onCmdFileMan),
100     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_COPYTO, FilePanel::onCmdFileMan),
101     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_MOVETO, FilePanel::onCmdFileMan),
102     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_RENAME, FilePanel::onCmdFileMan),
103     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_SYMLINK, FilePanel::onCmdFileMan),
104     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_COPY_CLIPBOARD, FilePanel::onCmdCopyCut),
105     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_CUT_CLIPBOARD, FilePanel::onCmdCopyCut),
106     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_ADDCOPY_CLIPBOARD, FilePanel::onCmdCopyCut),
107     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_ADDCUT_CLIPBOARD, FilePanel::onCmdCopyCut),
108     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_PASTE_CLIPBOARD, FilePanel::onCmdPaste),
109     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_TRASH, FilePanel::onCmdFileTrash),
110     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_RESTORE, FilePanel::onCmdFileRestore),
111     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_FILE_DELETE, FilePanel::onCmdFileDelete),
112     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_OPEN_WITH, FilePanel::onCmdOpenWith),
113     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_OPEN, FilePanel::onCmdOpen),
114     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_REFRESH, FilePanel::onCmdRefresh),
115     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_SHOW_BIG_ICONS, FilePanel::onCmdShow),
116     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_SHOW_MINI_ICONS, FilePanel::onCmdShow),
117     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_SHOW_DETAILS, FilePanel::onCmdShow),
118     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_TOGGLE_HIDDEN, FilePanel::onCmdToggleHidden),
119     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_TOGGLE_THUMBNAILS, FilePanel::onCmdToggleThumbnails),
120     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_SELECT_ALL, FilePanel::onCmdSelect),
121     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_DESELECT_ALL, FilePanel::onCmdSelect),
122     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_SELECT_INVERSE, FilePanel::onCmdSelect),
123     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_ADD_TO_ARCH, FilePanel::onCmdAddToArch),
124     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_EXTRACT, FilePanel::onCmdExtract),
125     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_EXTRACT_TO_FOLDER, FilePanel::onCmdExtractToFolder),
126     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_EXTRACT_HERE, FilePanel::onCmdExtractHere),
127     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_RUN_SCRIPT, FilePanel::onCmdRunScript),
128     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_RUN_SCRIPT, FilePanel::onUpdRunScript),
129     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_GO_SCRIPTDIR, FilePanel::onCmdGoScriptDir),
130     //FXMAPFUNC(SEL_COMMAND, FilePanel::ID_DIR_USAGE, FilePanel::onCmdDirUsage),
131     FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, FilePanel::ID_FILELIST, FilePanel::onCmdPopupMenu),
132     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_POPUP_MENU, FilePanel::onCmdPopupMenu),
133     FXMAPFUNC(SEL_DOUBLECLICKED, FilePanel::ID_FILELIST, FilePanel::onCmdItemDoubleClicked),
134     FXMAPFUNC(SEL_CLICKED, FilePanel::ID_FILELIST, FilePanel::onCmdItemClicked),
135     FXMAPFUNC(SEL_FOCUSIN, FilePanel::ID_FILELIST, FilePanel::onCmdFocus),
136     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_STATUS, FilePanel::onUpdStatus),
137     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_DIRECTORY_UP, FilePanel::onUpdUp),
138     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_COPY_CLIPBOARD, FilePanel::onUpdMenu),
139     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_CUT_CLIPBOARD, FilePanel::onUpdMenu),
140     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_PASTE_CLIPBOARD, FilePanel::onUpdPaste),
141     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_PROPERTIES, FilePanel::onUpdMenu),
142     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_TRASH, FilePanel::onUpdFileTrash),
143     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_RESTORE, FilePanel::onUpdFileRestore),
144     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_GO_TRASH, FilePanel::onUpdGoTrash),
145     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_DELETE, FilePanel::onUpdFileDelete),
146     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_MOVETO, FilePanel::onUpdMenu),
147     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_COPYTO, FilePanel::onUpdMenu),
148     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_FILE_RENAME, FilePanel::onUpdSelMult),
149     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_COMPARE, FilePanel::onUpdCompare),
150     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_EDIT, FilePanel::onUpdOpen),
151     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_VIEW, FilePanel::onUpdOpen),
152     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_OPEN, FilePanel::onUpdOpen),
153     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_ADD_TO_ARCH, FilePanel::onUpdAddToArch),
154     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_SHOW_BIG_ICONS, FilePanel::onUpdShow),
155     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_SHOW_MINI_ICONS, FilePanel::onUpdShow),
156     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_SHOW_DETAILS, FilePanel::onUpdShow),
157     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_TOGGLE_HIDDEN, FilePanel::onUpdToggleHidden),
158     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_TOGGLE_THUMBNAILS, FilePanel::onUpdToggleThumbnails),
159     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_DIR_USAGE, FilePanel::onUpdDirUsage),
160 #if defined(linux)
161     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_MOUNT, FilePanel::onCmdMount),
162     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_UMOUNT, FilePanel::onCmdMount),
163     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_MOUNT, FilePanel::onUpdMount),
164     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_UMOUNT, FilePanel::onUpdUnmount),
165     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_PKG_QUERY, FilePanel::onCmdPkgQuery),
166     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_PKG_INSTALL, FilePanel::onCmdPkgInstall),
167     FXMAPFUNC(SEL_COMMAND, FilePanel::ID_PKG_UNINSTALL, FilePanel::onCmdPkgUninstall),
168     FXMAPFUNC(SEL_UPDATE, FilePanel::ID_PKG_QUERY, FilePanel::onUpdPkgQuery),
169 #endif
170 };
171 
172 // Object implementation
FXIMPLEMENT(FilePanel,FXVerticalFrame,FilePanelMap,ARRAYNUMBER (FilePanelMap))173 FXIMPLEMENT(FilePanel, FXVerticalFrame, FilePanelMap, ARRAYNUMBER(FilePanelMap))
174 
175 // Construct File Panel
176 FilePanel::FilePanel(FXWindow* owner, const char* nm, FXComposite* p, DirPanel* dp, FXuint name_size, FXuint size_size, FXuint type_size, FXuint ext_size,
177                      FXuint modd_size, FXuint user_size, FXuint grou_size, FXuint attr_size, FXuint deldate_size, FXuint origpath_size, FXbool showthumbs, FXColor listbackcolor, FXColor listforecolor,
178                      FXColor attentioncolor, FXbool smoothscroll, FXuint opts, int x, int y, int w, int h) :
179     FXVerticalFrame(p, opts, x, y, w, h, 0, 0, 0, 0)
180 {
181     name = nm;
182     dirpanel = dp;
183     attenclr = attentioncolor;
184 
185     // Global container
186     FXVerticalFrame* cont = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
187 
188     // Container for the path linker
189     FXHorizontalFrame* pathframe = new FXHorizontalFrame(cont, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0);
190 
191     // File list
192 
193     // Smooth scrolling
194     FXuint options;
195     if (smoothscroll)
196     {
197         options = LAYOUT_FILL_X|LAYOUT_FILL_Y|_ICONLIST_MINI_ICONS;
198     }
199     else
200     {
201         options = LAYOUT_FILL_X|LAYOUT_FILL_Y|_ICONLIST_MINI_ICONS|SCROLLERS_DONT_TRACK;
202     }
203 
204     FXVerticalFrame* cont2 = new FXVerticalFrame(cont, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_SUNKEN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
205     list = new FileList(owner, cont2, this, ID_FILELIST, showthumbs, options);
206     list->setHeaderSize(0, name_size);
207     list->setHeaderSize(1, size_size);
208     list->setHeaderSize(2, type_size);
209     list->setHeaderSize(3, ext_size);
210     list->setHeaderSize(4, modd_size);
211     list->setHeaderSize(5, user_size);
212     list->setHeaderSize(6, grou_size);
213     list->setHeaderSize(7, attr_size);
214     list->setHeaderSize(8, deldate_size);
215     list->setHeaderSize(9, origpath_size);
216     list->setTextColor(listforecolor);
217     list->setBackColor(listbackcolor);
218 
219     // Visually indicate if the panel is active
220     activeicon = new FXButton(pathframe, "", graybuttonicon, this, FilePanel::ID_FILELIST, BUTTON_TOOLBAR|JUSTIFY_LEFT|LAYOUT_LEFT);
221 
222     // Path text
223     pathtext = new TextLabel(pathframe, 0, this, ID_FILELIST, LAYOUT_FILL_X|LAYOUT_FILL_Y);
224     pathtext->setBackColor(getApp()->getBaseColor());
225 
226     // Path linker
227     pathlink = new PathLinker(pathframe, list, dirpanel->getList(), JUSTIFY_LEFT|LAYOUT_LEFT|LAYOUT_FILL_X);
228 
229     // Status bar
230     statusbar = new FXHorizontalFrame(cont, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 3, 3, 3, 3);
231 
232     statusbar->setTarget(this);
233     statusbar->setSelector(FXSEL(SEL_UPDATE, FilePanel::ID_STATUS));
234 
235     FXString key = getApp()->reg().readStringEntry("KEYBINDINGS", "hidden_files", "Ctrl-F6");
236     new FXToggleButton(statusbar, TAB+_("Show hidden files")+PARS(key), TAB+_("Hide hidden files")+PARS(key), showhiddenicon, hidehiddenicon, this->list,
237                        FileList::ID_TOGGLE_HIDDEN, TOGGLEBUTTON_TOOLBAR|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
238 
239     key = getApp()->reg().readStringEntry("KEYBINDINGS", "thumbnails", "Ctrl-F7");
240     new FXToggleButton(statusbar, TAB+_("Show thumbnails")+PARS(key), TAB+_("Hide thumbnails")+PARS(key), showthumbicon, hidethumbicon, this->list,
241                        FileList::ID_TOGGLE_THUMBNAILS, TOGGLEBUTTON_TOOLBAR|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
242 
243     key = getApp()->reg().readStringEntry("KEYBINDINGS", "filter", "Ctrl-D");
244     new FXButton(statusbar, TAB+_("Filter")+PARS(key), filtericon, this,
245                  FilePanel::ID_FILTER, BUTTON_TOOLBAR|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_RAISED);
246 
247     FXHorizontalFrame* hframe = new FXHorizontalFrame(statusbar, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0);
248     statuslabel = new FXLabel(hframe, _("Status"), NULL, JUSTIFY_LEFT|LAYOUT_LEFT);
249     filterlabel = new FXLabel(hframe, "", NULL, JUSTIFY_LEFT|LAYOUT_LEFT|LAYOUT_FILL_X);
250 
251     corner = new FXDragCorner(statusbar);
252 
253     // Panel separator
254     panelsep = new FXHorizontalSeparator(cont, SEPARATOR_GROOVE|LAYOUT_FILL_X);
255 
256     // Initializations
257     selmult = false;
258     current = NULL;
259 
260     // Single click navigation
261     single_click = getApp()->reg().readUnsignedEntry("SETTINGS", "single_click", SINGLE_CLICK_NONE);
262     if (single_click == SINGLE_CLICK_DIR_FILE)
263     {
264         list->setDefaultCursor(getApp()->getDefaultCursor(DEF_HAND_CURSOR));
265     }
266 
267     // Dialogs
268     operationdialogsingle = NULL;
269     operationdialogrename = NULL;
270     operationdialogmultiple = NULL;
271     newdirdialog = NULL;
272     newfiledialog = NULL;
273     newlinkdialog = NULL;
274     opendialog = NULL;
275     archdialog = NULL;
276     filterdialog = NULL;
277     comparedialog = NULL;
278 
279     // Home and trahscan locations
280     trashlocation = xdgdatahome+PATHSEPSTRING TRASHPATH;
281     trashfileslocation = xdgdatahome + PATHSEPSTRING TRASHFILESPATH;
282     trashinfolocation = xdgdatahome + PATHSEPSTRING TRASHINFOPATH;
283 
284     // Start location (we return to the start location after each chdir)
285     startlocation = FXSystem::getCurrentDirectory();
286 
287     // Initialize clipboard flags
288     clipboard_locked = false;
289     fromPaste = false;
290 
291     // Initialize control flag for right click popup menu
292     ctrl = false;
293 
294     // Initialize the Shift-F10 flag
295     shiftf10 = false;
296 
297     // Initialize the active panel flag
298     isactive = false;
299 
300 	// Initialize the flag used in FilePanel::onCmdItemDoubleClicked
301 	called_from_iconlist = false;
302 
303     // Default programs identifiers
304     progs["<txtviewer>"] = TXTVIEWER;
305     progs["<txteditor>"] = TXTEDITOR;
306     progs["<imgviewer>"] = IMGVIEWER;
307     progs["<imgeditor>"] = IMGEDITOR;
308     progs["<pdfviewer>"] = PDFVIEWER;
309     progs["<audioplayer>"] = AUDIOPLAYER;
310     progs["<videoplayer>"] = VIDEOPLAYER;
311     progs["<archiver>"] = ARCHIVER;
312 }
313 
314 
315 // Create X window
create()316 void FilePanel::create()
317 {
318     // Register standard uri-list type
319     urilistType = getApp()->registerDragType("text/uri-list");
320 
321     // Register special uri-list type used for Gnome, XFCE and Xfe
322     xfelistType = getApp()->registerDragType("x-special/gnome-copied-files");
323 
324     // Register special uri-list type used for KDE
325     kdelistType = getApp()->registerDragType("application/x-kde-cutselection");
326 
327     // Register standard UTF-8 text type used for file dialogs
328     utf8Type = getApp()->registerDragType("UTF8_STRING");
329 
330     // Display or hide path linker
331     FXbool show_pathlink = getApp()->reg().readUnsignedEntry("SETTINGS", "show_pathlinker", true);
332     if (show_pathlink)
333     {
334         pathtext->hide();
335         pathlink->show();
336     }
337     else
338     {
339         pathtext->show();
340         pathlink->hide();
341     }
342 
343     FXVerticalFrame::create();
344 }
345 
346 
347 // Destructor
~FilePanel()348 FilePanel::~FilePanel()
349 {
350     delete list;
351     delete current;
352     delete next;
353     delete statuslabel;
354     delete filterlabel;
355     delete statusbar;
356     delete panelsep;
357     delete pathlink;
358     delete newfiledialog;
359     delete newlinkdialog;
360     delete newdirdialog;
361     delete opendialog;
362     delete archdialog;
363     delete filterdialog;
364     delete comparedialog;
365     delete operationdialogsingle;
366     delete operationdialogrename;
367     delete operationdialogmultiple;
368     delete pathtext;
369 }
370 
371 
372 // Make panel active
setActive()373 void FilePanel::setActive()
374 {
375     // Set active icon
376     activeicon->setIcon(greenbuttonicon);
377     activeicon->setTipText(_("Panel is active"));
378 
379     pathlink->focus();
380     current = this;
381 
382     // Make dirpanel point on the current directory,
383     // but only if Filepanel and Dirpanel directories are different
384     if (dirpanel->getDirectory() != current->list->getDirectory())
385     {
386         dirpanel->setDirectory(current->list->getDirectory(), true);
387     }
388 
389     // Make dirpanel inactive
390     dirpanel->setInactive();
391 
392     next->setInactive();
393     list->setFocus();
394 
395     isactive = true;
396 }
397 
398 
399 // Make panel inactive
setInactive(FXbool force)400 void FilePanel::setInactive(FXbool force)
401 {
402     // Set active icon
403     activeicon->setIcon(graybuttonicon);
404     activeicon->setTipText(_("Activate panel"));
405 
406     // By default we set the panel inactive
407     if (force)
408     {
409         current = next;
410         list->handle(this, FXSEL(SEL_COMMAND, FileList::ID_DESELECT_ALL), NULL);
411 
412         isactive = false;
413     }
414 }
415 
416 
417 // Make panel focus (i.e. active) when clicked
onCmdFocus(FXObject * sender,FXSelector sel,void * ptr)418 long FilePanel::onCmdFocus(FXObject* sender, FXSelector sel, void* ptr)
419 {
420     setActive();
421     return(1);
422 }
423 
424 
425 // Set Pointer to Another FilePanel
Next(FilePanel * nxt)426 void FilePanel::Next(FilePanel* nxt)
427 {
428     next = nxt;
429 }
430 
431 
432 // Show or hide drag corner
showCorner(FXbool show)433 void FilePanel::showCorner(FXbool show)
434 {
435     if (show)
436     {
437         corner->show();
438     }
439     else
440     {
441         corner->hide();
442     }
443 }
444 
445 
446 // Show or hide active icon
showActiveIcon(FXbool show)447 void FilePanel::showActiveIcon(FXbool show)
448 {
449     if (show)
450     {
451         activeicon->show();
452     }
453     else
454     {
455         activeicon->hide();
456     }
457 }
458 
459 
460 // Update location history when changing directory (home, up or double click)
updateLocation()461 void FilePanel::updateLocation()
462 {
463     FXString    item;
464     int         i = 0;
465     FXComboBox* address = ((XFileExplorer*)mainWindow)->getAddressBox();
466 
467     address->setNumVisible(5);
468     int      count = address->getNumItems();
469     FXString p = list->getDirectory();
470 
471     // Remember latest directory in the location address
472     if (!count)
473     {
474         count++;
475         address->insertItem(0, address->getText());
476     }
477     while (i < count)
478     {
479         item = address->getItem(i++);
480         if (streq((const char*)&p[0], (const char*)&item[0]))
481         {
482             i--;
483             break;
484         }
485     }
486     if (i == count)
487     {
488         address->insertItem(0, list->getDirectory());
489     }
490 
491     // Make current directory visible to avoid scrolling again
492     list->makeItemVisible(list->getCurrentItem());
493 }
494 
495 
496 // We now really do have the clipboard, keep clipboard content
onClipboardGained(FXObject * sender,FXSelector sel,void * ptr)497 long FilePanel::onClipboardGained(FXObject* sender, FXSelector sel, void* ptr)
498 {
499     FXVerticalFrame::onClipboardGained(sender, sel, ptr);
500     return(1);
501 }
502 
503 
504 // We lost the clipboard
onClipboardLost(FXObject * sender,FXSelector sel,void * ptr)505 long FilePanel::onClipboardLost(FXObject* sender, FXSelector sel, void* ptr)
506 {
507     FXVerticalFrame::onClipboardLost(sender, sel, ptr);
508     return(1);
509 }
510 
511 
512 // Somebody wants our clipboard content
onClipboardRequest(FXObject * sender,FXSelector sel,void * ptr)513 long FilePanel::onClipboardRequest(FXObject* sender, FXSelector sel, void* ptr)
514 {
515     FXEvent* event = (FXEvent*)ptr;
516     FXuchar* data;
517     FXuint   len;
518 
519     // Perhaps the target wants to supply its own data for the clipboard
520     if (FXVerticalFrame::onClipboardRequest(sender, sel, ptr))
521     {
522         return(1);
523     }
524 
525     // Clipboard target is xfelistType (Xfe, Gnome or XFCE)
526     if (event->target == xfelistType)
527     {
528         // Don't modify the clipboard if we are called from updPaste()
529         if (!clipboard_locked)
530         {
531             // Prepend "copy" or "cut" as in the Gnome way and avoid duplicating these strings
532             if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
533             {
534                 if (clipboard_type == CUT_CLIPBOARD)
535                 {
536                     clipboard = "cut\n" + clipboard;
537                 }
538                 else
539                 {
540                     clipboard = "copy\n" + clipboard;
541                 }
542             }
543         }
544 
545         // Return clipboard content
546         if (event->target == xfelistType)
547         {
548             if (!clipboard.empty())
549             {
550                 len = clipboard.length();
551                 FXMEMDUP(&data, clipboard.text(), FXuchar, len);
552                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
553 
554                 // Return because xfelistType is not compatible with other types
555                 return(1);
556             }
557         }
558     }
559 
560     // Clipboard target is kdelisType (KDE)
561     if (event->target == kdelistType)
562     {
563         // The only data to be passed in this case is "0" for copy and "1" for cut
564         // The uri data are passed using the standard uri-list type
565         FXString flag;
566         if (clipboard_type == CUT_CLIPBOARD)
567         {
568             flag = "1";
569         }
570         else
571         {
572             flag = "0";
573         }
574 
575         // Return clipboard content
576         if (event->target == kdelistType)
577         {
578             FXMEMDUP(&data, flag.text(), FXuchar, 1);
579             setDNDData(FROM_CLIPBOARD, event->target, data, 1);
580         }
581     }
582 
583     // Clipboard target is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
584     if (event->target == urilistType)
585     {
586         if (!clipboard.empty())
587         {
588             len = clipboard.length();
589             FXMEMDUP(&data, clipboard.text(), FXuchar, len);
590             setDNDData(FROM_CLIPBOARD, event->target, data, len);
591 
592             return(1);
593         }
594     }
595 
596     // Clipboard target is utf8Type (to paste file pathes as text to other applications)
597     if (event->target == utf8Type)
598     {
599         if (!clipboard.empty())
600         {
601             int      beg = 0, end = 0;
602             FXString str = "";
603             FXString pathname, url;
604 
605             // Clipboard don't contain 'copy\n' or 'cut\n' as first line
606             if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
607             {
608                 // Remove the 'file:' prefix for each file path
609                 while (1)
610                 {
611                     end = clipboard.find('\n', end);
612                     if (end < 0) // Last line
613                     {
614                         end = clipboard.length();
615                         url = clipboard.mid(beg, end-beg+1);
616                         pathname = FXURL::decode(FXURL::fileFromURL(url));
617                         str += pathname;
618                         break;
619                     }
620                     url = clipboard.mid(beg, end-beg+1);
621                     pathname = FXURL::decode(FXURL::fileFromURL(url));
622                     str += pathname;
623                     end++;
624                     beg = end;
625                 }
626                 end = str.length();
627             }
628 
629             // Clipboard contains 'copy\n' or 'cut\n' as first line, thus skip it
630             else
631             {
632                 // Start after the 'copy\n' or 'cut\n' prefix
633                 end = clipboard.find('\n', 0);
634                 end++;
635                 beg = end;
636 
637                 // Remove the 'file:' prefix for each file path
638                 while (1)
639                 {
640                     end = clipboard.find('\n', end);
641                     if (end < 0) // Last line
642                     {
643                         end = clipboard.length();
644                         url = clipboard.mid(beg, end-beg+1);
645                         pathname = FXURL::decode(FXURL::fileFromURL(url));
646                         str += pathname;
647                         break;
648                     }
649                     url = clipboard.mid(beg, end-beg+1);
650                     pathname = FXURL::decode(FXURL::fileFromURL(url));
651                     str += pathname;
652                     end++;
653                     beg = end;
654                 }
655                 end = str.length();
656             }
657 
658             if (!str.empty())
659             {
660                 len = str.length();
661                 FXMEMDUP(&data, str.text(), FXuchar, len);
662                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
663 
664                 return(1);
665             }
666         }
667     }
668     return(0);
669 }
670 
671 
672 // Copy or cut to clipboard (and add copy / add cut)
onCmdCopyCut(FXObject *,FXSelector sel,void *)673 long FilePanel::onCmdCopyCut(FXObject*, FXSelector sel, void*)
674 {
675     FXString name, curdir;
676 
677     // Clear clipboard if normal copy or cut
678     if ((FXSELID(sel) == ID_COPY_CLIPBOARD) || (FXSELID(sel) == ID_CUT_CLIPBOARD))
679     {
680         clipboard.clear();
681     }
682 
683     // Add an '\n' at the end if addcopy or addcut
684     else
685     {
686         clipboard += '\n';
687     }
688 
689     // Clipboard type
690     if ((FXSELID(sel) == ID_CUT_CLIPBOARD) || (FXSELID(sel) == ID_ADDCUT_CLIPBOARD))
691     {
692         clipboard_type = CUT_CLIPBOARD;
693     }
694     else
695     {
696         clipboard_type = COPY_CLIPBOARD;
697     }
698 
699     // Items number in the file list
700     int num = current->list->getNumSelectedItems();
701 
702     if (num == 0)
703     {
704         return(0);
705     }
706 
707     // If exist selected files, use them
708     else if (num >= 1)
709     {
710         // Eventually deselect the '..' directory
711         if (current->list->isItemSelected(0))
712         {
713             current->list->deselectItem(0);
714         }
715 
716         // Construct the uri list of files and fill the clipboard with it
717         curdir = current->list->getDirectory();
718 
719         for (int u = 0; u < current->list->getNumItems(); u++)
720         {
721             if (current->list->isItemSelected(u))
722             {
723                 name = current->list->getItemText(u).text();
724                 name = name.section('\t', 0);
725                 if (curdir == ROOTDIR)
726                 {
727                     clipboard += FXURL::encode(::fileToURI(curdir+name))+"\n";
728                 }
729                 else
730                 {
731                     clipboard += FXURL::encode(::fileToURI(curdir+PATHSEPSTRING+name))+"\n";
732                 }
733             }
734         }
735     }
736 
737     // Remove the last \n of the list, for compatibility with some file managers (e.g. nautilus, nemo)
738     clipboard.erase(clipboard.length()-1);
739 
740     // Acquire the clipboard
741     FXDragType types[4];
742     types[0] = xfelistType;
743     types[1] = kdelistType;
744     types[2] = urilistType;
745     types[3] = utf8Type;
746     if (acquireClipboard(types, 4))
747     {
748         return(0);
749     }
750 
751     return(1);
752 }
753 
754 
755 // Paste file(s) from clipboard
onCmdPaste(FXObject *,FXSelector sel,void *)756 long FilePanel::onCmdPaste(FXObject*, FXSelector sel, void*)
757 {
758     FXuchar* data;
759     FXuint   len;
760     int      beg, end, pos;
761     FXString url, param;
762     int      num = 0;
763     FXbool   from_kde = false;
764 
765     // If source is xfelistType (Gnome, XFCE, or Xfe app)
766     if (getDNDData(FROM_CLIPBOARD, xfelistType, data, len))
767     {
768         FXRESIZE(&data, FXuchar, len+1);
769         data[len] = '\0';
770 
771         clipboard = (char*)data;
772 
773         // Loop over clipboard items
774         for (beg = 0; beg < clipboard.length(); beg = end+1)
775         {
776             if ((end = clipboard.find("\n", beg)) < 0)
777             {
778                 end = clipboard.length();
779             }
780 
781             // Obtain item url
782             url = clipboard.mid(beg, end-beg);
783 
784             // Eventually remove the trailing '\r' if any
785             if ((pos = url.rfind('\r')) > 0)
786             {
787                 url.erase(pos);
788             }
789 
790             // Process first item
791             if (num == 0)
792             {
793                 // First item should be "copy" or "cut"
794                 if (url == "copy")
795                 {
796                     clipboard_type = COPY_CLIPBOARD;
797                     num++;
798                 }
799                 else if (url == "cut")
800                 {
801                     clipboard_type = CUT_CLIPBOARD;
802                     num++;
803                 }
804 
805                 // If first item is not "copy" nor "cut", process it as a normal url
806                 // and use default clipboard type
807                 else
808                 {
809                     // Update the param string
810                     param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
811 
812                     // Add one more because the first line "copy" or "cut" was not present
813                     num += 2;
814                 }
815             }
816 
817             // Process other items
818             else
819             {
820                 // Update the param string
821                 param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
822                 num++;
823             }
824         }
825 
826         // Construct the final param string passed to the file management routine
827         param = current->list->getDirectory()+"\n" + FXStringVal(num-1) + "\n" + param;
828 
829         // Copy or cut operation depending on the clipboard type
830         switch (clipboard_type)
831         {
832         case COPY_CLIPBOARD:
833             sel = FXSEL(SEL_COMMAND, FilePanel::ID_FILE_COPY);
834             break;
835 
836         case CUT_CLIPBOARD:
837             clipboard.clear();
838             sel = FXSEL(SEL_COMMAND, FilePanel::ID_FILE_CUT);
839             break;
840         }
841         fromPaste = true;
842         handle(this, sel, (void*)param.text());
843 
844         // Free data pointer
845         FXFREE(&data);
846 
847         // Return here because xfelistType is not compatible with other types
848         return(1);
849     }
850 
851     // If source type is kdelistType (KDE)
852     if (getDNDData(FROM_CLIPBOARD, kdelistType, data, len))
853     {
854         from_kde = true;
855 
856         FXRESIZE(&data, FXuchar, len+1);
857         data[len] = '\0';
858         clipboard = (char*)data;
859 
860         // Obtain clipboard type (copy or cut)
861         if (clipboard == "1")
862         {
863             clipboard_type = CUT_CLIPBOARD;
864         }
865         else
866         {
867             clipboard_type = COPY_CLIPBOARD;
868         }
869 
870         FXFREE(&data);
871     }
872 
873     // If source type is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
874     if (getDNDData(FROM_CLIPBOARD, urilistType, data, len))
875     {
876         // For non KDE apps, set action to copy
877         if (!from_kde)
878         {
879             clipboard_type = COPY_CLIPBOARD;
880         }
881 
882         FXRESIZE(&data, FXuchar, len+1);
883         data[len] = '\0';
884         clipboard = (char*)data;
885 
886         // Loop over clipboard items
887         for (beg = 0; beg < clipboard.length(); beg = end+1)
888         {
889             if ((end = clipboard.find("\n", beg)) < 0)
890             {
891                 end = clipboard.length();
892             }
893 
894             // Obtain item url
895             url = clipboard.mid(beg, end-beg);
896 
897             // Eventually remove the trailing '\r' if any
898             if ((pos = url.rfind('\r')) > 0)
899             {
900                 url.erase(pos);
901             }
902 
903             // Update the param string
904             param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
905             num++;
906         }
907 
908         // Construct the final param string passed to the file management routine
909         param = current->list->getDirectory()+"\n" + FXStringVal(num) + "\n" + param;
910 
911         // Copy or cut operation depending on the clipboard type
912         switch (clipboard_type)
913         {
914         case COPY_CLIPBOARD:
915             sel = FXSEL(SEL_COMMAND, FilePanel::ID_FILE_COPY);
916             break;
917 
918         case CUT_CLIPBOARD:
919             clipboard.clear();
920             sel = FXSEL(SEL_COMMAND, FilePanel::ID_FILE_CUT);
921             break;
922         }
923         fromPaste = true;
924         handle(this, sel, (void*)param.text());
925 
926         FXFREE(&data);
927         return(1);
928     }
929 
930     // If source type is utf8Type (simple text)
931     if (getDNDData(FROM_CLIPBOARD, utf8Type, data, len))
932     {
933         FXRESIZE(&data, FXuchar, len+1);
934         data[len] = '\0';
935         clipboard = (char*)data;
936 
937         // Loop over clipboard items
938         FXString filepath;
939         for (beg = 0; beg < clipboard.length(); beg = end+1)
940         {
941             if ((end = clipboard.find("\n", beg)) < 0)
942             {
943                 end = clipboard.length();
944             }
945 
946             // Obtain item file path
947             filepath = clipboard.mid(beg, end-beg);
948 
949             // Eventually remove the trailing '\r' if any
950             if ((pos = filepath.rfind('\r')) > 0)
951             {
952                 filepath.erase(pos);
953             }
954 
955             // Update the param string
956             param += filepath + "\n";
957             num++;
958         }
959 
960         // Construct the final param string passed to the file management routine
961         param = current->list->getDirectory()+"\n" + FXStringVal(num) + "\n" + param;
962 
963         // Copy
964         sel = FXSEL(SEL_COMMAND, FilePanel::ID_FILE_COPY);
965         fromPaste = true;
966         handle(this, sel, (void*)param.text());
967 
968         FXFREE(&data);
969         return(1);
970     }
971 
972     return(0);
973 
974 }
975 
976 
977 // Execute file with an optional confirm dialog
execFile(FXString pathname)978 void FilePanel::execFile(FXString pathname)
979 {
980     int      ret;
981     FXString cmd, cmdname;
982 
983 #ifdef STARTUP_NOTIFICATION
984     // Startup notification option and exceptions (if any)
985     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
986     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
987 #endif
988 
989     // File is executable, but is it a text file?
990     FXString str = mimetype(pathname);
991     FXbool   isTextFile = true;
992     if (strstr(str.text(), "charset=binary"))
993     {
994         isTextFile = false;
995     }
996 
997 	// Text file
998 	if (isTextFile)
999 	{
1000 		// Execution forbidden, edit the file
1001     	FXbool no_script = getApp()->reg().readUnsignedEntry("OPTIONS", "no_script", false);
1002 		if (no_script)
1003 		{
1004 			FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
1005 			cmd = txteditor;
1006 			cmdname = cmd;
1007 
1008 			// If command exists, run it
1009 			if (::existCommand(cmdname))
1010 			{
1011 				cmd = cmdname + " " + ::quote(pathname);
1012 #ifdef STARTUP_NOTIFICATION
1013 	            runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
1014 #else
1015 	            runcmd(cmd, current->list->getDirectory(), startlocation);
1016 #endif
1017         	}
1018 
1019 			// If command does not exist, call the "Open with..." dialog
1020 			else
1021 			{
1022 				current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1023 			}
1024 		}
1025 
1026 		// Execution allowed, execute file with optional confirmation dialog
1027 		else
1028 		{
1029 			// With confirmation dialog
1030 			FXbool confirm_execute = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_execute", true);
1031 			if (confirm_execute)
1032 			{
1033 				FXString msg;
1034 				msg.format(_("File %s is an executable text file, what do you want to do?"), pathname.text());
1035 				ExecuteBox* dlg = new ExecuteBox(this, _("Confirm Execute"), msg);
1036 				FXuint      answer = dlg->execute(PLACEMENT_CURSOR);
1037 				delete dlg;
1038 
1039 				// Execute
1040 				if (answer == EXECBOX_CLICKED_EXECUTE)
1041 				{
1042 					cmdname = FXPath::name(pathname);
1043 					cmd = ::quote(pathname);
1044 #ifdef STARTUP_NOTIFICATION
1045 					// No startup notification in this case
1046 					runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, false, "");
1047 #else
1048 					runcmd(cmd, current->list->getDirectory(), startlocation);
1049 #endif
1050 				}
1051 
1052 				// Execute in console mode
1053 				if (answer == EXECBOX_CLICKED_CONSOLE)
1054 				{
1055 					ret = chdir(current->list->getDirectory().text());
1056 					if (ret < 0)
1057 					{
1058 						int errcode = errno;
1059 						if (errcode)
1060 						{
1061 							MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), current->list->getDirectory().text(), strerror(errcode));
1062 						}
1063 						else
1064 						{
1065 							MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), current->list->getDirectory().text());
1066 						}
1067 					}
1068 
1069 					cmdname = FXPath::name(pathname);
1070 					cmd = ::quote(pathname);
1071 
1072 					// Make and show command window
1073 					// The CommandWindow object will delete itself when closed!
1074 					CommandWindow* cmdwin = new CommandWindow(getApp(), _("Command log"), cmd, 30, 80);
1075 					cmdwin->create();
1076 					cmdwin->setIcon(runicon);
1077 
1078 					ret = chdir(startlocation.text());
1079 					if (ret < 0)
1080 					{
1081 						int errcode = errno;
1082 						if (errcode)
1083 						{
1084 							MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
1085 						}
1086 						else
1087 						{
1088 							MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
1089 						}
1090 					}
1091 				}
1092 
1093 				// Edit
1094 				if (answer == EXECBOX_CLICKED_EDIT)
1095 				{
1096 					FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
1097 					cmd = txteditor;
1098 					cmdname = cmd;
1099 
1100 					// If command exists, run it
1101 					if (::existCommand(cmdname))
1102 					{
1103 						cmd = cmdname + " " + ::quote(pathname);
1104 #ifdef STARTUP_NOTIFICATION
1105 						runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
1106 #else
1107 						runcmd(cmd, current->list->getDirectory(), startlocation);
1108 #endif
1109 					}
1110 
1111 					// If command does not exist, call the "Open with..." dialog
1112 					else
1113 					{
1114 						current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1115 					}
1116 				}
1117 			}
1118 		}
1119 	}
1120 
1121 	// Binary file, execute it
1122 	else
1123 	{
1124         cmdname = FXPath::name(pathname);
1125         cmd = ::quote(pathname);
1126 #ifdef STARTUP_NOTIFICATION
1127         runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
1128 #else
1129         runcmd(cmd, current->list->getDirectory(), startlocation);
1130 #endif
1131 	}
1132 }
1133 
1134 
1135 // Double Click on File Item
onCmdItemDoubleClicked(FXObject * sender,FXSelector sel,void * ptr)1136 long FilePanel::onCmdItemDoubleClicked(FXObject* sender, FXSelector sel, void* ptr)
1137 {
1138     // Don't do anything if not called from icon list and single click mode
1139     // and if first column in detailed list, or big or mini icon list
1140     if (!called_from_iconlist && (single_click == SINGLE_CLICK_DIR_FILE))
1141     {
1142 		int    x, y;
1143 		FXuint state;
1144 		getCursorPosition(x, y, state);
1145 		if ( (!(list->getListStyle()&(_ICONLIST_BIG_ICONS|_ICONLIST_MINI_ICONS)) && ((x-list->getXPosition()) < list->getHeaderSize(0)))
1146 		     || (list->getListStyle()&(_ICONLIST_BIG_ICONS|_ICONLIST_MINI_ICONS)) )
1147 		{
1148 			return(1);
1149 		}
1150 	}
1151 
1152 	// Reset flag
1153 	called_from_iconlist = false;
1154 
1155     // Make panel active
1156     setActive();
1157 
1158     // Wait cursor
1159     getApp()->beginWaitCursor();
1160 
1161     // At most one item selected
1162     if (current->list->getNumSelectedItems() <= 1)
1163     {
1164 	    FXString cmd, cmdname, filename, pathname;
1165 
1166         FXlong item = (FXlong)ptr;
1167         if (item > -1)
1168         {
1169 #ifdef STARTUP_NOTIFICATION
1170             // Startup notification option and exceptions (if any)
1171             FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
1172             FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
1173 #endif
1174             // Default programs
1175             FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
1176             FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
1177             FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
1178             FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
1179             FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
1180             FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
1181             FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
1182             FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
1183 
1184             // File name and path
1185             filename = list->getItemFilename(item);
1186             pathname = list->getItemPathname(item);
1187 
1188             // If directory, open the directory
1189             if (list->isItemDirectory(item))
1190             {
1191                 // Does not have access
1192                 if (!::isReadExecutable(pathname))
1193                 {
1194                     MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), pathname.text());
1195                     getApp()->endWaitCursor();
1196                     return(0);
1197                 }
1198                 if (filename == "..")
1199                 {
1200                     list->handle(this, FXSEL(SEL_COMMAND, FileList::ID_DIRECTORY_UP), NULL);
1201                 }
1202                 else
1203                 {
1204                     list->setDirectory(pathname);
1205                 }
1206 
1207                 // Change directory in tree list
1208                 dirpanel->setDirectory(pathname, true);
1209                 current->updatePath();
1210 
1211                 // Update location history
1212                 updateLocation();
1213             }
1214             else if (list->isItemFile(item))
1215             {
1216                 // Update associations dictionary
1217                 FileDict*  assocdict = new FileDict(getApp());
1218                 FileAssoc* association = assocdict->findFileBinding(pathname.text());
1219 
1220                 // If there is an association
1221                 if (association)
1222                 {
1223                     // Use it to open the file
1224                     if (association->command.section(',', 0) != "")
1225                     {
1226                         cmdname = association->command.section(',', 0);
1227 
1228                         // Use a default program if possible
1229                         switch (progs[cmdname])
1230                         {
1231                         case TXTVIEWER:
1232                             cmdname = txtviewer;
1233                             break;
1234 
1235                         case TXTEDITOR:
1236                             cmdname = txteditor;
1237                             break;
1238 
1239                         case IMGVIEWER:
1240                             cmdname = imgviewer;
1241                             break;
1242 
1243                         case IMGEDITOR:
1244                             cmdname = imgeditor;
1245                             break;
1246 
1247                         case PDFVIEWER:
1248                             cmdname = pdfviewer;
1249                             break;
1250 
1251                         case AUDIOPLAYER:
1252                             cmdname = audioplayer;
1253                             break;
1254 
1255                         case VIDEOPLAYER:
1256                             cmdname = videoplayer;
1257                             break;
1258 
1259                         case ARCHIVER:
1260                             cmdname = archiver;
1261                             break;
1262 
1263                         case NONE: // No program found
1264                             ;
1265                             break;
1266                         }
1267 
1268                         // If command exists, run it
1269                         if (::existCommand(cmdname))
1270                         {
1271                             cmd = cmdname+" "+::quote(pathname);
1272 #ifdef STARTUP_NOTIFICATION
1273                             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
1274 #else
1275                             runcmd(cmd, current->list->getDirectory(), startlocation);
1276 #endif
1277                         }
1278 
1279                         // If command does not exist, call the "Open with..." dialog
1280                         else
1281                         {
1282                             getApp()->endWaitCursor();
1283                             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1284                         }
1285                     }
1286 
1287                     // Or execute the file
1288                     else if (list->isItemExecutable(item))
1289                     {
1290                         execFile(pathname);
1291                     }
1292 
1293                     // Or call the "Open with..." dialog
1294                     else
1295                     {
1296                         getApp()->endWaitCursor();
1297                         current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1298                     }
1299                 }
1300 
1301                 // If no association but executable
1302                 else if (list->isItemExecutable(item))
1303                 {
1304                     execFile(pathname);
1305                 }
1306 
1307                 // Other cases
1308                 else
1309                 {
1310                     getApp()->endWaitCursor();
1311                     current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1312                 }
1313             }
1314         }
1315     }
1316 
1317     // More than one selected files
1318     else
1319     {
1320         current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN), NULL);
1321     }
1322 
1323     getApp()->endWaitCursor();
1324 
1325     return(1);
1326 }
1327 
1328 
1329 // Single click on File Item
onCmdItemClicked(FXObject * sender,FXSelector sel,void * ptr)1330 long FilePanel::onCmdItemClicked(FXObject* sender, FXSelector sel, void* ptr)
1331 {
1332     // Make panel active
1333     setActive();
1334 
1335     if (single_click != SINGLE_CLICK_NONE)
1336     {
1337 		// Single click with control or shift
1338 		int    x, y;
1339 		FXuint state;
1340 		getCursorPosition(x, y, state);
1341 		if (state&(CONTROLMASK|SHIFTMASK))
1342 		{
1343 			return(1);
1344 		}
1345 
1346 		// In detailed mode, avoid single click when mouse cursor is not over the first column
1347 		FXbool allow = true;
1348 		if (!(list->getListStyle()&(_ICONLIST_BIG_ICONS|_ICONLIST_MINI_ICONS)) && ((x-list->getXPosition()) > list->getHeaderSize(0)))
1349 		{
1350 			allow = false;
1351 		}
1352 
1353 		// Wait cursor
1354 		getApp()->beginWaitCursor();
1355 
1356 		// At most one item selected
1357     	if (current->list->getNumSelectedItems() <= 1)
1358     	{
1359 			// Default programs
1360 			FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
1361 			FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
1362 			FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
1363 			FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
1364 			FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
1365 			FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
1366 			FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
1367 			FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
1368 
1369 			FXString cmd, cmdname, filename, pathname;
1370 
1371 #ifdef STARTUP_NOTIFICATION
1372 			// Startup notification option and exceptions (if any)
1373 			FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
1374 			FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
1375 #endif
1376 
1377 			FXlong item = (FXlong)ptr;
1378 			if (item > -1)
1379 			{
1380 				// File name and path
1381 				filename = list->getItemFilename(item);
1382 				pathname = list->getItemPathname(item);
1383 
1384 				// If directory, open the directory
1385 				if ((single_click != SINGLE_CLICK_NONE) && list->isItemDirectory(item) && allow)
1386 				{
1387 					// Does not have access
1388 					if (!::isReadExecutable(pathname))
1389 					{
1390 						MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), pathname.text());
1391 						getApp()->endWaitCursor();
1392 						return(0);
1393 					}
1394 					if (filename == "..")
1395 					{
1396 						list->handle(this, FXSEL(SEL_COMMAND, FileList::ID_DIRECTORY_UP), NULL);
1397 					}
1398 					else
1399 					{
1400 						list->setDirectory(pathname);
1401 					}
1402 
1403 					// Change directory in tree list
1404 					dirpanel->setDirectory(pathname, true);
1405 					current->updatePath();
1406 
1407 					// Update location history
1408 					updateLocation();
1409 				}
1410 
1411 				// If file, use the association if any
1412 				else if ((single_click == SINGLE_CLICK_DIR_FILE) && list->isItemFile(item) && allow)
1413 				{
1414 					// Update associations dictionary
1415 					FileDict*  assocdict = new FileDict(getApp());
1416 					FileAssoc* association = assocdict->findFileBinding(pathname.text());
1417 
1418 					// If there is an association
1419 					if (association)
1420 					{
1421 						// Use it to open the file
1422 						if (association->command.section(',', 0) != "")
1423 						{
1424 							cmdname = association->command.section(',', 0);
1425 
1426 							// Use a default program if possible
1427 							switch (progs[cmdname])
1428 							{
1429 							case TXTVIEWER:
1430 								cmdname = txtviewer;
1431 								break;
1432 
1433 							case TXTEDITOR:
1434 								cmdname = txteditor;
1435 								break;
1436 
1437 							case IMGVIEWER:
1438 								cmdname = imgviewer;
1439 								break;
1440 
1441 							case IMGEDITOR:
1442 								cmdname = imgeditor;
1443 								break;
1444 
1445 							case PDFVIEWER:
1446 								cmdname = pdfviewer;
1447 								break;
1448 
1449 							case AUDIOPLAYER:
1450 								cmdname = audioplayer;
1451 								break;
1452 
1453 							case VIDEOPLAYER:
1454 								cmdname = videoplayer;
1455 								break;
1456 
1457 							case ARCHIVER:
1458 								cmdname = archiver;
1459 								break;
1460 
1461 							case NONE: // No program found
1462 								;
1463 								break;
1464 							}
1465 
1466 							// If command exists, run it
1467 							if (::existCommand(cmdname))
1468 							{
1469 								cmd = cmdname+" "+::quote(pathname);
1470 #ifdef STARTUP_NOTIFICATION
1471 								runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
1472 #else
1473 								runcmd(cmd, current->list->getDirectory(), startlocation);
1474 #endif
1475 							}
1476 
1477 							// If command does not exist, call the "Open with..." dialog
1478 							else
1479 							{
1480 								getApp()->endWaitCursor();
1481 								current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1482 							}
1483 						}
1484 
1485 						// Or execute the file
1486 						else if (list->isItemExecutable(item))
1487 						{
1488 							execFile(pathname);
1489 						}
1490 
1491 						// Or call the "Open with..." dialog
1492 						else
1493 						{
1494 							getApp()->endWaitCursor();
1495 							current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1496 						}
1497 					}
1498 
1499 					// If no association but executable
1500 					else if (list->isItemExecutable(item))
1501 					{
1502 						execFile(pathname);
1503 					}
1504 
1505 					// Other cases
1506 					else
1507 					{
1508 						getApp()->endWaitCursor();
1509 						current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
1510 					}
1511 				}
1512 			}
1513     	}
1514 
1515 		// More than one selected files
1516 		else if ( (single_click == SINGLE_CLICK_DIR_FILE) && allow)
1517 		{
1518 			current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN), NULL);
1519 		}
1520 
1521 		getApp()->endWaitCursor();
1522 
1523     }
1524     return(1);
1525 }
1526 
1527 
1528 // Go to parent directory
onCmdDirectoryUp(FXObject * sender,FXSelector sel,void * ptr)1529 long FilePanel::onCmdDirectoryUp(FXObject* sender, FXSelector sel, void* ptr)
1530 {
1531     current->list->handle(this, FXSEL(SEL_COMMAND, FileList::ID_DIRECTORY_UP), NULL);
1532     current->list->setFocus();
1533     dirpanel->setDirectory(current->list->getDirectory(), true);
1534     current->updatePath();
1535     updateLocation();
1536     return(1);
1537 }
1538 
1539 
1540 // Go to home directory
onCmdGoHome(FXObject * sender,FXSelector sel,void * ptr)1541 long FilePanel::onCmdGoHome(FXObject* sender, FXSelector sel, void* ptr)
1542 {
1543     current->list->setDirectory(homedir);
1544     current->list->setFocus();
1545     dirpanel->setDirectory(homedir, true);
1546     current->updatePath();
1547     updateLocation();
1548     return(1);
1549 }
1550 
1551 
1552 // Go to trash directory
onCmdGoTrash(FXObject * sender,FXSelector sel,void * ptr)1553 long FilePanel::onCmdGoTrash(FXObject* sender, FXSelector sel, void* ptr)
1554 {
1555     current->list->setDirectory(trashfileslocation);
1556     current->list->setFocus();
1557     dirpanel->setDirectory(trashfileslocation, true);
1558     current->updatePath();
1559     updateLocation();
1560     return(1);
1561 }
1562 
1563 
1564 // Set the flag that allows to stop the file list refresh
onCmdStopListRefreshTimer(FXObject *,FXSelector,void *)1565 long FilePanel::onCmdStopListRefreshTimer(FXObject*, FXSelector, void*)
1566 {
1567     stopListRefresh = true;
1568 
1569     return(0);
1570 }
1571 
1572 
1573 // Copy/Move/Rename/Symlink file(s)
onCmdFileMan(FXObject * sender,FXSelector sel,void * ptr)1574 long FilePanel::onCmdFileMan(FXObject* sender, FXSelector sel, void* ptr)
1575 {
1576     int      num;
1577     FXString src, targetdir, target, name, source;
1578     int      firstitem = 0, lastitem = 0;
1579 
1580     // Focus on this panel list
1581     current->list->setFocus();
1582 
1583     // Confirmation dialog?
1584     FXbool ask_before_copy = getApp()->reg().readUnsignedEntry("OPTIONS", "ask_before_copy", true);
1585 
1586     // If we are we called from the paste command, get the parameters from the pointer
1587     if (fromPaste)
1588     {
1589         // Reset the flag
1590         fromPaste = false;
1591 
1592         // Get the parameters
1593         FXString str = (char*)ptr;
1594         targetdir = str.section('\n', 0);
1595         num = FXUIntVal(str.section('\n', 1));
1596         src = str.after('\n', 2);
1597 
1598         // If no item in clipboard, return
1599         if (num <= 0)
1600         {
1601             return(0);
1602         }
1603 
1604         // If there is a selected directory in file panel, use it as target directory
1605         if (current->list->getNumSelectedItems() == 1)
1606         {
1607             int item = current->list->getCurrentItem();
1608             if (current->list->isItemDirectory(item))
1609             {
1610                 targetdir = list->getItemPathname(item);
1611             }
1612         }
1613     }
1614 
1615     // Obtain the parameters from the file panel
1616     else
1617     {
1618         // Current directory
1619         FXString curdir = current->list->getDirectory();
1620 
1621         // Number of selected items
1622         num = current->list->getNumSelectedItems();
1623 
1624         // If no item, return
1625         if (num <= 0)
1626         {
1627             return(0);
1628         }
1629 
1630         // Eventually deselect the '..' directory
1631         if (current->list->isItemSelected(0))
1632         {
1633             current->list->deselectItem(0);
1634         }
1635 
1636         // Obtain the list of source files and the target directory
1637         for (int u = 0; u < current->list->getNumItems(); u++)
1638         {
1639             if (current->list->isItemSelected(u))
1640             {
1641                 if (firstitem == 0)
1642                 {
1643                     firstitem = u;
1644                 }
1645                 lastitem = u;
1646                 name = current->list->getItemText(u).text();
1647                 name = name.section('\t', 0);
1648                 src += curdir+PATHSEPSTRING+name+"\n";
1649             }
1650         }
1651         targetdir = current->next->list->getDirectory();
1652 
1653         if (!current->next->shown() || (FXSELID(sel) == ID_FILE_RENAME))
1654         {
1655             targetdir = current->list->getDirectory();
1656         }
1657     }
1658 
1659     // Number of items in the FileList
1660     int numitems = current->list->getNumItems();
1661 
1662     // Name and directory of the first source file
1663     source = src.section('\n', 0);
1664     name = FXPath::name(source);
1665     FXString dir = FXPath::directory(source);
1666 
1667     // Initialize target name
1668     if (targetdir != ROOTDIR)
1669     {
1670         target = targetdir+PATHSEPSTRING;
1671     }
1672     else
1673     {
1674         target = targetdir;
1675     }
1676 
1677     // Configure the command, title, message, etc.
1678     FXIcon*  icon = NULL;
1679     FXString command, title, message;
1680     if (FXSELID(sel) == ID_FILE_COPY)
1681     {
1682         command = "copy";
1683         title = _("Copy");
1684         icon = copy_bigicon;
1685         if (num == 1)
1686         {
1687             message = _("Copy ");
1688             message += source;
1689             if (::isFile(source))
1690             {
1691                 target += name;
1692             }
1693 
1694             // Source and target are identical => add a suffix to the name
1695             FXString tgt = ::cleanPath(target); // Remove trailing / if any
1696             if ((::identical(source, tgt) && (tgt != current->list->getDirectory())) || // Check we are not within target
1697                 (::isDirectory(source) && (source == tgt+PATHSEPSTRING+FXPath::name(source))))
1698             {
1699                 target = ::buildCopyName(source, ::isDirectory(source));
1700             }
1701         }
1702         else
1703         {
1704             message.format(_("Copy %s items from: %s"), FXStringVal(num).text(), dir.text());
1705         }
1706     }
1707     if (FXSELID(sel) == ID_FILE_RENAME)
1708     {
1709         command = "rename";
1710         title = _("Rename");
1711         icon = move_bigicon;
1712         if (num == 1)
1713         {
1714             message = _("Rename ");
1715             message += name;
1716             target = name;
1717             title = _("Rename");
1718         }
1719         else
1720         {
1721             return(0);
1722         }
1723     }
1724     if (FXSELID(sel) == ID_FILE_COPYTO)
1725     {
1726         command = "copy";
1727         title = _("Copy");
1728         icon = copy_bigicon;
1729         if (num == 1)
1730         {
1731             message = _("Copy ");
1732             message += source;
1733         }
1734         else
1735         {
1736             message.format(_("Copy %s items from: %s"), FXStringVal(num).text(), dir.text());
1737         }
1738     }
1739     if (FXSELID(sel) == ID_FILE_MOVETO)
1740     {
1741         command = "move";
1742         title = _("Move");
1743         icon = move_bigicon;
1744         if (num == 1)
1745         {
1746             message = _("Move ");
1747             message += source;
1748             title = _("Move");
1749         }
1750         else
1751         {
1752             message.format(_("Move %s items from: %s"), FXStringVal(num).text(), dir.text());
1753         }
1754     }
1755     if (FXSELID(sel) == ID_FILE_CUT)
1756     {
1757         command = "move";
1758         title = _("Move");
1759         icon = move_bigicon;
1760         if (num == 1)
1761         {
1762             message = _("Move ");
1763             message += source;
1764             if (::isFile(source))
1765             {
1766                 target += name;
1767             }
1768             title = _("Move");
1769         }
1770         else
1771         {
1772             message.format(_("Move %s items from: %s"), FXStringVal(num).text(), dir.text());
1773         }
1774     }
1775     if (FXSELID(sel) == ID_FILE_SYMLINK)
1776     {
1777         command = "symlink";
1778         title = _("Symlink");
1779         icon = link_bigicon;
1780         if (num == 1)
1781         {
1782             message = _("Symlink ");
1783             message += source;
1784             target += name;
1785         }
1786         else
1787         {
1788             message.format(_("Symlink %s items from: %s"), FXStringVal(num).text(), dir.text());
1789         }
1790     }
1791 
1792     // File operation dialog, if needed
1793     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))
1794     {
1795         if (num == 1)
1796         {
1797             if (FXSELID(sel) == ID_FILE_RENAME)
1798             {
1799                 if (operationdialogrename == NULL)
1800                 {
1801                     operationdialogrename = new InputDialog(this, "", "", title, _("To:"), icon);
1802                 }
1803                 operationdialogrename->setTitle(title);
1804                 operationdialogrename->setIcon(icon);
1805 				operationdialogrename->setMessage(message);
1806                 operationdialogrename->setText(target);
1807 
1808                 if (::isDirectory(source))  // directory
1809                 {
1810                     operationdialogrename->selectAll();
1811                 }
1812                 else
1813                 {
1814                     int pos = target.rfind('.');
1815                     if (pos <= 0)
1816                     {
1817                         operationdialogrename->selectAll(); // no extension or dot file
1818                     }
1819                     else
1820                     {
1821                         operationdialogrename->setSelection(0, pos);
1822                     }
1823                 }
1824 
1825                 int rc = 1;
1826                 rc = operationdialogrename->execute(PLACEMENT_CURSOR);
1827                 target = operationdialogrename->getText();
1828 
1829                 // Target name contains '/'
1830                 if (target.contains(PATHSEPCHAR))
1831                 {
1832 					MessageBox::warning(this, BOX_OK, _("Warning"), _("The / character is not allowed in file or folder names, operation cancelled"));
1833 					return(0);
1834 				}
1835 
1836                 if (!rc)
1837                 {
1838                     return(0);
1839                 }
1840             }
1841             else
1842             {
1843                 if (operationdialogsingle == NULL)
1844                 {
1845                     operationdialogsingle = new BrowseInputDialog(this, "", "", title, _("To:"), icon, BROWSE_INPUT_MIXED);
1846                 }
1847                 operationdialogsingle->setTitle(title);
1848                 operationdialogsingle->setIcon(icon);
1849                 operationdialogsingle->setMessage(message);
1850                 operationdialogsingle->setText(target);
1851 
1852                 // Select file name without path
1853                 if (FXSELID(sel) == ID_FILE_SYMLINK)
1854                 {
1855                     int pos = target.rfind(PATHSEPSTRING);
1856                     if (pos >= 0)
1857                     {
1858                         operationdialogsingle->setSelection(pos+1, target.length());
1859                     }
1860                 }
1861 
1862                 operationdialogsingle->setDirectory(targetdir);
1863                 int rc = 1;
1864                 rc = operationdialogsingle->execute(PLACEMENT_CURSOR);
1865                 target = operationdialogsingle->getText();
1866                 if (!rc)
1867                 {
1868                     return(0);
1869                 }
1870             }
1871         }
1872         else
1873         {
1874             if (operationdialogmultiple == NULL)
1875             {
1876                 operationdialogmultiple = new BrowseInputDialog(this, "", "", title, _("To folder:"), icon, BROWSE_INPUT_FOLDER);
1877             }
1878             operationdialogmultiple->setTitle(title);
1879             operationdialogmultiple->setIcon(icon);
1880             operationdialogmultiple->setMessage(message);
1881             operationdialogmultiple->setText(target);
1882             operationdialogmultiple->CursorEnd();
1883             operationdialogmultiple->setDirectory(targetdir);
1884             int rc = 1;
1885             rc = operationdialogmultiple->execute(PLACEMENT_CURSOR);
1886             target = operationdialogmultiple->getText();
1887             if (!rc)
1888             {
1889                 return(0);
1890             }
1891         }
1892     }
1893 
1894     // Nothing entered
1895     if (target == "")
1896     {
1897         MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
1898         return(0);
1899     }
1900 
1901     // Update target and target parent directory
1902 	target = ::filePath(target,current->list->getDirectory());
1903 	if (::isDirectory(target))
1904 	{
1905 		targetdir = target;
1906 	}
1907 	else
1908 	{
1909 		targetdir = FXPath::directory(target);
1910 	}
1911 
1912     // Target parent directory doesn't exist
1913     if (!existFile(targetdir))
1914     {
1915         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), targetdir.text());
1916         return(0);
1917     }
1918 
1919     // Target parent directory not writable
1920     if (!::isWritable(targetdir))
1921     {
1922         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
1923         return(0);
1924     }
1925 
1926     // Target parent directory is not a directory
1927     if (!::isDirectory(targetdir))
1928     {
1929         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), targetdir.text());
1930         return(0);
1931     }
1932 
1933     // Multiple sources and non existent destination
1934     if ((num > 1) && !existFile(target))
1935     {
1936         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), target.text());
1937         return(0);
1938     }
1939 
1940     // Multiple sources and target is a file
1941     if ((num > 1) && ::isFile(target))
1942     {
1943         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), target.text());
1944         return(0);
1945     }
1946 
1947     // Target is a directory and is not writable
1948     if (::isDirectory(target) && !::isWritable(target))
1949     {
1950         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), target.text());
1951         return(0);
1952     }
1953 
1954     // Target is a file and its parent directory is not writable
1955     if (::isFile(target) && !::isWritable(targetdir))
1956     {
1957         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
1958         return(0);
1959     }
1960 
1961     // One source
1962     File* f = NULL;
1963     int   ret;
1964     if (num == 1)
1965     {
1966         // An empty source file name corresponds to the ".." file
1967         // Don't perform any file operation on it!
1968         if (source == "")
1969         {
1970             return(0);
1971         }
1972 
1973         // Wait cursor
1974         getApp()->beginWaitCursor();
1975 
1976         // File object
1977         if (command == "copy")
1978         {
1979             f = new File(this, _("File copy"), COPY, num);
1980             f->create();
1981 
1982             // If target file is located at trash location, also create the corresponding trashinfo file
1983             // Do it silently and don't report any error if it fails
1984             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
1985             if (use_trash_can && (target == trashfileslocation))
1986             {
1987                 // Trash files path name
1988                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
1989 
1990                 // Adjust target name to get the _N suffix if any
1991                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
1992 
1993                 // Create trashinfo file
1994                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
1995 
1996                 // Copy source to trash target
1997                 ret = f->copy(source, trashtarget);
1998             }
1999 
2000             // Copy source to target
2001             else
2002             {
2003                 ret = f->copy(source, target);
2004             }
2005 
2006             // An unknown error has occurred
2007             if ((ret == 0) && !f->isCancelled())
2008             {
2009                 f->hideProgressDialog();
2010                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
2011             }
2012 
2013             // If action is cancelled in progress dialog
2014             if (f->isCancelled())
2015             {
2016                 f->hideProgressDialog();
2017                 MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
2018             }
2019         }
2020         else if (command == "rename")
2021         {
2022             f = new File(this, _("File rename"), RENAME, num);
2023             f->create();
2024             ret = f->rename(source, target);
2025 
2026             // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
2027             // Do it silently and don't report any error if it fails
2028             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2029             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
2030             {
2031                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
2032                 ::unlink(trashinfopathname.text());
2033             }
2034         }
2035         else if (command == "move")
2036         {
2037             f = new File(this, _("File move"), MOVE, num);
2038             f->create();
2039 
2040             // If target file is located at trash location, also create the corresponding trashinfo file
2041             // Do it silently and don't report any error if it fails
2042             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2043             if (use_trash_can && (target == trashfileslocation))
2044             {
2045                 // Trash files path name
2046                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
2047 
2048                 // Adjust target name to get the _N suffix if any
2049                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
2050 
2051                 // Create trashinfo file
2052                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
2053 
2054                 // Move source to trash target
2055                 ret = f->move(source, trashtarget);
2056             }
2057 
2058             // Move source to target
2059             else
2060             {
2061                 ret = f->move(source, target);
2062             }
2063 
2064             // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
2065             // Do it silently and don't report any error if it fails
2066             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
2067             {
2068                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
2069                 ::unlink(trashinfopathname.text());
2070             }
2071 
2072             // An unknown error has occurred
2073             if ((ret == 0) && !f->isCancelled())
2074             {
2075                 f->hideProgressDialog();
2076                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
2077             }
2078 
2079             // If action is cancelled in progress dialog
2080             if (f->isCancelled())
2081             {
2082                 f->hideProgressDialog();
2083                 MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
2084             }
2085         }
2086         else if (command == "symlink")
2087         {
2088             f = new File(this, _("Symlink"), SYMLINK, num);
2089             f->create();
2090             f->symlink(source, target);
2091         }
2092         // Shouldn't happen
2093         else
2094         {
2095             exit(EXIT_FAILURE);
2096         }
2097 
2098         getApp()->endWaitCursor();
2099         delete f;
2100     }
2101 
2102     // Multiple sources
2103     // Note : rename cannot be used in this case!
2104     else if (num > 1)
2105     {
2106         // Wait cursor
2107         getApp()->beginWaitCursor();
2108 
2109         // File object
2110         if (command == "copy")
2111         {
2112             f = new File(this, _("File copy"), COPY, num);
2113         }
2114         else if (command == "move")
2115         {
2116             f = new File(this, _("File move"), MOVE, num);
2117         }
2118         else if (command == "symlink")
2119         {
2120             f = new File(this, _("Symlink"), SYMLINK, num);
2121         }
2122         // Shouldn't happen
2123         else
2124         {
2125             exit(EXIT_FAILURE);
2126         }
2127         f->create();
2128 
2129         // Initialize file list stop refresh timer and flag
2130         stopListRefresh = false;
2131         getApp()->addTimeout(this, ID_STOP_LIST_REFRESH_TIMER, STOP_LIST_REFRESH_INTERVAL);
2132 
2133         // Loop on the multiple files
2134         for (int i = 0; i < num; i++)
2135         {
2136             // Stop refreshing the file list if file operation is long and has many files
2137             // This avoids flickering and speeds up things a bit
2138             if (stopListRefresh && (i > STOP_LIST_REFRESH_NBMAX))
2139             {
2140                 // Force a last refresh if current panel is destination
2141                 if (current->getDirectory() == targetdir)
2142                 {
2143                     current->list->onCmdRefresh(0, 0, 0);
2144                 }
2145 
2146                 // Force a last refresh if next panel is destination
2147                 if (next->getDirectory() == targetdir)
2148                 {
2149                     next->list->onCmdRefresh(0, 0, 0);
2150                 }
2151 
2152                 // Tell the dir and file list to not refresh anymore
2153                 setAllowRefresh(false);
2154                 next->setAllowRefresh(false);
2155                 dirpanel->setAllowDirsizeRefresh(false);
2156 
2157                 // Don't need to stop again
2158                 stopListRefresh = false;
2159             }
2160 
2161             // Individual source file
2162             source = src.section('\n', i);
2163 
2164             // File could have already been moved above in the tree
2165             if (!existFile(source))
2166             {
2167                 continue;
2168             }
2169 
2170             // An empty file name corresponds to the ".." file (why?)
2171             // Don't perform any file operation on it!
2172             if (source != "")
2173             {
2174                 if (command == "copy")
2175                 {
2176                     // If target file is located at trash location, also create the corresponding trashinfo file
2177                     // Do it silently and don't report any error if it fails
2178                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2179                     if (use_trash_can && (target == trashfileslocation))
2180                     {
2181                         // Trash files path name
2182                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
2183 
2184                         // Adjust target name to get the _N suffix if any
2185                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
2186 
2187                         // Create trashinfo file
2188                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
2189 
2190                         // Copy source to trash target
2191                         ret = f->copy(source, trashtarget);
2192                     }
2193 
2194                     // Copy source to target
2195                     else
2196                     {
2197                         ret = f->copy(source, target);
2198                     }
2199 
2200                     // An known error has occurred
2201                     if (ret == -1)
2202                     {
2203                         f->hideProgressDialog();
2204                         break;
2205                     }
2206 
2207                     // An unknown error has occurred
2208                     if ((ret == 0) && !f->isCancelled())
2209                     {
2210                         f->hideProgressDialog();
2211                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
2212                         break;
2213                     }
2214 
2215                     // If action is cancelled in progress dialog
2216                     if (f->isCancelled())
2217                     {
2218                         f->hideProgressDialog();
2219                         MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
2220                         break;
2221                     }
2222                 }
2223                 else if (command == "move")
2224                 {
2225                     // If target file is located at trash location, also create the corresponding trashinfo file
2226                     // Do it silently and don't report any error if it fails
2227                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2228                     if (use_trash_can && (target == trashfileslocation))
2229                     {
2230                         // Trash files path name
2231                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
2232 
2233                         // Adjust target name to get the _N suffix if any
2234                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
2235 
2236                         // Create trashinfo file
2237                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
2238 
2239                         // Move source to trash target
2240                         ret = f->move(source, trashtarget);
2241                     }
2242 
2243                     // Move source to target
2244                     else
2245                     {
2246                         ret = f->move(source, target);
2247                     }
2248 
2249                     // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
2250                     // Do it silently and don't report any error if it fails
2251                     if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
2252                     {
2253                         FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
2254                         ::unlink(trashinfopathname.text());
2255                     }
2256 
2257                     // An known error has occurred
2258                     if (ret == -1)
2259                     {
2260                         f->hideProgressDialog();
2261                         break;
2262                     }
2263 
2264                     // An unknown error has occurred
2265                     if ((ret == 0) && !f->isCancelled())
2266                     {
2267                         f->hideProgressDialog();
2268                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
2269                         break;
2270                     }
2271 
2272                     // If action is cancelled in progress dialog
2273                     if (f->isCancelled())
2274                     {
2275                         f->hideProgressDialog();
2276                         MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
2277                         break;
2278                     }
2279                 }
2280                 else if (command == "symlink")
2281                 {
2282                     ret = f->symlink(source, target);
2283 
2284                     // An known error has occurred
2285                     if (ret == -1)
2286                     {
2287                         f->hideProgressDialog();
2288                         break;
2289                     }
2290 
2291                     // An unknown error has occurred
2292                     if ((ret == 0) && !f->isCancelled())
2293                     {
2294                         f->hideProgressDialog();
2295                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the symlink operation!"));
2296                         break;
2297                     }
2298 
2299                     // If action is cancelled in progress dialog
2300                     if (f->isCancelled())
2301                     {
2302                         f->hideProgressDialog();
2303                         MessageBox::error(this, BOX_OK, _("Warning"), _("Symlink operation cancelled!"));
2304                         break;
2305                     }
2306                 }
2307                 // Shouldn't happen
2308                 else
2309                 {
2310                     exit(EXIT_FAILURE);
2311                 }
2312             }
2313         }
2314 
2315         // Reinit timer and refresh flags
2316         getApp()->removeTimeout(this, ID_STOP_LIST_REFRESH_TIMER);
2317         current->setAllowRefresh(true);
2318         next->setAllowRefresh(true);
2319         dirpanel->setAllowDirsizeRefresh(true);
2320 
2321         getApp()->endWaitCursor();
2322         delete f;
2323     }
2324 
2325     // Force panels refresh
2326     next->onCmdRefresh(0, 0, 0);
2327     current->onCmdRefresh(0, 0, 0);
2328 
2329     // Enable previous or last selected item for keyboard navigation
2330     if (((FXSELID(sel) == ID_FILE_MOVETO) || (FXSELID(sel) == ID_FILE_RENAME)) && (current->list->getNumItems() < numitems))
2331     {
2332         firstitem = (firstitem < 1) ? 0 : firstitem-1;
2333         current->list->enableItem(firstitem);
2334         current->list->setCurrentItem(firstitem);
2335     }
2336     else
2337     {
2338         current->list->enableItem(lastitem);
2339         current->list->setCurrentItem(lastitem);
2340     }
2341 
2342     return(1);
2343 }
2344 
2345 
2346 // Trash files from the file list or the tree list
onCmdFileTrash(FXObject *,FXSelector,void *)2347 long FilePanel::onCmdFileTrash(FXObject*, FXSelector, void*)
2348 {
2349     int   firstitem = 0;
2350     File* f = NULL;
2351 
2352     current->list->setFocus();
2353     FXString dir = current->list->getDirectory();
2354 
2355     FXbool confirm_trash = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_trash", true);
2356 
2357     // If we don't have permission to write to the parent directory
2358     if (!::isWritable(dir))
2359     {
2360         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), dir.text());
2361         return(0);
2362     }
2363 
2364     // If we don't have permission to write to the trash directory
2365     if (!::isWritable(trashfileslocation))
2366     {
2367         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to trash location %s: Permission denied"), trashfileslocation.text());
2368         return(0);
2369     }
2370 
2371     // Items number in the file list
2372     int num = current->list->getNumSelectedItems();
2373 
2374     // If nothing selected, return
2375     if (num == 0)
2376     {
2377         return(0);
2378     }
2379 
2380     // If exist selected files, use them
2381     else if (num >= 1)
2382     {
2383         // Eventually deselect the '..' directory
2384         if (current->list->isItemSelected(0))
2385         {
2386             current->list->deselectItem(0);
2387         }
2388 
2389         if (confirm_trash)
2390         {
2391             FXString message;
2392             if (num == 1)
2393             {
2394                 FXString pathname;
2395                 for (int u = 0; u < current->list->getNumItems(); u++)
2396                 {
2397                     if (current->list->isItemSelected(u))
2398                     {
2399                         pathname = current->list->getItemPathname(u);
2400                     }
2401                 }
2402                 if (::isDirectory(pathname))
2403                 {
2404                     message.format(_("Move folder %s to trash can?"), pathname.text());
2405                 }
2406                 else
2407                 {
2408                     message.format(_("Move file %s to trash can?"), pathname.text());
2409                 }
2410             }
2411             else
2412             {
2413                 message.format(_("Move %s selected items to trash can?"), FXStringVal(num).text());
2414             }
2415 
2416             MessageBox box(this, _("Confirm Trash"), message, delete_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2417             if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2418             {
2419                 return(0);
2420             }
2421         }
2422 
2423         // Wait cursor
2424         getApp()->beginWaitCursor();
2425 
2426         // File object
2427         f = new File(this, _("Move to trash"), DELETE, num);
2428         f->create();
2429         list->setAllowRefresh(false);
2430 
2431         // Overwrite initialisations
2432         FXbool overwrite = false;
2433         FXbool overwrite_all = false;
2434         FXbool skip_all = false;
2435 
2436         // Delete selected files
2437         FXString filename, pathname;
2438         int      i = 0;
2439         stopListRefresh = false;
2440         for (int u = 0; u < current->list->getNumItems(); u++)
2441         {
2442             if (current->list->isItemSelected(u))
2443             {
2444                 // Get index of first selected item
2445                 if (firstitem == 0)
2446                 {
2447                     firstitem = u;
2448                 }
2449 
2450                 // Stop refreshing the dirsize in dirpanel
2451                 // when there are many files to delete
2452                 i++;
2453                 if (!stopListRefresh && (i > STOP_LIST_REFRESH_NBMAX))
2454                 {
2455                     dirpanel->setAllowDirsizeRefresh(false);
2456                     stopListRefresh = true;
2457                 }
2458 
2459                 // Get file name and path
2460                 filename = current->list->getItemFilename(u);
2461                 pathname = current->list->getItemPathname(u);
2462 
2463                 // If we don't have permission to write to the file
2464                 if (!::isWritable(pathname))
2465                 {
2466                     // Overwrite dialog if necessary
2467                     if (!(overwrite_all | skip_all))
2468                     {
2469                         f->hideProgressDialog();
2470                         FXString msg;
2471                         msg.format(_("File %s is write-protected, move it anyway to trash can?"), pathname.text());
2472 
2473                         if (num ==1)
2474                         {
2475 							OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Trash"), msg, OVWBOX_SINGLE_FILE);
2476 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
2477 							delete dlg;
2478 							if (answer == 1)
2479 							{
2480 								overwrite = true;
2481 							}
2482 							else
2483 							{
2484 								goto end;
2485 							}
2486 						}
2487                         else
2488                         {
2489 							OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Trash"), msg);
2490 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
2491 							delete dlg;
2492 							switch (answer)
2493 							{
2494 							// Cancel
2495 							case 0:
2496 								goto end;
2497 								break;
2498 
2499 							// Overwrite
2500 							case 1:
2501 								overwrite = true;
2502 								break;
2503 
2504 							// Overwrite all
2505 							case 2:
2506 								overwrite_all = true;
2507 								break;
2508 
2509 							// Skip
2510 							case 3:
2511 								overwrite = false;
2512 								break;
2513 
2514 							// Skip all
2515 							case 4:
2516 								skip_all = true;
2517 								break;
2518 							}
2519 
2520 						}
2521                     }
2522                     if ((overwrite | overwrite_all) & !skip_all)
2523                     {
2524                         // Caution!! Don't delete parent directory!!
2525                         if (filename != "..")
2526                         {
2527                             // Trash files path name
2528                             FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
2529 
2530                             // Create trashinfo file
2531                             createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
2532 
2533                             // Move file to trash files location
2534                             int ret = f->move(pathname, trashpathname);
2535 
2536                             // An error has occurred
2537                             if ((ret == 0) && !f->isCancelled())
2538                             {
2539                                 f->hideProgressDialog();
2540                                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
2541                                 break;
2542                             }
2543                         }
2544                     }
2545                     f->showProgressDialog();
2546                 }
2547 
2548                 // If we have permission to write
2549                 else
2550                 {
2551                     // Caution!! Don't delete parent directory!!
2552                     if (filename != "..")
2553                     {
2554                         // Trash files path name
2555                         FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
2556 
2557                         // Create trashinfo file
2558                         createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
2559 
2560                         // Move file to trash files location
2561                         int ret = f->move(pathname, trashpathname);
2562 
2563                         // An error has occurred
2564                         if ((ret == 0) && !f->isCancelled())
2565                         {
2566                             f->hideProgressDialog();
2567                             MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
2568                             break;
2569                         }
2570                     }
2571                     // If action is cancelled in progress dialog
2572                     if (f->isCancelled())
2573                     {
2574                         f->hideProgressDialog();
2575                         MessageBox::error(this, BOX_OK, _("Warning"), _("Move to trash file operation cancelled!"));
2576                         break;
2577                     }
2578                 }
2579             }
2580         }
2581 end:
2582         getApp()->endWaitCursor();
2583         delete f;
2584     }
2585     // Force FilePanel and DirPanel refresh
2586     list->setAllowRefresh(true);
2587     stopListRefresh = false;
2588     dirpanel->setAllowDirsizeRefresh(true);
2589     onCmdRefresh(0, 0, 0);
2590 
2591     // Enable last item before the first selected item (for keyboard navigation)
2592     firstitem = (firstitem < 1) ? 0 : firstitem-1;
2593     current->list->enableItem(firstitem);
2594     current->list->setCurrentItem(firstitem);
2595 
2596     return(1);
2597 }
2598 
2599 
2600 // Restore files from trash can
onCmdFileRestore(FXObject *,FXSelector,void *)2601 long FilePanel::onCmdFileRestore(FXObject*, FXSelector, void*)
2602 {
2603     int   firstitem = 0;
2604     File* f = NULL;
2605 
2606     current->list->setFocus();
2607     FXString dir = current->list->getDirectory();
2608     FXbool   confirm_trash = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_trash", true);
2609 
2610     // Items number in the file list
2611     int num = current->list->getNumSelectedItems();
2612 
2613     // If nothing selected, return
2614     if (num == 0)
2615     {
2616         return(0);
2617     }
2618 
2619     // If exist selected files, use them
2620     else if (num >= 1)
2621     {
2622         // Eventually deselect the '..' directory
2623         if (current->list->isItemSelected(0))
2624         {
2625             current->list->deselectItem(0);
2626         }
2627 
2628         // Wait cursor
2629         getApp()->beginWaitCursor();
2630 
2631         // File object
2632         f = new File(this, _("Restore from trash"), DELETE, num);
2633         f->create();
2634         list->setAllowRefresh(false);
2635 
2636         // Restore (i.e. move to their original location) selected files
2637         FXString filename, pathname;
2638         int      i = 0;
2639         stopListRefresh = false;
2640         for (int u = 0; u < current->list->getNumItems(); u++)
2641         {
2642             if (current->list->isItemSelected(u))
2643             {
2644                 // Get index of first selected item
2645                 if (firstitem == 0)
2646                 {
2647                     firstitem = u;
2648                 }
2649 
2650                 // Stop refreshing the dirsize in dirpanel
2651                 // when there are many files to delete
2652                 i++;
2653                 if (!stopListRefresh && (i > STOP_LIST_REFRESH_NBMAX))
2654                 {
2655                     dirpanel->setAllowDirsizeRefresh(false);
2656                     stopListRefresh = true;
2657                 }
2658 
2659                 // Get file name and path
2660                 filename = current->list->getItemFilename(u);
2661                 pathname = current->list->getItemPathname(u);
2662 
2663                 // Don't restore '..' directory
2664                 if (filename != "..")
2665                 {
2666                     // Obtain trash base name and sub path
2667                     FXString subpath = pathname;
2668                     subpath.erase(0, trashfileslocation.length()+1);
2669                     FXString trashbasename = subpath.before('/');
2670                     if (trashbasename == "")
2671                     {
2672                         trashbasename = name;
2673                     }
2674                     subpath.erase(0, trashbasename.length());
2675 
2676                     // Read the .trashinfo file
2677                     FILE*    fp;
2678                     char     line[1024];
2679                     FXbool   success = true;
2680                     FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+trashbasename+".trashinfo";
2681                     FXString origpathname = "";
2682 
2683                     if ((fp = fopen(trashinfopathname.text(), "r")) != NULL)
2684                     {
2685                         // Read the first two lines and get the strings
2686                         if (fgets(line, sizeof(line), fp) == NULL)
2687                         {
2688                             success = false;
2689                         }
2690                         if (fgets(line, sizeof(line), fp) == NULL)
2691                         {
2692                             success = false;
2693                         }
2694                         if (success)
2695                         {
2696                             origpathname = line;
2697                             origpathname = origpathname.after('=');
2698                             origpathname = origpathname.before('\n');
2699                         }
2700                         fclose(fp);
2701                         origpathname = origpathname+subpath;
2702                     }
2703 
2704                     // Confirm restore dialog
2705                     if (confirm_trash && (u == firstitem))
2706                     {
2707                         FXString message;
2708                         if (num == 1)
2709                         {
2710                             if (::isDirectory(pathname))
2711                             {
2712                                 message.format(_("Restore folder %s to its original location %s ?"), filename.text(), origpathname.text());
2713                             }
2714                             else
2715                             {
2716                                 message.format(_("Restore file %s to its original location %s ?"), filename.text(), origpathname.text());
2717                             }
2718                         }
2719                         else
2720                         {
2721                             message.format(_("Restore %s selected items to their original locations?"), FXStringVal(num).text());
2722                         }
2723                         f->hideProgressDialog();
2724                         MessageBox box(this, _("Confirm Restore"), message, restore_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2725                         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2726                         {
2727                             getApp()->endWaitCursor();
2728                             delete f;
2729                             return(0);
2730                         }
2731                         f->showProgressDialog();
2732                     }
2733 
2734                     if (origpathname == "")
2735                     {
2736                         f->hideProgressDialog();
2737                         MessageBox::error(this, BOX_OK, _("Error"), _("Restore information not available for %s"), pathname.text());
2738                         goto end;
2739                     }
2740 
2741                     // If parent dir of the original location does not exist
2742                     FXString origparentdir = FXPath::directory(origpathname);
2743                     if (!existFile(origparentdir))
2744                     {
2745                         // Ask the user if he wants to create it
2746                         f->hideProgressDialog();
2747                         FXString message;
2748                         message.format(_("Parent folder %s does not exist, do you want to create it?"), origparentdir.text());
2749                         MessageBox box(this, _("Confirm Restore"), message, restore_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2750                         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2751                         {
2752                             goto end;
2753                         }
2754                         else
2755                         {
2756                             errno = 0;
2757                             int ret = mkpath(origparentdir.text(), 0755);
2758                             int errcode = errno;
2759                             if (ret == -1)
2760                             {
2761                                 f->hideProgressDialog();
2762                                 if (errcode)
2763                                 {
2764                                     MessageBox::error(this, BOX_OK, _("Error"), _("Can't create folder %s: %s"), origparentdir.text(), strerror(errcode));
2765                                 }
2766                                 else
2767                                 {
2768                                     MessageBox::error(this, BOX_OK, _("Error"), _("Can't create folder %s"), origparentdir.text());
2769                                 }
2770                                 goto end;
2771                             }
2772                             f->showProgressDialog();
2773                         }
2774                     }
2775 
2776                     // Move file to original location (with restore option)
2777                     int ret = f->move(pathname, origpathname, true);
2778 
2779                     // An error has occurred
2780                     if ((ret == 0) && !f->isCancelled())
2781                     {
2782                         f->hideProgressDialog();
2783                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the restore from trash operation!"));
2784                         goto end;
2785                     }
2786 
2787                     // Silently remove trashinfo file
2788                     FXString trashfilespathname = trashfileslocation+PATHSEPSTRING+trashbasename;
2789                     if ((pathname == trashfilespathname) && !existFile(trashfilespathname))
2790                     {
2791                         ::unlink(trashinfopathname.text());
2792                     }
2793                 }
2794                 // If action is cancelled in progress dialog
2795                 if (f->isCancelled())
2796                 {
2797                     f->hideProgressDialog();
2798                     MessageBox::error(this, BOX_OK, _("Warning"), _("Restore from trash file operation cancelled!"));
2799                     goto end;
2800                 }
2801             }
2802         }
2803 end:
2804         getApp()->endWaitCursor();
2805         delete f;
2806     }
2807     // Force FilePanel and DirPanel refresh
2808     list->setAllowRefresh(true);
2809     stopListRefresh = false;
2810     dirpanel->setAllowDirsizeRefresh(true);
2811     onCmdRefresh(0, 0, 0);
2812 
2813     // Enable last item before the first selected item (for keyboard navigation)
2814     firstitem = (firstitem < 1) ? 0 : firstitem-1;
2815     current->list->enableItem(firstitem);
2816     current->list->setCurrentItem(firstitem);
2817 
2818     return(1);
2819 }
2820 
2821 
2822 // Definitively delete files from the file list or the tree list (no trash can)
onCmdFileDelete(FXObject *,FXSelector,void *)2823 long FilePanel::onCmdFileDelete(FXObject*, FXSelector, void*)
2824 {
2825     int   firstitem = 0;
2826     File* f = NULL;
2827 
2828     current->list->setFocus();
2829     FXString dir = current->list->getDirectory();
2830 
2831     FXbool confirm_del = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete", true);
2832     FXbool confirm_del_emptydir = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete_emptydir", true);
2833 
2834     // If we don't have permission to write to the parent directory
2835     if (!::isWritable(dir))
2836     {
2837         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), dir.text());
2838         return(0);
2839     }
2840 
2841     // Items number in the file list
2842     int num = current->list->getNumSelectedItems();
2843 
2844     // If nothing selected, return
2845     if (num == 0)
2846     {
2847         return(0);
2848     }
2849 
2850     // If exist selected files, use them
2851     else if (num >= 1)
2852     {
2853         // Eventually deselect the '..' directory
2854         if (current->list->isItemSelected(0))
2855         {
2856             current->list->deselectItem(0);
2857         }
2858 
2859         if (confirm_del)
2860         {
2861             FXString message;
2862             if (num == 1)
2863             {
2864                 FXString pathname;
2865                 for (int u = 0; u < current->list->getNumItems(); u++)
2866                 {
2867                     if (current->list->isItemSelected(u))
2868                     {
2869                         pathname = current->list->getItemPathname(u);
2870                     }
2871                 }
2872                 if (::isDirectory(pathname))
2873                 {
2874                     message.format(_("Definitively delete folder %s ?"), pathname.text());
2875                 }
2876                 else
2877                 {
2878                     message.format(_("Definitively delete file %s ?"), pathname.text());
2879                 }
2880             }
2881             else
2882             {
2883                 message.format(_("Definitively delete %s selected items?"), FXStringVal(num).text());
2884             }
2885             MessageBox box(this, _("Confirm Delete"), message, delete_big_permicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2886             if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2887             {
2888                 return(0);
2889             }
2890         }
2891         // Wait cursor
2892         getApp()->beginWaitCursor();
2893 
2894         // File object
2895         f = new File(this, _("File delete"), DELETE, num);
2896         f->create();
2897         list->setAllowRefresh(false);
2898 
2899         // Overwrite initialisations
2900         FXbool overwrite = false;
2901         FXbool overwrite_all = false;
2902         FXbool skip_all = false;
2903         FXbool ask_del_empty = true;
2904         FXbool skip_all_del_emptydir = false;
2905 
2906         // Delete selected files
2907         FXString filename, pathname;
2908         int      i = 0;
2909         stopListRefresh = false;
2910         for (int u = 0; u < current->list->getNumItems(); u++)
2911         {
2912             if (current->list->isItemSelected(u))
2913             {
2914                 // Get index of first selected item
2915                 if (firstitem == 0)
2916                 {
2917                     firstitem = u;
2918                 }
2919 
2920                 // Stop refreshing the dirsize in dirpanel
2921                 // when there are many files to delete
2922                 i++;
2923                 if (!stopListRefresh && (i > STOP_LIST_REFRESH_NBMAX))
2924                 {
2925                     dirpanel->setAllowDirsizeRefresh(false);
2926                     stopListRefresh = true;
2927                 }
2928 
2929                 // Get file name and path
2930                 filename = current->list->getItemFilename(u);
2931                 pathname = current->list->getItemPathname(u);
2932 
2933                 // Confirm empty directory deletion
2934                 if (confirm_del & confirm_del_emptydir & ask_del_empty)
2935                 {
2936                     if ((::isEmptyDir(pathname) == 0) && !::isLink(pathname))
2937                     {
2938                         if (skip_all_del_emptydir)
2939                         {
2940                             continue;
2941                         }
2942 
2943                         f->hideProgressDialog();
2944                         FXString msg;
2945                         msg.format(_("Folder %s is not empty, delete it anyway?"), pathname.text());
2946 
2947                         if (num ==1)
2948                         {
2949                         	OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg, OVWBOX_SINGLE_FILE);
2950 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
2951 							delete dlg;
2952 
2953 							if (answer == 0)
2954 							{
2955 								goto end;
2956 							}
2957 						}
2958 
2959                         else
2960                         {
2961 							OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg);
2962 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
2963 							delete dlg;
2964 							switch (answer)
2965 							{
2966 							// Cancel
2967 							case 0:
2968 								goto end;
2969 								break;
2970 
2971 							// Yes
2972 							case 1:
2973 								break;
2974 
2975 							// Yes for all
2976 							case 2:
2977 								ask_del_empty = false;
2978 								break;
2979 
2980 							// Skip
2981 							case 3:
2982 								continue;
2983 								break;
2984 
2985 							// Skip all
2986 							case 4:
2987 								skip_all_del_emptydir = true;
2988 								continue;
2989 								break;
2990 							}
2991 						}
2992 						f->showProgressDialog();
2993                     }
2994                 }
2995 
2996                 // If we don't have permission to write to the file
2997                 if (!::isWritable(pathname))
2998                 {
2999                     // Overwrite dialog if necessary
3000                     if (!(overwrite_all | skip_all))
3001                     {
3002                         f->hideProgressDialog();
3003                         FXString msg;
3004                         msg.format(_("File %s is write-protected, delete it anyway?"), pathname.text());
3005 
3006   						if (num == 1)
3007   						{
3008                         	OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg, OVWBOX_SINGLE_FILE);
3009 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
3010 							delete dlg;
3011 							if (answer == 1)
3012 							{
3013 								overwrite = true;
3014 							}
3015 							else
3016 							{
3017 								goto end;
3018 							}
3019 						}
3020 
3021  						else
3022  						{
3023 							OverwriteBox* dlg = new OverwriteBox(this, _("Confirm Delete"), msg);
3024 							FXuint answer = dlg->execute(PLACEMENT_OWNER);
3025 							delete dlg;
3026 							switch (answer)
3027 							{
3028 							// Cancel
3029 							case 0:
3030 								goto end;
3031 								break;
3032 
3033 							// Yes
3034 							case 1:
3035 								overwrite = true;
3036 								break;
3037 
3038 							// Yes for all
3039 							case 2:
3040 								overwrite_all = true;
3041 								break;
3042 
3043 							// Skip
3044 							case 3:
3045 								overwrite = false;
3046 								break;
3047 
3048 							// Skip all
3049 							case 4:
3050 								skip_all = true;
3051 								break;
3052 							}
3053 						}
3054                     }
3055                     if ((overwrite | overwrite_all) & !skip_all)
3056                     {
3057                         // Caution!! Don't delete parent directory!!
3058                         if (filename != "..")
3059                         {
3060                             // Definitively remove file or folder
3061                             f->remove(pathname);
3062                         }
3063                     }
3064                     f->showProgressDialog();
3065                 }
3066 
3067                 // If we have permission to write
3068                 else
3069                 {
3070                     // Caution!! Don't delete parent directory!!
3071                     if (filename != "..")
3072                     {
3073                         // Definitively remove file or folder
3074                         f->remove(pathname);
3075 
3076                         // If is located at trash location, try to also remove the corresponding trashinfo file if it exists
3077                         // Do it silently and don't report any error if it fails
3078                         FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
3079                         if (use_trash_can && (pathname.left(trashfileslocation.length()) == trashfileslocation))
3080                         {
3081                             FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+filename+".trashinfo";
3082                             ::unlink(trashinfopathname.text());
3083                         }
3084                     }
3085                     // If action is cancelled in progress dialog
3086                     if (f->isCancelled())
3087                     {
3088                         f->hideProgressDialog();
3089                         MessageBox::error(this, BOX_OK, _("Warning"), _("Delete file operation cancelled!"));
3090                         break;
3091                     }
3092                 }
3093             }
3094         }
3095 end:
3096         getApp()->endWaitCursor();
3097         delete f;
3098     }
3099     // Force FilePanel and DirPanel refresh
3100     list->setAllowRefresh(true);
3101     stopListRefresh = false;
3102     dirpanel->setAllowDirsizeRefresh(true);
3103     onCmdRefresh(0, 0, 0);
3104 
3105     // Enable last item before the first selected item (for keyboard navigation)
3106     firstitem = (firstitem < 1) ? 0 : firstitem-1;
3107     current->list->enableItem(firstitem);
3108     current->list->setCurrentItem(firstitem);
3109 
3110     return(1);
3111 }
3112 
3113 
3114 // View/Edit files
onCmdEdit(FXObject *,FXSelector s,void *)3115 long FilePanel::onCmdEdit(FXObject*, FXSelector s, void*)
3116 {
3117     // Wait cursor
3118     getApp()->beginWaitCursor();
3119 
3120     FXString   pathname, samecmd, cmd, cmdname, itemslist = " ";
3121     FileAssoc* association;
3122     FXbool     same = true;
3123     FXbool     first = true;
3124 
3125     current->list->setFocus();
3126 
3127     // At most one item selected, select item under cursor if not called from the popup menu
3128     if ( (current->list->getNumSelectedItems() <= 1) && !allowPopupScroll )
3129     {
3130         int    x, y;
3131         FXuint state;
3132         list->getCursorPosition(x, y, state);
3133 
3134         int item = list->getItemAt(x, y);
3135 
3136         if (list->getCurrentItem() >= 0)
3137         {
3138             list->deselectItem(list->getCurrentItem());
3139         }
3140         if (item >= 0)
3141         {
3142             list->setCurrentItem(item);
3143             list->selectItem(item);
3144         }
3145     }
3146 
3147     FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
3148     FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
3149     FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
3150     FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
3151     FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
3152     FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
3153     FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
3154     FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
3155 
3156     // Update associations dictionary
3157     FileDict* assocdict = new FileDict(getApp());
3158 
3159     // Check if all files have the same association
3160     for (int u = 0; u < current->list->getNumItems(); u++)
3161     {
3162         if (current->list->isItemSelected(u))
3163         {
3164             // Increment number of selected items
3165             pathname = current->list->getItemPathname(u);
3166             association = assocdict->findFileBinding(pathname.text());
3167 
3168             // If there is an association
3169             if (association)
3170             {
3171                 // Use it to edit/view the files
3172                 if (FXSELID(s) == ID_EDIT) // Edit
3173                 {
3174                     cmd = association->command.section(',', 2);
3175 
3176                     // Use a default editor if possible
3177                     switch (progs[cmd])
3178                     {
3179                     case TXTEDITOR:
3180                         cmd = txteditor;
3181                         break;
3182 
3183                     case IMGEDITOR:
3184                         cmd = imgeditor;
3185                         break;
3186 
3187                     case ARCHIVER:
3188                         cmd = archiver;
3189                         break;
3190 
3191                     case NONE: // No default editor found
3192                         ;
3193                         break;
3194                     }
3195 
3196                     if (cmd.length() == 0)
3197                     {
3198                         cmd = txteditor;
3199                     }
3200                 }
3201                 else // Any other is View
3202                 {
3203                     cmd = association->command.section(',', 1);
3204 
3205                     // Use a default viewer if possible
3206                     switch (progs[cmd])
3207                     {
3208                     case TXTVIEWER:
3209                         cmd = txtviewer;
3210                         break;
3211 
3212                     case IMGVIEWER:
3213                         cmd = imgviewer;
3214                         break;
3215 
3216                     case PDFVIEWER:
3217                         cmd = pdfviewer;
3218                         break;
3219 
3220                     case AUDIOPLAYER:
3221                         cmd = audioplayer;
3222                         break;
3223 
3224                     case VIDEOPLAYER:
3225                         cmd = videoplayer;
3226                         break;
3227 
3228                     case ARCHIVER:
3229                         cmd = archiver;
3230                         break;
3231 
3232                     case NONE: // No default viewer found
3233                         ;
3234                         break;
3235                     }
3236 
3237                     if (cmd.length() == 0)
3238                     {
3239                         cmd = txtviewer;
3240                     }
3241                 }
3242                 if (cmd.text() != NULL)
3243                 {
3244                     // First selected item
3245                     if (first)
3246                     {
3247                         samecmd = cmd;
3248                         first = false;
3249                     }
3250 
3251                     if (samecmd != cmd)
3252                     {
3253                         same = false;
3254                         break;
3255                     }
3256 
3257                     // List of selected items
3258                     itemslist += ::quote(pathname) + " ";
3259                 }
3260                 else
3261                 {
3262                     same = false;
3263                     break;
3264                 }
3265             }
3266 
3267             // No association
3268             else
3269             {
3270                 same = false;
3271                 break;
3272             }
3273         }
3274     }
3275 
3276 #ifdef STARTUP_NOTIFICATION
3277     // Startup notification option and exceptions (if any)
3278     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
3279     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
3280 #endif
3281 
3282     // Same association for all files : execute the associated or default editor or viewer
3283     if (same)
3284     {
3285         cmdname = samecmd;
3286 
3287         // If command exists, run it
3288         if (::existCommand(cmdname))
3289         {
3290             cmd = cmdname+itemslist;
3291 #ifdef STARTUP_NOTIFICATION
3292             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
3293 #else
3294             runcmd(cmd, current->list->getDirectory(), startlocation);
3295 #endif
3296         }
3297 
3298         // If command does not exist, call the "Open with..." dialog
3299         else
3300         {
3301             getApp()->endWaitCursor();
3302             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
3303         }
3304     }
3305 
3306     // Files have different associations : handle them separately
3307     else
3308     {
3309         for (int u = 0; u < current->list->getNumItems(); u++)
3310         {
3311             if (current->list->isItemSelected(u))
3312             {
3313                 pathname = current->list->getItemPathname(u);
3314 
3315                 // Only View / Edit regular files (not directories)
3316                 if (::isFile(pathname))
3317                 {
3318                     association = assocdict->findFileBinding(pathname.text());
3319 
3320                     // If there is an association
3321                     if (association)
3322                     {
3323                         // Use it to edit/view the file
3324                         if (FXSELID(s) == ID_EDIT) // Edit
3325                         {
3326                             cmd = association->command.section(',', 2);
3327 
3328                             // Use a default editor if possible
3329                             switch (progs[cmd])
3330                             {
3331                             case TXTEDITOR:
3332                                 cmd = txteditor;
3333                                 break;
3334 
3335                             case IMGEDITOR:
3336                                 cmd = imgeditor;
3337                                 break;
3338 
3339                             case ARCHIVER:
3340                                 cmd = archiver;
3341                                 break;
3342                             }
3343 
3344                             if (cmd.length() == 0)
3345                             {
3346                                 cmd = txteditor;
3347                             }
3348                         }
3349                         else // Any other is View
3350                         {
3351                             cmd = association->command.section(',', 1);
3352 
3353                             // Use a default viewer if possible
3354                             switch (progs[cmd])
3355                             {
3356                             case TXTVIEWER:
3357                                 cmd = txtviewer;
3358                                 break;
3359 
3360                             case IMGVIEWER:
3361                                 cmd = imgviewer;
3362                                 break;
3363 
3364                             case PDFVIEWER:
3365                                 cmd = pdfviewer;
3366                                 break;
3367 
3368                             case AUDIOPLAYER:
3369                                 cmd = audioplayer;
3370                                 break;
3371 
3372                             case VIDEOPLAYER:
3373                                 cmd = videoplayer;
3374                                 break;
3375 
3376                             case ARCHIVER:
3377                                 cmd = archiver;
3378                                 break;
3379 
3380                             case NONE: // No default viewer found
3381                                 ;
3382                                 break;
3383                             }
3384 
3385                             if (cmd.length() == 0)
3386                             {
3387                                 cmd = txtviewer;
3388                             }
3389                         }
3390 
3391                         if (cmd.text() != NULL)
3392                         {
3393                             cmdname = cmd;
3394 
3395                             // If command exists, run it
3396                             if (::existCommand(cmdname))
3397                             {
3398                                 cmd = cmdname+" "+::quote(pathname);
3399 #ifdef STARTUP_NOTIFICATION
3400                                 runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
3401 #else
3402                                 runcmd(cmd, current->list->getDirectory(), startlocation);
3403 #endif
3404                             }
3405 
3406                             // If command does not exist, call the "Open with..." dialog
3407                             else
3408                             {
3409                                 getApp()->endWaitCursor();
3410                                 current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
3411                             }
3412                         }
3413                     }
3414 
3415                     // No association
3416                     else
3417                     {
3418                         if (FXSELID(s) == ID_EDIT)
3419                         {
3420                             cmd = txteditor;
3421                         }
3422                         else
3423                         {
3424                             cmd = txtviewer;
3425                         }
3426 
3427                         cmdname = cmd;
3428 
3429                         // If command exists, run it
3430                         if (::existCommand(cmdname))
3431                         {
3432                             cmd = cmdname+" "+::quote(pathname);
3433 #ifdef STARTUP_NOTIFICATION
3434                             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
3435 #else
3436                             runcmd(cmd, current->list->getDirectory(), startlocation);
3437 #endif
3438                         }
3439 
3440                         // If command does not exist, call the "Open with..." dialog
3441                         else
3442                         {
3443                             getApp()->endWaitCursor();
3444                             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
3445                         }
3446                     }
3447                 }
3448             }
3449         }
3450     }
3451 
3452     getApp()->endWaitCursor();
3453 
3454     return(1);
3455 }
3456 
3457 
3458 // Compare two files
onCmdCompare(FXObject *,FXSelector s,void *)3459 long FilePanel::onCmdCompare(FXObject*, FXSelector s, void*)
3460 {
3461     current->list->setFocus();
3462     int num = current->list->getNumSelectedItems();
3463 
3464     // Only one or two selected items can be handled
3465     if ((num != 1) && (num != 2))
3466     {
3467         getApp()->endWaitCursor();
3468         return(0);
3469     }
3470 
3471 #ifdef STARTUP_NOTIFICATION
3472     // Startup notification option and exceptions (if any)
3473     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
3474     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
3475 #endif
3476 
3477     FXString filecomparator = getApp()->reg().readStringEntry("PROGS", "filecomparator", DEFAULT_FILECOMPARATOR);
3478     FXString pathname, cmd, cmdname, itemslist = " ";
3479 
3480     // One selected item
3481     if (num == 1)
3482     {
3483         // Get the selected item
3484         for (int u = 0; u < current->list->getNumItems(); u++)
3485         {
3486             if (current->list->isItemSelected(u))
3487             {
3488                 pathname = current->list->getItemPathname(u);
3489                 itemslist += ::quote(pathname) + " ";
3490             }
3491         }
3492 
3493         // Open a dialog to select the other item to be compared
3494         if (comparedialog == NULL)
3495         {
3496             comparedialog = new BrowseInputDialog(this, "", "", _("Compare"), _("With:"), bigcompareicon, BROWSE_INPUT_FILE);
3497         }
3498         comparedialog->setIcon(bigcompareicon);
3499         comparedialog->setMessage(pathname);
3500         comparedialog->setText("");
3501         int rc = 1;
3502         rc = comparedialog->execute(PLACEMENT_CURSOR);
3503 
3504         // Get item path and add it to the list
3505         FXString str = comparedialog->getText();
3506         itemslist += ::quote(str);
3507         if (!rc || (str == ""))
3508         {
3509             return(0);
3510         }
3511     }
3512 
3513     // Two selected items
3514     else if (num == 2)
3515     {
3516         // Get the two selected items
3517         for (int u = 0; u < current->list->getNumItems(); u++)
3518         {
3519             if (current->list->isItemSelected(u))
3520             {
3521                 pathname = current->list->getItemPathname(u);
3522                 itemslist += ::quote(pathname) + " ";
3523             }
3524         }
3525     }
3526 
3527     // Wait cursor
3528     getApp()->beginWaitCursor();
3529 
3530     // If command exists, run it
3531     cmdname = filecomparator;
3532     if (::existCommand(cmdname))
3533     {
3534         cmd = cmdname+itemslist;
3535 #ifdef STARTUP_NOTIFICATION
3536         runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
3537 #else
3538         runcmd(cmd, current->list->getDirectory(), startlocation);
3539 #endif
3540     }
3541 
3542     // If command does not exist, issue an error message
3543     else
3544     {
3545         getApp()->endWaitCursor();
3546         MessageBox::error(this, BOX_OK, _("Error"), _("Program %s not found. Please define a file comparator program in the Preferences dialog!"), cmdname.text());
3547     }
3548 
3549     getApp()->endWaitCursor();
3550 
3551     return(1);
3552 }
3553 
3554 
3555 // File or directory properties
onCmdProperties(FXObject * sender,FXSelector,void *)3556 long FilePanel::onCmdProperties(FXObject* sender, FXSelector, void*)
3557 {
3558     int ret;
3559     int num, itm;
3560 
3561     current->list->setFocus();
3562 
3563     // If no selected files in the file list, use the selected folder from the tree list (if any)
3564     num = current->list->getNumSelectedItems(&itm);
3565     if (num == 0)
3566     {
3567 		return(0);
3568     }
3569 
3570     // There is one selected file in the file list
3571     else if (num == 1)
3572     {
3573         // Eventually deselect the '..' directory
3574         if (current->list->isItemSelected(0))
3575         {
3576             current->list->deselectItem(0);
3577         }
3578 
3579         FXString path = current->list->getDirectory();
3580         FXString filename = current->list->getItemText(itm);
3581         filename = filename.section('\t', 0);
3582         PropertiesBox* attrdlg = new PropertiesBox(this, filename, path);
3583         attrdlg->create();
3584         attrdlg->show(PLACEMENT_OWNER);
3585     }
3586 
3587     // There are multiple selected files in the file list
3588     else if (num > 1)
3589     {
3590         ret = chdir(current->list->getDirectory().text());
3591         if (ret < 0)
3592         {
3593             int errcode = errno;
3594             if (errcode)
3595             {
3596                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), current->list->getDirectory().text(), strerror(errcode));
3597             }
3598             else
3599             {
3600                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), current->list->getDirectory().text());
3601             }
3602 
3603             return(0);
3604         }
3605 
3606         FXString  path = current->list->getDirectory();
3607         FXString* files = new FXString[num];
3608         FXString* paths = new FXString[num];
3609 
3610         // Eventually deselect the '..' directory
3611         if (current->list->isItemSelected(0))
3612         {
3613             current->list->deselectItem(0);
3614         }
3615 
3616         int i = 0;
3617         for (int u = 0; u < current->list->getNumItems(); u++)
3618         {
3619             if (current->list->isItemSelected(u))
3620             {
3621                 files[i] = current->list->getItemText(u).section('\t', 0);
3622                 paths[i] = path;
3623                 i++;
3624             }
3625         }
3626 
3627         PropertiesBox* attrdlg = new PropertiesBox(this, files, num, paths);
3628         attrdlg->create();
3629         attrdlg->show(PLACEMENT_OWNER);
3630 
3631         ret = chdir(startlocation.text());
3632         if (ret < 0)
3633         {
3634             int errcode = errno;
3635             if (errcode)
3636             {
3637                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
3638             }
3639             else
3640             {
3641                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
3642             }
3643 
3644             return(0);
3645         }
3646     }
3647 
3648     // Force panel refresh
3649     return(1);
3650 }
3651 
3652 
3653 // Create new directory
onCmdNewDir(FXObject *,FXSelector,void *)3654 long FilePanel::onCmdNewDir(FXObject*, FXSelector, void*)
3655 {
3656     FXString dirname = "";
3657 
3658     // Focus on current panel list
3659     current->list->setFocus();
3660 
3661     FXString dirpath = current->list->getDirectory();
3662     if (dirpath != ROOTDIR)
3663     {
3664         dirpath += PATHSEPSTRING;
3665     }
3666 
3667     if (newdirdialog == NULL)
3668     {
3669         newdirdialog = new InputDialog(this, "", _("Create new folder:"), _("New Folder"),"",bignewfoldericon);
3670     }
3671     newdirdialog->setText("");
3672 
3673     // Accept was pressed
3674     if (newdirdialog->execute(PLACEMENT_CURSOR))
3675     {
3676         if (newdirdialog->getText() == "")
3677         {
3678             MessageBox::warning(this, BOX_OK, _("Warning"), _("Folder name is empty, operation cancelled"));
3679             return(0);
3680         }
3681 
3682 		// Directory name contains '/'
3683 		if (newdirdialog->getText().contains(PATHSEPCHAR))
3684 		{
3685 			MessageBox::warning(this, BOX_OK, _("Warning"), _("The / character is not allowed in folder names, operation cancelled"));
3686 			return(0);
3687 		}
3688 
3689         dirname = dirpath+newdirdialog->getText();
3690         if (dirname != dirpath)
3691         {
3692             // Create the new dir according to the current umask
3693             int mask;
3694             mask = umask(0);
3695             umask(mask);
3696 
3697             // Note that the umask value is in decimal (511 means octal 0777)
3698             errno = 0;
3699             int ret = ::mkdir(dirname.text(), 511 & ~mask);
3700             int errcode = errno;
3701             if (ret == -1)
3702             {
3703                 if (errcode)
3704                 {
3705                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s: %s"), dirname.text(), strerror(errcode));
3706                 }
3707                 else
3708                 {
3709                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s"), dirname.text());
3710                 }
3711                 return(0);
3712             }
3713         }
3714     }
3715 
3716     // Cancel was pressed
3717     else
3718     {
3719         return(0);
3720     }
3721 
3722     // Force panel refresh
3723     onCmdRefresh(0, 0, 0);
3724 
3725     // Enable created item, if any (for keyboard navigation)
3726     FXString name;
3727     for (int u = 0; u < current->list->getNumItems(); u++)
3728     {
3729         name = current->list->getItemPathname(u);
3730         if (name == dirname)
3731         {
3732             current->list->enableItem(u);
3733             current->list->setCurrentItem(u);
3734             break;
3735         }
3736     }
3737 
3738     return(1);
3739 }
3740 
3741 
3742 // Create new file
onCmdNewFile(FXObject *,FXSelector,void *)3743 long FilePanel::onCmdNewFile(FXObject*, FXSelector, void*)
3744 {
3745     FXString filename = "";
3746 
3747     // Focus on current panel list
3748     current->list->setFocus();
3749 
3750     FXString pathname = current->list->getDirectory();
3751     if (pathname != ROOTDIR)
3752     {
3753         pathname += PATHSEPSTRING;
3754     }
3755 
3756     if (newfiledialog == NULL)
3757     {
3758         newfiledialog = new InputDialog(this, "", _("Create new file:"), _("New File"), "", bignewfileicon, false);
3759     }
3760     newfiledialog->setText("");
3761 
3762     // Accept was pressed
3763     if (newfiledialog->execute(PLACEMENT_CURSOR))
3764     {
3765         if (newfiledialog->getText() == "")
3766         {
3767             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
3768             return(0);
3769         }
3770 
3771 		// File name contains '/'
3772 		if (newfiledialog->getText().contains(PATHSEPCHAR))
3773 		{
3774 			MessageBox::warning(this, BOX_OK, _("Warning"), _("The / character is not allowed in file names, operation cancelled"));
3775 			return(0);
3776 		}
3777 
3778         filename = pathname+newfiledialog->getText();
3779         FILE* file;
3780         if (filename != pathname)
3781         {
3782             // Test some error conditions
3783             if (existFile(filename))
3784             {
3785                 MessageBox::error(this, BOX_OK, _("Error"), _("File or folder %s already exists"), filename.text());
3786                 return(0);
3787             }
3788             // Create the new file
3789             errno = 0;
3790             if (!(file = fopen(filename.text(), "w+")) || fclose(file))
3791             {
3792                 if (errno)
3793                 {
3794                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create file %s: %s"), filename.text(), strerror(errno));
3795                 }
3796                 else
3797                 {
3798                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create file %s"), filename.text());
3799                 }
3800                 return(0);
3801             }
3802             // Change the file permissions according to the current umask
3803             int mask;
3804             mask = umask(0);
3805             umask(mask);
3806             errno = 0;
3807             int rc = chmod(filename.text(), 438 & ~mask);
3808             int errcode = errno;
3809             if (rc)
3810             {
3811                 if (errcode)
3812                 {
3813                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't set permissions in %s: %s"), filename.text(), strerror(errcode));
3814                 }
3815                 else
3816                 {
3817                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't set permissions in %s"), filename.text());
3818                 }
3819             }
3820         }
3821     }
3822 
3823     // Cancel was pressed
3824     else
3825     {
3826         return(0);
3827     }
3828 
3829     // Force panel refresh
3830     onCmdRefresh(0, 0, 0);
3831 
3832     // Enable created item, if any (for keyboard navigation)
3833     FXString name;
3834     for (int u = 0; u < current->list->getNumItems(); u++)
3835     {
3836         name = current->list->getItemPathname(u);
3837         if (name == filename)
3838         {
3839             current->list->enableItem(u);
3840             current->list->setCurrentItem(u);
3841             break;
3842         }
3843     }
3844 
3845     return(1);
3846 }
3847 
3848 
3849 // Create new symbolic link
onCmdNewSymlink(FXObject *,FXSelector,void *)3850 long FilePanel::onCmdNewSymlink(FXObject*, FXSelector, void*)
3851 {
3852     FXString linkname = "";
3853 
3854     // Focus on current panel list
3855     current->list->setFocus();
3856 
3857     FXString linkpath = current->list->getDirectory();
3858     if (linkpath != ROOTDIR)
3859     {
3860         linkpath += PATHSEPSTRING;
3861     }
3862 
3863     if (newlinkdialog == NULL)
3864     {
3865         newlinkdialog = new InputDialog(this, "", _("Create new symbolic link:"), _("New Symlink"), "", bignewlinkicon, false);
3866     }
3867     newlinkdialog->setText("");
3868 
3869     // Accept was pressed
3870     if (newlinkdialog->execute(PLACEMENT_CURSOR))
3871     {
3872         if (newlinkdialog->getText() == "")
3873         {
3874             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
3875             return(0);
3876         }
3877         linkname = linkpath+newlinkdialog->getText();
3878         File* f;
3879         if (linkname != linkpath)
3880         {
3881             // Test some error conditions
3882             if (existFile(linkname))
3883             {
3884                 MessageBox::error(this, BOX_OK, _("Error"), _("File or folder %s already exists"), linkname.text());
3885                 return(0);
3886             }
3887 
3888             // Select target
3889             FileDialog browse(this, _("Select the symlink refered file or folder"));
3890             browse.setDirectory(linkpath);
3891             browse.setSelectMode(SELECT_FILE_MIXED);
3892             if (browse.execute())
3893             {
3894                 FXString linksource = browse.getFilename();
3895 
3896                 // Source does not exist
3897                 if (!existFile(linksource))
3898                 {
3899                     MessageBox::error(this, BOX_OK, _("Error"), _("Symlink source %s does not exist"), linksource.text());
3900                     return(0);
3901                 }
3902 
3903                 f = new File(this, _("Symlink"), SYMLINK);
3904                 f->create();
3905                 f->symlink(linksource, linkname);
3906                 delete f;
3907             }
3908             //else
3909             //return 0;
3910         }
3911     }
3912 
3913     // Cancel was pressed
3914     else
3915     {
3916         return(0);
3917     }
3918 
3919     // Force panel refresh
3920     onCmdRefresh(0, 0, 0);
3921 
3922     // Enable created item, if any (for keyboard navigation)
3923     FXString name;
3924     for (int u = 0; u < current->list->getNumItems(); u++)
3925     {
3926         name = current->list->getItemPathname(u);
3927         if (name == linkname)
3928         {
3929             current->list->enableItem(u);
3930             current->list->setCurrentItem(u);
3931             break;
3932         }
3933     }
3934 
3935     return(1);
3936 }
3937 
3938 
3939 // Open single or multiple files
onCmdOpen(FXObject *,FXSelector,void *)3940 long FilePanel::onCmdOpen(FXObject*, FXSelector, void*)
3941 {
3942     // Wait cursor
3943     getApp()->beginWaitCursor();
3944 
3945     FXString   pathname, samecmd, cmd, cmdname, itemslist = " ";
3946     FileAssoc* association;
3947     FXbool     same = true;
3948     FXbool     first = true;
3949 
3950     current->list->setFocus();
3951     if (current->list->getNumSelectedItems() == 0)
3952     {
3953         getApp()->endWaitCursor();
3954         return(0);
3955     }
3956 
3957     // Default programs
3958     FXString txtviewer = getApp()->reg().readStringEntry("PROGS", "txtviewer", DEFAULT_TXTVIEWER);
3959     FXString txteditor = getApp()->reg().readStringEntry("PROGS", "txteditor", DEFAULT_TXTEDITOR);
3960     FXString imgviewer = getApp()->reg().readStringEntry("PROGS", "imgviewer", DEFAULT_IMGVIEWER);
3961     FXString imgeditor = getApp()->reg().readStringEntry("PROGS", "imgeditor", DEFAULT_IMGEDITOR);
3962     FXString pdfviewer = getApp()->reg().readStringEntry("PROGS", "pdfviewer", DEFAULT_PDFVIEWER);
3963     FXString audioplayer = getApp()->reg().readStringEntry("PROGS", "audioplayer", DEFAULT_AUDIOPLAYER);
3964     FXString videoplayer = getApp()->reg().readStringEntry("PROGS", "videoplayer", DEFAULT_VIDEOPLAYER);
3965     FXString archiver = getApp()->reg().readStringEntry("PROGS", "archiver", DEFAULT_ARCHIVER);
3966 
3967     // Update associations dictionary
3968     FileDict* assocdict = new FileDict(getApp());
3969 
3970     // Check if all files have the same association
3971     for (int u = 0; u < current->list->getNumItems(); u++)
3972     {
3973         if (current->list->isItemSelected(u))
3974         {
3975             // Increment number of selected items
3976             pathname = current->list->getItemPathname(u);
3977 
3978             // If directory, skip it
3979             if (::isDirectory(pathname))
3980             {
3981                 continue;
3982             }
3983 
3984             // If association found
3985             association = assocdict->findFileBinding(pathname.text());
3986             if (association)
3987             {
3988                 cmd = association->command.section(',', 0);
3989 
3990                 // Use a default program if possible
3991                 switch (progs[cmd])
3992                 {
3993                 case TXTVIEWER:
3994                     cmd = txtviewer;
3995                     break;
3996 
3997                 case TXTEDITOR:
3998                     cmd = txteditor;
3999                     break;
4000 
4001                 case IMGVIEWER:
4002                     cmd = imgviewer;
4003                     break;
4004 
4005                 case IMGEDITOR:
4006                     cmd = imgeditor;
4007                     break;
4008 
4009                 case PDFVIEWER:
4010                     cmd = pdfviewer;
4011                     break;
4012 
4013                 case AUDIOPLAYER:
4014                     cmd = audioplayer;
4015                     break;
4016 
4017                 case VIDEOPLAYER:
4018                     cmd = videoplayer;
4019                     break;
4020 
4021                 case ARCHIVER:
4022                     cmd = archiver;
4023                     break;
4024 
4025                 case NONE: // No program found
4026                     ;
4027                     break;
4028                 }
4029 
4030                 if (cmd != "")
4031                 {
4032                     // First selected item
4033                     if (first)
4034                     {
4035                         samecmd = cmd;
4036                         first = false;
4037                     }
4038 
4039                     if (samecmd != cmd)
4040                     {
4041                         same = false;
4042                         break;
4043                     }
4044 
4045                     // List of selected items
4046                     itemslist += ::quote(pathname) + " ";
4047                 }
4048                 else
4049                 {
4050                     same = false;
4051                     break;
4052                 }
4053             }
4054             else
4055             {
4056                 same = false;
4057                 break;
4058             }
4059         }
4060     }
4061 
4062 #ifdef STARTUP_NOTIFICATION
4063     // Startup notification option and exceptions (if any)
4064     FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
4065     FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
4066 #endif
4067 
4068     // Same command for all files : open them
4069     if (same && (itemslist != " "))
4070     {
4071         cmdname = samecmd;
4072 
4073         // If command exists, run it
4074         if (::existCommand(cmdname))
4075         {
4076             cmd = samecmd+itemslist;
4077 #ifdef STARTUP_NOTIFICATION
4078             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
4079 #else
4080             runcmd(cmd, current->list->getDirectory(), startlocation);
4081 #endif
4082         }
4083 
4084         // If command does not exist, call the "Open with..." dialog
4085         else
4086         {
4087             getApp()->endWaitCursor();
4088             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
4089         }
4090     }
4091 
4092     // Files have different commands : handle them separately
4093     else
4094     {
4095         for (int u = 0; u < current->list->getNumItems(); u++)
4096         {
4097             if (current->list->isItemSelected(u))
4098             {
4099                 pathname = current->list->getItemPathname(u);
4100 
4101                 // If directory, skip it
4102                 if (::isDirectory(pathname))
4103                 {
4104                     continue;
4105                 }
4106 
4107                 association = assocdict->findFileBinding(pathname.text());
4108                 if (association)
4109                 {
4110                     // Use association to open the file
4111                     cmd = association->command.section(',', 0);
4112 
4113                     // Use a default program if possible
4114                     switch (progs[cmd])
4115                     {
4116                     case TXTVIEWER:
4117                         cmd = txtviewer;
4118                         break;
4119 
4120                     case TXTEDITOR:
4121                         cmd = txteditor;
4122                         break;
4123 
4124                     case IMGVIEWER:
4125                         cmd = imgviewer;
4126                         break;
4127 
4128                     case IMGEDITOR:
4129                         cmd = imgeditor;
4130                         break;
4131 
4132                     case PDFVIEWER:
4133                         cmd = pdfviewer;
4134                         break;
4135 
4136                     case AUDIOPLAYER:
4137                         cmd = audioplayer;
4138                         break;
4139 
4140                     case VIDEOPLAYER:
4141                         cmd = videoplayer;
4142                         break;
4143 
4144                     case ARCHIVER:
4145                         cmd = archiver;
4146                         break;
4147 
4148                     case NONE: // No program found
4149                         ;
4150                         break;
4151                     }
4152 
4153                     if (cmd != "")
4154                     {
4155                         cmdname = cmd;
4156 
4157                         // If command exists, run it
4158                         if (::existCommand(cmdname))
4159                         {
4160                             cmd = cmdname+" "+::quote(pathname);
4161 #ifdef STARTUP_NOTIFICATION
4162                             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
4163 #else
4164                             runcmd(cmd, current->list->getDirectory(), startlocation);
4165 #endif
4166                         }
4167 
4168                         // If command does not exist, call the "Open with..." dialog
4169                         else
4170                         {
4171                             getApp()->endWaitCursor();
4172                             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
4173                         }
4174                     }
4175 
4176                     // Or execute the file
4177                     else if (current->list->isItemExecutable(u))
4178                     {
4179                         execFile(pathname);
4180                     }
4181 
4182                     // Or call the "Open with..." dialog
4183                     else
4184                     {
4185                         getApp()->endWaitCursor();
4186                         current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
4187                     }
4188                 }
4189 
4190                 // If no association but executable
4191                 else if (current->list->isItemExecutable(u))
4192                 {
4193                     execFile(pathname);
4194                 }
4195 
4196                 // Other cases
4197                 else
4198                 {
4199                     getApp()->endWaitCursor();
4200                     current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
4201                 }
4202             }
4203         }
4204     }
4205 
4206     getApp()->endWaitCursor();
4207 
4208     return(1);
4209 }
4210 
4211 
4212 // Open with
onCmdOpenWith(FXObject *,FXSelector,void *)4213 long FilePanel::onCmdOpenWith(FXObject*, FXSelector, void*)
4214 {
4215     char** str = NULL;
4216 
4217     current->list->setFocus();
4218 
4219     if (current->list->getNumSelectedItems() == 0)
4220     {
4221         return(0);
4222     }
4223 
4224     FXString cmd = "", cmdname;
4225     if (opendialog == NULL)
4226     {
4227         opendialog = new HistInputDialog(this, "", _("Open selected file(s) with:"), _("Open With"), "", bigfileopenicon, HIST_INPUT_EXECUTABLE_FILE, true, _("A&ssociate"));
4228     }
4229     opendialog->setText(cmd);
4230 
4231     // Dialog with history list and associate checkbox
4232     opendialog->CursorEnd();
4233     opendialog->selectAll();
4234     opendialog->clearItems();
4235     for (int i = 0; i < OpenNum; i++)
4236     {
4237         opendialog->appendItem(OpenHistory[i]);
4238     }
4239     opendialog->setDirectory(ROOTDIR);
4240     opendialog->sortItems();
4241     if (opendialog->execute())
4242     {
4243         cmd = opendialog->getText();
4244         if (cmd == "")
4245         {
4246             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
4247             return(0);
4248         }
4249 
4250         for (int u = 0; u < current->list->getNumItems(); u++)
4251         {
4252             if (current->list->isItemSelected(u))
4253             {
4254                 // Handles "associate" checkbox for "open with..." dialog
4255                 if (opendialog->getOption())
4256                 {
4257                     FXString filename = current->list->getItemFilename(u);
4258                     FXString ext = filename.rafter('.', 2).lower();
4259 
4260                     if ((ext == "tar.gz") || (ext == "tar.bz2") || (ext == "tar.xz") || (ext == "tar.z")) // Special cases
4261                     {
4262                     }
4263                     else
4264                     {
4265                         ext = FXPath::extension(filename).lower();
4266                     }
4267 
4268                     if (ext == "")
4269                     {
4270                         ext = FXPath::name(filename);
4271                     }
4272 
4273                     FileAssoc* association = current->list->getItemAssoc(u);
4274 
4275                     if (association)
4276                     {
4277                         // Update existing association
4278                         FXString oldfileassoc = getApp()->reg().readStringEntry("FILETYPES", ext.text(), "");
4279                         oldfileassoc.erase(0, oldfileassoc.section(';', 0).section(',', 0).length());
4280                         oldfileassoc.prepend(opendialog->getText());
4281                         getApp()->reg().writeStringEntry("FILETYPES", ext.text(), oldfileassoc.text());
4282 
4283                         // Handle file association
4284                         str = new char* [2];
4285                         str[0] = new char[strlen(ext.text())+1];
4286                         str[1] = new char[strlen(oldfileassoc.text())+1];
4287                         strlcpy(str[0], ext.text(), ext.length()+1);
4288                         strlcpy(str[1], oldfileassoc.text(), oldfileassoc.length()+1);
4289                         mainWindow->handle(this, FXSEL(SEL_COMMAND, XFileExplorer::ID_FILE_ASSOC), str);
4290                     }
4291                     else
4292                     {
4293                         // New association
4294                         FXString newcmd = opendialog->getText().append(";Document;;;;");
4295                         getApp()->reg().writeStringEntry("FILETYPES", ext.text(), newcmd.text());
4296 
4297                         // Handle file association
4298                         str = new char* [2];
4299                         str[0] = new char[strlen(ext.text())+1];
4300                         str[1] = new char[strlen(newcmd.text())+1];
4301                         strlcpy(str[0], ext.text(), ext.length()+1);
4302                         strlcpy(str[1], newcmd.text(), newcmd.length()+1);
4303                         mainWindow->handle(this, FXSEL(SEL_COMMAND, XFileExplorer::ID_FILE_ASSOC), str);
4304                     }
4305                 }
4306                 // End
4307 
4308                 FXString pathname = current->list->getItemPathname(u);
4309                 cmdname = cmd;
4310                 cmd += " ";
4311                 cmd = cmd+::quote(pathname);
4312             }
4313         }
4314 
4315         // Run command if it exists
4316         getApp()->beginWaitCursor();
4317 
4318 #ifdef STARTUP_NOTIFICATION
4319         // Startup notification option and exceptions (if any)
4320         FXbool   usesn = getApp()->reg().readUnsignedEntry("OPTIONS", "use_startup_notification", true);
4321         FXString snexcepts = getApp()->reg().readStringEntry("OPTIONS", "startup_notification_exceptions", "");
4322 #endif
4323 
4324         // If command exists, run it
4325         if (::existCommand(cmdname))
4326 #ifdef STARTUP_NOTIFICATION
4327         {
4328             runcmd(cmd, cmdname, current->list->getDirectory(), startlocation, usesn, snexcepts);
4329         }
4330 #else
4331         {
4332             runcmd(cmd, current->list->getDirectory(), startlocation);
4333         }
4334 #endif
4335         // If command does not exist, call the "Open with..." dialog
4336         else
4337         {
4338             getApp()->endWaitCursor();
4339             current->handle(this, FXSEL(SEL_COMMAND, ID_OPEN_WITH), NULL);
4340             return(1);
4341         }
4342 
4343         // Update history list
4344         OpenNum = opendialog->getHistorySize();
4345         cmd = opendialog->getText();
4346 
4347         // Check if cmd is a new string, i.e. is not already in history
4348         FXbool newstr = true;
4349         for (int i = 0; i < OpenNum-1; i++)
4350         {
4351             if (streq(OpenHistory[i], cmd.text()))
4352             {
4353                 newstr = false;
4354                 break;
4355             }
4356         }
4357 
4358         // History limit reached
4359         if (OpenNum > OPEN_HIST_SIZE)
4360         {
4361             OpenNum--;
4362         }
4363 
4364         // Restore original history order
4365         opendialog->clearItems();
4366         for (int i = 0; i < OpenNum; i++)
4367         {
4368             opendialog->appendItem(OpenHistory[i]);
4369 		}
4370 
4371         // New string
4372         if (newstr)
4373         {
4374             // FIFO
4375             strlcpy(OpenHistory[0], cmd.text(), cmd.length()+1);
4376             for (int i = 1; i < OpenNum; i++)
4377             {
4378                 strlcpy(OpenHistory[i], opendialog->getHistoryItem(i-1).text(), opendialog->getHistoryItem(i-1).length()+1);
4379             }
4380         }
4381 
4382         getApp()->endWaitCursor();
4383     }
4384 
4385     return(1);
4386 }
4387 
4388 
onCmdItemFilter(FXObject * o,FXSelector sel,void *)4389 long FilePanel::onCmdItemFilter(FXObject* o, FXSelector sel, void*)
4390 {
4391     if (FilterNum == 0)
4392     {
4393         strlcpy(FilterHistory[FilterNum], "*", 2);
4394         FilterNum++;
4395     }
4396 
4397     int      i;
4398     FXString pat = list->getPattern();
4399     if (filterdialog == NULL)
4400     {
4401         filterdialog = new HistInputDialog(this, pat, _("Show files:"), _("Filter"), "", bigfiltericon, HIST_INPUT_FILE);
4402     }
4403     filterdialog->CursorEnd();
4404     filterdialog->selectAll();
4405     filterdialog->clearItems();
4406     for (int i = 0; i < FilterNum; i++)
4407     {
4408         filterdialog->appendItem(FilterHistory[i]);
4409     }
4410     filterdialog->sortItems();
4411 
4412     if (filterdialog->execute() && ((pat = filterdialog->getText()) != ""))
4413     {
4414         // Change file list patten
4415         if (FXSELID(sel) == ID_FILTER_CURRENT)
4416         {
4417             current->list->setPattern(pat);
4418         }
4419         else
4420         {
4421             list->setPattern(pat);
4422         }
4423 
4424         FXbool newstr = true;
4425         for (i = 0; i < FilterNum; i++)
4426         {
4427             if (streq(FilterHistory[i], pat.text()))
4428             {
4429                 newstr = false;
4430                 break;
4431             }
4432         }
4433         // Append new string to the list bottom
4434         if (newstr && (FilterNum < FILTER_HIST_SIZE))
4435         {
4436             strlcpy(FilterHistory[FilterNum], pat.text(), pat.length()+1);
4437             FilterNum++;
4438         }
4439     }
4440 
4441     list->setFocus();
4442     return(1);
4443 }
4444 
4445 
4446 // Panel context menu
onCmdPopupMenu(FXObject * o,FXSelector s,void * p)4447 long FilePanel::onCmdPopupMenu(FXObject* o, FXSelector s, void* p)
4448 {
4449     // Make panel active
4450     setActive();
4451 
4452     // Check if control key or Shift-F10 or menu was pressed
4453     if (p != NULL)
4454     {
4455         FXEvent* event = (FXEvent*)p;
4456         if (event->state&CONTROLMASK)
4457         {
4458             ctrl = true;
4459         }
4460         if ((event->state&SHIFTMASK && event->code == KEY_F10) || event->code == KEY_Menu)
4461         {
4462             shiftf10 = true;
4463         }
4464     }
4465 
4466     // Use to select the item under cursor when right clicking
4467     // Only when Shift-F10 was not pressed
4468     if (!shiftf10 && (list->getNumSelectedItems() <= 1))
4469     {
4470         int    x, y;
4471         FXuint state;
4472         list->getCursorPosition(x, y, state);
4473 
4474         int item = list->getItemAt(x, y);
4475 
4476         if (list->getCurrentItem() >= 0)
4477         {
4478             list->deselectItem(list->getCurrentItem());
4479         }
4480         if (item >= 0)
4481         {
4482             list->setCurrentItem(item);
4483             list->selectItem(item);
4484         }
4485     }
4486 
4487     // If first item (i.e. the '..' item)
4488     if ((list->getNumSelectedItems() == 1) && list->isItemSelected(0))
4489     {
4490         ctrl = true;
4491     }
4492 
4493     // If control flag is set, deselect all items
4494     if (ctrl)
4495     {
4496         list->handle(o, FXSEL(SEL_COMMAND, FileList::ID_DESELECT_ALL), p);
4497     }
4498 
4499     // Popup menu pane
4500     FXMenuPane* menu = new FXMenuPane(this);
4501     int         x, y;
4502     FXuint      state;
4503     getRoot()->getCursorPosition(x, y, state);
4504 
4505     int num, itm;
4506     num = current->list->getNumSelectedItems(&itm);
4507 
4508     // No selection or control flag set
4509     if ((num == 0) || current->ctrl)
4510     {
4511         // Menu items
4512         new FXMenuCommand(menu, _("New& file..."), newfileicon, current, FilePanel::ID_NEW_FILE);
4513         new FXMenuCommand(menu, _("New f&older..."), newfoldericon, current, FilePanel::ID_NEW_DIR);
4514         new FXMenuCommand(menu, _("New s&ymlink..."), newlinkicon, current, FilePanel::ID_NEW_SYMLINK);
4515         new FXMenuCommand(menu, _("Fi&lter..."), filtericon, current, FilePanel::ID_FILTER);
4516         new FXMenuSeparator(menu);
4517         new FXMenuCommand(menu, _("&Paste"), paste_clpicon, current, FilePanel::ID_PASTE_CLIPBOARD);
4518         new FXMenuSeparator(menu);
4519         new FXMenuCheck(menu, _("&Hidden files"), current->list, FileList::ID_TOGGLE_HIDDEN);
4520         new FXMenuCheck(menu, _("Thum&bnails"), current->list, FileList::ID_TOGGLE_THUMBNAILS);
4521         new FXMenuSeparator(menu);
4522         new FXMenuRadio(menu, _("B&ig icons"), current->list, IconList::ID_SHOW_BIG_ICONS);
4523         new FXMenuRadio(menu, _("&Small icons"), current->list, IconList::ID_SHOW_MINI_ICONS);
4524         new FXMenuRadio(menu, _("&Full file list"), current->list, IconList::ID_SHOW_DETAILS);
4525         new FXMenuSeparator(menu);
4526         new FXMenuRadio(menu, _("&Rows"), current->list, FileList::ID_ARRANGE_BY_ROWS);
4527         new FXMenuRadio(menu, _("&Columns"), current->list, FileList::ID_ARRANGE_BY_COLUMNS);
4528         new FXMenuCheck(menu, _("Autosize"), current->list, FileList::ID_AUTOSIZE);
4529         new FXMenuSeparator(menu);
4530         new FXMenuRadio(menu, _("&Name"), current->list, FileList::ID_SORT_BY_NAME);
4531         new FXMenuRadio(menu, _("Si&ze"), current->list, FileList::ID_SORT_BY_SIZE);
4532         new FXMenuRadio(menu, _("&Type"), current->list, FileList::ID_SORT_BY_TYPE);
4533         new FXMenuRadio(menu, _("E&xtension"), current->list, FileList::ID_SORT_BY_EXT);
4534         new FXMenuRadio(menu, _("&Date"), current->list, FileList::ID_SORT_BY_TIME);
4535         new FXMenuRadio(menu, _("&User"), current->list, FileList::ID_SORT_BY_USER);
4536         new FXMenuRadio(menu, _("&Group"), current->list, FileList::ID_SORT_BY_GROUP);
4537         new FXMenuRadio(menu, _("Per&missions"), current->list, FileList::ID_SORT_BY_PERM);
4538         new FXMenuRadio(menu, _("Deletion date"), current->list, FileList::ID_SORT_BY_DELTIME);
4539         new FXMenuSeparator(menu);
4540         new FXMenuCheck(menu, _("Ignore c&ase"), current->list, FileList::ID_SORT_CASE);
4541         new FXMenuCheck(menu, _("Fold&ers first"), current->list, FileList::ID_DIRS_FIRST);
4542         new FXMenuCheck(menu, _("Re&verse order"), current->list, FileList::ID_SORT_REVERSE);
4543     }
4544     // Non empty selection
4545     else
4546     {
4547         // Deselect the '..' item
4548         if (current->list->isItemSelected(0))
4549         {
4550             current->list->deselectItem(0);
4551         }
4552 
4553         // Panel submenu items
4554         FXMenuPane* submenu = new FXMenuPane(this);
4555         new FXMenuCommand(submenu, _("Ne&w file..."), newfileicon, current, FilePanel::ID_NEW_FILE);
4556         new FXMenuCommand(submenu, _("New f&older..."), newfoldericon, current, FilePanel::ID_NEW_DIR);
4557         new FXMenuCommand(submenu, _("New s&ymlink..."), newlinkicon, current, FilePanel::ID_NEW_SYMLINK);
4558         new FXMenuCommand(submenu, _("Fi&lter..."), filtericon, current, FilePanel::ID_FILTER);
4559         new FXMenuSeparator(submenu);
4560         new FXMenuCommand(submenu, _("&Paste"), paste_clpicon, current, FilePanel::ID_PASTE_CLIPBOARD);
4561         new FXMenuSeparator(submenu);
4562         new FXMenuCheck(submenu, _("&Hidden files"), current->list, FileList::ID_TOGGLE_HIDDEN);
4563         new FXMenuCheck(submenu, _("Thum&bnails"), current->list, FileList::ID_TOGGLE_THUMBNAILS);
4564         new FXMenuSeparator(submenu);
4565         new FXMenuRadio(submenu, _("B&ig icons"), current->list, IconList::ID_SHOW_BIG_ICONS);
4566         new FXMenuRadio(submenu, _("&Small icons"), current->list, IconList::ID_SHOW_MINI_ICONS);
4567         new FXMenuRadio(submenu, _("&Full file list"), current->list, IconList::ID_SHOW_DETAILS);
4568         new FXMenuSeparator(submenu);
4569         new FXMenuRadio(submenu, _("&Rows"), current->list, FileList::ID_ARRANGE_BY_ROWS);
4570         new FXMenuRadio(submenu, _("&Columns"), current->list, FileList::ID_ARRANGE_BY_COLUMNS);
4571         new FXMenuCheck(submenu, _("Autosize"), current->list, FileList::ID_AUTOSIZE);
4572         new FXMenuSeparator(submenu);
4573         new FXMenuRadio(submenu, _("&Name"), current->list, FileList::ID_SORT_BY_NAME);
4574         new FXMenuRadio(submenu, _("Si&ze"), current->list, FileList::ID_SORT_BY_SIZE);
4575         new FXMenuRadio(submenu, _("&Type"), current->list, FileList::ID_SORT_BY_TYPE);
4576         new FXMenuRadio(submenu, _("E&xtension"), current->list, FileList::ID_SORT_BY_EXT);
4577         new FXMenuRadio(submenu, _("&Date"), current->list, FileList::ID_SORT_BY_TIME);
4578         new FXMenuRadio(submenu, _("&User"), current->list, FileList::ID_SORT_BY_USER);
4579         new FXMenuRadio(submenu, _("&Group"), current->list, FileList::ID_SORT_BY_GROUP);
4580         new FXMenuRadio(submenu, _("Per&missions"), current->list, FileList::ID_SORT_BY_PERM);
4581         new FXMenuRadio(submenu, _("Deletion date"), current->list, FileList::ID_SORT_BY_DELTIME);
4582         new FXMenuSeparator(submenu);
4583         new FXMenuCheck(submenu, _("Ignore c&ase"), current->list, FileList::ID_SORT_CASE);
4584         new FXMenuCheck(submenu, _("Fold&ers first"), current->list, FileList::ID_DIRS_FIRST);
4585         new FXMenuCheck(submenu, _("Re&verse order"), current->list, FileList::ID_SORT_REVERSE);
4586         new FXMenuCascade(menu, _("Pane&l"), NULL, submenu);
4587         new FXMenuSeparator(menu);
4588 
4589 #if defined(linux)
4590         FXString name = current->list->getItemPathname(itm);
4591         if ((num == 1) && (fsdevices->find(name.text()) || mtdevices->find(name.text())))
4592         {
4593             new FXMenuCommand(menu, _("&Mount"), maphosticon, current, FilePanel::ID_MOUNT);
4594             new FXMenuCommand(menu, _("Unmount"), unmaphosticon, current, FilePanel::ID_UMOUNT);
4595             new FXMenuSeparator(menu);
4596         }
4597 #endif
4598 
4599         FXbool ar = false;
4600         if (current->list->getItem(itm) && current->list->isItemFile(itm))
4601         {
4602             new FXMenuCommand(menu, _("Open &with..."), fileopenicon, current, FilePanel::ID_OPEN_WITH);
4603             new FXMenuCommand(menu, _("&Open"), fileopenicon, current, FilePanel::ID_OPEN);
4604             FXString name = current->list->getItemText(itm).section('\t', 0);
4605 
4606             // Last and before last file extensions
4607             FXString ext1 = name.rafter('.', 1).lower();
4608             FXString ext2 = name.rafter('.', 2).lower();
4609 
4610             // Destination folder name
4611             FXString extract_to_folder;
4612             if ((ext2 == "tar.gz") || (ext2 == "tar.bz2") || (ext2 == "tar.xz") || (ext2 == "tar.z"))
4613             {
4614                 extract_to_folder = _("Extr&act to folder ")+name.section('\t', 0).rbefore('.', 2);
4615             }
4616             else
4617             {
4618                 extract_to_folder = _("Extr&act to folder ")+name.section('\t', 0).rbefore('.', 1);
4619             }
4620 
4621             // Display the extract and package menus according to the archive extensions
4622             if ((num == 1) && ((ext2 == "tar.gz") || (ext2 == "tar.bz2") || (ext2 == "tar.xz") || (ext2 == "tar.z")))
4623             {
4624                 ar = true;
4625                 new FXMenuCommand(menu, _("&Extract here"), archexticon, current, FilePanel::ID_EXTRACT_HERE);
4626                 new FXMenuCommand(menu, extract_to_folder, archexticon, current, FilePanel::ID_EXTRACT_TO_FOLDER);
4627                 new FXMenuCommand(menu, _("E&xtract to..."), archexticon, current, FilePanel::ID_EXTRACT);
4628             }
4629             else if ((num == 1) && ((ext1 == "gz") || (ext1 == "bz2") || (ext1 == "xz") || (ext1 == "z")))
4630             {
4631                 ar = true;
4632                 new FXMenuCommand(menu, _("&Extract here"), archexticon, current, FilePanel::ID_EXTRACT_HERE);
4633             }
4634             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")))
4635             {
4636                 ar = true;
4637                 new FXMenuCommand(menu, _("&Extract here"), archexticon, current, FilePanel::ID_EXTRACT_HERE);
4638                 new FXMenuCommand(menu, extract_to_folder, archexticon, current, FilePanel::ID_EXTRACT_TO_FOLDER);
4639                 new FXMenuCommand(menu, _("E&xtract to..."), archexticon, current, FilePanel::ID_EXTRACT);
4640             }
4641 #if defined(linux)
4642             else if ((num == 1) && ((ext1 == "rpm") || (ext1 == "deb")))
4643             {
4644                 ar = true;
4645                 new FXMenuCommand(menu, _("&View"), packageicon, current, FilePanel::ID_VIEW);
4646                 new FXMenuCommand(menu, _("Install/Up&grade"), packageicon, current, ID_PKG_INSTALL);
4647                 new FXMenuCommand(menu, _("Un&install"), packageicon, current, ID_PKG_UNINSTALL);
4648             }
4649 #endif
4650             // Not archive nor package
4651             if (!ar)
4652             {
4653                 new FXMenuCommand(menu, _("&View"), viewicon, current, FilePanel::ID_VIEW);
4654                 new FXMenuCommand(menu, _("&Edit"), editicon, current, FilePanel::ID_EDIT);
4655                 if (num == 1)
4656                 {
4657                     new FXMenuCommand(menu, _("Com&pare..."), compareicon, current, FilePanel::ID_COMPARE);
4658                 }
4659                 else
4660                 {
4661                     new FXMenuCommand(menu, _("Com&pare"), compareicon, current, FilePanel::ID_COMPARE);
4662                 }
4663             }
4664         }
4665         if (!ar)
4666         {
4667             new FXMenuCommand(menu, _("&Add to archive..."), archaddicon, current, FilePanel::ID_ADD_TO_ARCH);
4668         }
4669 #if defined(linux)
4670         if ((num == 1) && !ar)
4671         {
4672             new FXMenuCommand(menu, _("Packages &query "), packageicon, current, FilePanel::ID_PKG_QUERY);
4673         }
4674 #endif
4675 
4676         // Build scripts menu
4677         new FXMenuSeparator(menu);
4678         FXString    scriptpath = homedir + PATHSEPSTRING CONFIGPATH PATHSEPSTRING XFECONFIGPATH PATHSEPSTRING SCRIPTPATH;
4679         FXMenuPane* scriptsmenu = new FXMenuPane(this);
4680         new FXMenuCascade(menu, _("Scripts"), runicon, scriptsmenu);
4681         readScriptDir(scriptsmenu, scriptpath);
4682         new FXMenuSeparator(scriptsmenu);
4683         new FXMenuCommand(scriptsmenu, _("&Go to script folder"), gotodiricon, this, FilePanel::ID_GO_SCRIPTDIR);
4684 
4685         new FXMenuSeparator(menu);
4686         new FXMenuCommand(menu, _("&Copy"), copy_clpicon, current, FilePanel::ID_COPY_CLIPBOARD);
4687         new FXMenuCommand(menu, _("C&ut"), cut_clpicon, current, FilePanel::ID_CUT_CLIPBOARD);
4688         new FXMenuCommand(menu, _("&Paste"), paste_clpicon, current, FilePanel::ID_PASTE_CLIPBOARD);
4689         new FXMenuSeparator(menu);
4690         new FXMenuCommand(menu, _("Re&name..."), renameiticon, current, FilePanel::ID_FILE_RENAME);
4691         new FXMenuCommand(menu, _("Copy &to..."), copy_clpicon, current, FilePanel::ID_FILE_COPYTO);
4692         new FXMenuCommand(menu, _("&Move to..."), moveiticon, current, FilePanel::ID_FILE_MOVETO);
4693         new FXMenuCommand(menu, _("Symlin&k to..."), minilinkicon, current, FilePanel::ID_FILE_SYMLINK);
4694         new FXMenuCommand(menu, _("M&ove to trash"), filedeleteicon, current, FilePanel::ID_FILE_TRASH);
4695         new FXMenuCommand(menu, _("Restore &from trash"), filerestoreicon, current, FilePanel::ID_FILE_RESTORE);
4696         new FXMenuCommand(menu, _("&Delete"), filedelete_permicon, current, FilePanel::ID_FILE_DELETE);
4697         new FXMenuSeparator(menu);
4698         new FXMenuCommand(menu, _("Compare &sizes"), charticon, current, FilePanel::ID_DIR_USAGE);
4699         new FXMenuCommand(menu, _("P&roperties"), attribicon, current, FilePanel::ID_PROPERTIES);
4700     }
4701     menu->create();
4702 
4703     // Reset flags
4704     ctrl = false;
4705     shiftf10 = false;
4706     allowPopupScroll = true;  // Allow keyboard scrolling
4707 
4708 
4709     menu->popup(NULL, x, y);
4710     getApp()->runModalWhileShown(menu);
4711     allowPopupScroll = false;
4712 
4713     return(1);
4714 }
4715 
4716 
4717 // Read all executable file names that are located into the script directory
4718 // Sort entries alphabetically, directories first
readScriptDir(FXMenuPane * scriptsmenu,FXString dir)4719 int FilePanel::readScriptDir(FXMenuPane* scriptsmenu, FXString dir)
4720 {
4721     DIR* dp;
4722     struct dirent** namelist;
4723 
4724     // Open directory
4725     if ((dp = opendir(dir.text())) == NULL)
4726     {
4727         return(0);
4728     }
4729 
4730     // Eventually add a / at the end of the directory name
4731     if (dir[dir.length()-1] != '/')
4732     {
4733         dir = dir+"/";
4734     }
4735 
4736 	// First, read only directory entries and sort them alphabetically
4737     int n;
4738     n = scandir(dir.text(), &namelist, NULL, alphasort);
4739     if (n < 0)
4740     {
4741         perror("scandir");
4742     }
4743     else
4744     {
4745         for (int k = 0; k < n; k++)
4746         {
4747             // Avoid hidden directories and '.' and '..'
4748             if (namelist[k]->d_name[0] != '.')
4749             {
4750                 FXString pathname = dir + namelist[k]->d_name;
4751 
4752                 // Recurse if non empty directory
4753                 if (::isDirectory(pathname))
4754                 {
4755                     if (!::isEmptyDir(pathname))
4756                     {
4757                         FXMenuPane* submenu = new FXMenuPane(this);
4758                         new FXMenuCascade(scriptsmenu, namelist[k]->d_name, NULL, submenu);
4759                         readScriptDir(submenu, pathname);
4760                     }
4761                 }
4762             }
4763             free(namelist[k]);
4764         }
4765         free(namelist);
4766     }
4767 
4768 	// Then, read only executable files and sort them alphabetically
4769     n = scandir(dir.text(), &namelist, NULL, alphasort);
4770     if (n < 0)
4771     {
4772         perror("scandir");
4773     }
4774     else
4775     {
4776         for (int k = 0; k < n; k++)
4777         {
4778 			// Add only executable files to the list
4779 			FXString pathname = dir + namelist[k]->d_name;
4780 			if (!::isDirectory(pathname) && isReadExecutable(pathname))
4781 			{
4782 				new FXMenuCommand(scriptsmenu, namelist[k]->d_name + FXString("\t\t") + pathname, miniexecicon, this, FilePanel::ID_RUN_SCRIPT);
4783 			}
4784             free(namelist[k]);
4785         }
4786         free(namelist);
4787     }
4788 
4789     // Close directory
4790     (void)closedir(dp);
4791 
4792     return(1);
4793 }
4794 
4795 
4796 // Run Terminal in the selected directory
onCmdXTerm(FXObject *,FXSelector,void *)4797 long FilePanel::onCmdXTerm(FXObject*, FXSelector, void*)
4798 {
4799     int ret;
4800 
4801     getApp()->beginWaitCursor();
4802     ret = chdir(current->list->getDirectory().text());
4803     if (ret < 0)
4804     {
4805         int errcode = errno;
4806         if (errcode)
4807         {
4808             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), current->list->getDirectory().text(), strerror(errcode));
4809         }
4810         else
4811         {
4812             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), current->list->getDirectory().text());
4813         }
4814 
4815         return(0);
4816     }
4817 
4818     FXString cmd = getApp()->reg().readStringEntry("PROGS", "xterm", "xterm -sb");
4819     cmd += " &";
4820     ret = system(cmd.text());
4821     if (ret < 0)
4822     {
4823         MessageBox::error(this, BOX_OK, _("Error"), _("Can't execute command %s"), cmd.text());
4824     }
4825 
4826     current->list->setFocus();
4827     ret = chdir(startlocation.text());
4828     if (ret < 0)
4829     {
4830         int errcode = errno;
4831         if (errcode)
4832         {
4833             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
4834         }
4835         else
4836         {
4837             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
4838         }
4839 
4840         return(0);
4841     }
4842 
4843     getApp()->endWaitCursor();
4844     return(1);
4845 }
4846 
4847 
4848 // Add files or directory to an archive
onCmdAddToArch(FXObject * o,FXSelector,void *)4849 long FilePanel::onCmdAddToArch(FXObject* o, FXSelector, void*)
4850 {
4851     int      ret;
4852     FXString name, ext1, ext2, cmd, archive = "";
4853     File*    f;
4854 
4855     ret = chdir(current->list->getDirectory().text());
4856     if (ret < 0)
4857     {
4858         int errcode = errno;
4859         if (errcode)
4860         {
4861             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), list->getDirectory().text(), strerror(errcode));
4862         }
4863         else
4864         {
4865             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), list->getDirectory().text());
4866         }
4867 
4868         return(0);
4869     }
4870 
4871     // Eventually deselect the '..' directory
4872     if (current->list->isItemSelected(0))
4873     {
4874         current->list->deselectItem(0);
4875     }
4876 
4877     // Return if nothing is selected
4878     if (current->list->getNumSelectedItems() == 0)
4879     {
4880         return(0);
4881     }
4882 
4883     // If only one item is selected, use its name as a starting guess for the archive name
4884     if (current->list->getNumSelectedItems() == 1)
4885     {
4886         for (int u = 0; u < current->list->getNumItems(); u++)
4887         {
4888             if (current->list->isItemSelected(u))
4889             {
4890                 name = current->list->getItemFilename(u);
4891                 break;
4892             }
4893         }
4894         archive = name;
4895     }
4896 
4897     // Initial archive name with full path and default extension
4898     FXString archpath = current->list->getDirectory();
4899     if (archpath == PATHSEPSTRING)
4900     {
4901         archive = archpath+archive+".tar.gz";
4902     }
4903     else
4904     {
4905         archive = archpath+PATHSEPSTRING+archive+".tar.gz";
4906     }
4907 
4908     // Archive dialog
4909     if (archdialog == NULL)
4910     {
4911         archdialog = new ArchInputDialog(this, "");
4912     }
4913     archdialog->setText(archive);
4914     archdialog->CursorEnd();
4915 
4916     if (archdialog->execute())
4917     {
4918         if (archdialog->getText() == "")
4919         {
4920             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
4921             return(0);
4922         }
4923 
4924         // Get string and preserve escape characters
4925         archive = ::quote(archdialog->getText());
4926 
4927         // Get extensions of the archive name
4928         ext1 = archdialog->getText().rafter('.', 1).lower();
4929         ext2 = archdialog->getText().rafter('.', 2).lower();
4930 
4931         // Handle different archive formats
4932         if (ext2 == "tar.gz")
4933         {
4934             cmd = "tar -zcvf "+archive+" ";
4935         }
4936         else if (ext2 == "tar.bz2")
4937         {
4938             cmd = "tar -jcvf "+archive+" ";
4939         }
4940         else if (ext2 == "tar.xz")
4941         {
4942             cmd = "tar -Jcvf "+archive+" ";
4943         }
4944         else if (ext2 == "tar.z")
4945         {
4946             cmd = "tar -Zcvf "+archive+" ";
4947         }
4948         else if (ext1 == "tar")
4949         {
4950             cmd = "tar -cvf "+archive+" ";
4951         }
4952         else if (ext1 == "gz")
4953         {
4954             cmd = "gzip -v ";
4955         }
4956         else if (ext1 == "tgz")
4957         {
4958             cmd = "tar -zcvf "+archive+" ";
4959         }
4960         else if (ext1 == "taz")
4961         {
4962             cmd = "tar -Zcvf "+archive+" ";
4963         }
4964         else if (ext1 == "bz2")
4965         {
4966             cmd = "bzip2 -v ";
4967         }
4968         else if (ext1 == "xz")
4969         {
4970             cmd = "xz -v ";
4971         }
4972         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
4973         {
4974             cmd = "tar -jcvf "+archive+" ";
4975         }
4976         else if (ext1 == "txz")
4977         {
4978             cmd = "tar -Jcvf "+archive+" ";
4979         }
4980         else if (ext1 == "z")
4981         {
4982             cmd = "compress -v ";
4983         }
4984         else if (ext1 == "zip")
4985         {
4986             cmd = "zip -r "+archive+" ";
4987         }
4988         else if (ext1 == "7z")
4989         {
4990             cmd = "7z a "+archive+" ";
4991         }
4992 
4993         // Default archive format
4994         else
4995         {
4996             archive += ".tar.gz";
4997             cmd = "tar -zcvf "+archive+" ";
4998         }
4999 
5000         for (int u = 0; u < current->list->getNumItems(); u++)
5001         {
5002             if (current->list->isItemSelected(u))
5003             {
5004                 // Don't include '..' in the list
5005                 name = current->list->getItemFilename(u);
5006                 if (name != "..")
5007                 {
5008                     cmd += " ";
5009                     cmd = cmd+::quote(name);
5010                     cmd += " ";
5011                 }
5012             }
5013         }
5014 
5015         // Wait cursor
5016         getApp()->beginWaitCursor();
5017 
5018         // File object
5019         f = new File(this, _("Create archive"), ARCHIVE);
5020         f->create();
5021 
5022         // Create archive
5023         f->archive(archive, cmd);
5024         ret = chdir(startlocation.text());
5025         if (ret < 0)
5026         {
5027             int errcode = errno;
5028             if (errcode)
5029             {
5030                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
5031             }
5032             else
5033             {
5034                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
5035             }
5036 
5037             return(0);
5038         }
5039 
5040         getApp()->endWaitCursor();
5041         delete f;
5042 
5043         // Force panel refresh
5044         onCmdRefresh(0, 0, 0);
5045     }
5046     return(1);
5047 }
5048 
5049 
5050 // Extract archive
onCmdExtract(FXObject *,FXSelector,void *)5051 long FilePanel::onCmdExtract(FXObject*, FXSelector, void*)
5052 {
5053     FXString name, ext1, ext2, cmd, dir, cdir;
5054     File*    f;
5055 
5056     // Current directory
5057     cdir = current->list->getDirectory();
5058 
5059     // File selection dialog
5060     FileDialog  browse(this, _("Select a destination folder"));
5061     const char* patterns[] =
5062     {
5063         _("All Files"), "*", NULL
5064     };
5065     browse.setDirectory(homedir);
5066     browse.setPatternList(patterns);
5067     browse.setSelectMode(SELECT_FILE_DIRECTORY);
5068 
5069     int item;
5070     current->list->getNumSelectedItems(&item);
5071     if (current->list->getItem(item))
5072     {
5073         // Archive name and extensions
5074         name = current->list->getItemText(item).text();
5075 
5076         ext1 = name.section('\t', 0).rafter('.', 1).lower();
5077         ext2 = name.section('\t', 0).rafter('.', 2).lower();
5078         name = ::quote(cdir + PATHSEPSTRING + name.section('\t', 0));
5079 
5080         // Handle different archive formats
5081         if (ext2 == "tar.gz")
5082         {
5083             cmd = "tar -zxvf ";
5084         }
5085         else if (ext2 == "tar.bz2")
5086         {
5087             cmd = "tar -jxvf ";
5088         }
5089         else if (ext2 == "tar.xz")
5090         {
5091             cmd = "tar -Jxvf ";
5092         }
5093         else if (ext2 == "tar.z")
5094         {
5095             cmd = "tar -Zxvf ";
5096         }
5097         else if (ext1 == "tar")
5098         {
5099             cmd = "tar -xvf ";
5100         }
5101         else if (ext1 == "gz")
5102         {
5103             cmd = "gunzip -v ";
5104         }
5105         else if (ext1 == "tgz")
5106         {
5107             cmd = "tar -zxvf ";
5108         }
5109         else if (ext1 == "taz")
5110         {
5111             cmd = "tar -Zxvf ";
5112         }
5113         else if (ext1 == "bz2")
5114         {
5115             cmd = "bunzip2 -v ";
5116         }
5117         else if (ext1 == "xz")
5118         {
5119             cmd = "unxz -v ";
5120         }
5121         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
5122         {
5123             cmd = "tar -jxvf ";
5124         }
5125         else if (ext1 == "txz")
5126         {
5127             cmd = "tar -Jxvf ";
5128         }
5129         else if (ext1 == "z")
5130         {
5131             cmd = "uncompress -v ";
5132         }
5133         else if (ext1 == "zip")
5134         {
5135             cmd = "unzip -o ";
5136         }
5137         else if (ext1 == "7z")
5138         {
5139             cmd = "7z x -y ";
5140         }
5141         else if (ext1 == "rar")
5142         {
5143             cmd = "unrar x -o+ ";
5144         }
5145         else if (ext1 == "lzh")
5146         {
5147             cmd = "lha -xf ";
5148         }
5149         else if (ext1 == "ace")
5150         {
5151             cmd = "unace x ";
5152         }
5153         else if (ext1 == "arj")
5154         {
5155             cmd = "arj x -y ";
5156         }
5157         else
5158         {
5159             cmd = "tar -zxvf ";
5160         }
5161 
5162         // Final extract command
5163         cmd += name+" ";
5164 
5165 
5166         // Extract archive
5167         if (browse.execute())
5168         {
5169             dir = browse.getFilename();
5170 
5171             if (isWritable(dir))
5172             {
5173                 // Wait cursor
5174                 getApp()->beginWaitCursor();
5175 
5176                 // File object
5177                 f = new File(this, _("Extract archive"), EXTRACT);
5178                 f->create();
5179 
5180                 // Extract archive
5181                 f->extract(name, dir, cmd);
5182 
5183                 getApp()->endWaitCursor();
5184                 delete f;
5185             }
5186             else
5187             {
5188                 MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), dir.text());
5189             }
5190         }
5191     }
5192 
5193     // Force panel refresh
5194     onCmdRefresh(0, 0, 0);
5195 
5196     return(1);
5197 }
5198 
5199 
5200 // Extract archive to a folder name based on the archive name
onCmdExtractToFolder(FXObject *,FXSelector,void *)5201 long FilePanel::onCmdExtractToFolder(FXObject*, FXSelector, void*)
5202 {
5203     FXString name, pathname, ext1, ext2, cmd, dirname, dirpath, cdir;
5204     File*    f;
5205 
5206     // Current directory
5207     cdir = current->list->getDirectory();
5208 
5209     int item;
5210     current->list->getNumSelectedItems(&item);
5211     if (current->list->getItem(item))
5212     {
5213         // Archive name and extensions
5214         name = current->list->getItemText(item).text();
5215         ext1 = name.section('\t', 0).rafter('.', 1).lower();
5216         ext2 = name.section('\t', 0).rafter('.', 2).lower();
5217 
5218         // Destination folder name
5219         if ((ext2 == "tar.gz") || (ext2 == "tar.bz2") || (ext2 == "tar.xz") || (ext2 == "tar.z"))
5220         {
5221             dirname = name.section('\t', 0).rbefore('.', 2);
5222         }
5223         else
5224         {
5225             dirname = name.section('\t', 0).rbefore('.', 1);
5226         }
5227 
5228         // Create the new dir according to the current umask
5229         // Don't complain if directory already exists
5230         int mask = umask(0);
5231         umask(mask);
5232         dirpath = cdir + PATHSEPSTRING + dirname;
5233         errno = 0;
5234         int ret = ::mkdir(dirpath.text(), 511 & ~mask);
5235         int errcode = errno;
5236         if ((ret == -1) && (errcode != EEXIST))
5237         {
5238             if (errcode)
5239             {
5240                 MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s: %s"), dirpath.text(), strerror(errcode));
5241             }
5242             else
5243             {
5244                 MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s"), dirpath.text());
5245             }
5246             return(0);
5247         }
5248 
5249         // Archive pathname
5250         pathname = ::quote(cdir + PATHSEPSTRING + name.section('\t', 0));
5251 
5252         // Handle different archive formats
5253         if (ext2 == "tar.gz")
5254         {
5255             cmd = "tar -zxvf ";
5256         }
5257         else if (ext2 == "tar.bz2")
5258         {
5259             cmd = "tar -jxvf ";
5260         }
5261         else if (ext2 == "tar.xz")
5262         {
5263             cmd = "tar -Jxvf ";
5264         }
5265         else if (ext2 == "tar.z")
5266         {
5267             cmd = "tar -Zxvf ";
5268         }
5269         else if (ext1 == "tar")
5270         {
5271             cmd = "tar -xvf ";
5272         }
5273         else if (ext1 == "gz")
5274         {
5275             cmd = "gunzip -v ";
5276         }
5277         else if (ext1 == "tgz")
5278         {
5279             cmd = "tar -zxvf ";
5280         }
5281         else if (ext1 == "taz")
5282         {
5283             cmd = "tar -Zxvf ";
5284         }
5285         else if (ext1 == "bz2")
5286         {
5287             cmd = "bunzip2 -v ";
5288         }
5289         else if (ext1 == "xz")
5290         {
5291             cmd = "unxz -v ";
5292         }
5293         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
5294         {
5295             cmd = "tar -jxvf ";
5296         }
5297         else if (ext1 == "txz")
5298         {
5299             cmd = "tar -Jxvf ";
5300         }
5301         else if (ext1 == "z")
5302         {
5303             cmd = "uncompress -v ";
5304         }
5305         else if (ext1 == "zip")
5306         {
5307             cmd = "unzip -o ";
5308         }
5309         else if (ext1 == "7z")
5310         {
5311             cmd = "7z x -y ";
5312         }
5313         else if (ext1 == "rar")
5314         {
5315             cmd = "unrar x -o+ ";
5316         }
5317         else if (ext1 == "lzh")
5318         {
5319             cmd = "lha -xf ";
5320         }
5321         else if (ext1 == "ace")
5322         {
5323             cmd = "unace x ";
5324         }
5325         else if (ext1 == "arj")
5326         {
5327             cmd = "arj x -y ";
5328         }
5329         else
5330         {
5331             cmd = "tar -zxvf ";
5332         }
5333 
5334         // Final extract command
5335         cmd += pathname+" ";
5336 
5337         // Wait cursor
5338         getApp()->beginWaitCursor();
5339 
5340         // File object
5341         f = new File(this, _("Extract archive"), EXTRACT);
5342         f->create();
5343 
5344         // Extract archive
5345         f->extract(pathname, dirpath, cmd);
5346 
5347         getApp()->endWaitCursor();
5348         delete f;
5349     }
5350 
5351     // Force panel refresh
5352     onCmdRefresh(0, 0, 0);
5353 
5354     return(1);
5355 }
5356 
5357 
5358 // Extract archive in the current directory
onCmdExtractHere(FXObject *,FXSelector,void *)5359 long FilePanel::onCmdExtractHere(FXObject*, FXSelector, void*)
5360 {
5361     FXString name, ext1, ext2, cmd, cdir;
5362     File*    f;
5363 
5364     // Current directory
5365     cdir = current->list->getDirectory();
5366 
5367     int item;
5368     current->list->getNumSelectedItems(&item);
5369     if (current->list->getItem(item))
5370     {
5371         if (isWritable(cdir))
5372         {
5373             // Archive name and extensions
5374             name = current->list->getItemText(item).text();
5375             ext1 = name.section('\t', 0).rafter('.', 1);
5376             lower();
5377             ext2 = name.section('\t', 0).rafter('.', 2).lower();
5378             name = ::quote(cdir + PATHSEPSTRING + name.section('\t', 0));
5379 
5380             // Handle different archive formats
5381             if (ext2 == "tar.gz")
5382             {
5383                 cmd = "tar -zxvf ";
5384             }
5385             else if (ext2 == "tar.bz2")
5386             {
5387                 cmd = "tar -jxvf ";
5388             }
5389             else if (ext2 == "tar.xz")
5390             {
5391                 cmd = "tar -Jxvf ";
5392             }
5393             else if (ext2 == "tar.z")
5394             {
5395                 cmd = "tar -Zxvf ";
5396             }
5397             else if (ext1 == "tar")
5398             {
5399                 cmd = "tar -xvf ";
5400             }
5401             else if (ext1 == "gz")
5402             {
5403                 cmd = "gunzip -v ";
5404             }
5405             else if (ext1 == "tgz")
5406             {
5407                 cmd = "tar -zxvf ";
5408             }
5409             else if (ext1 == "taz")
5410             {
5411                 cmd = "tar -Zxvf ";
5412             }
5413             else if (ext1 == "bz2")
5414             {
5415                 cmd = "bunzip2 -v ";
5416             }
5417             else if (ext1 == "xz")
5418             {
5419                 cmd = "unxz -v ";
5420             }
5421             else if ((ext1 == "tbz2") || (ext1 == "tbz"))
5422             {
5423                 cmd = "tar -jxvf ";
5424             }
5425             else if (ext1 == "txz")
5426             {
5427                 cmd = "tar -Jxvf ";
5428             }
5429             else if (ext1 == "z")
5430             {
5431                 cmd = "uncompress -v ";
5432             }
5433             else if (ext1 == "zip")
5434             {
5435                 cmd = "unzip -o ";
5436             }
5437             else if (ext1 == "7z")
5438             {
5439                 cmd = "7z x -y ";
5440             }
5441             else if (ext1 == "rar")
5442             {
5443                 cmd = "unrar x -o+ ";
5444             }
5445             else if (ext1 == "lzh")
5446             {
5447                 cmd = "lha -xf ";
5448             }
5449             else if (ext1 == "ace")
5450             {
5451                 cmd = "unace x ";
5452             }
5453             else if (ext1 == "arj")
5454             {
5455                 cmd = "arj x -y ";
5456             }
5457             else
5458             {
5459                 cmd = "tar -zxvf ";
5460             }
5461 
5462             // Final extract command
5463             cmd += name+" ";
5464 
5465             // Wait cursor
5466             getApp()->beginWaitCursor();
5467 
5468             // File object
5469             f = new File(this, _("Extract archive"), EXTRACT);
5470             f->create();
5471 
5472             // Extract archive
5473             f->extract(name, cdir, cmd);
5474 
5475             getApp()->endWaitCursor();
5476             delete f;
5477         }
5478         else
5479         {
5480             MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), cdir.text());
5481         }
5482     }
5483 
5484     // Force panel refresh
5485     onCmdRefresh(0, 0, 0);
5486 
5487     return(1);
5488 }
5489 
5490 
5491 #if defined(linux)
5492 // Install/Upgrade package
onCmdPkgInstall(FXObject *,FXSelector,void *)5493 long FilePanel::onCmdPkgInstall(FXObject*, FXSelector, void*)
5494 {
5495     FXString name, path, cmd, dir, cdir;
5496     File*    f;
5497 
5498     cdir = current->list->getDirectory();
5499 
5500     int itm;
5501     current->list->getNumSelectedItems(&itm);
5502     if (current->list->getItem(itm))
5503     {
5504         name = current->list->getItemText(itm).text();
5505         name = name.section('\t', 0);
5506         path = ::quote(cdir + PATHSEPSTRING + name);
5507 
5508         // Command to perform
5509         FXString ext = FXPath::extension(name);
5510         if (comparecase(ext, "rpm") == 0)
5511         {
5512             cmd = "rpm -Uvh " + path;
5513         }
5514         else if (comparecase(ext, "deb") == 0)
5515         {
5516             cmd = "dpkg -i "+ path;
5517         }
5518 
5519         // Wait cursor
5520         getApp()->beginWaitCursor();
5521 
5522         // File object
5523         f = new File(this, _("Package Install/Upgrade"), PKG_INSTALL);
5524         f->create();
5525 
5526         // Install/Upgrade package
5527         f->pkgInstall(name, cmd);
5528 
5529         getApp()->endWaitCursor();
5530         delete f;
5531     }
5532 
5533     // Force panel refresh
5534     onCmdRefresh(0, 0, 0);
5535 
5536     return(1);
5537 }
5538 
5539 
5540 // Uninstall package based on its name (package version is ignored)
onCmdPkgUninstall(FXObject *,FXSelector,void *)5541 long FilePanel::onCmdPkgUninstall(FXObject*, FXSelector, void*)
5542 {
5543     FXString name, cmd, dir, cdir;
5544     File*    f;
5545 
5546     cdir = current->list->getDirectory();
5547 
5548     int itm;
5549     current->list->getNumSelectedItems(&itm);
5550     if (current->list->getItem(itm))
5551     {
5552         name = current->list->getItemText(itm).text();
5553         name = name.section('\t', 0);
5554 
5555         // Command to perform
5556         FXString ext = FXPath::extension(name);
5557         if (comparecase(ext, "rpm") == 0)
5558         {
5559             name = name.section('-', 0);
5560             cmd = "rpm -e " + name;
5561         }
5562         else if (comparecase(ext, "deb") == 0)
5563         {
5564             name = name.section('_', 0);
5565             cmd = "dpkg -r "+ name;
5566         }
5567 
5568         // Wait cursor
5569         getApp()->beginWaitCursor();
5570 
5571         // File object
5572         f = new File(this, _("Package Uninstall"), PKG_UNINSTALL);
5573         f->create();
5574 
5575         // Uninstall package
5576         f->pkgUninstall(name, cmd);
5577 
5578         getApp()->endWaitCursor();
5579         delete f;
5580     }
5581 
5582     // Force panel refresh
5583     onCmdRefresh(0, 0, 0);
5584 
5585     return(1);
5586 }
5587 
5588 
5589 #endif
5590 
5591 
5592 // Force FilePanel and DirPanel refresh
onCmdRefresh(FXObject *,FXSelector,void *)5593 long FilePanel::onCmdRefresh(FXObject*, FXSelector, void*)
5594 {
5595     // Refresh panel
5596     FXString dir = list->getDirectory();
5597     list->setDirectory(ROOTDIR, false);
5598     list->setDirectory(dir, false);
5599     updatePath();
5600 
5601 	// Focus on current panel
5602 	current-> list->setFocus();
5603 
5604     return(1);
5605 }
5606 
5607 
5608 // Handle item selection
onCmdSelect(FXObject * sender,FXSelector sel,void * ptr)5609 long FilePanel::onCmdSelect(FXObject* sender, FXSelector sel, void* ptr)
5610 {
5611     current->list->setFocus();
5612     switch (FXSELID(sel))
5613     {
5614     case ID_SELECT_ALL:
5615         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SELECT_ALL), ptr);
5616         return(1);
5617 
5618     case ID_DESELECT_ALL:
5619         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_DESELECT_ALL), ptr);
5620         return(1);
5621 
5622     case ID_SELECT_INVERSE:
5623         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SELECT_INVERSE), ptr);
5624         return(1);
5625     }
5626     return(1);
5627 }
5628 
5629 
5630 // Handle show commands
onCmdShow(FXObject * sender,FXSelector sel,void * ptr)5631 long FilePanel::onCmdShow(FXObject* sender, FXSelector sel, void* ptr)
5632 {
5633     switch (FXSELID(sel))
5634     {
5635     case ID_SHOW_BIG_ICONS:
5636         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SHOW_BIG_ICONS), ptr);
5637         break;
5638 
5639     case ID_SHOW_MINI_ICONS:
5640         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SHOW_MINI_ICONS), ptr);
5641         break;
5642 
5643     case ID_SHOW_DETAILS:
5644         current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_SHOW_DETAILS), ptr);
5645         break;
5646     }
5647 
5648     // Set focus on current panel list
5649     current->list->setFocus();
5650 
5651     return(1);
5652 }
5653 
5654 
5655 // Update show commands
onUpdShow(FXObject * sender,FXSelector sel,void * ptr)5656 long FilePanel::onUpdShow(FXObject* sender, FXSelector sel, void* ptr)
5657 {
5658     FXuint msg = FXWindow::ID_UNCHECK;
5659     FXuint style = current->list->getListStyle();
5660 
5661     switch (FXSELID(sel))
5662     {
5663     case ID_SHOW_BIG_ICONS:
5664         if (style & _ICONLIST_BIG_ICONS)
5665         {
5666             msg = FXWindow::ID_CHECK;
5667         }
5668         break;
5669 
5670     case ID_SHOW_MINI_ICONS:
5671         if (style & _ICONLIST_MINI_ICONS)
5672         {
5673             msg = FXWindow::ID_CHECK;
5674         }
5675         break;
5676 
5677     case ID_SHOW_DETAILS:
5678         if (!(style & (_ICONLIST_MINI_ICONS | _ICONLIST_BIG_ICONS)))
5679         {
5680             msg = FXWindow::ID_CHECK;
5681         }
5682         break;
5683     }
5684     sender->handle(this, FXSEL(SEL_COMMAND, msg), ptr);
5685 
5686     return(1);
5687 }
5688 
5689 
5690 // Handle toggle hidden command
onCmdToggleHidden(FXObject * sender,FXSelector sel,void * ptr)5691 long FilePanel::onCmdToggleHidden(FXObject* sender, FXSelector sel, void* ptr)
5692 {
5693     current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_TOGGLE_HIDDEN), ptr);
5694     return(1);
5695 }
5696 
5697 
5698 // Update toggle hidden command
onUpdToggleHidden(FXObject * sender,FXSelector sel,void * ptr)5699 long FilePanel::onUpdToggleHidden(FXObject* sender, FXSelector sel, void* ptr)
5700 {
5701     FXuint msg = FXWindow::ID_UNCHECK;
5702     FXbool hidden = current->list->shownHiddenFiles();
5703 
5704     if (hidden == false)
5705     {
5706         msg = FXWindow::ID_CHECK;
5707     }
5708     sender->handle(this, FXSEL(SEL_COMMAND, msg), ptr);
5709     return(1);
5710 }
5711 
5712 
5713 // Handle toggle thumbnails command
onCmdToggleThumbnails(FXObject * sender,FXSelector sel,void * ptr)5714 long FilePanel::onCmdToggleThumbnails(FXObject* sender, FXSelector sel, void* ptr)
5715 {
5716     current->list->handle(sender, FXSEL(SEL_COMMAND, FileList::ID_TOGGLE_THUMBNAILS), ptr);
5717     return(1);
5718 }
5719 
5720 
5721 // Update toggle hidden command
onUpdToggleThumbnails(FXObject * sender,FXSelector sel,void * ptr)5722 long FilePanel::onUpdToggleThumbnails(FXObject* sender, FXSelector sel, void* ptr)
5723 {
5724     FXuint msg = FXWindow::ID_UNCHECK;
5725     FXbool showthumb = current->list->shownThumbnails();
5726 
5727     if (showthumb == false)
5728     {
5729         msg = FXWindow::ID_CHECK;
5730     }
5731     sender->handle(this, FXSEL(SEL_COMMAND, msg), ptr);
5732     return(1);
5733 }
5734 
5735 
5736 // Run script
onCmdRunScript(FXObject * o,FXSelector sel,void *)5737 long FilePanel::onCmdRunScript(FXObject* o, FXSelector sel, void*)
5738 {
5739     // Wait cursor
5740     getApp()->beginWaitCursor();
5741 
5742     FXString pathname, cmd, itemslist = " ";
5743     FXString scriptpath = dynamic_cast<FXMenuCommand*>(o)->getHelpText();
5744 
5745     // Construct selected files list
5746     current->list->setFocus();
5747     for (int u = 0; u < current->list->getNumItems(); u++)
5748     {
5749         if (current->list->isItemSelected(u))
5750         {
5751             pathname = current->list->getItemPathname(u);
5752 
5753             // List of selected items
5754             itemslist += ::quote(pathname) + " ";
5755         }
5756     }
5757 
5758     // Construct command line
5759     cmd = ::quote(scriptpath) + itemslist + " &";
5760 
5761     // Go to the current directory
5762     int ret = chdir(current->list->getDirectory().text());
5763     if (ret < 0)
5764     {
5765         int errcode = errno;
5766         if (errcode)
5767         {
5768             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), current->list->getDirectory().text(), strerror(errcode));
5769         }
5770         else
5771         {
5772             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), current->list->getDirectory().text());
5773         }
5774     }
5775 
5776     // Execute command
5777     static pid_t child_pid = 0;
5778     switch ((child_pid = fork()))
5779     {
5780     case -1:
5781         fprintf(stderr, _("Error: Fork failed: %s\n"), strerror(errno));
5782         break;
5783 
5784     case 0:
5785         execl("/bin/sh", "sh", "-c", cmd.text(), (char*)NULL);
5786         _exit(EXIT_SUCCESS);
5787         break;
5788     }
5789 
5790     // Return to the starting directory
5791     ret = chdir(startlocation.text());
5792     if (ret < 0)
5793     {
5794         int errcode = errno;
5795         if (errcode)
5796         {
5797             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
5798         }
5799         else
5800         {
5801             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
5802         }
5803     }
5804 
5805     getApp()->endWaitCursor();
5806 
5807     return(1);
5808 }
5809 
5810 
5811 // Go to scripts directory
onCmdGoScriptDir(FXObject * o,FXSelector sel,void *)5812 long FilePanel::onCmdGoScriptDir(FXObject* o, FXSelector sel, void*)
5813 {
5814     FXString scriptpath = homedir + PATHSEPSTRING CONFIGPATH PATHSEPSTRING XFECONFIGPATH PATHSEPSTRING SCRIPTPATH;
5815 
5816     if (!existFile(scriptpath))
5817     {
5818         // Create the script directory according to the umask
5819         int mask = umask(0);
5820         umask(mask);
5821         errno = 0;
5822         int ret = mkpath(scriptpath.text(), 511 & ~mask);
5823         int errcode = errno;
5824         if (ret == -1)
5825         {
5826             if (errcode)
5827             {
5828                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't create script folder %s: %s"), scriptpath.text(), strerror(errcode));
5829             }
5830             else
5831             {
5832                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't create script folder %s"), scriptpath.text());
5833             }
5834 
5835             return(0);
5836         }
5837     }
5838 
5839     // Go to scripts directory
5840     current->list->setDirectory(scriptpath);
5841     current->list->setFocus();
5842     dirpanel->setDirectory(scriptpath, true);
5843     current->updatePath();
5844     updateLocation();
5845 
5846     return(1);
5847 }
5848 
5849 
5850 #if defined(linux)
5851 // Mount/Unmount file systems
onCmdMount(FXObject *,FXSelector sel,void *)5852 long FilePanel::onCmdMount(FXObject*, FXSelector sel, void*)
5853 {
5854     int      ret;
5855     FXString cmd, msg, text;
5856     FXuint   op;
5857     File*    f;
5858     FXString dir;
5859 
5860     current->list->setFocus();
5861 
5862     // Use the selected directory in FilePanel if any
5863     // or use the selected directory in DirPanel
5864     if (current->list->getNumSelectedItems() == 0)
5865     {
5866         dir = current->list->getDirectory();
5867     }
5868     else
5869     {
5870         for (int u = 0; u < current->list->getNumItems(); u++)
5871         {
5872             if (current->list->isItemSelected(u))
5873             {
5874                 dir = current->list->getItemPathname(u);
5875             }
5876         }
5877     }
5878 
5879     // If symbolic link, read the linked directory
5880     if (::isLink(dir))
5881     {
5882         dir = ::readLink(dir);
5883     }
5884 
5885     if (FXSELID(sel) == ID_MOUNT)
5886     {
5887         op = MOUNT;
5888         msg = _("Mount");
5889 		cmd = getApp()->reg().readStringEntry("PROGS", "mount", DEFAULT_MOUNTCMD) + FXString(" ");
5890     }
5891     else
5892     {
5893         op = UNMOUNT;
5894         msg = _("Unmount");
5895 	    cmd = getApp()->reg().readStringEntry("PROGS", "unmount", DEFAULT_UMOUNTCMD) + FXString(" ");
5896     }
5897     cmd += ::quote(dir);
5898     cmd += " 2>&1";
5899     ret = chdir(ROOTDIR);
5900     if (ret < 0)
5901     {
5902         int errcode = errno;
5903         if (errcode)
5904         {
5905             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), ROOTDIR, strerror(errcode));
5906         }
5907         else
5908         {
5909             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), ROOTDIR);
5910         }
5911 
5912         return(0);
5913     }
5914 
5915     // Wait cursor
5916     getApp()->beginWaitCursor();
5917 
5918     // File object
5919     text = msg + _(" file system...");
5920     f = new File(this, text.text(), op);
5921     f->create();
5922 
5923     // Mount/unmount file system
5924     text = msg + _(" the folder:");
5925     f->mount(dir, text, cmd, op);
5926     ret = chdir(startlocation.text());
5927     if (ret < 0)
5928     {
5929         int errcode = errno;
5930         if (errcode)
5931         {
5932             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
5933         }
5934         else
5935         {
5936             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
5937         }
5938 
5939         return(0);
5940     }
5941 
5942     // If action is cancelled in progress dialog
5943     if (f->isCancelled())
5944     {
5945         f->hide();
5946         text = msg + _(" operation cancelled!");
5947         MessageBox::error(this, BOX_OK, _("Warning"), "%s", text.text());
5948         delete f;
5949         return(0);
5950     }
5951 
5952     getApp()->endWaitCursor();
5953     delete f;
5954 
5955     // Force panel refresh
5956     onCmdRefresh(0, 0, 0);
5957 
5958     return(1);
5959 }
5960 
5961 
5962 // Update the Mount button
onUpdMount(FXObject * o,FXSelector sel,void *)5963 long FilePanel::onUpdMount(FXObject* o, FXSelector sel, void*)
5964 {
5965     FXString dir;
5966 
5967     int num = current->list->getNumSelectedItems();
5968 
5969     // Use the selected directory in FilePanel if any
5970     // or use the selected directory in DirPanel
5971     if (num == 0)
5972     {
5973         dir = current->list->getDirectory();
5974     }
5975     else
5976     {
5977         for (int u = 0; u < current->list->getNumItems(); u++)
5978         {
5979             if (current->list->isItemSelected(u))
5980             {
5981                 dir = current->list->getItemPathname(u);
5982             }
5983         }
5984     }
5985 
5986     if (fsdevices->find(dir.text()) && !mtdevices->find(dir.text()) && current->list->getNumItems() && !current->list->isItemSelected(0))
5987     {
5988         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
5989     }
5990     else
5991     {
5992         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
5993     }
5994 
5995     return(1);
5996 }
5997 
5998 
5999 // Update the Unmount button
onUpdUnmount(FXObject * o,FXSelector sel,void *)6000 long FilePanel::onUpdUnmount(FXObject* o, FXSelector sel, void*)
6001 {
6002     FXString dir;
6003 
6004     int num = current->list->getNumSelectedItems();
6005 
6006     // Use the selected directory in FilePanel if any
6007     // or use the selected directory in DirPanel
6008     if (num == 0)
6009     {
6010         dir = current->list->getDirectory();
6011     }
6012     else
6013     {
6014         for (int u = 0; u < current->list->getNumItems(); u++)
6015         {
6016             if (current->list->isItemSelected(u))
6017             {
6018                 dir = current->list->getItemPathname(u);
6019             }
6020         }
6021     }
6022 
6023     if ((fsdevices->find(dir.text()) || mtdevices->find(dir.text())) && current->list->getNumItems() && !current->list->isItemSelected(0))
6024     {
6025         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6026     }
6027     else
6028     {
6029         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6030     }
6031 
6032     return(1);
6033 }
6034 
6035 
6036 // Query packages data base
onCmdPkgQuery(FXObject * o,FXSelector sel,void *)6037 long FilePanel::onCmdPkgQuery(FXObject* o, FXSelector sel, void*)
6038 {
6039     FXString cmd;
6040 
6041     // Name of the current selected file
6042     FXString file = current->list->getCurrentFile();
6043 
6044     // Command to perform
6045     if (pkg_format == DEB_PKG)
6046     {
6047         cmd = "dpkg -S " + ::quote(file);
6048     }
6049     else if (pkg_format == RPM_PKG)
6050     {
6051         cmd = "rpm -qf " + ::quote(file);
6052     }
6053     else
6054     {
6055         MessageBox::error(this, BOX_OK, _("Error"), _("No compatible package manager (rpm or dpkg) found!"));
6056         return(0);
6057     }
6058 
6059     // Query command
6060     cmd += " 2>&1";
6061 
6062     // Wait cursor
6063     getApp()->beginWaitCursor();
6064 
6065     // Perform the command
6066     FILE* pcmd = popen(cmd.text(), "r");
6067     if (!pcmd)
6068     {
6069         MessageBox::error(this, BOX_OK, _("Error"), _("Failed command: %s"), cmd.text());
6070         return(0);
6071     }
6072 
6073     // Get command output
6074     char     text[10000] = { 0 };
6075     FXString buf;
6076     while (fgets(text, sizeof(text), pcmd))
6077     {
6078         buf += text;
6079     }
6080     snprintf(text, sizeof(text)-1, "%s", buf.text());
6081 
6082     // Close the stream and display error message if any
6083     if ((pclose(pcmd) == -1) && (errno != ECHILD))   // ECHILD can be set if the child was caught by sigHarvest
6084     {
6085         getApp()->endWaitCursor();
6086         MessageBox::error(this, BOX_OK, _("Error"), "%s", text);
6087         return(0);
6088     }
6089     getApp()->endWaitCursor();
6090 
6091     // Get package name, or detect when the file isn't in a package
6092     FXString str = text;
6093     if (pkg_format == DEB_PKG)  // DEB based distribution
6094     {
6095         int idx = str.find(" ");               // Split output at first whitespace
6096         FXString pkgname = str.left(idx-1);    // Remove trailing colon
6097         FXString fname = str.right(str.length()-idx);
6098         fname.trim();                          // Remove leading space and trailing newline
6099         if (streq(fname.text(), file.text()))  // No other word than the file name
6100         {
6101             str = pkgname.text();
6102         }
6103         else
6104         {
6105             str = "";
6106         }
6107     }
6108     if (pkg_format == RPM_PKG)   // RPM based distribution
6109     {
6110         if (str.find(' ') != -1) // Space character exists in the string
6111         {
6112             str = "";
6113         }
6114     }
6115 
6116     // Display the related output message
6117     FXString message;
6118     if (str == "")
6119     {
6120         message.format(_("File %s does not belong to any package."), file.text());
6121         MessageBox::information(this, BOX_OK, _("Information"), "%s", message.text());
6122     }
6123     else
6124     {
6125         message.format(_("File %s belongs to the package: %s"), file.text(), str.text());
6126         MessageBox::information(this, BOX_OK, _("Information"), "%s", message.text());
6127     }
6128 
6129     return(1);
6130 }
6131 
6132 
6133 // Update the package query menu
onUpdPkgQuery(FXObject * o,FXSelector sel,void *)6134 long FilePanel::onUpdPkgQuery(FXObject* o, FXSelector sel, void*)
6135 {
6136     // Menu item is disabled when nothing is selected or multiple selection
6137     // or when unique selection and the selected item is a directory
6138 
6139     int num;
6140 
6141     num = current->list->getNumSelectedItems();
6142 
6143     if ((num == 0) || (num > 1))
6144     {
6145         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6146     }
6147     else // num=1
6148     {
6149         int item = current->list->getCurrentItem();
6150         if ((item >= 0) && current->list->isItemDirectory(item))
6151         {
6152             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6153         }
6154         else
6155         {
6156             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6157         }
6158     }
6159 
6160     return(1);
6161 }
6162 
6163 
6164 #endif // End #if defined(linux)
6165 
6166 
6167 // Directory usage on file selection
onCmdDirUsage(FXObject * o,FXSelector,void *)6168 long FilePanel::onCmdDirUsage(FXObject* o, FXSelector, void*)
6169 {
6170 	FXString name, command, itemslist = " ";
6171 	FXString cmd1 = "/usr/bin/du --apparent-size -k -s ";
6172 	FXString cmd2 = " 2> /dev/null | /usr/bin/sort -rn | /usr/bin/cut -f2 | /usr/bin/xargs -d '\n' /usr/bin/du --apparent-size --total --si -s 2> /dev/null";
6173 
6174     // Enter current directory
6175     int ret=chdir(current->getDirectory().text());
6176     if (ret < 0)
6177     {
6178         int errcode=errno;
6179         if (errcode)
6180         {
6181             MessageBox::error(this,BOX_OK,_("Error"),_("Can't enter folder %s: %s"),current->getDirectory().text(),strerror(errcode));
6182 		}
6183         else
6184         {
6185             MessageBox::error(this,BOX_OK,_("Error"),_("Can't enter folder %s"),current->getDirectory().text());
6186 		}
6187 
6188         return 0;
6189     }
6190 
6191     // Eventually deselect the '..' directory
6192     if (current->list->isItemSelected(0))
6193     {
6194         current->list->deselectItem(0);
6195     }
6196 
6197     // Return if nothing is selected
6198     if (current->list->getNumSelectedItems() == 0)
6199     {
6200         return(0);
6201     }
6202 
6203     // Construct selected files list
6204     current->list->setFocus();
6205     for (int u = 0; u < current->list->getNumItems(); u++)
6206     {
6207         if (current->list->isItemSelected(u))
6208         {
6209  			name = current->list->getItemFilename(u);
6210 
6211             // List of selected items
6212             itemslist += ::quote(name) + " ";
6213         }
6214     }
6215 
6216 	// Command to be executed
6217 	command = cmd1 + itemslist + cmd2;
6218 
6219 	// Make and show command window
6220 	CommandWindow* cmdwin=new CommandWindow(getApp(),_("Sizes of Selected Items"),command,25,50);
6221 	cmdwin->create();
6222 	cmdwin->setIcon(charticon);
6223 
6224 	// The CommandWindow object will delete itself when closed!
6225 
6226 	// Return to start location
6227 	ret = chdir(startlocation.text());
6228 	if (ret < 0)
6229 	{
6230 		int errcode = errno;
6231 		if (errcode)
6232 		{
6233 			MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
6234 		}
6235 		else
6236 		{
6237 			MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
6238 		}
6239 	}
6240 
6241     return(1);
6242 }
6243 
6244 
6245 // Update the status bar and the path linker
onUpdStatus(FXObject * sender,FXSelector,void *)6246 long FilePanel::onUpdStatus(FXObject* sender, FXSelector, void*)
6247 {
6248     // Update the status bar
6249     int      item = -1;
6250     FXString str, linkto;
6251     char     usize[64];
6252     FXulong  size = 0;
6253     FXString hsize = _("0 bytes");
6254 
6255     FXString path = list->getDirectory();
6256 
6257     int num = list->getNumSelectedItems();
6258 
6259     // To handle the update rename (ugly, I know)
6260     if (current == this)
6261     {
6262         if (num <= 1)
6263         {
6264             selmult = false;
6265         }
6266         else if (num > 1)
6267         {
6268             selmult = true;
6269         }
6270     }
6271 
6272     item = list->getCurrentItem();
6273 
6274     if (num > 1)
6275     {
6276 		int nbdirs = 0;
6277         for (int u = 0; u < list->getNumItems(); u++)
6278         {
6279             if (list->isItemSelected(u) && !list->isItemDirectory(u))
6280             {
6281                 size += list->getItemFileSize(u);
6282 #if __WORDSIZE == 64
6283                 snprintf(usize, sizeof(usize)-1, "%lu", size);
6284 #else
6285                 snprintf(usize, sizeof(usize)-1, "%llu", size);
6286 #endif
6287                 hsize = ::hSize(usize);
6288             }
6289 
6290             if (list->isItemSelected(u) && list->isItemDirectory(u) && (list->getItemText(u) != "..") )
6291             {
6292 				nbdirs++;
6293 			}
6294         }
6295 
6296 		int nbfiles = num - nbdirs;
6297         if (nbdirs <= 1 && nbfiles <= 1)
6298         {
6299 	        str.format(_("%s in %s selected items (%s folder, %s file)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6300 		}
6301         else if (nbdirs <=1 && nbfiles > 1)
6302         {
6303 	        str.format(_("%s in %s selected items (%s folder, %s files)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6304 		}
6305         else if (nbdirs > 1 && nbfiles <= 1)
6306         {
6307 	        str.format(_("%s in %s selected items (%s folders, %s file)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6308 		}
6309 		else
6310 		{
6311 	        str.format(_("%s in %s selected items (%s folders, %s files)"), hsize.text(), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6312 		}
6313     }
6314     else
6315     {
6316         // Nothing selected
6317         if ((num == 0) || (item < 0))
6318         {
6319             num = list->getNumItems();
6320             if (num == 1)
6321             {
6322                 str = _("1 item (1 folder)");
6323             }
6324             else
6325             {
6326 				int nbdirs = 0;
6327 				for (int u = 0; u < num; u++)
6328 				{
6329 					if (list->isItemDirectory(u))
6330 					{
6331 						nbdirs++;
6332 					}
6333 				}
6334 
6335 				int nbfiles = num - nbdirs;
6336 		        str.format(_("%s items (%s folders, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6337 				if (nbdirs <= 1 && nbfiles <= 1)
6338 				{
6339 					str.format(_("%s items (%s folder, %s file)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6340 				}
6341 				else if (nbdirs <=1 && nbfiles > 1)
6342 				{
6343 					str.format(_("%s items (%s folder, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6344 				}
6345 				else if (nbdirs > 1 && nbfiles <= 1)
6346 				{
6347 					str.format(_("%s items (%s folders, %s file)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6348 				}
6349 				else
6350 				{
6351 					str.format(_("%s items (%s folders, %s files)"), FXStringVal(num).text(), FXStringVal(nbdirs).text(), FXStringVal(nbfiles).text());
6352 				}
6353             }
6354         }
6355         else
6356         {
6357             FXString string = list->getItemText(item);
6358             FXString name = string.section('\t', 0);
6359             FXString type = string.section('\t', 2);
6360 
6361             FXString date = string.section('\t', 4);
6362             FXString usr = string.section('\t', 5);
6363             FXString grp = string.section('\t', 6);
6364             FXString perm = string.section('\t', 7);
6365 
6366             if (type.contains(_("Broken link")))
6367             {
6368                 linkto = ::readLink(path+PATHSEPSTRING+name);
6369                 str = name + "->" + linkto.text() + " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
6370             }
6371             else if (type.contains(_("Link")))
6372             {
6373                 linkto = ::readLink(path+PATHSEPSTRING+name);
6374                 str = name + "->" + linkto.text() + " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
6375             }
6376             else
6377             {
6378                 for (int u = 0; u < list->getNumItems(); u++)
6379                 {
6380                     if (list->isItemSelected(u) && !list->isItemDirectory(u))
6381                     {
6382                         size = list->getItemFileSize(u);
6383 #if __WORDSIZE == 64
6384                         snprintf(usize, sizeof(usize)-1, "%lu", size);
6385 #else
6386                         snprintf(usize, sizeof(usize)-1, "%llu", size);
6387 #endif
6388                         hsize = ::hSize(usize);
6389                         break;
6390                     }
6391                 }
6392                 str = hsize+ " | " + type + " | " + date + " | " + usr + " | " + grp + " | " + perm;
6393             }
6394         }
6395     }
6396 
6397     statuslabel->setText(str);
6398 
6399     // Add the filter pattern if any
6400     if ((list->getPattern() != "*") && (list->getPattern() != "*.*"))
6401     {
6402         str.format(_(" - Filter: %s"), list->getPattern().text());
6403         filterlabel->setText(str);
6404         filterlabel->setTextColor(attenclr);
6405     }
6406     else
6407     {
6408         filterlabel->setText("");
6409     }
6410 
6411     return(1);
6412 }
6413 
6414 
6415 // Update the path text and the path link
updatePath()6416 void FilePanel::updatePath()
6417 {
6418     pathlink->setPath(list->getDirectory());
6419     pathtext->setText(list->getDirectory());
6420 }
6421 
6422 
6423 // Update the go to parent directory command
onUpdUp(FXObject * o,FXSelector,void *)6424 long FilePanel::onUpdUp(FXObject* o, FXSelector, void*)
6425 {
6426     FXButton* button = (FXButton*)o;
6427     int       style = button->getButtonStyle();
6428 
6429     if (style & TOGGLEBUTTON_TOOLBAR)
6430     {
6431         if (current->list->getDirectory() != ROOTDIR)
6432         {
6433             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6434         }
6435         else
6436         {
6437             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6438         }
6439     }
6440     else
6441     {
6442         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6443     }
6444     return(1);
6445 }
6446 
6447 
6448 // Update the paste button
onUpdPaste(FXObject * o,FXSelector,void *)6449 long FilePanel::onUpdPaste(FXObject* o, FXSelector, void*)
6450 {
6451     FXuchar* data;
6452     FXuint   len;
6453     FXString buf;
6454     FXbool   clipboard_empty = true;
6455 
6456     // Lock clipboard to prevent changes in method onCmdRequestClipboard()
6457     clipboard_locked = true;
6458 
6459     // If source is xfelistType (Gnome, XFCE, or Xfe app)
6460     if (getDNDData(FROM_CLIPBOARD, xfelistType, data, len))
6461     {
6462         FXRESIZE(&data, FXuchar, len+1);
6463         data[len] = '\0';
6464         buf = (char*)data;
6465 
6466         // Check if valid clipboard
6467         if (buf.find("file:/") >= 0)
6468         {
6469             clipboard_empty = false;
6470         }
6471 
6472         // Free data pointer
6473         FXFREE(&data);
6474     }
6475 
6476     // If source type is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
6477     else if (getDNDData(FROM_CLIPBOARD, urilistType, data, len))
6478     {
6479         FXRESIZE(&data, FXuchar, len+1);
6480         data[len] = '\0';
6481         buf = (char*)data;
6482 
6483         // Check if valid clipboard
6484         if (buf.find("file:/") >= 0)
6485         {
6486             clipboard_empty = false;
6487         }
6488 
6489         // Free data pointer
6490         FXFREE(&data);
6491     }
6492 
6493     // If source is utf8Type (simple text)
6494     else if (getDNDData(FROM_CLIPBOARD, utf8Type, data, len))
6495     {
6496         FXRESIZE(&data, FXuchar, len+1);
6497         data[len] = '\0';
6498         buf = (char*)data;
6499 
6500         // Check if valid clipboard
6501 		int beg, end;
6502 		FXString filepath;
6503         FXbool clipboard_valid = true;
6504         for (beg = 0; beg < buf.length(); beg = end+1)
6505         {
6506             if ((end = buf.find("\n", beg)) < 0)
6507             {
6508                 end = buf.length();
6509             }
6510 
6511             // Obtain item file path
6512             filepath = buf.mid(beg, end-beg);
6513 
6514             // File path does not begin with '/'
6515             if (filepath[0] != PATHSEPCHAR)
6516             {
6517 				clipboard_valid = false;
6518 				break;
6519 			}
6520 
6521 			// File path is not an existing file or directory
6522 			else
6523 			{
6524 				if (!existFile(filepath))
6525 				{
6526 					clipboard_valid = false;
6527 					break;
6528 				}
6529 			}
6530 		}
6531 
6532 		// Clipboard not empty
6533 		if (clipboard_valid)
6534 		{
6535 			clipboard_empty = false;
6536 		}
6537 
6538         // Free data pointer
6539         FXFREE(&data);
6540     }
6541 
6542     // Gray out the paste button, if necessary
6543     if (clipboard_empty)
6544     {
6545         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6546     }
6547     else
6548     {
6549         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6550     }
6551 
6552     // Unlock clipboard
6553     clipboard_locked = false;
6554 
6555     return(1);
6556 }
6557 
6558 
6559 // Update menu items and toolbar buttons that are related to file operations
onUpdMenu(FXObject * o,FXSelector sel,void *)6560 long FilePanel::onUpdMenu(FXObject* o, FXSelector sel, void*)
6561 {
6562     // Menu item is disabled when nothing or only ".." is selected
6563     int num;
6564 
6565     num = current->list->getNumSelectedItems();
6566     DirItem* item = (DirItem*)dirpanel->getCurrentItem();
6567 
6568     if ((dirpanel->shown() && item))
6569     {
6570         if (num == 0)
6571         {
6572             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6573         }
6574         else if ((num == 1) && current->list->isItemSelected(0))
6575         {
6576             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6577         }
6578         else
6579         {
6580             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6581         }
6582     }
6583     else
6584     {
6585         if (num == 0)
6586         {
6587             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6588         }
6589         else if ((num == 1) && current->list->isItemSelected(0))
6590         {
6591             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6592         }
6593         else
6594         {
6595             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6596         }
6597     }
6598 
6599     return(1);
6600 }
6601 
6602 
6603 // Update file delete menu item and toolbar button
onUpdFileDelete(FXObject * o,FXSelector sel,void *)6604 long FilePanel::onUpdFileDelete(FXObject* o, FXSelector sel, void*)
6605 {
6606     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
6607     FXbool use_trash_bypass = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_bypass", false);
6608 
6609     if ( (!use_trash_can) | use_trash_bypass)
6610     {
6611         int num = current->list->getNumSelectedItems();
6612         if (num == 0)
6613         {
6614             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6615         }
6616         else if ((num == 1) && current->list->isItemSelected(0))
6617         {
6618             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6619         }
6620         else
6621         {
6622             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6623         }
6624     }
6625     else
6626     {
6627         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6628     }
6629 
6630     return(1);
6631 }
6632 
6633 
6634 // Update move to trash menu item and toolbar button
onUpdFileTrash(FXObject * o,FXSelector sel,void *)6635 long FilePanel::onUpdFileTrash(FXObject* o, FXSelector sel, void*)
6636 {
6637     // Disable move to trash menu if we are in trash can
6638     // or if the trash can directory is selected
6639 
6640     FXbool   trashenable = true;
6641     FXString trashparentdir = trashlocation.rbefore('/');
6642     FXString curdir = current->list->getDirectory();
6643 
6644     if (curdir.left(trashlocation.length()) == trashlocation)
6645     {
6646         trashenable = false;
6647     }
6648 
6649     if (curdir == trashparentdir)
6650     {
6651         FXString pathname;
6652         for (int u = 0; u < current->list->getNumItems(); u++)
6653         {
6654             if (current->list->isItemSelected(u))
6655             {
6656                 pathname = current->list->getItemPathname(u);
6657                 if (pathname == trashlocation)
6658                 {
6659                     trashenable = false;
6660                 }
6661             }
6662         }
6663     }
6664 
6665     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
6666     if (use_trash_can && trashenable)
6667     {
6668         int num = current->list->getNumSelectedItems();
6669         if (num == 0)
6670         {
6671             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6672         }
6673         else if ((num == 1) && current->list->isItemSelected(0))
6674         {
6675             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6676         }
6677         else
6678         {
6679             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6680         }
6681     }
6682     else
6683     {
6684         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6685     }
6686 
6687     return(1);
6688 }
6689 
6690 
6691 // Update restore from trash menu item and toolbar button
onUpdFileRestore(FXObject * o,FXSelector sel,void *)6692 long FilePanel::onUpdFileRestore(FXObject* o, FXSelector sel, void*)
6693 {
6694     // Enable restore from trash menu if we are in trash can
6695 
6696     FXbool   restoreenable = false;
6697     FXString curdir = current->list->getDirectory();
6698 
6699     if (curdir.left(trashfileslocation.length()) == trashfileslocation)
6700     {
6701         restoreenable = true;
6702     }
6703 
6704     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
6705     if (use_trash_can && restoreenable)
6706     {
6707         int num = current->list->getNumSelectedItems();
6708         if (num == 0)
6709         {
6710             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6711         }
6712         else if ((num == 1) && current->list->isItemSelected(0))
6713         {
6714             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6715         }
6716         else
6717         {
6718             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6719         }
6720     }
6721     else
6722     {
6723         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6724     }
6725 
6726     return(1);
6727 }
6728 
6729 
6730 // Update go trash menu item and toolbar button
onUpdGoTrash(FXObject * o,FXSelector sel,void *)6731 long FilePanel::onUpdGoTrash(FXObject* o, FXSelector sel, void*)
6732 {
6733     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
6734 
6735     if (use_trash_can)
6736     {
6737         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6738     }
6739     else
6740     {
6741         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6742     }
6743 
6744     return(1);
6745 }
6746 
6747 
6748 // Update file open menu
onUpdOpen(FXObject * o,FXSelector,void *)6749 long FilePanel::onUpdOpen(FXObject* o, FXSelector, void*)
6750 {
6751     // Menu item is disabled when nothing or a directory (including "..") is selected
6752     int num, item;
6753 
6754     num = current->list->getNumSelectedItems(&item);
6755 
6756     if (num == 0)
6757     {
6758         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6759     }
6760     else
6761     {
6762         if (current->list->getItem(item) && current->list->isItemFile(item))
6763         {
6764             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6765         }
6766         else
6767         {
6768             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6769         }
6770     }
6771     return(1);
6772 }
6773 
6774 
6775 // Update the status of the menu items that should be disabled when selecting multiple files
onUpdSelMult(FXObject * o,FXSelector sel,void *)6776 long FilePanel::onUpdSelMult(FXObject* o, FXSelector sel, void*)
6777 {
6778     // Menu item is disabled when nothing is selected or multiple selection or ".." is only selected
6779     int num;
6780 
6781     num = current->list->getNumSelectedItems();
6782     DirItem* item = (DirItem*)dirpanel->getCurrentItem();
6783 
6784     if (num == 0)
6785     {
6786         if (!item || !dirpanel->shown())
6787         {
6788             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6789         }
6790         else
6791         {
6792             o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6793         }
6794     }
6795     else if (current->selmult || ((num == 1) && current->list->isItemSelected(0)))
6796     {
6797         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6798     }
6799     else
6800     {
6801         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6802     }
6803 
6804     return(1);
6805 }
6806 
6807 
6808 // Update the file compare menu item
onUpdCompare(FXObject * o,FXSelector sel,void *)6809 long FilePanel::onUpdCompare(FXObject* o, FXSelector sel, void*)
6810 {
6811     // Menu item is enabled only when two files are selected
6812     int num;
6813 
6814     num = current->list->getNumSelectedItems();
6815 
6816     if ((num == 1) || (num == 2))
6817     {
6818         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6819     }
6820     else
6821     {
6822         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6823     }
6824 
6825     return(1);
6826 }
6827 
6828 
6829 // Update Add to archive menu
onUpdAddToArch(FXObject * o,FXSelector,void *)6830 long FilePanel::onUpdAddToArch(FXObject* o, FXSelector, void*)
6831 {
6832     // Menu item is disabled when nothing or ".." is selected
6833     int num, item;
6834 
6835     num = current->list->getNumSelectedItems(&item);
6836     if (num == 0)
6837     {
6838         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6839     }
6840     else if ((num == 1) && current->list->isItemSelected(0))
6841     {
6842         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6843     }
6844     else
6845     {
6846         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6847     }
6848     return(1);
6849 }
6850 
6851 
6852 // Update scripts menu item
onUpdRunScript(FXObject * o,FXSelector,void *)6853 long FilePanel::onUpdRunScript(FXObject* o, FXSelector, void*)
6854 {
6855     // Menu item is disabled when nothing or ".." is selected
6856     int num, item;
6857 
6858     num = current->list->getNumSelectedItems(&item);
6859     if (num == 0)
6860     {
6861         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6862     }
6863     else
6864     {
6865         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6866     }
6867     return(1);
6868 }
6869 
6870 
6871 // Update directory usage menu item
onUpdDirUsage(FXObject * o,FXSelector,void *)6872 long FilePanel::onUpdDirUsage(FXObject* o, FXSelector, void*)
6873 {
6874     // Menu item is enabled only when at least two items are selected
6875     int num, item;
6876 
6877     num = current->list->getNumSelectedItems(&item);
6878     if (num > 1)
6879     {
6880         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
6881     }
6882     else
6883     {
6884         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
6885     }
6886     return(1);
6887 }
6888