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