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