1 #include "config.h"
2 #include "i18n.h"
3 
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <sys/wait.h>
8 #include <sys/time.h>
9 
10 #include <fx.h>
11 #include <fxkeys.h>
12 #include <FXPNGIcon.h>
13 
14 #include "xfedefs.h"
15 #include "icons.h"
16 #include "xfeutils.h"
17 #include "File.h"
18 #include "DirList.h"
19 #include "Properties.h"
20 #include "BrowseInputDialog.h"
21 #include "ArchInputDialog.h"
22 #include "XFileExplorer.h"
23 #include "MessageBox.h"
24 #include "DirPanel.h"
25 
26 
27 
28 // Refresh interval for the directory size (ms)
29 #define DIRSIZE_REFRESH_INTERVAL    1000
30 
31 // Duration (in ms) before we can stop refreshing the file list
32 // Used for file operations on a large list of files
33 #define STOP_LIST_REFRESH_INTERVAL    5000
34 
35 // Number of files before stopping the file list refresh
36 #define STOP_LIST_REFRESH_NBMAX       100
37 
38 // Clipboard
39 extern FXString clipboard;
40 extern FXuint   clipboard_type;
41 
42 // Global variables
43 extern FXMainWindow* mainWindow;
44 extern FXbool        allowPopupScroll;
45 #if defined(linux)
46 extern FXStringDict* fsdevices;
47 extern FXStringDict* mtdevices;
48 #endif
49 extern FXuint   single_click;
50 extern FXString xdgdatahome;
51 
52 
53 
54 // Dirty hack to change the KEY_up and KEY_down behaviour
55 // These keys are no more associated with the mouse click action
56 #define SELECT_MASK    (TREELIST_SINGLESELECT|TREELIST_BROWSESELECT)
57 FXbool fromKeyPress = false;
onKeyPress(FXObject *,FXSelector,void * ptr)58 long FXTreeList::onKeyPress(FXObject*, FXSelector, void* ptr)
59 {
60     FXEvent*    event = (FXEvent*)ptr;
61     FXTreeItem* item = currentitem;
62     FXTreeItem* succ;
63     int         page;
64 
65     flags &= ~FLAG_TIP;
66     if (!isEnabled())
67     {
68         return(0);
69     }
70     if (target && target->tryHandle(this, FXSEL(SEL_KEYPRESS, message), ptr))
71     {
72         return(1);
73     }
74     if (item == NULL)
75     {
76         item = firstitem;
77     }
78     switch (event->code)
79     {
80     case KEY_Control_L:
81     case KEY_Control_R:
82     case KEY_Shift_L:
83     case KEY_Shift_R:
84     case KEY_Alt_L:
85     case KEY_Alt_R:
86         if (flags&FLAG_DODRAG)
87         {
88             handle(this, FXSEL(SEL_DRAGGED, 0), ptr);
89         }
90         return(1);
91 
92     case KEY_Page_Up:
93     case KEY_KP_Page_Up:
94         for (succ = item, page = verticalScrollBar()->getPage(); succ && 0 < page; )
95         {
96             item = succ;
97             page -= succ->getHeight(this);
98             if (succ->prev)
99             {
100                 succ = succ->prev;
101                 while (succ->last && ((options&TREELIST_AUTOSELECT) || succ->isExpanded()))
102                 {
103                     succ = succ->last;
104                 }
105             }
106             else if (succ->parent)
107             {
108                 succ = succ->parent;
109             }
110         }
111         goto hop;
112 
113     case KEY_Page_Down:
114     case KEY_KP_Page_Down:
115         for (succ = item, page = verticalScrollBar()->getPage(); succ && 0 < page; )
116         {
117             item = succ;
118             page -= succ->getHeight(this);
119             if (succ->first && ((options&TREELIST_AUTOSELECT) || succ->isExpanded()))
120             {
121                 succ = succ->first;
122             }
123             else
124             {
125                 while (!succ->next && succ->parent)
126                 {
127                     succ = succ->parent;
128                 }
129                 succ = succ->next;
130             }
131         }
132         goto hop;
133 
134     case KEY_Up:                          // Move up
135     case KEY_KP_Up:
136         if (item)
137         {
138             if (item->prev)
139             {
140                 item = item->prev;
141                 while (item->last && ((options&TREELIST_AUTOSELECT) || item->isExpanded()))
142                 {
143                     item = item->last;
144                 }
145             }
146             else if (item->parent)
147             {
148                 item = item->parent;
149             }
150         }
151         goto hop;
152 
153     case KEY_Down:                        // Move down
154     case KEY_KP_Down:
155         if (item)
156         {
157             if (item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded()))
158             {
159                 item = item->first;
160             }
161             else
162             {
163                 while (!item->next && item->parent)
164                 {
165                     item = item->parent;
166                 }
167                 item = item->next;
168             }
169         }
170         goto hop;
171 
172     case KEY_Right:                       // Move right/down and open subtree
173     case KEY_KP_Right:
174         if (item)
175         {
176             if (!(options&TREELIST_AUTOSELECT) && !item->isExpanded() && (item->hasItems() || item->getFirst()))
177             {
178                 expandTree(item, true);
179             }
180             else if (item->first)
181             {
182                 item = item->first;
183             }
184             else
185             {
186                 while (!item->next && item->parent)
187                 {
188                     item = item->parent;
189                 }
190                 item = item->next;
191             }
192         }
193         goto hop;
194 
195     case KEY_Left:                        // Move left/up and close subtree
196     case KEY_KP_Left:
197         if (item)
198         {
199             if (!(options&TREELIST_AUTOSELECT) && item->isExpanded() && (item->hasItems() || item->getFirst()))
200             {
201                 collapseTree(item, true);
202             }
203             else if (item->parent)
204             {
205                 item = item->parent;
206             }
207             else if (item->prev)
208             {
209                 item = item->prev;
210             }
211         }
212         goto hop;
213 
214     case KEY_Home:                        // Move to first
215     case KEY_KP_Home:
216         item = firstitem;
217         goto hop;
218 
219     case KEY_End:                         // Move to last
220     case KEY_KP_End:
221         item = lastitem;
222         while (item)
223         {
224             if (item->last && ((options&TREELIST_AUTOSELECT) || item->isExpanded()))
225             {
226                 item = item->last;
227             }
228             else if (item->next)
229             {
230                 item = item->next;
231             }
232             else
233             {
234                 break;
235             }
236         }
237 hop:
238         lookup = FXString::null;
239         if (item)
240         {
241             setCurrentItem(item, true);
242             makeItemVisible(item);
243             if ((options&SELECT_MASK) == TREELIST_EXTENDEDSELECT)
244             {
245                 if (item->isEnabled())
246                 {
247                     if (event->state&SHIFTMASK)
248                     {
249                         if (anchoritem)
250                         {
251                             selectItem(anchoritem, true);
252                             extendSelection(item, true);
253                         }
254                         else
255                         {
256                             selectItem(item, true);
257                             setAnchorItem(item);
258                         }
259                     }
260                     else if (!(event->state&CONTROLMASK))
261                     {
262                         killSelection(true);
263                         selectItem(item, true);
264                         setAnchorItem(item);
265                     }
266                 }
267             }
268         }
269 
270         // !!!! Hack to change the KEY_up and KEY_down behaviour !!!
271         fromKeyPress = true;
272         // !!!! End of hack !!!
273         handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentitem);
274 
275         if (currentitem && currentitem->isEnabled())
276         {
277             handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentitem);
278         }
279         return(1);
280 
281     case KEY_space:
282     case KEY_KP_Space:
283         lookup = FXString::null;
284         if (item && item->isEnabled())
285         {
286             switch (options&SELECT_MASK)
287             {
288             case TREELIST_EXTENDEDSELECT:
289                 if (event->state&SHIFTMASK)
290                 {
291                     if (anchoritem)
292                     {
293                         selectItem(anchoritem, true);
294                         extendSelection(item, true);
295                     }
296                     else
297                     {
298                         selectItem(item, true);
299                     }
300                 }
301                 else if (event->state&CONTROLMASK)
302                 {
303                     toggleItem(item, true);
304                 }
305                 else
306                 {
307                     killSelection(true);
308                     selectItem(item, true);
309                 }
310                 break;
311 
312             case TREELIST_MULTIPLESELECT:
313             case TREELIST_SINGLESELECT:
314                 toggleItem(item, true);
315                 break;
316             }
317             setAnchorItem(item);
318         }
319         handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentitem);
320         if (currentitem && currentitem->isEnabled())
321         {
322             handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentitem);
323         }
324         return(1);
325 
326     case KEY_Return:
327     case KEY_KP_Enter:
328         lookup = FXString::null;
329         handle(this, FXSEL(SEL_DOUBLECLICKED, 0), (void*)currentitem);
330         if (currentitem && currentitem->isEnabled())
331         {
332             handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentitem);
333         }
334         return(1);
335 
336     default:
337         if ((FXuchar)event->text[0] < ' ')
338         {
339             return(0);
340         }
341         if (event->state&(CONTROLMASK|ALTMASK))
342         {
343             return(0);
344         }
345         if (!Ascii::isPrint(event->text[0]))
346         {
347             return(0);
348         }
349         lookup.append(event->text);
350         getApp()->addTimeout(this, ID_LOOKUPTIMER, getApp()->getTypingSpeed());
351         item = findItem(lookup, currentitem, SEARCH_FORWARD|SEARCH_WRAP|SEARCH_PREFIX);
352         if (item)
353         {
354             setCurrentItem(item, true);
355             makeItemVisible(item);
356             if ((options&SELECT_MASK) == TREELIST_EXTENDEDSELECT)
357             {
358                 if (item->isEnabled())
359                 {
360                     killSelection(true);
361                     selectItem(item, true);
362                 }
363             }
364             setAnchorItem(item);
365         }
366         handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentitem);
367         if (currentitem && currentitem->isEnabled())
368         {
369             handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentitem);
370         }
371         return(1);
372     }
373     return(0);
374 }
375 
376 
377 // Map
378 FXDEFMAP(DirPanel) DirPanelMap[] =
379 {
380     FXMAPFUNC(SEL_CLIPBOARD_LOST, 0, DirPanel::onClipboardLost),
381     FXMAPFUNC(SEL_CLIPBOARD_GAINED, 0, DirPanel::onClipboardGained),
382     FXMAPFUNC(SEL_CLIPBOARD_REQUEST, 0, DirPanel::onClipboardRequest),
383     FXMAPFUNC(SEL_TIMEOUT, DirPanel::ID_STOP_LIST_REFRESH_TIMER, DirPanel::onCmdStopListRefreshTimer),
384     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_EXPANDTREE, DirPanel::onExpandTree),
385     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_COLLAPSETREE, DirPanel::onCollapseTree),
386     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_PROPERTIES, DirPanel::onCmdProperties),
387     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_ARCHIVE, DirPanel::onCmdAddToArch),
388     FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, DirPanel::ID_FILELIST, DirPanel::onCmdPopupMenu),
389     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_POPUP_MENU, DirPanel::onCmdPopupMenu),
390     FXMAPFUNC(SEL_CLICKED, DirPanel::ID_FILELIST, DirPanel::onCmdDirectory),
391     FXMAPFUNC(SEL_EXPANDED, DirPanel::ID_FILELIST, DirPanel::onExpand),
392     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_COPY_CLIPBOARD, DirPanel::onCmdCopyCut),
393     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_CUT_CLIPBOARD, DirPanel::onCmdCopyCut),
394     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_ADDCOPY_CLIPBOARD, DirPanel::onCmdCopyCut),
395     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_ADDCUT_CLIPBOARD, DirPanel::onCmdCopyCut),
396     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_PASTE_CLIPBOARD, DirPanel::onCmdPaste),
397     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_COPY, DirPanel::onCmdDirMan),
398     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_CUT, DirPanel::onCmdDirMan),
399     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_COPYTO, DirPanel::onCmdDirMan),
400     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_MOVETO, DirPanel::onCmdDirMan),
401     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_RENAME, DirPanel::onCmdDirMan),
402     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_SYMLINK, DirPanel::onCmdDirMan),
403     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_DELETE, DirPanel::onCmdDirDelete),
404     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_TRASH, DirPanel::onCmdDirTrash),
405     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_DIR_RESTORE, DirPanel::onCmdDirRestore),
406     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_NEW_DIR, DirPanel::onCmdNewDir),
407     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_XTERM, DirPanel::onCmdXTerm),
408     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_TOGGLE_HIDDEN, DirPanel::onCmdToggleHidden),
409     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_TOGGLE_TREE, DirPanel::onCmdToggleTree),
410     FXMAPFUNC(SEL_TIMEOUT, DirPanel::ID_DIRSIZE_REFRESH, DirPanel::onCmdDirsizeRefresh),
411     FXMAPFUNC(SEL_FOCUSIN, DirPanel::ID_FILELIST, DirPanel::onCmdFocus),
412 #if defined(linux)
413     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_MOUNT, DirPanel::onCmdMount),
414     FXMAPFUNC(SEL_COMMAND, DirPanel::ID_UMOUNT, DirPanel::onCmdMount),
415     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_MOUNT, DirPanel::onUpdMount),
416     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_UMOUNT, DirPanel::onUpdUnmount),
417 #endif
418     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_PASTE_CLIPBOARD, DirPanel::onUpdPaste),
419     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_TOGGLE_HIDDEN, DirPanel::onUpdToggleHidden),
420     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_TOGGLE_TREE, DirPanel::onUpdToggleTree),
421     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_CUT_CLIPBOARD, DirPanel::onUpdMenu),
422     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_ARCHIVE, DirPanel::onUpdMenu),
423     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_DIR_MOVETO, DirPanel::onUpdMenu),
424     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_DIR_RENAME, DirPanel::onUpdMenu),
425     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_DIR_TRASH, DirPanel::onUpdDirTrash),
426     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_DIR_RESTORE, DirPanel::onUpdDirRestore),
427     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_DIR_DELETE, DirPanel::onUpdDirDelete),
428     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_NEW_DIR, DirPanel::onUpdMenu),
429     FXMAPFUNC(SEL_UPDATE, DirPanel::ID_TITLE, DirPanel::onUpdTitle),
430     FXMAPFUNC(SEL_UPDATE, 0, DirPanel::onUpdDirsizeRefresh),
431 };
432 
433 // Object implementation
FXIMPLEMENT(DirPanel,FXVerticalFrame,DirPanelMap,ARRAYNUMBER (DirPanelMap))434 FXIMPLEMENT(DirPanel, FXVerticalFrame, DirPanelMap, ARRAYNUMBER(DirPanelMap))
435 
436 // Construct Directory Panel
437 DirPanel::DirPanel(FXWindow* owner, FXComposite* p, FXColor listbackcolor, FXColor listforecolor, FXbool smoothscroll, FXuint opts, int x, int y, int w, int h) :
438     FXVerticalFrame(p, opts, x, y, w, h, 0, 0, 0, 0)
439 {
440     // Construct directory panel
441     FXVerticalFrame* cont = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
442     FXPacker*        packer = new FXHorizontalFrame(cont, LAYOUT_LEFT|JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 0, 0, 0, 0);
443 
444     // Visually indicate if the panel is active
445     activeicon = new FXButton(packer, "", greenbuttonicon, this, DirPanel::ID_FILELIST, BUTTON_TOOLBAR|JUSTIFY_LEFT|LAYOUT_LEFT);
446 
447     // Panel title
448     paneltitle = new TextLabel(packer, 0, this, ID_FILELIST, LAYOUT_FILL_X|LAYOUT_FILL_Y);
449     paneltitle->setText(_("Folders"));
450     paneltitle->setBackColor(getApp()->getBaseColor());
451 
452     FXuint options;
453     if (smoothscroll)
454     {
455         options = LAYOUT_FILL_X|LAYOUT_FILL_Y|TREELIST_BROWSESELECT|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|FRAME_SUNKEN;
456     }
457     else
458     {
459         options = LAYOUT_FILL_X|LAYOUT_FILL_Y|TREELIST_BROWSESELECT|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|SCROLLERS_DONT_TRACK;
460     }
461 
462     FXVerticalFrame* cont2 = new FXVerticalFrame(cont, LAYOUT_FILL_Y|LAYOUT_FILL_X|FRAME_SUNKEN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
463     list = new DirList(owner, cont2, this, ID_FILELIST, options);
464     list->setTextColor(listforecolor);
465     list->setBackColor(listbackcolor);
466 
467     statusbar = new FXHorizontalFrame(cont, JUSTIFY_LEFT|LAYOUT_FILL_X|FRAME_NONE, 0, 0, 0, 0, 3, 3, 3, 3);
468 
469     FXString key = getApp()->reg().readStringEntry("KEYBINDINGS", "hidden_dirs", "Ctrl-F5");
470     new FXToggleButton(statusbar, TAB+_("Show hidden folders")+PARS(key), TAB+_("Hide hidden folders")+PARS(key), showhiddenicon, hidehiddenicon, this,
471                        ID_TOGGLE_HIDDEN, BUTTON_TOOLBAR|LAYOUT_LEFT|ICON_BEFORE_TEXT|FRAME_NONE);
472 
473     status = new FXLabel(statusbar, _("0 bytes in root"), NULL, JUSTIFY_LEFT|LAYOUT_LEFT|LAYOUT_FILL_X|FRAME_NONE);
474     status->setTarget(this);
475     status->setSelector(FXSEL(SEL_UPDATE, DirPanel::ID_TITLE));
476 
477     // Home and trahscan locations
478     trashlocation = xdgdatahome+PATHSEPSTRING TRASHPATH;
479     trashfileslocation = xdgdatahome + PATHSEPSTRING TRASHFILESPATH;
480     trashinfolocation = xdgdatahome + PATHSEPSTRING TRASHINFOPATH;
481 
482     // Start location (we return to the start location after each chdir)
483     startlocation = FXSystem::getCurrentDirectory();
484 
485     // Single click navigation
486     single_click = getApp()->reg().readUnsignedEntry("SETTINGS", "single_click", SINGLE_CLICK_NONE);
487     if (single_click == SINGLE_CLICK_DIR_FILE)
488     {
489         list->setDefaultCursor(getApp()->getDefaultCursor(DEF_HAND_CURSOR));
490     }
491 
492     // Dialogs
493     newdirdialog = NULL;
494     archdialog = NULL;
495     operationdialog = NULL;
496     operationdialogrename = NULL;
497 
498     // Initialize clipboard flag
499     fromPaste = false;
500 
501     // Initialize control flag for right click popup menu
502     ctrlflag = false;
503 
504     // Other initializations
505     focuswindow = owner;
506     isactive = false;
507     curr_mtime = 0;
508     curr_dirpath = "";
509     allowDirsizeRefresh = true;
510 }
511 
512 
513 // Destructor
~DirPanel()514 DirPanel::~DirPanel()
515 {
516     getApp()->removeTimeout(this, ID_DIRSIZE_REFRESH);
517     delete list;
518     delete statusbar;
519     delete status;
520     delete newdirdialog;
521     delete archdialog;
522     delete operationdialog;
523     delete operationdialogrename;
524     delete paneltitle;
525 }
526 
527 
528 // Create X window
create()529 void DirPanel::create()
530 {
531     // Register standard uri-list type
532     urilistType = getApp()->registerDragType("text/uri-list");
533 
534     // Register special uri-list type used for Gnome, XFCE and Xfe
535     xfelistType = getApp()->registerDragType("x-special/gnome-copied-files");
536 
537     // Register special uri-list type used for KDE
538     kdelistType = getApp()->registerDragType("application/x-kde-cutselection");
539 
540     // Register standard UTF-8 text type used for file dialogs
541     utf8Type = getApp()->registerDragType("UTF8_STRING");
542 
543     getApp()->addTimeout(this, ID_DIRSIZE_REFRESH, DIRSIZE_REFRESH_INTERVAL);
544     FXVerticalFrame::create();
545 }
546 
547 
548 // Make DirPanel active
setActive()549 void DirPanel::setActive()
550 {
551     // Set active icon
552     activeicon->setIcon(greenbuttonicon);
553     activeicon->setTipText(_("Panel is active"));
554     list->setFocus();
555     isactive = true;
556 
557     // Current panel must get an inactive icon (but not get the inactive status!)
558     FilePanel* currentpanel = ((XFileExplorer*)mainWindow)->getCurrentPanel();
559     currentpanel->setInactive(false);
560 }
561 
562 
563 // Make DirPanel inactive
setInactive()564 void DirPanel::setInactive()
565 {
566     // Set active icon
567     activeicon->setIcon(graybuttonicon);
568     activeicon->setTipText(_("Activate panel"));
569     isactive = false;
570 }
571 
572 
573 // Focus on DirPanel when clicked (i.e. make panel active)
onCmdFocus(FXObject * sender,FXSelector sel,void * ptr)574 long DirPanel::onCmdFocus(FXObject* sender, FXSelector sel, void* ptr)
575 {
576     setActive();
577     return(1);
578 }
579 
580 
581 // To pass the expand message to DirList
onExpand(FXObject *,FXSelector,void * ptr)582 long DirPanel::onExpand(FXObject*, FXSelector, void* ptr)
583 {
584     list->handle(this, FXSEL(SEL_EXPANDED, 0), (void*)ptr);
585     return(1);
586 }
587 
588 
589 // Change the directory when clicking on the tree list
onCmdDirectory(FXObject *,FXSelector,void * ptr)590 long DirPanel::onCmdDirectory(FXObject*, FXSelector, void* ptr)
591 {
592     TreeItem* item = (TreeItem*)ptr;
593 
594     if (item)
595     {
596         FXString directory = list->getItemPathname(item);
597         if (!::isReadExecutable(directory))
598         {
599             MessageBox::error(this, BOX_OK_SU, _("Error"), _(" Permission to: %s denied."), directory.text());
600             return(0);
601         }
602         FilePanel*  currentpanel = ((XFileExplorer*)mainWindow)->getCurrentPanel();
603         FXComboBox* address = ((XFileExplorer*)mainWindow)->getAddressBox();
604         currentpanel->setDirectory(directory, true);
605         currentpanel->updatePath();
606 
607         // Remember latest directory in the location address
608         FXString item;
609         int      i = 0;
610         int      count = address->getNumItems();
611         FXString p = currentpanel->getDirectory();
612 
613         if (!count)
614         {
615             count++;
616             address->insertItem(0, address->getText());
617         }
618         while (i < count)
619         {
620             item = address->getItem(i++);
621             if (streq((const char*)&p[0], (const char*)&item[0]))
622             {
623                 i--;
624                 break;
625             }
626         }
627         if (i == count)
628         {
629             address->insertItem(0, currentpanel->getDirectory());
630         }
631     }
632 
633     // Manage single click navigation
634     if (item && (single_click != SINGLE_CLICK_NONE) && !fromKeyPress)
635     {
636         list->handle(this, FXSEL(SEL_EXPANDED, 0), (void*)ptr);
637     }
638     fromKeyPress = false;
639 
640     return(1);
641 }
642 
643 
644 // Toggle hidden files
onCmdToggleHidden(FXObject *,FXSelector,void *)645 long DirPanel::onCmdToggleHidden(FXObject*, FXSelector, void*)
646 {
647     list->showHiddenFiles(!list->shownHiddenFiles());
648     return(1);
649 }
650 
651 
652 // Update toggle hidden files widget
onUpdToggleHidden(FXObject * sender,FXSelector,void *)653 long DirPanel::onUpdToggleHidden(FXObject* sender, FXSelector, void*)
654 {
655     if (list->shownHiddenFiles())
656     {
657         sender->handle(this, FXSEL(SEL_COMMAND, ID_CHECK), NULL);
658     }
659     else
660     {
661         sender->handle(this, FXSEL(SEL_COMMAND, ID_UNCHECK), NULL);
662     }
663     return(1);
664 }
665 
666 
onCmdToggleTree(FXObject * sender,FXSelector sel,void * ptr)667 long DirPanel::onCmdToggleTree(FXObject* sender, FXSelector sel, void* ptr)
668 {
669     return(this->handle(sender, FXSEL(SEL_COMMAND, FXWindow::ID_TOGGLESHOWN), ptr));
670 }
671 
672 
onUpdToggleTree(FXObject * sender,FXSelector sel,void * ptr)673 long DirPanel::onUpdToggleTree(FXObject* sender, FXSelector sel, void* ptr)
674 {
675     if (this->shown())
676     {
677         sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_CHECK), ptr);
678     }
679     else
680     {
681         sender->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_UNCHECK), ptr);
682     }
683     return(1);
684 }
685 
686 
687 // Directory list context menu
onCmdPopupMenu(FXObject * o,FXSelector s,void * p)688 long DirPanel::onCmdPopupMenu(FXObject* o, FXSelector s, void* p)
689 {
690     // Check if control key was pressed
691     if (p != NULL)
692     {
693         FXEvent* event = (FXEvent*)p;
694         if (event->state&CONTROLMASK)
695         {
696             ctrlflag = true;
697         }
698     }
699 
700     // Current item becomes item under cursor
701     int    x, y, xitem, yitem;
702     FXuint state;
703     getRoot()->getCursorPosition(x, y, state);
704     list->getCursorPosition(xitem, yitem, state);
705     DirItem* item = (DirItem*)list->getItemAt(xitem, yitem);
706 
707     // If item, then set it current and set directory in DirList and FileList
708     FXString dir;
709     if (item)
710     {
711         list->setCurrentItem(item, true);
712         dir = list->getItemPathname((TreeItem*)item);
713         list->setDirectory(dir, true);
714         ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(dir, true);
715         ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
716     }
717     else
718     {
719         ctrlflag = true;
720     }
721 
722     // Popup menu pane
723     FXMenuPane* menu = new FXMenuPane(this);
724 
725     // Menu
726 
727     // Control flag set
728     if (ctrlflag)
729     {
730         // Reset the control flag
731         ctrlflag = false;
732 
733         // Panel menu items
734         new FXMenuCommand(menu, _("New &folder..."), newfoldericon, this, DirPanel::ID_NEW_DIR);
735 
736         new FXMenuSeparator(menu);
737         new FXMenuCheck(menu, _("&Hidden folders"), this, DirPanel::ID_TOGGLE_HIDDEN);
738         new FXMenuCheck(menu, _("Ignore c&ase"), list, DirList::ID_SORT_CASE);
739         new FXMenuCheck(menu, _("&Reverse order"), list, DirList::ID_SORT_REVERSE);
740         new FXMenuCommand(menu, _("E&xpand tree"), exptreeicon, this, DirPanel::ID_EXPANDTREE);
741         new FXMenuCommand(menu, _("Collap&se tree"), colltreeicon, this, DirPanel::ID_COLLAPSETREE);
742     }
743     else
744     {
745         // Panel submenu items
746         FXMenuPane* submenu = new FXMenuPane(this);
747 
748         new FXMenuCommand(submenu, _("New &folder..."), newfoldericon, this, DirPanel::ID_NEW_DIR);
749 
750         new FXMenuSeparator(submenu);
751         new FXMenuCheck(submenu, _("&Hidden folders"), this, DirPanel::ID_TOGGLE_HIDDEN);
752         new FXMenuCheck(submenu, _("Ignore c&ase"), list, DirList::ID_SORT_CASE);
753         new FXMenuCheck(submenu, _("&Reverse order"), list, DirList::ID_SORT_REVERSE);
754         new FXMenuCommand(submenu, _("E&xpand tree"), exptreeicon, this, DirPanel::ID_EXPANDTREE);
755         new FXMenuCommand(submenu, _("Collap&se tree"), colltreeicon, this, DirPanel::ID_COLLAPSETREE);
756 
757         new FXMenuCascade(menu, _("Pane&l"), NULL, submenu);
758         new FXMenuSeparator(menu);
759 
760 #if defined(linux)
761         if (::isLink(dir))
762         {
763             dir = ::readLink(dir);
764         }
765         if (fsdevices->find(dir.text()) || mtdevices->find(dir.text()))
766         {
767             new FXMenuCommand(menu, _("M&ount"), maphosticon, this, ID_MOUNT);
768             new FXMenuCommand(menu, _("Unmoun&t"), unmaphosticon, this, ID_UMOUNT);
769             new FXMenuSeparator(menu);
770         }
771 #endif
772         new FXMenuCommand(menu, _("&Add to archive..."), archaddicon, this, DirPanel::ID_ARCHIVE);
773         new FXMenuSeparator(menu);
774         new FXMenuCommand(menu, _("&Copy"), copy_clpicon, this, DirPanel::ID_COPY_CLIPBOARD);
775         new FXMenuCommand(menu, _("C&ut"), cut_clpicon, this, DirPanel::ID_CUT_CLIPBOARD);
776         new FXMenuCommand(menu, _("&Paste"), paste_clpicon, this, DirPanel::ID_PASTE_CLIPBOARD);
777         new FXMenuSeparator(menu);
778         new FXMenuCommand(menu, _("Re&name..."), renameiticon, this, DirPanel::ID_DIR_RENAME);
779         new FXMenuCommand(menu, _("Cop&y to..."), copy_clpicon, this, DirPanel::ID_DIR_COPYTO);
780         new FXMenuCommand(menu, _("&Move to..."), moveiticon, this, DirPanel::ID_DIR_MOVETO);
781         new FXMenuCommand(menu, _("Symlin&k to..."), minilinkicon, this, DirPanel::ID_DIR_SYMLINK);
782         new FXMenuCommand(menu, _("Mo&ve to trash"), filedeleteicon, this, DirPanel::ID_DIR_TRASH);
783         new FXMenuCommand(menu, _("R&estore from trash"), filerestoreicon, this, DirPanel::ID_DIR_RESTORE);
784         new FXMenuCommand(menu, _("&Delete"), filedelete_permicon, this, DirPanel::ID_DIR_DELETE);
785         new FXMenuSeparator(menu);
786         new FXMenuCommand(menu, _("Prop&erties"), attribicon, this, DirPanel::ID_PROPERTIES);
787     }
788 
789     menu->create();
790     allowPopupScroll = true;  // Allow keyboard scrolling
791     menu->popup(NULL, x, y);
792     getApp()->runModalWhileShown(menu);
793     allowPopupScroll = false;
794     return(1);
795 }
796 
797 
798 // Helper function used to explore the directory tree and expand or collapse it
exploreUp(DirItem * item,const DirItem * rootitem,const int task)799 long DirPanel::exploreUp(DirItem* item, const DirItem* rootitem, const int task)
800 {
801     DirItem* parentitem = item;
802 
803     if (task == ID_EXPANDTREE)
804     {
805         list->expandTree((TreeItem*)item, true);
806     }
807     else
808     {
809         list->collapseTree((TreeItem*)item, true);
810     }
811 
812     item = (DirItem*)item->getFirst();
813     if (!item)
814     {
815         exploreDown(parentitem, rootitem, task);
816     }
817     else
818     {
819         exploreUp(item, rootitem, task);
820     }
821     return(1);
822 }
823 
824 
825 // Helper function used to explore the directory tree and expand or collapse it
exploreDown(DirItem * item,const DirItem * rootitem,const int task)826 long DirPanel::exploreDown(DirItem* item, const DirItem* rootitem, const int task)
827 {
828     if (item == rootitem)
829     {
830         return(1);
831     }
832 
833     DirItem* parentitem = (DirItem*)item->getParent();
834 
835     if (task == ID_EXPANDTREE)
836     {
837         list->expandTree((TreeItem*)item, true);
838     }
839     else
840     {
841         list->collapseTree((TreeItem*)item, true);
842     }
843     item = (DirItem*)item->getNext();
844 
845     if (!item)
846     {
847         exploreDown(parentitem, rootitem, task);
848     }
849     else
850     {
851         if (task == ID_EXPANDTREE)
852         {
853             list->expandTree((TreeItem*)item, true);
854         }
855         else
856         {
857             list->collapseTree((TreeItem*)item, true);
858         }
859         if (!list->isItemLeaf(item))
860         {
861             exploreUp(item, rootitem, task);
862         }
863         else
864         {
865             exploreDown(item, rootitem, task);
866         }
867     }
868     return(1);
869 }
870 
871 
872 // Expand the directory tree under cursor
onExpandTree(FXObject * sender,FXSelector sel,void *)873 long DirPanel::onExpandTree(FXObject* sender, FXSelector sel, void*)
874 {
875     DirItem* rootitem = (DirItem*)list->getCurrentItem();
876 
877     getApp()->beginWaitCursor();
878     exploreUp(rootitem, rootitem, ID_EXPANDTREE);
879     getApp()->endWaitCursor();
880 
881     return(1);
882 }
883 
884 
885 // Collapse the directory tree under cursor
onCollapseTree(FXObject * sender,FXSelector sel,void *)886 long DirPanel::onCollapseTree(FXObject* sender, FXSelector sel, void*)
887 {
888     DirItem* rootitem = (DirItem*)list->getCurrentItem();
889 
890     getApp()->beginWaitCursor();
891     exploreUp(rootitem, rootitem, ID_COLLAPSETREE);
892     getApp()->endWaitCursor();
893 
894     return(1);
895 }
896 
897 
898 // Directory properties
onCmdProperties(FXObject * sender,FXSelector,void *)899 long DirPanel::onCmdProperties(FXObject* sender, FXSelector, void*)
900 {
901     // Current item
902     DirItem* item = (DirItem*)list->getCurrentItem();
903     FXString pathname = list->getItemPathname((TreeItem*)item);
904 
905     PropertiesBox* attrdlg = new PropertiesBox(this, FXPath::name(pathname), FXPath::directory(pathname));
906 
907             attrdlg->create();
908 
909         attrdlg->show(PLACEMENT_OWNER);
910 
911     //~ if (attrdlg->execute(PLACEMENT_OWNER))
912     //~ {
913         list->setDirectory(pathname, true);
914     //~ }
915     //~ delete attrdlg;
916     return(1);
917 }
918 
919 
920 // Add files or directory to an archive
onCmdAddToArch(FXObject * o,FXSelector,void *)921 long DirPanel::onCmdAddToArch(FXObject* o, FXSelector, void*)
922 {
923     int      ret;
924     FXString ext1, ext2, cmd, archive;
925     File*    f;
926 
927     // Name and path of the current item
928     DirItem* item = (DirItem*)list->getCurrentItem();
929     FXString name = list->getItemText(item);
930     FXString pathname = list->getItemPathname((TreeItem*)item);
931     FXString parentdir = FXPath::directory(pathname);
932 
933     // Initial archive name with full path and default extension
934     if (parentdir == PATHSEPSTRING)
935     {
936         if (name == PATHSEPSTRING) // Archive is root file system
937         {
938             archive = parentdir+"ROOT"+".tar.gz";
939         }
940         else
941         {
942             archive = parentdir+name+".tar.gz";
943         }
944     }
945     else
946     {
947         archive = parentdir+PATHSEPSTRING+name+".tar.gz";
948     }
949 
950     ret = chdir(parentdir.text());
951     if (ret < 0)
952     {
953         int errcode = errno;
954         if (errcode)
955         {
956             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), parentdir.text(), strerror(errcode));
957         }
958         else
959         {
960             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), parentdir.text());
961         }
962 
963         return(0);
964     }
965 
966     // Archive dialog
967     if (archdialog == NULL)
968     {
969         archdialog = new ArchInputDialog(this, "");
970     }
971     archdialog->setText(archive);
972     archdialog->CursorEnd();
973 
974     if (archdialog->execute())
975     {
976         if (archdialog->getText() == "")
977         {
978             MessageBox::warning(this, BOX_OK, _("Warning"), _("File name is empty, operation cancelled"));
979             return(0);
980         }
981 
982         // Get string and preserve escape characters
983         archive = ::quote(archdialog->getText());
984 
985         // Get extensions of the archive name
986         ext1 = archdialog->getText().rafter('.', 1).lower();
987         ext2 = archdialog->getText().rafter('.', 2).lower();
988 
989         // Handle different archive formats
990         if (ext2 == "tar.gz")
991         {
992             cmd = "tar -zcvf "+archive+" ";
993         }
994         else if (ext2 == "tar.bz2")
995         {
996             cmd = "tar -jcvf "+archive+" ";
997         }
998         else if (ext2 == "tar.xz")
999         {
1000             cmd = "tar -Jcvf "+archive+" ";
1001         }
1002         else if (ext2 == "tar.z")
1003         {
1004             cmd = "tar -Zcvf "+archive+" ";
1005         }
1006         else if (ext1 == "tar")
1007         {
1008             cmd = "tar -cvf "+archive+" ";
1009         }
1010         else if (ext1 == "gz")
1011         {
1012             cmd = "gzip -v ";
1013         }
1014         else if (ext1 == "tgz")
1015         {
1016             cmd = "tar -zcvf "+archive+" ";
1017         }
1018         else if (ext1 == "taz")
1019         {
1020             cmd = "tar -Zcvf "+archive+" ";
1021         }
1022         else if (ext1 == "bz2")
1023         {
1024             cmd = "bzip2 -v ";
1025         }
1026         else if (ext1 == "xz")
1027         {
1028             cmd = "xz -v ";
1029         }
1030         else if ((ext1 == "tbz2") || (ext1 == "tbz"))
1031         {
1032             cmd = "tar -jcvf "+archive+" ";
1033         }
1034         else if (ext1 == "txz")
1035         {
1036             cmd = "tar -Jcvf "+archive+" ";
1037         }
1038         else if (ext1 == "z")
1039         {
1040             cmd = "compress -v ";
1041         }
1042         else if (ext1 == "zip")
1043         {
1044             cmd = "zip -r "+archive+" ";
1045         }
1046         else if (ext1 == "7z")
1047         {
1048             cmd = "7z a "+archive+" ";
1049         }
1050 
1051         // Default archive format
1052         else
1053         {
1054             archive += ".tar.gz";
1055             cmd = "tar -zcvf "+archive+" ";
1056         }
1057 
1058         // Archive command name
1059         cmd = cmd+::quote(name);
1060 
1061         // Wait cursor
1062         getApp()->beginWaitCursor();
1063 
1064         // File object
1065         f = new File(this, _("Create archive"), ARCHIVE);
1066         f->create();
1067 
1068         // Create archive
1069         f->archive(archive, cmd);
1070         ret = chdir(startlocation.text());
1071         if (ret < 0)
1072         {
1073             int errcode = errno;
1074             if (errcode)
1075             {
1076                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
1077             }
1078             else
1079             {
1080                 MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
1081             }
1082 
1083             return(0);
1084         }
1085 
1086         getApp()->endWaitCursor();
1087         delete f;
1088 
1089         // Display parent directory in DirList and FileList
1090         list->setDirectory(parentdir, true);
1091         ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
1092         ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
1093     }
1094     return(1);
1095 }
1096 
1097 
1098 // We now really do have the clipboard, keep clipboard content
onClipboardGained(FXObject * sender,FXSelector sel,void * ptr)1099 long DirPanel::onClipboardGained(FXObject* sender, FXSelector sel, void* ptr)
1100 {
1101     FXVerticalFrame::onClipboardGained(sender, sel, ptr);
1102     return(1);
1103 }
1104 
1105 
1106 // We lost the clipboard, free clipboard content
onClipboardLost(FXObject * sender,FXSelector sel,void * ptr)1107 long DirPanel::onClipboardLost(FXObject* sender, FXSelector sel, void* ptr)
1108 {
1109     FXVerticalFrame::onClipboardLost(sender, sel, ptr);
1110     return(1);
1111 }
1112 
1113 
1114 // Somebody wants our clipboard content
onClipboardRequest(FXObject * sender,FXSelector sel,void * ptr)1115 long DirPanel::onClipboardRequest(FXObject* sender, FXSelector sel, void* ptr)
1116 {
1117     FXEvent* event = (FXEvent*)ptr;
1118     FXuchar* data;
1119     FXuint   len;
1120 
1121     // Perhaps the target wants to supply its own data for the clipboard
1122     if (FXVerticalFrame::onClipboardRequest(sender, sel, ptr))
1123     {
1124         return(1);
1125     }
1126 
1127     // Clipboard target is xfelistType (Xfe, Gnome or XFCE)
1128     if (event->target == xfelistType)
1129     {
1130         // Don't modify the clipboard if we are called from updPaste()
1131         if (!clipboard_locked)
1132         {
1133             // Prepend "copy" or "cut" as in the Gnome way and avoid duplicating these strings
1134             if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
1135             {
1136                 if (clipboard_type == CUT_CLIPBOARD)
1137                 {
1138                     clipboard = "cut\n" + clipboard;
1139                 }
1140                 else
1141                 {
1142                     clipboard = "copy\n" + clipboard;
1143                 }
1144             }
1145         }
1146 
1147         // Return clipboard content
1148         if (event->target == xfelistType)
1149         {
1150             if (!clipboard.empty())
1151             {
1152                 len = clipboard.length();
1153                 FXMEMDUP(&data, clipboard.text(), FXuchar, len);
1154                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
1155 
1156                 // Return because xfelistType is not compatible with other types
1157                 return(1);
1158             }
1159         }
1160     }
1161 
1162     // Clipboard target is kdelisType (KDE)
1163     if (event->target == kdelistType)
1164     {
1165         // The only data to be passed in this case is "0" for copy and "1" for cut
1166         // The uri data are passed using the standard uri-list type
1167         FXString flag;
1168         if (clipboard_type == CUT_CLIPBOARD)
1169         {
1170             flag = "1";
1171         }
1172         else
1173         {
1174             flag = "0";
1175         }
1176 
1177         // Return clipboard content
1178         if (event->target == kdelistType)
1179         {
1180             FXMEMDUP(&data, flag.text(), FXuchar, 1);
1181             setDNDData(FROM_CLIPBOARD, event->target, data, 1);
1182         }
1183     }
1184 
1185     // Clipboard target is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
1186     if (event->target == urilistType)
1187     {
1188         if (!clipboard.empty())
1189         {
1190             len = clipboard.length();
1191             FXMEMDUP(&data, clipboard.text(), FXuchar, len);
1192             setDNDData(FROM_CLIPBOARD, event->target, data, len);
1193 
1194             return(1);
1195         }
1196     }
1197 
1198     // Clipboard target is utf8Type (to paste file pathes as text to other applications)
1199     if (event->target == utf8Type)
1200     {
1201         if (!clipboard.empty())
1202         {
1203             int      beg = 0, end = 0;
1204             FXString str = "";
1205             FXString pathname, url;
1206 
1207             // Clipboard don't contain 'copy\n' or 'cut\n' as first line
1208             if ((clipboard.find("copy\n") < 0) && (clipboard.find("cut\n") < 0))
1209             {
1210                 // Remove the 'file:' prefix for each file path
1211                 while (1)
1212                 {
1213                     end = clipboard.find('\n', end);
1214                     if (end < 0) // Last line
1215                     {
1216                         end = clipboard.length();
1217                         url = clipboard.mid(beg, end-beg+1);
1218                         pathname = FXURL::decode(FXURL::fileFromURL(url));
1219                         str += pathname;
1220                         break;
1221                     }
1222                     url = clipboard.mid(beg, end-beg+1);
1223                     pathname = FXURL::decode(FXURL::fileFromURL(url));
1224                     str += pathname;
1225                     end++;
1226                     beg = end;
1227                 }
1228                 end = str.length();
1229                 str = str.mid(0, end-2);  // Why is it end-2 here????
1230             }
1231 
1232             // Clipboard contains 'copy\n' or 'cut\n' as first line, thus skip it
1233             else
1234             {
1235                 // Start after the 'copy\n' or 'cut\n' prefix
1236                 end = clipboard.find('\n', 0);
1237                 end++;
1238                 beg = end;
1239 
1240                 // Remove the 'file:' prefix for each file path
1241                 while (1)
1242                 {
1243                     end = clipboard.find('\n', end);
1244                     if (end < 0) // Last line
1245                     {
1246                         end = clipboard.length();
1247                         url = clipboard.mid(beg, end-beg+1);
1248                         pathname = FXURL::decode(FXURL::fileFromURL(url));
1249                         str += pathname;
1250                         break;
1251                     }
1252                     url = clipboard.mid(beg, end-beg+1);
1253                     pathname = FXURL::decode(FXURL::fileFromURL(url));
1254                     str += pathname;
1255                     end++;
1256                     beg = end;
1257                 }
1258                 end = str.length();
1259                 //str=str.mid(0,end-1);
1260             }
1261 
1262             if (!str.empty())
1263             {
1264                 len = str.length();
1265                 FXMEMDUP(&data, str.text(), FXuchar, len);
1266                 setDNDData(FROM_CLIPBOARD, event->target, data, len);
1267 
1268                 return(1);
1269             }
1270         }
1271     }
1272     return(0);
1273 }
1274 
1275 
1276 // Copy or cut to clipboard (and add copy / add cut)
onCmdCopyCut(FXObject *,FXSelector sel,void *)1277 long DirPanel::onCmdCopyCut(FXObject*, FXSelector sel, void*)
1278 {
1279     // Clear clipboard
1280     if ((FXSELID(sel) == ID_COPY_CLIPBOARD) || (FXSELID(sel) == ID_CUT_CLIPBOARD))
1281     {
1282         clipboard.clear();
1283     }
1284 
1285     // Add an '\n' at the end if addcopy or addcut
1286     else
1287     {
1288         clipboard += '\n';
1289     }
1290 
1291     // Clipboard type
1292     if ((FXSELID(sel) == ID_CUT_CLIPBOARD) || (FXSELID(sel) == ID_ADDCUT_CLIPBOARD))
1293     {
1294         clipboard_type = CUT_CLIPBOARD;
1295     }
1296     else
1297     {
1298         clipboard_type = COPY_CLIPBOARD;
1299     }
1300 
1301     // Current item
1302     DirItem* item = (DirItem*)list->getCurrentItem();
1303     FXString pathname = list->getItemPathname((TreeItem*)item);
1304     clipboard += FXURL::encode(::fileToURI(pathname));
1305 
1306     // Acquire the clipboard
1307     FXDragType types[4];
1308     types[0] = xfelistType;
1309     types[1] = kdelistType;
1310     types[2] = urilistType;
1311     types[3] = utf8Type;
1312     if (acquireClipboard(types, 4))
1313     {
1314         return(0);
1315     }
1316 
1317     return(1);
1318 }
1319 
1320 
1321 // Paste file(s) from clipboard
onCmdPaste(FXObject *,FXSelector sel,void *)1322 long DirPanel::onCmdPaste(FXObject*, FXSelector sel, void*)
1323 {
1324     FXuchar* data;
1325     FXuint   len;
1326     int      beg, end, pos;
1327     FXString chaine, url, param;
1328     int      num = 0;
1329     FXbool   from_kde = false;
1330 
1331     // Target directory
1332     FXString targetdir = ((XFileExplorer*)mainWindow)->getCurrentPanel()->getDirectory();
1333 
1334     // If source is xfelistType (Gnome, XFCE, or Xfe app)
1335     if (getDNDData(FROM_CLIPBOARD, xfelistType, data, len))
1336     {
1337         FXRESIZE(&data, FXuchar, len+1);
1338         data[len] = '\0';
1339 
1340         clipboard = (char*)data;
1341 
1342         // Loop over clipboard items
1343         for (beg = 0; beg < clipboard.length(); beg = end+1)
1344         {
1345             if ((end = clipboard.find("\n", beg)) < 0)
1346             {
1347                 end = clipboard.length();
1348             }
1349 
1350             // Obtain item url
1351             url = clipboard.mid(beg, end-beg);
1352 
1353             // Eventually remove the trailing '\r' if any
1354             if ((pos = url.rfind('\r')) > 0)
1355             {
1356                 url.erase(pos);
1357             }
1358 
1359             // Process first item
1360             if (num == 0)
1361             {
1362                 // First item should be "copy" or "cut"
1363                 if (url == "copy")
1364                 {
1365                     clipboard_type = COPY_CLIPBOARD;
1366                     num++;
1367                 }
1368                 else if (url == "cut")
1369                 {
1370                     clipboard_type = CUT_CLIPBOARD;
1371                     num++;
1372                 }
1373 
1374                 // If first item is not "copy" nor "cut", process it as a normal url
1375                 // and use default clipboard type
1376                 else
1377                 {
1378                     // Update the param string
1379                     param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
1380 
1381                     // Add one more because the first line "copy" or "cut" was not present
1382                     num += 2;
1383                 }
1384             }
1385 
1386             // Process other items
1387             else
1388             {
1389                 // Update the param string
1390                 param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
1391                 num++;
1392             }
1393         }
1394 
1395         // Construct the final param string passed to the file management routine
1396         param = targetdir + "\n" + FXStringVal(num-1) + "\n" + param;
1397 
1398         // Copy or cut operation depending on the clipboard type
1399         switch (clipboard_type)
1400         {
1401         case COPY_CLIPBOARD:
1402             sel = FXSEL(SEL_COMMAND, DirPanel::ID_DIR_COPY);
1403             break;
1404 
1405         case CUT_CLIPBOARD:
1406             clipboard.clear();
1407             sel = FXSEL(SEL_COMMAND, DirPanel::ID_DIR_CUT);
1408             break;
1409         }
1410         fromPaste = true;
1411         this->handle(this, sel, (void*)param.text());
1412 
1413         // Free data pointer
1414         FXFREE(&data);
1415 
1416         // Return here because xfelistType is not compatible with other types
1417         return(1);
1418     }
1419 
1420     // If source type is kdelistType (KDE)
1421     if (getDNDData(FROM_CLIPBOARD, kdelistType, data, len))
1422     {
1423         from_kde = true;
1424 
1425         FXRESIZE(&data, FXuchar, len+1);
1426         data[len] = '\0';
1427         clipboard = (char*)data;
1428 
1429         // Obtain clipboard type (copy or cut)
1430         if (clipboard == "1")
1431         {
1432             clipboard_type = CUT_CLIPBOARD;
1433         }
1434         else
1435         {
1436             clipboard_type = COPY_CLIPBOARD;
1437         }
1438 
1439         FXFREE(&data);
1440     }
1441 
1442     // If source type is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
1443     if (getDNDData(FROM_CLIPBOARD, urilistType, data, len))
1444     {
1445         // For non KDE apps, set action to copy
1446         if (!from_kde)
1447         {
1448             clipboard_type = COPY_CLIPBOARD;
1449         }
1450 
1451         FXRESIZE(&data, FXuchar, len+1);
1452         data[len] = '\0';
1453         clipboard = (char*)data;
1454 
1455         // Loop over clipboard items
1456         for (beg = 0; beg < clipboard.length(); beg = end+1)
1457         {
1458             if ((end = clipboard.find("\n", beg)) < 0)
1459             {
1460                 end = clipboard.length();
1461             }
1462 
1463             // Obtain item url
1464             url = clipboard.mid(beg, end-beg);
1465 
1466             // Eventually remove the trailing '\r' if any
1467             if ((pos = url.rfind('\r')) > 0)
1468             {
1469                 url.erase(pos);
1470             }
1471 
1472             // Update the param string
1473             param += FXURL::decode(FXURL::fileFromURL(url)) + "\n";
1474             num++;
1475         }
1476 
1477         // Construct the final param string passed to the file management routine
1478         param = targetdir + "\n" + FXStringVal(num) + "\n" + param;
1479 
1480         // Copy or cut operation depending on the clipboard type
1481         switch (clipboard_type)
1482         {
1483         case COPY_CLIPBOARD:
1484             sel = FXSEL(SEL_COMMAND, DirPanel::ID_DIR_COPY);
1485             break;
1486 
1487         case CUT_CLIPBOARD:
1488             clipboard.clear();
1489             sel = FXSEL(SEL_COMMAND, DirPanel::ID_DIR_CUT);
1490             break;
1491         }
1492         fromPaste = true;
1493         handle(this, sel, (void*)param.text());
1494 
1495         FXFREE(&data);
1496         return(1);
1497     }
1498 
1499     // If source type is utf8Type (simple text)
1500     if (getDNDData(FROM_CLIPBOARD, utf8Type, data, len))
1501     {
1502         FXRESIZE(&data, FXuchar, len+1);
1503         data[len] = '\0';
1504         clipboard = (char*)data;
1505 
1506         // Loop over clipboard items
1507         FXString filepath;
1508         for (beg = 0; beg < clipboard.length(); beg = end+1)
1509         {
1510             if ((end = clipboard.find("\n", beg)) < 0)
1511             {
1512                 end = clipboard.length();
1513             }
1514 
1515             // Obtain item file path
1516             filepath = clipboard.mid(beg, end-beg);
1517 
1518             // Eventually remove the trailing '\r' if any
1519             if ((pos = filepath.rfind('\r')) > 0)
1520             {
1521                 filepath.erase(pos);
1522             }
1523 
1524             // Update the param string
1525             param += filepath + "\n";
1526             num++;
1527         }
1528 
1529         // Construct the final param string passed to the file management routine
1530         param = targetdir + "\n" + FXStringVal(num) + "\n" + param;
1531 
1532         // Copy
1533         sel = FXSEL(SEL_COMMAND, DirPanel::ID_DIR_COPY);
1534         fromPaste = true;
1535         handle(this, sel, (void*)param.text());
1536 
1537         FXFREE(&data);
1538         return(1);
1539     }
1540 
1541     return(0);
1542 }
1543 
1544 
1545 // Set the flag that allows to stop the file list refresh
onCmdStopListRefreshTimer(FXObject *,FXSelector,void *)1546 long DirPanel::onCmdStopListRefreshTimer(FXObject*, FXSelector, void*)
1547 {
1548     stopListRefresh = true;
1549     return(0);
1550 }
1551 
1552 
1553 // Copy/Move/Rename/Symlink directory
onCmdDirMan(FXObject * sender,FXSelector sel,void * ptr)1554 long DirPanel::onCmdDirMan(FXObject* sender, FXSelector sel, void* ptr)
1555 {
1556     int      num;
1557     FXString src, targetdir, target, name, source;
1558 
1559     // Confirmation dialog?
1560     FXbool ask_before_copy = getApp()->reg().readUnsignedEntry("OPTIONS", "ask_before_copy", true);
1561 
1562     // If we are called from the paste command, get the parameters from the pointer
1563     // Multiple sources are allowed
1564     if (fromPaste)
1565     {
1566         // Reset the flag
1567         fromPaste = false;
1568 
1569         // Get the parameters
1570         FXString str = (char*)ptr;
1571         targetdir = str.section('\n', 0);
1572         num = FXUIntVal(str.section('\n', 1));
1573         src = str.after('\n', 2);
1574         source = src.section('\n', 0);
1575 
1576         // If no item, return
1577         if (num <= 0)
1578         {
1579             return(0);
1580         }
1581     }
1582 
1583     // Obtain the parameters from the dir panel (only one source)
1584     else
1585     {
1586         // Current item
1587         DirItem* item = (DirItem*)list->getCurrentItem();
1588 
1589         // Number of items
1590         if (item)
1591         {
1592             num = 1;
1593         }
1594         else
1595         {
1596             return(0);
1597         }
1598 
1599         // Source directory
1600         source = list->getItemPathname((TreeItem*)item);
1601 
1602         // Target directory
1603         targetdir = FXPath::directory(source);
1604     }
1605 
1606     // Go to target directory
1607     //chdir(targetdir.text());
1608 
1609     // Name and directory of the first source file
1610     name = FXPath::name(source);
1611     FXString dir = FXPath::directory(source);
1612 
1613     // Initialise target name
1614     if (targetdir != ROOTDIR)
1615     {
1616         target = targetdir+PATHSEPSTRING;
1617     }
1618     else
1619     {
1620         target = targetdir;
1621     }
1622 
1623     // Configure the command, title, message, etc.
1624     FXIcon*  icon = NULL;
1625     FXString command, title, message;
1626     if (FXSELID(sel) == ID_DIR_COPY)
1627     {
1628         command = "copy";
1629         title = _("Copy");
1630         icon = copy_bigicon;
1631         if (num == 1)
1632         {
1633             message = _("Copy ");
1634             message += source;
1635             if (::isFile(source))
1636             {
1637                 target += name;
1638             }
1639 
1640             // Source and target are identical => add a suffix to the name
1641             FXString tgt = ::cleanPath(target); // Remove trailing / if any
1642             if ((::identical(source, tgt)) ||
1643                 (::isDirectory(source) && (source == tgt+PATHSEPSTRING+FXPath::name(source))))
1644             {
1645                 target = ::buildCopyName(source, ::isDirectory(source));
1646             }
1647         }
1648         else
1649         {
1650             message.format(_("Copy %s items from: %s"), FXStringVal(num).text(), dir.text());
1651         }
1652     }
1653     if (FXSELID(sel) == ID_DIR_RENAME)
1654     {
1655         command = "rename";
1656         title = _("Rename");
1657         icon = move_bigicon;
1658         if (num == 1)
1659         {
1660             message = _("Rename ");
1661             message += name;
1662             target = name;
1663             title = _("Rename");
1664         }
1665         else
1666         {
1667             return(0);
1668         }
1669     }
1670     if (FXSELID(sel) == ID_DIR_COPYTO)
1671     {
1672         command = "copy";
1673         title = _("Copy to");
1674         icon = copy_bigicon;
1675         if (num == 1)
1676         {
1677             message = _("Copy ");
1678             message += source;
1679         }
1680         else
1681         {
1682             message.format(_("Copy %s items from: %s"), FXStringVal(num).text(), dir.text());
1683         }
1684     }
1685     if (FXSELID(sel) == ID_DIR_MOVETO)
1686     {
1687         command = "move";
1688         title = _("Move");
1689         icon = move_bigicon;
1690         if (num == 1)
1691         {
1692             message = _("Move ");
1693             message += source;
1694             title = _("Move");
1695         }
1696         else
1697         {
1698             message.format(_("Move %s items from: %s"), FXStringVal(num).text(), dir.text());
1699         }
1700     }
1701     if (FXSELID(sel) == ID_DIR_CUT)
1702     {
1703         command = "move";
1704         title = _("Move");
1705         icon = move_bigicon;
1706         if (num == 1)
1707         {
1708             message = _("Move ");
1709             message += source;
1710             if (::isFile(source))
1711             {
1712                 target += name;
1713             }
1714             title = _("Move");
1715         }
1716         else
1717         {
1718             message.format(_("Move %s items from: %s"), FXStringVal(num).text(), dir.text());
1719         }
1720     }
1721     if (FXSELID(sel) == ID_DIR_SYMLINK)
1722     {
1723         command = "symlink";
1724         title = _("Symlink");
1725         icon = link_bigicon;
1726         if (num == 1)
1727         {
1728             message = _("Symlink ");
1729             message += source;
1730             target += name;
1731         }
1732         else
1733         {
1734             message.format(_("Symlink %s items from: %s"), FXStringVal(num).text(), dir.text());
1735         }
1736     }
1737 
1738     // File operation dialog, if needed
1739     if (ask_before_copy || (source == target) || (FXSELID(sel) == ID_DIR_COPYTO) || (FXSELID(sel) == ID_DIR_MOVETO) || (FXSELID(sel) == ID_DIR_RENAME) || (FXSELID(sel) == ID_DIR_SYMLINK))
1740     {
1741 		if (FXSELID(sel) == ID_DIR_RENAME)
1742 		{
1743 			if (operationdialogrename == NULL)
1744 			{
1745 				operationdialogrename = new InputDialog(this, "", "", title, _("To:"), icon);
1746 			}
1747 			operationdialogrename->setTitle(title);
1748 			operationdialogrename->setIcon(icon);
1749 			operationdialogrename->setMessage(message);
1750 			operationdialogrename->setText(target);
1751             operationdialogrename->selectAll();
1752 			operationdialogrename->CursorEnd();
1753 			int rc = 1;
1754 			rc = operationdialogrename->execute(PLACEMENT_CURSOR);
1755 			target = operationdialogrename->getText();
1756 
1757 			// Target name contains '/'
1758 			if (target.contains(PATHSEPCHAR))
1759 			{
1760 				MessageBox::error(this, BOX_OK, _("Error"), _("The / character is not allowed in folder names, operation cancelled"));
1761 				return(0);
1762 			}
1763 
1764 			if (!rc)
1765 			{
1766 				return(0);
1767 			}
1768 		}
1769 		else
1770 		{
1771 			if (operationdialog == NULL)
1772 			{
1773 				operationdialog = new BrowseInputDialog(this, "", "", title, _("To:"), icon, BROWSE_INPUT_FOLDER);
1774 			}
1775 			operationdialog->setTitle(title);
1776 			operationdialog->setIcon(icon);
1777 			operationdialog->setMessage(message);
1778 			operationdialog->setText(target);
1779 			operationdialog->CursorEnd();
1780 			operationdialog->setDirectory(targetdir);
1781 			int rc = 1;
1782 			rc = operationdialog->execute(PLACEMENT_CURSOR);
1783 			target = operationdialog->getText();
1784 			if (!rc)
1785 			{
1786 				return(0);
1787 			}
1788 		}
1789     }
1790 
1791     // Update target and target parent directory
1792     target = ::filePath(target, targetdir);
1793 	if (::isDirectory(target))
1794 	{
1795 		targetdir = target;
1796 	}
1797 	else
1798 	{
1799 		targetdir = FXPath::directory(target);
1800 	}
1801 
1802     // Target directory not writable
1803     if (!::isWritable(targetdir))
1804     {
1805         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
1806         return(0);
1807     }
1808 
1809     // Multiple sources and non existent destination
1810     if ((num > 1) && !existFile(target))
1811     {
1812         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), target.text());
1813         return(0);
1814     }
1815 
1816     // Multiple sources and target is a file
1817     if ((num > 1) && ::isFile(target))
1818     {
1819         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), target.text());
1820         return(0);
1821     }
1822 
1823     // Target is a directory and is not writable
1824     if (::isDirectory(target) & !::isWritable(target))
1825     {
1826         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), target.text());
1827         return(0);
1828     }
1829 
1830     // Target is a file and its parent directory is not writable
1831     if (::isFile(target) && !::isWritable(targetdir))
1832     {
1833         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), targetdir.text());
1834         return(0);
1835     }
1836 
1837     // Target parent directory doesn't exist
1838     if (!existFile(targetdir))
1839     {
1840         MessageBox::error(this, BOX_OK, _("Error"), _("Folder %s doesn't exist"), targetdir.text());
1841         return(0);
1842     }
1843 
1844     // Target parent directory is not a directory
1845     if (!::isDirectory(targetdir))
1846     {
1847         MessageBox::error(this, BOX_OK, _("Error"), _("%s is not a folder"), targetdir.text());
1848         return(0);
1849     }
1850 
1851     // Current and next panel
1852     FilePanel* currentpanel = ((XFileExplorer*)mainWindow)->getCurrentPanel();
1853     FilePanel* nextpanel = ((XFileExplorer*)mainWindow)->getNextPanel();
1854 
1855     // One source
1856     File* f;
1857     int   ret;
1858     f = NULL;
1859     if (num == 1)
1860     {
1861         // An empty source file name corresponds to the ".." file
1862         // Don't perform any file operation on it!
1863         if (source == "")
1864         {
1865             return(0);
1866         }
1867 
1868         // Wait cursor
1869         getApp()->beginWaitCursor();
1870 
1871         // File object
1872         if (command == "copy")
1873         {
1874             f = new File(this, _("File copy"), COPY);
1875             f->create();
1876 
1877             // If target file is located at trash location, also create the corresponding trashinfo file
1878             // Do it silently and don't report any error if it fails
1879             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
1880             if (use_trash_can && (target == trashfileslocation))
1881             {
1882                 // Trash files path name
1883                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
1884 
1885                 // Adjust target name to get the _N suffix if any
1886                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
1887 
1888                 // Create trashinfo file
1889                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
1890 
1891                 // Copy source to trash target
1892                 ret = f->copy(source, trashtarget);
1893             }
1894 
1895             // Copy source to target
1896             else
1897             {
1898                 ret = f->copy(source, target);
1899             }
1900 
1901             // An error has occurred
1902             if ((ret == 0) && !f->isCancelled())
1903             {
1904                 f->hideProgressDialog();
1905                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
1906             }
1907 
1908             // If action is cancelled in progress dialog
1909             if (f->isCancelled())
1910             {
1911                 f->hideProgressDialog();
1912                 MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
1913             }
1914         }
1915         else if (command == "rename")
1916         {
1917             f = new File(this, _("File rename"), RENAME);
1918             f->create();
1919             ret = f->rename(source, target);
1920 
1921             // If file is located at trash location, try to also remove the corresponding trashinfo if it exists
1922             // Do it silently and don't report any error if it fails
1923             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
1924             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
1925             {
1926                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
1927                 ::unlink(trashinfopathname.text());
1928             }
1929         }
1930         else if (command == "move")
1931         {
1932             f = new File(this, _("File move"), MOVE);
1933             f->create();
1934 
1935             // If target file is located at trash location, also create the corresponding trashinfo file
1936             // Do it silently and don't report any error if it fails
1937             FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
1938             if (use_trash_can && (target == trashfileslocation))
1939             {
1940                 // Trash files path name
1941                 FXString trashpathname = createTrashpathname(source, trashfileslocation);
1942 
1943                 // Adjust target name to get the _N suffix if any
1944                 FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
1945 
1946                 // Create trashinfo file
1947                 createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
1948 
1949                 // Move source to trash target
1950                 ret = f->move(source, trashtarget);
1951             }
1952 
1953             // Move source to target
1954             else
1955             {
1956                 ret = f->move(source, target);
1957             }
1958 
1959             // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
1960             // Do it silently and don't report any error if it fails
1961             if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
1962             {
1963                 FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
1964                 ::unlink(trashinfopathname.text());
1965             }
1966 
1967             // An error has occurred
1968             if ((ret == 0) && !f->isCancelled())
1969             {
1970                 f->hideProgressDialog();
1971                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
1972             }
1973 
1974             // If action is cancelled in progress dialog
1975             if (f->isCancelled())
1976             {
1977                 f->hideProgressDialog();
1978                 MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
1979             }
1980         }
1981         else if (command == "symlink")
1982         {
1983             f = new File(this, _("Symlink"), SYMLINK);
1984             f->create();
1985             f->symlink(source, target);
1986         }
1987 
1988         // Shouldn't happen
1989         else
1990         {
1991             exit(EXIT_FAILURE);
1992         }
1993 
1994         getApp()->endWaitCursor();
1995         delete f;
1996     }
1997 
1998     // Multiple sources
1999     // Note : rename cannot be used in this case!
2000     else if (num > 1)
2001     {
2002         // Wait cursor
2003         getApp()->beginWaitCursor();
2004 
2005         // File object
2006         if (command == "copy")
2007         {
2008             f = new File(this, _("File copy"), COPY);
2009         }
2010         else if (command == "move")
2011         {
2012             f = new File(this, _("File move"), MOVE);
2013         }
2014         else if (command == "symlink")
2015         {
2016             f = new File(this, _("Symlink"), SYMLINK);
2017         }
2018 
2019         if (f == NULL)
2020         {
2021             fprintf(stderr, "%s::onCmdDirMan: NULL pointer specified.\n", getClassName());
2022             exit(EXIT_FAILURE);
2023         }
2024         else
2025         {
2026             f->create();
2027         }
2028 
2029         // Initialize file list stop refresh timer and flag
2030         stopListRefresh = false;
2031         getApp()->addTimeout(this, ID_STOP_LIST_REFRESH_TIMER, STOP_LIST_REFRESH_INTERVAL);
2032 
2033         // Loop on the multiple files
2034         for (int i = 0; i < num; i++)
2035         {
2036             // Stop refreshing the file list and directory size if file operation is long and has many files
2037             // This avoids flickering and speeds up things a bit
2038             if (stopListRefresh && (i > STOP_LIST_REFRESH_NBMAX))
2039             {
2040                 // Force a last refresh if current panel is destination
2041                 if (currentpanel->getDirectory() == targetdir)
2042                 {
2043                     currentpanel->getList()->onCmdRefresh(0, 0, 0);
2044                 }
2045 
2046                 // Force a last refresh if next panel is destination
2047                 if (nextpanel->getDirectory() == targetdir)
2048                 {
2049                     nextpanel->getList()->onCmdRefresh(0, 0, 0);
2050                 }
2051 
2052                 // Tell the file list to not refresh anymore
2053                 currentpanel->setAllowRefresh(false);
2054                 nextpanel->setAllowRefresh(false);
2055 
2056                 // Avoid to refresh the dirsize
2057                 setAllowDirsizeRefresh(false);
2058 
2059                 // Don't need to stop again
2060                 stopListRefresh = false;
2061             }
2062 
2063             // Individual source file
2064             source = src.section('\n', i);
2065 
2066             // An empty file name corresponds to the ".." file (why?)
2067             // Don't perform any file operation on it!
2068             if (source != "")
2069             {
2070                 if (command == "copy")
2071                 {
2072                     // If target file is located at trash location, also create the corresponding trashinfo file
2073                     // Do it silently and don't report any error if it fails
2074                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2075                     if (use_trash_can && (target == trashfileslocation))
2076                     {
2077                         // Trash files path name
2078                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
2079 
2080                         // Adjust target name to get the _N suffix if any
2081                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
2082 
2083                         // Create trashinfo file
2084                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
2085 
2086                         // Copy source to trash target
2087                         ret = f->copy(source, trashtarget);
2088                     }
2089 
2090                     // Copy source to target
2091                     else
2092                     {
2093                         ret = f->copy(source, target);
2094                     }
2095 
2096                     // An error has occurred
2097                     if ((ret == 0) && !f->isCancelled())
2098                     {
2099                         f->hideProgressDialog();
2100                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the copy file operation!"));
2101                         break;
2102                     }
2103 
2104                     // If action is cancelled in progress dialog
2105                     if (f->isCancelled())
2106                     {
2107                         f->hideProgressDialog();
2108                         MessageBox::error(this, BOX_OK, _("Warning"), _("Copy file operation cancelled!"));
2109                         break;
2110                     }
2111                 }
2112                 else if (command == "move")
2113                 {
2114                     // If target file is located at trash location, also create the corresponding trashinfo file
2115                     // Do it silently and don't report any error if it fails
2116                     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2117                     if (use_trash_can && (target == trashfileslocation))
2118                     {
2119                         // Trash files path name
2120                         FXString trashpathname = createTrashpathname(source, trashfileslocation);
2121 
2122                         // Adjust target name to get the _N suffix if any
2123                         FXString trashtarget = target+PATHSEPSTRING+FXPath::name(trashpathname);
2124 
2125                         // Create trashinfo file
2126                         createTrashinfo(source, trashpathname, trashfileslocation, trashinfolocation);
2127 
2128                         // Move source to trash target
2129                         ret = f->move(source, trashtarget);
2130                     }
2131 
2132                     // Move source to target
2133                     else
2134                     {
2135                         ret = f->move(source, target);
2136                     }
2137 
2138                     // If source file is located at trash location, try to also remove the corresponding trashinfo file if it exists
2139                     // Do it silently and don't report any error if it fails
2140                     if (use_trash_can && ret && (source.left(trashfileslocation.length()) == trashfileslocation))
2141                     {
2142                         FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(source)+".trashinfo";
2143                         ::unlink(trashinfopathname.text());
2144                     }
2145 
2146                     // An error has occurred
2147                     if ((ret == 0) && !f->isCancelled())
2148                     {
2149                         f->hideProgressDialog();
2150                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move file operation!"));
2151                         break;
2152                     }
2153 
2154                     // If action is cancelled in progress dialog
2155                     if (f->isCancelled())
2156                     {
2157                         f->hideProgressDialog();
2158                         MessageBox::error(this, BOX_OK, _("Warning"), _("Move file operation cancelled!"));
2159                         break;
2160                     }
2161                 }
2162                 else if (command == "symlink")
2163                 {
2164                     ret = f->symlink(source, target);
2165 
2166                     // An error has occurred
2167                     if ((ret == 0) && !f->isCancelled())
2168                     {
2169                         f->hideProgressDialog();
2170                         MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the symlink operation!"));
2171                         break;
2172                     }
2173 
2174                     // If action is cancelled in progress dialog
2175                     if (f->isCancelled())
2176                     {
2177                         f->hideProgressDialog();
2178                         MessageBox::error(this, BOX_OK, _("Warning"), _("Symlink operation cancelled!"));
2179                         break;
2180                     }
2181                 }
2182                 // Shouldn't happen
2183                 else
2184                 {
2185                     exit(EXIT_FAILURE);
2186                 }
2187             }
2188         }
2189 
2190         // Reinit timer and refresh flags
2191         getApp()->removeTimeout(this, ID_STOP_LIST_REFRESH_TIMER);
2192         currentpanel->setAllowRefresh(true);
2193         nextpanel->setAllowRefresh(true);
2194         setAllowDirsizeRefresh(true);
2195 
2196         getApp()->endWaitCursor();
2197         delete f;
2198     }
2199 
2200     // Refresh the path link and the directory list
2201     currentpanel->updatePath();
2202     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2203     return(1);
2204 }
2205 
2206 
2207 // Delete directory
onCmdDirDelete(FXObject *,FXSelector,void *)2208 long DirPanel::onCmdDirDelete(FXObject*, FXSelector, void*)
2209 {
2210     // Current item
2211     DirItem* item = (DirItem*)list->getCurrentItem();
2212     FXString pathname = list->getItemPathname((TreeItem*)item);
2213     FXString parentdir = FXPath::directory(pathname);
2214 
2215     // If we don't have permission to write to the parent directory
2216     if (!::isWritable(parentdir))
2217     {
2218         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), parentdir.text());
2219         return(0);
2220     }
2221 
2222     FXbool confirm_del = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete", true);
2223     FXbool confirm_del_emptydir = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_delete_emptydir", true);
2224 
2225     if (confirm_del)
2226     {
2227         FXString message;
2228         message.format(_("Definitively delete folder %s ?"), pathname.text());
2229         MessageBox box(this, _("Confirm Delete"), message, delete_big_permicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2230         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2231         {
2232             return(0);
2233         }
2234     }
2235 
2236     // File object
2237     File* f = new File(this, _("File delete"), DELETE);
2238     f->create();
2239 
2240     // Confirm empty directory deletion
2241     if (confirm_del & confirm_del_emptydir)
2242     {
2243         if ((::isEmptyDir(pathname) == 0) && !::isLink(pathname))
2244         {
2245             // Dialog to confirm file deletion
2246             f->hideProgressDialog();
2247             FXString msg;
2248             msg.format(_("Folder %s is not empty, delete it anyway?"), pathname.text());
2249             MessageBox box(this, _("Confirm Delete"), msg, delete_big_permicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2250             if (box.execute(PLACEMENT_CURSOR) == BOX_CLICKED_CANCEL)
2251             {
2252                 goto end;
2253             }
2254             f->showProgressDialog();
2255         }
2256     }
2257 
2258     // If we don't have permission to write to the directory
2259     if (!::isWritable(pathname))
2260     {
2261         // Dialog to confirm directory deletion
2262         f->hideProgressDialog();
2263         FXString msg;
2264         msg.format(_("Folder %s is write-protected, definitively delete it anyway?"), pathname.text());
2265         MessageBox box(this, _("Confirm Delete"), msg, errorbigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2266         FXuint     answer = box.execute(PLACEMENT_OWNER);
2267         if (answer == BOX_CLICKED_OK)
2268         {
2269             f->showProgressDialog();
2270             f->remove(pathname);
2271 
2272             // If action is cancelled in progress dialog
2273             if (f->isCancelled())
2274             {
2275                 f->hideProgressDialog();
2276                 MessageBox::error(this, BOX_OK, _("Warning"), _("Delete folder operation cancelled!"));
2277             }
2278 
2279             // Return to parent directory in DirList and FileList
2280             list->setDirectory(parentdir, true);
2281             ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
2282             ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
2283         }
2284     }
2285 
2286     // If we have permission to write
2287     else
2288     {
2289         f->remove(pathname);
2290 
2291         // If directory is located at trash location, try to also remove the trashinfo if it exists
2292         // Do it silently and don't report any error if it fails
2293         FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2294         if (use_trash_can && (pathname.left(trashfileslocation.length()) == trashfileslocation))
2295         {
2296             FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+FXPath::name(pathname)+".trashinfo";
2297             ::unlink(trashinfopathname.text());
2298         }
2299 
2300         // If action is cancelled in progress dialog
2301         if (f->isCancelled())
2302         {
2303             f->hideProgressDialog();
2304             MessageBox::error(this, BOX_OK, _("Warning"), _("Delete folder operation cancelled!"));
2305         }
2306         // Return to parent directory in DirList and FileList
2307         list->setDirectory(parentdir, true);
2308         ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
2309         ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
2310     }
2311 
2312 end:
2313     delete f;
2314 
2315     // Force DirPanel and FilePanel refresh
2316     setAllowDirsizeRefresh(true);
2317     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2318     return(1);
2319 }
2320 
2321 
2322 // Move directory to trash can
onCmdDirTrash(FXObject *,FXSelector,void *)2323 long DirPanel::onCmdDirTrash(FXObject*, FXSelector, void*)
2324 {
2325     // Current item
2326     DirItem* item = (DirItem*)list->getCurrentItem();
2327     FXString pathname = list->getItemPathname((TreeItem*)item);
2328     FXString parentdir = FXPath::directory(pathname);
2329 
2330     // If we don't have permission to write to the parent directory
2331     if (!::isWritable(parentdir))
2332     {
2333         MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't write to %s: Permission denied"), parentdir.text());
2334         return(0);
2335     }
2336 
2337     FXbool confirm_trash = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_trash", true);
2338 
2339     if (confirm_trash)
2340     {
2341         FXString message;
2342         message.format(_("Move folder %s to trash can?"), pathname.text());
2343         MessageBox box(this, _("Confirm Trash"), message, delete_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2344         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2345         {
2346             return(0);
2347         }
2348     }
2349 
2350     // File object
2351     File* f = new File(this, _("Move to trash"), DELETE);
2352     f->create();
2353 
2354     // If we don't have permission to write to the directory
2355     if (!::isWritable(pathname))
2356     {
2357         // Dialog to confirm directory deletion
2358         f->hideProgressDialog();
2359         FXString str;
2360         str.format(_("Folder %s is write-protected, move it to trash can anyway?"), pathname.text());
2361         MessageBox box(this, _("Confirm Trash"), str, errorbigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2362         FXuint     answer = box.execute(PLACEMENT_OWNER);
2363         if (answer == BOX_CLICKED_OK)
2364         {
2365             // Allow progress dialog
2366             f->showProgressDialog();
2367 
2368             // Trash files path name
2369             FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
2370 
2371             // Create trashinfo file
2372             createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
2373 
2374             // Move file to trash files location
2375             int ret = f->move(pathname, trashpathname);
2376 
2377             // An error has occurred
2378             if ((ret == 0) && !f->isCancelled())
2379             {
2380                 f->hideProgressDialog();
2381                 MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
2382             }
2383 
2384             // If action is cancelled in progress dialog
2385             if (f->isCancelled())
2386             {
2387                 f->hideProgressDialog();
2388                 MessageBox::error(this, BOX_OK, _("Warning"), _("Move to trash folder operation cancelled!"));
2389             }
2390 
2391             // Return to parent directory in DirList and FileList
2392             list->setDirectory(parentdir, true);
2393             ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
2394             ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
2395         }
2396     }
2397 
2398     // If we have permission to write
2399     else
2400     {
2401         // Trash files path name
2402         FXString trashpathname = createTrashpathname(pathname, trashfileslocation);
2403 
2404         // Create trashinfo file
2405         createTrashinfo(pathname, trashpathname, trashfileslocation, trashinfolocation);
2406 
2407         // Move file to trash files location
2408         int ret = f->move(pathname, trashpathname);
2409 
2410         // An error has occurred
2411         if ((ret == 0) && !f->isCancelled())
2412         {
2413             f->hideProgressDialog();
2414             MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the move to trash operation!"));
2415         }
2416 
2417         // If action is cancelled in progress dialog
2418         if (f->isCancelled())
2419         {
2420             f->hideProgressDialog();
2421             MessageBox::error(this, BOX_OK, _("Warning"), _("Move to trash folder operation cancelled!"));
2422         }
2423         // Return to parent directory in DirList and FileList
2424         list->setDirectory(parentdir, true);
2425         ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
2426         ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
2427     }
2428     delete f;
2429 
2430     // Force DirPanel and FilePanel refresh
2431     setAllowDirsizeRefresh(true);
2432     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2433     return(1);
2434 }
2435 
2436 
2437 // Restore directory from trash can
onCmdDirRestore(FXObject *,FXSelector,void *)2438 long DirPanel::onCmdDirRestore(FXObject*, FXSelector, void*)
2439 {
2440     // Current item
2441     DirItem* item = (DirItem*)list->getCurrentItem();
2442     FXString pathname = list->getItemPathname((TreeItem*)item);
2443     FXString parentdir = FXPath::directory(pathname);
2444     FXbool   confirm_trash = getApp()->reg().readUnsignedEntry("OPTIONS", "confirm_trash", true);
2445 
2446     // File object
2447     File* f = new File(this, _("Restore from trash"), DELETE);
2448 
2449     f->create();
2450 
2451     // Obtain trash base name and sub path
2452     FXString subpath = pathname;
2453     subpath.erase(0, trashfileslocation.length()+1);
2454     FXString trashbasename = subpath.before('/');
2455     if (trashbasename == "")
2456     {
2457         trashbasename = FXPath::name(pathname);
2458     }
2459     subpath.erase(0, trashbasename.length());
2460 
2461     // Read the .trashinfo file
2462     FILE*    fp;
2463     char     line[1024];
2464     FXbool   success = true;
2465     FXString trashinfopathname = trashinfolocation+PATHSEPSTRING+trashbasename+".trashinfo";
2466     FXString origpathname = "";
2467 
2468     if ((fp = fopen(trashinfopathname.text(), "r")) != NULL)
2469     {
2470         // Read the first two lines and get the strings
2471         if (fgets(line, sizeof(line), fp) == NULL)
2472         {
2473             success = false;
2474         }
2475         if (fgets(line, sizeof(line), fp) == NULL)
2476         {
2477             success = false;
2478         }
2479         if (success)
2480         {
2481             origpathname = line;
2482             origpathname = origpathname.after('=');
2483             origpathname = origpathname.before('\n');
2484         }
2485         fclose(fp);
2486         origpathname = origpathname+subpath;
2487     }
2488 
2489     // Confirm restore dialog
2490     if (confirm_trash)
2491     {
2492         FXString message;
2493         message.format(_("Restore folder %s to its original location %s ?"), FXPath::name(pathname).text(), origpathname.text());
2494         f->hideProgressDialog();
2495         MessageBox box(this, _("Confirm Restore"), message, restore_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2496         if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2497         {
2498             getApp()->endWaitCursor();
2499             delete f;
2500             return(0);
2501         }
2502         f->showProgressDialog();
2503     }
2504 
2505     // Bracket used because of a compilation problem with gotos
2506     {
2507         if (origpathname == "")
2508         {
2509             f->hideProgressDialog();
2510             MessageBox::error(this, BOX_OK, _("Error"), _("Restore information not available for %s"), pathname.text());
2511             goto end;
2512         }
2513 
2514         // If parent dir of the original location does not exist
2515         FXString origparentdir = FXPath::directory(origpathname);
2516         if (!existFile(origparentdir))
2517         {
2518             // Ask the user if he wants to create it
2519             f->hideProgressDialog();
2520             FXString message;
2521             message.format(_("Parent folder %s does not exist, do you want to create it?"), origparentdir.text());
2522             MessageBox box(this, _("Confirm Restore"), message, restore_bigicon, BOX_OK_CANCEL|DECOR_TITLE|DECOR_BORDER);
2523             if (box.execute(PLACEMENT_CURSOR) != BOX_CLICKED_OK)
2524             {
2525                 goto end;
2526             }
2527             else
2528             {
2529                 f->showProgressDialog();
2530                 errno = 0;
2531                 int ret = mkpath(origparentdir.text(), 0755);
2532                 int errcode = errno;
2533                 if (ret == -1)
2534                 {
2535                     f->hideProgressDialog();
2536                     if (errcode)
2537                     {
2538                         MessageBox::error(this, BOX_OK, _("Error"), _("Can't create folder %s : %s"), origparentdir.text(), strerror(errcode));
2539                     }
2540                     else
2541                     {
2542                         MessageBox::error(this, BOX_OK, _("Error"), _("Can't create folder %s"), origparentdir.text());
2543                     }
2544                     goto end;
2545                 }
2546             }
2547         }
2548 
2549         // Move file to original location (with restore option)
2550         int ret = f->move(pathname, origpathname, true);
2551 
2552         // An error has occurred
2553         if ((ret == 0) && !f->isCancelled())
2554         {
2555             f->hideProgressDialog();
2556             MessageBox::error(this, BOX_OK, _("Error"), _("An error has occurred during the restore from trash operation!"));
2557             goto end;
2558         }
2559 
2560         // Silently remove trashinfo file
2561         FXString trashfilespathname = trashfileslocation+PATHSEPSTRING+trashbasename;
2562         if ((pathname == trashfilespathname) && !existFile(trashfilespathname))
2563         {
2564             ::unlink(trashinfopathname.text());
2565         }
2566 
2567         // If action is cancelled in progress dialog
2568         if (f->isCancelled())
2569         {
2570             f->hideProgressDialog();
2571             MessageBox::error(this, BOX_OK, _("Warning"), _("Restore from trash file operation cancelled!"));
2572             goto end;
2573         }
2574 
2575         // Return to parent directory in DirList and FileList
2576         list->setDirectory(parentdir, true);
2577         ((XFileExplorer*)mainWindow)->getCurrentPanel()->setDirectory(parentdir);
2578         ((XFileExplorer*)mainWindow)->getCurrentPanel()->updatePath();
2579     }
2580 
2581 end:
2582     delete f;
2583 
2584     // Force DirPanel and FilePanel refresh
2585     setAllowDirsizeRefresh(true);
2586     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2587     return(1);
2588 }
2589 
2590 
2591 // Create new directory
onCmdNewDir(FXObject *,FXSelector,void *)2592 long DirPanel::onCmdNewDir(FXObject*, FXSelector, void*)
2593 {
2594     // Current item
2595     DirItem* item = (DirItem*)list->getCurrentItem();
2596 
2597     FXString dirpath = list->getItemPathname((TreeItem*)item);
2598 
2599     if (dirpath != ROOTDIR)
2600     {
2601         dirpath += PATHSEPSTRING;
2602     }
2603 
2604     if (newdirdialog == NULL)
2605     {
2606         newdirdialog = new InputDialog(this, "", _("Create new folder:"), _("New Folder"), "", bignewfoldericon);
2607     }
2608     newdirdialog->setText("");
2609     if (newdirdialog->execute(PLACEMENT_CURSOR))
2610     {
2611         if (newdirdialog->getText() == "")
2612         {
2613             MessageBox::warning(this, BOX_OK, _("Warning"), _("Folder name is empty, operation cancelled"));
2614             return(0);
2615         }
2616 
2617 		// Directory name contains '/'
2618 		if (newdirdialog->getText().contains(PATHSEPCHAR))
2619 		{
2620 			MessageBox::warning(this, BOX_OK, _("Warning"), _("The / character is not allowed in folder names, operation cancelled"));
2621 			return(0);
2622 		}
2623 
2624         FXString dirname = dirpath+newdirdialog->getText();
2625         if (dirname != dirpath)
2626         {
2627             // Create the new dir according to the current umask
2628             int mask;
2629             mask = umask(0);
2630             umask(mask);
2631 
2632             // Note that the umask value is in decimal (511 means octal 0777)
2633             errno = 0;
2634             int ret = ::mkdir(dirname.text(), 511 & ~mask);
2635             int errcode = errno;
2636             if (ret == -1)
2637             {
2638                 if (errcode)
2639                 {
2640                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s : %s"), dirname.text(), strerror(errcode));
2641                 }
2642                 else
2643                 {
2644                     MessageBox::error(this, BOX_OK_SU, _("Error"), _("Can't create folder %s"), dirname.text());
2645                 }
2646                 return(0);
2647             }
2648         }
2649     }
2650 
2651     // Force dirpanel refresh
2652     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2653     return(1);
2654 }
2655 
2656 
2657 // Run Terminal in the selected directory
onCmdXTerm(FXObject *,FXSelector,void *)2658 long DirPanel::onCmdXTerm(FXObject*, FXSelector, void*)
2659 {
2660     int ret;
2661 
2662     getApp()->beginWaitCursor();
2663     DirItem* item = (DirItem*)list->getCurrentItem();
2664     FXString buf = list->getItemPathname((TreeItem*)item);
2665     ret = chdir(buf.text());
2666     if (ret < 0)
2667     {
2668         int errcode = errno;
2669         if (errcode)
2670         {
2671             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), buf.text(), strerror(errcode));
2672         }
2673         else
2674         {
2675             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), buf.text());
2676         }
2677 
2678         return(0);
2679     }
2680 
2681     FXString cmd = getApp()->reg().readStringEntry("PROGS", "xterm", "xterm -sb");
2682     cmd += " &";
2683 
2684     ret = system(cmd.text());
2685     if (ret < 0)
2686     {
2687         MessageBox::error(this, BOX_OK, _("Error"), _("Can't execute command %s"), cmd.text());
2688         return(0);
2689     }
2690 
2691     ret = chdir(startlocation.text());
2692     if (ret < 0)
2693     {
2694         int errcode = errno;
2695         if (errcode)
2696         {
2697             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
2698         }
2699         else
2700         {
2701             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
2702         }
2703 
2704         return(0);
2705     }
2706 
2707     getApp()->endWaitCursor();
2708     return(1);
2709 }
2710 
2711 
2712 #if defined(linux)
2713 // Mount/Unmount directory
onCmdMount(FXObject *,FXSelector sel,void *)2714 long DirPanel::onCmdMount(FXObject*, FXSelector sel, void*)
2715 {
2716     int      ret;
2717     FXString cmd, msg, text;
2718     FXuint   op;
2719     File*    f;
2720 
2721     // Current item
2722     DirItem* item = (DirItem*)list->getCurrentItem();
2723     FXString dir = list->getItemPathname((TreeItem*)item);
2724 
2725     // If symbolic link
2726     if (::isLink(dir))
2727     {
2728         dir = FXFile::symlink(dir);
2729     }
2730 
2731     // Select the command and set the appropriate message
2732     if (FXSELID(sel) == ID_MOUNT)
2733     {
2734         op = MOUNT;
2735         msg = _("Mount");
2736 	    cmd = getApp()->reg().readStringEntry("PROGS", "mount", DEFAULT_MOUNTCMD) + FXString(" ");
2737     }
2738     else
2739     {
2740         op = UNMOUNT;
2741         msg = _("Unmount");
2742 		cmd = getApp()->reg().readStringEntry("PROGS", "unmount", DEFAULT_UMOUNTCMD) + FXString(" ");
2743     }
2744     cmd += ::quote(dir);
2745    	cmd += " 2>&1";
2746 
2747     ret = chdir(ROOTDIR);
2748     if (ret < 0)
2749     {
2750         int errcode = errno;
2751         if (errcode)
2752         {
2753             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), ROOTDIR, strerror(errcode));
2754         }
2755         else
2756         {
2757             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), ROOTDIR);
2758         }
2759 
2760         return(0);
2761     }
2762 
2763     // Wait cursor
2764     getApp()->beginWaitCursor();
2765 
2766     // File object
2767     text = msg + _(" file system...");
2768     f = new File(this, text.text(), op);
2769     f->create();
2770 
2771     // Mount/unmount file system
2772     text = msg + _(" the folder:");
2773     f->mount(dir, text, cmd, op);
2774     ret = chdir(startlocation.text());
2775     if (ret < 0)
2776     {
2777         int errcode = errno;
2778         if (errcode)
2779         {
2780             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s: %s"), startlocation.text(), strerror(errcode));
2781         }
2782         else
2783         {
2784             MessageBox::error(this, BOX_OK, _("Error"), _("Can't enter folder %s"), startlocation.text());
2785         }
2786 
2787         return(0);
2788     }
2789 
2790     // If action is cancelled in progress dialog
2791     if (f->isCancelled())
2792     {
2793         getApp()->endWaitCursor();
2794         f->hide();
2795         text = msg + _(" operation cancelled!");
2796         MessageBox::error(this, BOX_OK, _("Error"), "%s", text.text());
2797         delete f;
2798         return(0);
2799     }
2800 
2801     getApp()->endWaitCursor();
2802     delete f;
2803 
2804     // Force dirpanel refresh
2805     list->handle(this, FXSEL(SEL_COMMAND, DirList::ID_REFRESH), NULL);
2806 
2807     return(1);
2808 }
2809 
2810 
2811 // Update the Mount menu item
onUpdMount(FXObject * o,FXSelector sel,void *)2812 long DirPanel::onUpdMount(FXObject* o, FXSelector sel, void*)
2813 {
2814     // Current item
2815     DirItem* item = (DirItem*)list->getCurrentItem();
2816     FXString dir = list->getItemPathname((TreeItem*)item);
2817 
2818     if (fsdevices->find(dir.text()) && !mtdevices->find(dir.text()))
2819     {
2820         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
2821     }
2822     else
2823     {
2824         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
2825     }
2826 
2827     return(1);
2828 }
2829 
2830 
2831 // Update the Unmount menu item
onUpdUnmount(FXObject * o,FXSelector sel,void *)2832 long DirPanel::onUpdUnmount(FXObject* o, FXSelector sel, void*)
2833 {
2834     // Current item
2835     DirItem* item = (DirItem*)list->getCurrentItem();
2836     FXString dir = list->getItemPathname((TreeItem*)item);
2837 
2838     if (fsdevices->find(dir.text()) || mtdevices->find(dir.text()))
2839     {
2840         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
2841     }
2842     else
2843     {
2844         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
2845     }
2846 
2847     return(1);
2848 }
2849 
2850 
2851 #endif
2852 
2853 
2854 // Update the paste button
onUpdPaste(FXObject * o,FXSelector,void *)2855 long DirPanel::onUpdPaste(FXObject* o, FXSelector, void*)
2856 {
2857     FXuchar* data;
2858     FXuint   len;
2859     FXString buf;
2860     FXbool   clipboard_empty = true;
2861 
2862     // Lock clipboard to prevent changes in method onCmdRequestClipboard()
2863     clipboard_locked = true;
2864 
2865     // If source is xfelistType (Gnome, XFCE, or Xfe app)
2866     if (getDNDData(FROM_CLIPBOARD, xfelistType, data, len))
2867     {
2868         FXRESIZE(&data, FXuchar, len+1);
2869         data[len] = '\0';
2870         buf = (char*)data;
2871 
2872         // Check if valid clipboard
2873         if (buf.find("file:/") >= 0)
2874         {
2875             clipboard_empty = false;
2876         }
2877 
2878         // Free data pointer
2879         FXFREE(&data);
2880     }
2881 
2882     // If source type is urilistType (KDE apps ; non Gnome, non XFCE and non Xfe apps)
2883     else if (getDNDData(FROM_CLIPBOARD, urilistType, data, len))
2884     {
2885         FXRESIZE(&data, FXuchar, len+1);
2886         data[len] = '\0';
2887         buf = (char*)data;
2888 
2889         // Test if valid clipboard
2890         if (buf.find("file:/") >= 0)
2891         {
2892             clipboard_empty = false;
2893         }
2894 
2895         // Free data pointer
2896         FXFREE(&data);
2897     }
2898 
2899     // If source is utf8Type (simple text)
2900     else if (getDNDData(FROM_CLIPBOARD, utf8Type, data, len))
2901     {
2902         FXRESIZE(&data, FXuchar, len+1);
2903         data[len] = '\0';
2904         buf = (char*)data;
2905 
2906         // Check if valid clipboard
2907 		int beg, end;
2908 		FXString filepath;
2909         FXbool clipboard_valid = true;
2910         for (beg = 0; beg < buf.length(); beg = end+1)
2911         {
2912             if ((end = buf.find("\n", beg)) < 0)
2913             {
2914                 end = buf.length();
2915             }
2916 
2917             // Obtain item file path
2918             filepath = buf.mid(beg, end-beg);
2919 
2920             // File path does not begin with '/'
2921             if (filepath[0] != PATHSEPCHAR)
2922             {
2923 				clipboard_valid = false;
2924 				break;
2925 			}
2926 
2927 			// File path is not an existing file or directory
2928 			else
2929 			{
2930 				if (!existFile(filepath))
2931 				{
2932 					clipboard_valid = false;
2933 					break;
2934 				}
2935 			}
2936 		}
2937 
2938 		// Clipboard not empty
2939 		if (clipboard_valid)
2940 		{
2941 			clipboard_empty = false;
2942 		}
2943 
2944         // Free data pointer
2945         FXFREE(&data);
2946     }
2947 
2948     // Gray out the paste button, if necessary
2949     if (clipboard_empty)
2950     {
2951         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
2952     }
2953     else
2954     {
2955         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
2956     }
2957 
2958     // Unlock clipboard
2959     clipboard_locked = false;
2960 
2961     return(1);
2962 }
2963 
2964 
2965 // Update menu items and toolbar buttons that are related to file operations
onUpdMenu(FXObject * o,FXSelector,void *)2966 long DirPanel::onUpdMenu(FXObject* o, FXSelector, void*)
2967 {
2968     // Name of the current selected item
2969     TreeItem* item = (TreeItem*)list->getCurrentItem();
2970 
2971     // There is no selected item
2972     if (item == NULL)
2973     {
2974         return(0);
2975     }
2976 
2977     // Path name of the selected item
2978     FXString dir = list->getItemPathname(item);
2979     return(1);
2980 }
2981 
2982 
2983 // Update menu items and toolbar buttons that are related to file operations
onUpdDirDelete(FXObject * o,FXSelector,void *)2984 long DirPanel::onUpdDirDelete(FXObject* o, FXSelector, void*)
2985 {
2986     // Name of the current selected item
2987     TreeItem* item = (TreeItem*)list->getCurrentItem();
2988 
2989     // There is no selected item
2990     if (item == NULL)
2991     {
2992         return(0);
2993     }
2994 
2995     // Path name of the selected item
2996     FXString dir = list->getItemPathname(item);
2997 
2998     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
2999     FXbool use_trash_bypass = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_bypass", false);
3000     if ( (!use_trash_can) | use_trash_bypass)
3001     {
3002         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
3003     }
3004     else
3005     {
3006         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
3007     }
3008 
3009     return(1);
3010 }
3011 
3012 
3013 // Update menu items and toolbar buttons that are related to file operations
onUpdDirTrash(FXObject * o,FXSelector,void *)3014 long DirPanel::onUpdDirTrash(FXObject* o, FXSelector, void*)
3015 {
3016     // Name of the current selected item
3017     TreeItem* item = (TreeItem*)list->getCurrentItem();
3018 
3019     // There is no selected item
3020     if (item == NULL)
3021     {
3022         return(0);
3023     }
3024 
3025     // Path name of the selected item
3026     FXString dir = list->getItemPathname(item);
3027 
3028     // Disable move to trash menu if we are in trash can
3029     // or if the trash can directory is selected
3030     FXbool   trashenable = true;
3031     FXString trashparentdir = trashlocation.rbefore('/');
3032 
3033     if (dir.left(trashlocation.length()) == trashlocation)
3034     {
3035         trashenable = false;
3036     }
3037 
3038     if (dir == trashparentdir)
3039     {
3040         trashenable = false;
3041     }
3042 
3043     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
3044     if (use_trash_can && trashenable)
3045     {
3046         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
3047     }
3048     else
3049     {
3050         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
3051     }
3052 
3053     return(1);
3054 }
3055 
3056 
onUpdDirRestore(FXObject * o,FXSelector,void *)3057 long DirPanel::onUpdDirRestore(FXObject* o, FXSelector, void*)
3058 {
3059     // Name of the current selected item
3060     TreeItem* item = (TreeItem*)list->getCurrentItem();
3061 
3062     // There is no selected item
3063     if (item == NULL)
3064     {
3065         return(0);
3066     }
3067 
3068     // Path name of the selected item
3069     FXString dir = list->getItemPathname(item);
3070 
3071     // Enable restore from trash menu if we are in trash can
3072     FXbool restoreenable = false;
3073     if (dir.left(trashfileslocation.length()+1) == trashfileslocation+PATHSEPSTRING)
3074     {
3075         restoreenable = true;
3076     }
3077 
3078     FXbool use_trash_can = getApp()->reg().readUnsignedEntry("OPTIONS", "use_trash_can", true);
3079     if (use_trash_can && restoreenable)
3080     {
3081         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_ENABLE), NULL);
3082     }
3083     else
3084     {
3085         o->handle(this, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), NULL);
3086     }
3087 
3088     return(1);
3089 }
3090 
3091 
3092 // Toggle dirsize refresh and force refresh if flag is true
setAllowDirsizeRefresh(FXbool flag)3093 void DirPanel::setAllowDirsizeRefresh(FXbool flag)
3094 {
3095     allowDirsizeRefresh = flag;
3096 
3097     // Force refresh
3098     if (allowDirsizeRefresh)
3099     {
3100         curr_dirpath = "";
3101         onCmdDirsizeRefresh(0, 0, 0);
3102     }
3103 }
3104 
3105 
3106 // Refresh the directory size in the status bar
onCmdDirsizeRefresh(FXObject * sender,FXSelector,void *)3107 long DirPanel::onCmdDirsizeRefresh(FXObject* sender, FXSelector, void*)
3108 {
3109     // Don't refresh if not allowed or window is minimized
3110     if (!allowDirsizeRefresh || ((FXTopWindow*)focuswindow)->isMinimized())
3111     {
3112         return(0);
3113     }
3114 
3115     FXulong  dnsize;
3116     char     dsize[64];
3117     FXString hsize;
3118 
3119     // Name of the current selected item
3120     TreeItem* item = (TreeItem*)list->getCurrentItem();
3121 
3122     // There is no selected item
3123     if (item == NULL)
3124     {
3125         status->setText(_("0 bytes in root"));
3126         return(0);
3127     }
3128 
3129     // Path name of the selected item (without trailing '/' except for the root path)
3130     FXString path = ::filePath(list->getItemPathname(item), "");
3131 
3132     // Compute directory size only if something has changed in directory
3133     struct stat statbuf;
3134     if (lstatrep(path.text(), &statbuf) == 0)
3135     {
3136         if (!((path == curr_dirpath) && (statbuf.st_mtime == curr_mtime)))
3137         {
3138             // Update curr directory mtime
3139             curr_mtime = statbuf.st_mtime;
3140 
3141             // Update curr directory path
3142             curr_dirpath = path;
3143 
3144             // Size of the files present in the directory
3145             //strlcpy(buf,path.text(),path.length()+1);
3146             dnsize = ::dirsize(path.text());
3147 
3148             // Size in human readable form
3149 #if __WORDSIZE == 64
3150             snprintf(dsize, sizeof(dsize)-1, "%lu", dnsize);
3151 #else
3152             snprintf(dsize, sizeof(dsize)-1, "%llu", dnsize);
3153 #endif
3154             hsize = ::hSize(dsize);
3155 
3156             // Refresh the status label
3157             FXString string = hsize +  _(" in root");
3158             status->setText(string);
3159         }
3160     }
3161 
3162     // Reset timer again
3163     getApp()->addTimeout(this, ID_DIRSIZE_REFRESH, DIRSIZE_REFRESH_INTERVAL);
3164 
3165     // Important : returning 0 here avoids to continuously update the GUI!
3166     return(0);
3167 }
3168 
3169 
3170 // Update the path name in the Window title
onUpdTitle(FXObject * sender,FXSelector,void *)3171 long DirPanel::onUpdTitle(FXObject* sender, FXSelector, void*)
3172 {
3173     // Name of the current selected item
3174     TreeItem* item = (TreeItem*)list->getCurrentItem();
3175 
3176     // There is no selected item
3177     if (item == NULL)
3178     {
3179         mainWindow->setTitle("Xfe - ");
3180         return(0);
3181     }
3182 
3183     // Path of the current directory in the file panel
3184     FilePanel* currentpanel = ((XFileExplorer*)mainWindow)->getCurrentPanel();
3185     FXString path = currentpanel->getDirectory();
3186 
3187     // Update the path in the window title
3188     if (getuid() == 0)
3189     {
3190         mainWindow->setTitle("Xfe (root) - " + path);
3191     }
3192     else
3193     {
3194         mainWindow->setTitle("Xfe - " + path);
3195     }
3196 
3197     return(1);
3198 }
3199 
3200 
3201 // Update dirsize refresh timer if the window gains focus
onUpdDirsizeRefresh(FXObject *,FXSelector,void *)3202 long DirPanel::onUpdDirsizeRefresh(FXObject*, FXSelector, void*)
3203 {
3204     static FXbool prevMinimized = true;
3205     static FXbool minimized = true;
3206 
3207     prevMinimized = minimized;
3208     if (((FXTopWindow*)focuswindow)->isMinimized())
3209     {
3210         minimized = false;
3211     }
3212     else
3213     {
3214         minimized = true;
3215     }
3216 
3217     // Update timer if window is unminimized
3218     if ((prevMinimized == false) && (minimized == true))
3219     {
3220         onCmdDirsizeRefresh(0, 0, 0);
3221     }
3222 
3223     return(1);
3224 }
3225