1 /********************************************************************************
2 *                                                                               *
3 *                        F i l e    L i s t   O b j e c t                       *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxkeys.h"
26 #include "fxascii.h"
27 #include "fxunicode.h"
28 #include "FXArray.h"
29 #include "FXHash.h"
30 #include "FXMutex.h"
31 #include "FXStream.h"
32 #include "FXObjectList.h"
33 #include "FXString.h"
34 #include "FXSize.h"
35 #include "FXPoint.h"
36 #include "FXRectangle.h"
37 #include "FXSystem.h"
38 #include "FXPath.h"
39 #include "FXStat.h"
40 #include "FXFile.h"
41 #include "FXDir.h"
42 #include "FXURL.h"
43 #include "FXStringDictionary.h"
44 #include "FXSettings.h"
45 #include "FXRegistry.h"
46 #include "FXFont.h"
47 #include "FXEvent.h"
48 #include "FXWindow.h"
49 #include "FXApp.h"
50 #include "FXIcon.h"
51 #include "FXGIFIcon.h"
52 #include "FXScrollBar.h"
53 #include "FXIconSource.h"
54 #include "FXShell.h"
55 #include "FXPopup.h"
56 #include "FXMenuPane.h"
57 #include "FXMenuCaption.h"
58 #include "FXMenuCommand.h"
59 #include "FXMenuCascade.h"
60 #include "FXMenuRadio.h"
61 #include "FXMenuCheck.h"
62 #include "FXMenuSeparator.h"
63 #include "FXDictionary.h"
64 #include "FXDictionaryOf.h"
65 #include "FXIconCache.h"
66 #include "FXFileAssociations.h"
67 #include "FXHeader.h"
68 #include "FXIconList.h"
69 #include "FXFileList.h"
70 #include "FXFileProgressDialog.h"
71 #include "FXMessageBox.h"
72 #include "icons.h"
73 
74 /*
75   Notes:
76   - Share icons with other widgets; upgrade icons to some nicer ones.
77   - Should some of these icons move to FXFileAssociations?
78   - Clipboard of filenames.
79   - Clipboard, DND, etc. support.
80   - When being dragged over, if hovering over a directory item for some
81     time we need to open it.
82   - We should generate SEL_INSERTED, SEL_DELETED, SEL_CHANGED
83     messages as the FXFileList updates itself from the file system.
84   - The solution currently used to determine whether or not to blend the
85     icon isn't so great; this class shouldn't have to know about FXPNGIcon.
86   - If you land in a large directory with images, things are a tad slow;
87     need to speed this up some how.
88 */
89 
90 
91 #define OPENDIRDELAY        700000000   // Delay before opening directory
92 #define REFRESHINTERVAL     1000000000  // Interval between refreshes
93 #define REFRESHCOUNT        30          // Refresh every REFRESHCOUNT-th time
94 
95 using namespace FX;
96 
97 /*******************************************************************************/
98 
99 namespace FX {
100 
101 
102 // Object implementation
103 FXIMPLEMENT(FXFileItem,FXIconItem,NULL,0)
104 
105 
106 // Map
107 FXDEFMAP(FXFileList) FXFileListMap[]={
108   FXMAPFUNC(SEL_DND_ENTER,0,FXFileList::onDNDEnter),
109   FXMAPFUNC(SEL_DND_LEAVE,0,FXFileList::onDNDLeave),
110   FXMAPFUNC(SEL_DND_DROP,0,FXFileList::onDNDDrop),
111   FXMAPFUNC(SEL_DND_MOTION,0,FXFileList::onDNDMotion),
112   FXMAPFUNC(SEL_DND_REQUEST,0,FXFileList::onDNDRequest),
113   FXMAPFUNC(SEL_BEGINDRAG,0,FXFileList::onBeginDrag),
114   FXMAPFUNC(SEL_DRAGGED,0,FXFileList::onDragged),
115   FXMAPFUNC(SEL_ENDDRAG,0,FXFileList::onEndDrag),
116   FXMAPFUNC(SEL_CLIPBOARD_LOST,0,FXFileList::onClipboardLost),
117   FXMAPFUNC(SEL_CLIPBOARD_REQUEST,0,FXFileList::onClipboardRequest),
118   FXMAPFUNC(SEL_CHORE,FXFileList::ID_PREVIEWCHORE,FXFileList::onPreviewChore),
119   FXMAPFUNC(SEL_TIMEOUT,FXFileList::ID_OPENTIMER,FXFileList::onOpenTimer),
120   FXMAPFUNC(SEL_TIMEOUT,FXFileList::ID_REFRESHTIMER,FXFileList::onRefreshTimer),
121   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_DIRECTORY_UP,FXFileList::onUpdDirectoryUp),
122   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_NAME,FXFileList::onUpdSortByName),
123   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_TYPE,FXFileList::onUpdSortByType),
124   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_SIZE,FXFileList::onUpdSortBySize),
125   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_TIME,FXFileList::onUpdSortByTime),
126   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_USER,FXFileList::onUpdSortByUser),
127   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_BY_GROUP,FXFileList::onUpdSortByGroup),
128   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_REVERSE,FXFileList::onUpdSortReverse),
129   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SORT_CASE,FXFileList::onUpdSortCase),
130   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SET_PATTERN,FXFileList::onUpdSetPattern),
131   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SET_DIRECTORY,FXFileList::onUpdSetDirectory),
132   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_SHOW_HIDDEN,FXFileList::onUpdShowHidden),
133   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_HIDE_HIDDEN,FXFileList::onUpdHideHidden),
134   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_TOGGLE_HIDDEN,FXFileList::onUpdToggleHidden),
135   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_TOGGLE_IMAGES,FXFileList::onUpdToggleImages),
136   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_HEADER,FXFileList::onUpdHeader),
137   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_CUT_SEL,FXFileList::onUpdHaveSel),
138   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_COPY_SEL,FXFileList::onUpdHaveSel),
139   FXMAPFUNC(SEL_UPDATE,FXFileList::ID_DELETE_SEL,FXFileList::onUpdHaveSel),
140   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_HEADER,FXFileList::onCmdHeader),
141   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_DIRECTORY_UP,FXFileList::onCmdDirectoryUp),
142   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_NAME,FXFileList::onCmdSortByName),
143   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_TYPE,FXFileList::onCmdSortByType),
144   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_SIZE,FXFileList::onCmdSortBySize),
145   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_TIME,FXFileList::onCmdSortByTime),
146   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_USER,FXFileList::onCmdSortByUser),
147   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_BY_GROUP,FXFileList::onCmdSortByGroup),
148   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_REVERSE,FXFileList::onCmdSortReverse),
149   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SORT_CASE,FXFileList::onCmdSortCase),
150   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SET_PATTERN,FXFileList::onCmdSetPattern),
151   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SET_DIRECTORY,FXFileList::onCmdSetDirectory),
152   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SETVALUE,FXFileList::onCmdSetValue),
153   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SETSTRINGVALUE,FXFileList::onCmdSetStringValue),
154   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_GETSTRINGVALUE,FXFileList::onCmdGetStringValue),
155   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_SHOW_HIDDEN,FXFileList::onCmdShowHidden),
156   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_HIDE_HIDDEN,FXFileList::onCmdHideHidden),
157   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_TOGGLE_HIDDEN,FXFileList::onCmdToggleHidden),
158   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_TOGGLE_IMAGES,FXFileList::onCmdToggleImages),
159   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_REFRESH,FXFileList::onCmdRefresh),
160   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_CUT_SEL,FXFileList::onCmdCutSel),
161   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_COPY_SEL,FXFileList::onCmdCopySel),
162   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_DELETE_SEL,FXFileList::onCmdDeleteSel),
163   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_PASTE_SEL,FXFileList::onCmdPasteSel),
164   FXMAPFUNC(SEL_CHORE,FXFileList::ID_DROPASK,FXFileList::onCmdDropAsk),
165   FXMAPFUNC(SEL_CHORE,FXFileList::ID_DROPCOPY,FXFileList::onCmdDropCopy),
166   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_DROPCOPY,FXFileList::onCmdDropCopy),
167   FXMAPFUNC(SEL_CHORE,FXFileList::ID_DROPMOVE,FXFileList::onCmdDropMove),
168   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_DROPMOVE,FXFileList::onCmdDropMove),
169   FXMAPFUNC(SEL_CHORE,FXFileList::ID_DROPLINK,FXFileList::onCmdDropLink),
170   FXMAPFUNC(SEL_COMMAND,FXFileList::ID_DROPLINK,FXFileList::onCmdDropLink),
171   };
172 
173 
174 // Object implementation
FXIMPLEMENT(FXFileList,FXIconList,FXFileListMap,ARRAYNUMBER (FXFileListMap))175 FXIMPLEMENT(FXFileList,FXIconList,FXFileListMap,ARRAYNUMBER(FXFileListMap))
176 
177 
178 // For serialization
179 FXFileList::FXFileList(){
180   dropEnable();
181   associations=NULL;
182   iconloader=NULL;
183   list=NULL;
184   big_folder=NULL;
185   mini_folder=NULL;
186   big_doc=NULL;
187   mini_doc=NULL;
188   big_app=NULL;
189   mini_app=NULL;
190 #ifdef WIN32
191   matchmode=FXPath::PathName|FXPath::NoEscape|FXPath::CaseFold;
192   setSortFunc(ascendingCase);
193 #else
194   matchmode=FXPath::PathName|FXPath::NoEscape;
195   setSortFunc(ascending);
196 #endif
197   dropaction=DRAG_COPY;
198   matchmode=0;
199   imagesize=32;
200   timestamp=0;
201   counter=0;
202   clipcut=false;
203   draggable=true;
204   }
205 
206 
207 // File List
FXFileList(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)208 FXFileList::FXFileList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXIconList(p,tgt,sel,opts,x,y,w,h),directory(PATHSEPSTRING),pattern("*"){
209   dropEnable();
210   appendHeader(tr("Name\tName"),NULL,200);
211   appendHeader(tr("Type\tFile type"),NULL,100);
212   appendHeader(tr("Size\tFile size"),NULL,60);
213   appendHeader(tr("Modified Date\tDate when last modified"),NULL,150);
214   appendHeader(tr("User\tUser name"),NULL,50);
215   appendHeader(tr("Group\tGroup name"),NULL,50);
216   appendHeader(tr("Attributes\tFile attributes"),NULL,100);
217 #ifndef WIN32
218   appendHeader(tr("Link\tSymbolic link to"),NULL,200);
219 #endif
220   associations=NULL;
221   if(!(options&FILELIST_NO_OWN_ASSOC)) associations=new FXFileAssociations(getApp());
222   iconloader=&FXIconSource::defaultIconSource;
223   list=NULL;
224   big_folder=new FXGIFIcon(getApp(),bigfolder);
225   mini_folder=new FXGIFIcon(getApp(),minifolder);
226   big_doc=new FXGIFIcon(getApp(),bigdoc);
227   mini_doc=new FXGIFIcon(getApp(),minidoc);
228   big_app=new FXGIFIcon(getApp(),bigapp);
229   mini_app=new FXGIFIcon(getApp(),miniapp);
230   timeformat=tr(FXSystem::defaultTimeFormat);
231   dropaction=DRAG_COPY;
232 #ifdef WIN32
233   matchmode=FXPath::PathName|FXPath::NoEscape|FXPath::CaseFold;
234   setSortFunc(ascendingCase);
235 #else
236   matchmode=FXPath::PathName|FXPath::NoEscape;
237   setSortFunc(ascending);
238 #endif
239   imagesize=32;
240   timestamp=0;
241   counter=0;
242   clipcut=false;
243   draggable=true;
244   }
245 
246 
247 // Starts the timer
create()248 void FXFileList::create(){
249   FXIconList::create();
250   getApp()->addTimeout(this,ID_REFRESHTIMER,REFRESHINTERVAL);
251   big_folder->create();
252   mini_folder->create();
253   big_doc->create();
254   mini_doc->create();
255   big_app->create();
256   mini_app->create();
257   }
258 
259 
260 // Detach disconnects the icons
detach()261 void FXFileList::detach(){
262   FXIconList::detach();
263   getApp()->removeTimeout(this,ID_REFRESHTIMER);
264   getApp()->removeTimeout(this,ID_OPENTIMER);
265   big_folder->detach();
266   mini_folder->detach();
267   big_doc->detach();
268   mini_doc->detach();
269   big_app->detach();
270   mini_app->detach();
271   deleteType=0;
272   urilistType=0;
273   actionType=0;
274   }
275 
276 
277 // Destroy zaps the icons
destroy()278 void FXFileList::destroy(){
279   FXIconList::destroy();
280   getApp()->removeTimeout(this,ID_REFRESHTIMER);
281   getApp()->removeTimeout(this,ID_OPENTIMER);
282   big_folder->destroy();
283   mini_folder->destroy();
284   big_doc->destroy();
285   mini_doc->destroy();
286   big_app->destroy();
287   mini_app->destroy();
288   }
289 
290 
291 // Create custom item
createItem(const FXString & text,FXIcon * big,FXIcon * mini,void * ptr)292 FXIconItem *FXFileList::createItem(const FXString& text,FXIcon *big,FXIcon* mini,void* ptr){
293   return new FXFileItem(text,big,mini,ptr);
294   }
295 
296 /*******************************************************************************/
297 
298 
299 // Compare file names
ascending(const FXIconItem * a,const FXIconItem * b)300 FXint FXFileList::ascending(const FXIconItem* a,const FXIconItem* b){
301   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
302   if(diff==0){
303     diff=compareSection(a->label.text(),b->label.text(),0);
304     }
305   return diff;
306   }
307 
308 
309 // Reversed compare file name
descending(const FXIconItem * a,const FXIconItem * b)310 FXint FXFileList::descending(const FXIconItem* a,const FXIconItem* b){
311   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
312   if(diff==0){
313     diff=compareSection(b->label.text(),a->label.text(),0);
314     }
315   return diff;
316   }
317 
318 
319 // Compare file names, case insensitive
ascendingCase(const FXIconItem * a,const FXIconItem * b)320 FXint FXFileList::ascendingCase(const FXIconItem* a,const FXIconItem* b){
321   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
322   if(diff==0){
323     diff=compareSectionCase(a->label.text(),b->label.text(),0);
324     }
325   return diff;
326   }
327 
328 
329 // Reversed compare file name, case insensitive
descendingCase(const FXIconItem * a,const FXIconItem * b)330 FXint FXFileList::descendingCase(const FXIconItem* a,const FXIconItem* b){
331   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
332   if(diff==0){
333     diff=compareSectionCase(b->label.text(),a->label.text(),0);
334     }
335   return diff;
336   }
337 
338 
339 // Compare file types
ascendingType(const FXIconItem * a,const FXIconItem * b)340 FXint FXFileList::ascendingType(const FXIconItem* a,const FXIconItem* b){
341   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
342   if(diff==0){
343     diff=compareSection(a->label.text(),b->label.text(),1);
344     if(diff==0){
345       diff=compareSection(a->label.text(),b->label.text(),0);
346       }
347     }
348   return diff;
349   }
350 
351 
352 // Reversed compare file type
descendingType(const FXIconItem * a,const FXIconItem * b)353 FXint FXFileList::descendingType(const FXIconItem* a,const FXIconItem* b){
354   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
355   if(diff==0){
356     diff=compareSection(b->label.text(),a->label.text(),1);
357     if(diff==0){
358       diff=compareSection(b->label.text(),a->label.text(),0);
359       }
360     }
361   return diff;
362   }
363 
364 
365 // Compare file size
ascendingSize(const FXIconItem * a,const FXIconItem * b)366 FXint FXFileList::ascendingSize(const FXIconItem* a,const FXIconItem* b){
367   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
368   if(diff==0){
369     diff=FXSGNZ(static_cast<const FXFileItem*>(a)->size - static_cast<const FXFileItem*>(b)->size);
370     if(diff==0){
371       diff=compareSection(a->label.text(),b->label.text(),0);
372       }
373     }
374   return diff;
375   }
376 
377 
378 // Reversed compare file size
descendingSize(const FXIconItem * a,const FXIconItem * b)379 FXint FXFileList::descendingSize(const FXIconItem* a,const FXIconItem* b){
380   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
381   if(diff==0){
382     diff=FXSGNZ(static_cast<const FXFileItem*>(b)->size - static_cast<const FXFileItem*>(a)->size);
383     if(diff==0){
384       diff=compareSection(b->label.text(),a->label.text(),0);
385       }
386     }
387   return diff;
388   }
389 
390 
391 // Compare file time
ascendingTime(const FXIconItem * a,const FXIconItem * b)392 FXint FXFileList::ascendingTime(const FXIconItem* a,const FXIconItem* b){
393   FXint diff=(FXint)((FXFileItem*)b)->isDirectory() - (FXint)((FXFileItem*)a)->isDirectory();
394   if(diff==0){
395     diff=FXSGNZ(static_cast<const FXFileItem*>(a)->date - static_cast<const FXFileItem*>(b)->date);
396     if(diff==0){
397       diff=compareSection(a->label.text(),b->label.text(),0);
398       }
399     }
400   return diff;
401   }
402 
403 
404 // Reversed compare file time
descendingTime(const FXIconItem * a,const FXIconItem * b)405 FXint FXFileList::descendingTime(const FXIconItem* a,const FXIconItem* b){
406   FXint diff=(FXint)((FXFileItem*)b)->isDirectory() - (FXint)((FXFileItem*)a)->isDirectory();
407   if(diff==0){
408     diff=FXSGNZ(static_cast<const FXFileItem*>(b)->date - static_cast<const FXFileItem*>(a)->date);
409     if(diff==0){
410       diff=compareSection(b->label.text(),a->label.text(),0);
411       }
412     }
413   return diff;
414   }
415 
416 
417 // Compare file user
ascendingUser(const FXIconItem * a,const FXIconItem * b)418 FXint FXFileList::ascendingUser(const FXIconItem* a,const FXIconItem* b){
419   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
420   if(diff==0){
421     diff=compareSection(a->label.text(),b->label.text(),4);
422     if(diff==0){
423       diff=compareSection(a->label.text(),b->label.text(),0);
424       }
425     }
426   return diff;
427   }
428 
429 
430 // Reversed compare file user
descendingUser(const FXIconItem * a,const FXIconItem * b)431 FXint FXFileList::descendingUser(const FXIconItem* a,const FXIconItem* b){
432   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
433   if(diff==0){
434     diff=compareSection(b->label.text(),a->label.text(),4);
435     if(diff==0){
436       diff=compareSection(b->label.text(),a->label.text(),0);
437       }
438     }
439   return diff;
440   }
441 
442 
443 // Compare file group
ascendingGroup(const FXIconItem * a,const FXIconItem * b)444 FXint FXFileList::ascendingGroup(const FXIconItem* a,const FXIconItem* b){
445   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
446   if(diff==0){
447     diff=compareSection(a->label.text(),b->label.text(),5);
448     if(diff==0){
449       diff=compareSection(a->label.text(),b->label.text(),0);
450       }
451     }
452   return diff;
453   }
454 
455 
456 // Reversed compare file group
descendingGroup(const FXIconItem * a,const FXIconItem * b)457 FXint FXFileList::descendingGroup(const FXIconItem* a,const FXIconItem* b){
458   FXint diff=static_cast<const FXFileItem*>(b)->isDirectory() - static_cast<const FXFileItem*>(a)->isDirectory();
459   if(diff==0){
460     diff=compareSection(b->label.text(),a->label.text(),5);
461     if(diff==0){
462       diff=compareSection(b->label.text(),a->label.text(),0);
463       }
464     }
465   return diff;
466   }
467 
468 /*******************************************************************************/
469 
470 // Return uri-list of selected files
getSelectedFiles() const471 FXString FXFileList::getSelectedFiles() const {
472   FXString result;
473   for(FXint i=0; i<getNumItems(); i++){
474     if(isItemSelected(i) && !isItemNavigational(i)){
475       result.append(FXURL::fileToURL(getItemPathname(i)));
476       result.append("\r\n");
477       }
478     }
479   return result;
480   }
481 
482 
483 // Update if we have selection
onUpdHaveSel(FXObject * sender,FXSelector,void *)484 long FXFileList::onUpdHaveSel(FXObject* sender,FXSelector,void*){
485   for(FXint i=0; i<getNumItems(); i++){
486     if(isItemSelected(i) && !isItemNavigational(i)){
487       sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
488       return 1;
489       }
490     }
491   sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
492   return 1;
493   }
494 
495 /*******************************************************************************/
496 
497 // Delete selection
onCmdDeleteSel(FXObject *,FXSelector,void *)498 long FXFileList::onCmdDeleteSel(FXObject*,FXSelector,void*){
499   FXString delfiles=getSelectedFiles();
500   delete_files(delfiles);       // FIXME confirmation would be nice
501   return 1;
502   }
503 
504 
505 // Paste clipboard
onCmdPasteSel(FXObject *,FXSelector,void *)506 long FXFileList::onCmdPasteSel(FXObject*,FXSelector,void*){
507   FXString files,action;
508   if(getDNDData(FROM_CLIPBOARD,urilistType,files)){
509     if(getDNDData(FROM_CLIPBOARD,actionType,action)){
510       FXTRACE((100,"%s::onCmdPasteSel(): Action: %s Files: %s\n",getClassName(),action.text(),files.text()));
511       if(action[0]=='1'){
512         move_files(directory,files);
513         }
514       else{
515 //        FXint count=files.contains("\r\n");
516 //        FXTRACE((1,"number of files=%d\n",count));
517 //FXFileProgressDialog fileprogress(this,"Copying Files","Copying 10 files (100MB)",big_folder,DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,0,0,600,0);
518 //fileprogress.execute();
519         copy_files(directory,files);
520         }
521       return 1;
522       }
523     }
524   getApp()->beep();
525   return 1;
526   }
527 
528 
529 // Cut
onCmdCutSel(FXObject *,FXSelector,void *)530 long FXFileList::onCmdCutSel(FXObject*,FXSelector,void*){
531   FXDragType types[2]={urilistType,actionType};
532   if(acquireClipboard(types,ARRAYNUMBER(types))){
533     clipfiles=getSelectedFiles();
534     clipcut=true;
535     }
536   return 1;
537   }
538 
539 
540 // Copy
onCmdCopySel(FXObject *,FXSelector,void *)541 long FXFileList::onCmdCopySel(FXObject*,FXSelector,void*){
542   FXDragType types[2]={urilistType,actionType};
543   if(acquireClipboard(types,ARRAYNUMBER(types))){
544     clipfiles=getSelectedFiles();
545     clipcut=false;
546     }
547   return 1;
548   }
549 
550 
551 // We lost the selection somehow
onClipboardLost(FXObject * sender,FXSelector sel,void * ptr)552 long FXFileList::onClipboardLost(FXObject* sender,FXSelector sel,void* ptr){
553   FXIconList::onClipboardLost(sender,sel,ptr);
554   FXTRACE((100,"deleting clipfiles\n"));
555   clipfiles=FXString::null;
556   clipcut=false;
557   return 1;
558   }
559 
560 
561 // Somebody wants our selection
onClipboardRequest(FXObject * sender,FXSelector sel,void * ptr)562 long FXFileList::onClipboardRequest(FXObject* sender,FXSelector sel,void* ptr){
563 
564   // Try base class first
565   if(FXIconList::onClipboardRequest(sender,sel,ptr)) return 1;
566 
567   // Return list of filenames as a uri-list
568   if(((FXEvent*)ptr)->target==urilistType){
569     FXTRACE((100,"Returning urilistType\n"));
570     setDNDData(FROM_CLIPBOARD,urilistType,clipfiles);
571     return 1;
572     }
573 
574   // Return type of clipboard action
575   if(((FXEvent*)ptr)->target==actionType){
576     FXTRACE((100,"Returning actionType\n"));
577     setDNDData(FROM_CLIPBOARD,actionType,clipcut?"1":"0");
578     return 1;
579     }
580 
581   return 0;
582   }
583 
584 /*******************************************************************************/
585 
586 
587 // Delete files from the systems
delete_files(const FXString & files)588 void FXFileList::delete_files(const FXString& files){
589   FXString filename;
590   FXint beg,end;
591   for(beg=0; beg<files.length(); beg=end+2){
592     if((end=files.find_first_of("\r\n",beg))<0) end=files.length();
593     filename=FXURL::fileFromURL(files.mid(beg,end-beg));
594     if(!FXFile::removeFiles(filename,true)){
595       if(FXMessageBox::question(this,MBOX_OK_CANCEL,tr("Failed Deleting Files"),tr("Failed to delete file: %s; continue?"),filename.text())==MBOX_CLICKED_CANCEL) break;
596       }
597     }
598   }
599 
600 
601 // Copy files to directory
copy_files(const FXString & dir,const FXString & files)602 void FXFileList::copy_files(const FXString& dir,const FXString& files){
603   FXString filedst,filesrc;
604   FXuint answer=0;
605   FXint beg,end;
606   FXbool ok;
607   for(beg=0; beg<files.length(); beg=end+2){
608     if((end=files.find_first_of("\r\n",beg))<0) end=files.length();
609     filesrc=FXURL::fileFromURL(files.mid(beg,end-beg));
610     filedst=FXPath::absolute(dir,FXPath::name(filesrc));
611     ok=FXFile::copyFiles(filesrc,filedst,(answer==MBOX_CLICKED_YESALL));
612     if(!ok){
613       if(answer==MBOX_CLICKED_NOALL) continue;
614       if(answer!=MBOX_CLICKED_YESALL){
615         answer=FXMessageBox::question(this,MBOX_YES_YESALL_NO_NOALL_CANCEL,tr("Overwrite File"),tr("Overwrite existing file or directory: %s?"),filedst.text());
616         if(answer==MBOX_CLICKED_CANCEL) break;
617         if(answer==MBOX_CLICKED_NO) continue;
618         if(answer==MBOX_CLICKED_NOALL) continue;
619         ok=FXFile::copyFiles(filesrc,filedst,true);
620         }
621       if(!ok){
622         if(FXMessageBox::question(this,MBOX_OK_CANCEL,tr("Failed Copying File"),tr("Failed to overwrite file: %s; continue?"),filedst.text())==MBOX_CLICKED_OK) continue;
623         break;
624         }
625       }
626     }
627   }
628 
629 
630 // Move files to directory
move_files(const FXString & dir,const FXString & files)631 void FXFileList::move_files(const FXString& dir,const FXString& files){
632   FXString filedst,filesrc;
633   FXuint answer=0;
634   FXint beg,end;
635   FXbool ok;
636   for(beg=0; beg<files.length(); beg=end+2){
637     if((end=files.find_first_of("\r\n",beg))<0) end=files.length();
638     filesrc=FXURL::fileFromURL(files.mid(beg,end-beg));
639     filedst=FXPath::absolute(dir,FXPath::name(filesrc));
640     ok=FXFile::moveFiles(filesrc,filedst,(answer==MBOX_CLICKED_YESALL));
641     if(!ok){
642       if(answer==MBOX_CLICKED_NOALL) continue;
643       if(answer!=MBOX_CLICKED_YESALL){
644         answer=FXMessageBox::question(this,MBOX_YES_YESALL_NO_NOALL_CANCEL,tr("Overwrite File"),tr("Overwrite existing file or directory: %s?"),filedst.text());
645         if(answer==MBOX_CLICKED_CANCEL) break;
646         if(answer==MBOX_CLICKED_NO) continue;
647         if(answer==MBOX_CLICKED_NOALL) continue;
648         ok=FXFile::moveFiles(filesrc,filedst,true);
649         }
650       if(!ok){
651         if(FXMessageBox::question(this,MBOX_OK_CANCEL,tr("Failed Moving File"),tr("Failed to overwrite file: %s; continue?"),filedst.text())==MBOX_CLICKED_OK) continue;
652         break;
653         }
654       }
655     }
656   }
657 
658 #if 0
659         FXProgressDialog progress(this,tr("Copying Files"),FXString::null,PROGRESSDIALOG_NORMAL|PROGRESSDIALOG_CANCEL,0,0,520,0);
660         FXuint ans=MBOX_CLICKED_NO;
661         FXString filedst,filesrc;
662         FXint beg,end,ok;
663         progress.create();
664         progress.show(PLACEMENT_CURSOR);
665         progress.setTotal(dropfiles.contains("\r\n"));
666         progress.setProgress(0);
667         getApp()->flush();
668         getApp()->runModalWhileEvents(&progress,500000000);
669         getApp()->flush();
670         for(beg=0; beg<files.length(); beg=end+2){
671           if(progress.isCancelled()) break;
672           if((end=files.find_first_of("\r\n",beg))<0) end=files.length();
673           filesrc=FXURL::fileFromURL(files.mid(beg,end-beg));
674           filedst=FXPath::absolute(directory,FXPath::name(filesrc));
675           progress.setMessage(tr("Copying file:\n\n")+filesrc);
676           progress.increment(1);
677           getApp()->flush();
678           getApp()->runModalWhileEvents(&progress,500000000);
679           FXThread::sleep(100000000);
680 //          FXFile::copyFiles(filesrc,filedst,false);
681 /*
682           ok=FXFile::copyFiles(filesrc,filedst,(ans==MBOX_CLICKED_YESALL));
683           if(!ok && (ans!=MBOX_CLICKED_NOALL)){
684             if(ans!=MBOX_CLICKED_YESALL && ans!=MBOX_CLICKED_NOALL){
685               ans=FXMessageBox::question(this,MBOX_YES_YESALL_NO_NOALL_CANCEL,tr("Overwrite File"),tr("Overwrite existing file or directory: %s?"),filedst.text());
686               if(ans==MBOX_CLICKED_CANCEL) break;
687               if((ans==MBOX_CLICKED_YESALL) || (ans==MBOX_CLICKED_YES)){
688                 ok=FXFile::copyFiles(filesrc,filedst,true);
689                 }
690               }
691             }
692 */
693           }
694 #endif
695 
696 /*******************************************************************************/
697 
698 // Copy files to drop directory
onCmdDropCopy(FXObject *,FXSelector,void *)699 long FXFileList::onCmdDropCopy(FXObject*,FXSelector,void*){
700   copy_files(dropdirectory,dropfiles);
701   dropdirectory=FXString::null;
702   dropfiles=FXString::null;
703   dropaction=DRAG_REJECT;
704   return 1;
705   }
706 
707 
708 // Move files to drop directory
onCmdDropMove(FXObject *,FXSelector,void *)709 long FXFileList::onCmdDropMove(FXObject*,FXSelector,void*){
710   move_files(dropdirectory,dropfiles);
711   dropdirectory=FXString::null;
712   dropfiles=FXString::null;
713   dropaction=DRAG_REJECT;
714   return 1;
715   }
716 
717 
718 // Link to files from dropdirectory
onCmdDropLink(FXObject *,FXSelector,void *)719 long FXFileList::onCmdDropLink(FXObject*,FXSelector,void*){
720   FXString filedst,filesrc; FXint beg,end;
721   for(beg=0; beg<dropfiles.length(); beg=end+2){
722     if((end=dropfiles.find_first_of("\r\n",beg))<0) end=dropfiles.length();
723     filesrc=FXURL::fileFromURL(dropfiles.mid(beg,end-beg));
724     filedst=FXPath::absolute(dropdirectory,FXPath::name(filesrc));
725     if(!FXFile::symlink(filesrc,filedst)){
726       if(FXMessageBox::question(this,MBOX_OK_CANCEL,tr("Failed Linking File"),tr("Failed to make symbolic link from: %s; continue?"),filedst.text())==MBOX_CLICKED_CANCEL) break;
727       }
728     }
729   dropdirectory=FXString::null;
730   dropfiles=FXString::null;
731   dropaction=DRAG_REJECT;
732   return 1;
733   }
734 
735 
736 // Deal with the drop that has just occurred
onCmdDropAsk(FXObject *,FXSelector,void * ptr)737 long FXFileList::onCmdDropAsk(FXObject*,FXSelector,void* ptr){
738   FXMenuPane dropmenu(this);
739   FXGIFIcon filemoveicon(getApp(),filemove);
740   FXGIFIcon filecopyicon(getApp(),filecopy);
741   FXGIFIcon filelinkicon(getApp(),filelink);
742   FXGIFIcon filecancelicon(getApp(),filecancel);
743   new FXMenuCommand(&dropmenu,tr("Move Here"),&filemoveicon,this,ID_DROPMOVE);
744   new FXMenuCommand(&dropmenu,tr("Copy Here"),&filecopyicon,this,ID_DROPCOPY);
745   new FXMenuCommand(&dropmenu,tr("Link Here"),&filelinkicon,this,ID_DROPLINK);
746   new FXMenuSeparator(&dropmenu);
747   new FXMenuCommand(&dropmenu,tr("Cancel"),&filecancelicon);
748   dropmenu.create();
749   dropmenu.popup(NULL,((FXEvent*)ptr)->root_x,((FXEvent*)ptr)->root_y);
750   getApp()->runModalWhileShown(&dropmenu);
751   dropdirectory=FXString::null;
752   dropfiles=FXString::null;
753   dropaction=DRAG_REJECT;
754   return 1;
755   }
756 
757 /*******************************************************************************/
758 
759 // Change directory when hovering over a folder
760 // Remember current directory prior to change
onOpenTimer(FXObject *,FXSelector,void *)761 long FXFileList::onOpenTimer(FXObject*,FXSelector,void*){
762   FXint xx,yy,index; FXuint buttons;
763   getCursorPosition(xx,yy,buttons);
764   index=getItemAt(xx,yy);
765   if(0<=index && isItemDirectory(index)){
766     if(startdirectory.empty()){ startdirectory=getDirectory(); }
767     dropdirectory=getItemPathname(index);
768     setDirectory(dropdirectory,true);
769     }
770   return 1;
771   }
772 
773 
774 // Handle drag-and-drop enter, remember current directory
onDNDEnter(FXObject * sender,FXSelector sel,void * ptr)775 long FXFileList::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){
776   FXIconList::onDNDEnter(sender,sel,ptr);
777   return 1;
778   }
779 
780 
781 // Handle drag-and-drop leave
782 // Restore current directory and scroll position prior drag
onDNDLeave(FXObject * sender,FXSelector sel,void * ptr)783 long FXFileList::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){
784   FXIconList::onDNDLeave(sender,sel,ptr);
785   getApp()->removeTimeout(this,ID_OPENTIMER);
786   stopAutoScroll();
787   if(!startdirectory.empty()){
788     setDirectory(startdirectory,true);
789     startdirectory=FXString::null;
790     }
791   dropdirectory=FXString::null;
792   dropfiles=FXString::null;
793   dropaction=DRAG_REJECT;
794   return 1;
795   }
796 
797 
798 // Handle drag-and-drop motion
onDNDMotion(FXObject * sender,FXSelector sel,void * ptr)799 long FXFileList::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){
800   FXEvent *event=(FXEvent*)ptr;
801   FXint index=-1;
802 
803   // Cancel open up timer
804   getApp()->removeTimeout(this,ID_OPENTIMER);
805 
806   // Start autoscrolling
807   if(startAutoScroll(event,false)) return 1;
808 
809   // Give base class a shot
810   if(FXIconList::onDNDMotion(sender,sel,ptr)) return 1;
811 
812   // Dropping list of filenames
813   if(offeredDNDType(FROM_DRAGNDROP,urilistType)){
814 
815     // Drop in the background
816     dropdirectory=getDirectory();
817 
818     // Drop action to be performed
819     dropaction=inquireDNDAction();
820 
821     // We will open up a folder if we can hover over it for a while
822     index=getItemAt(event->win_x,event->win_y);
823     if(0<=index && isItemDirectory(index)){
824 
825       // Start open up timer when hovering over item
826       getApp()->addTimeout(this,ID_OPENTIMER,OPENDIRDELAY);
827 
828       // Directory where to drop, or directory to open up
829       dropdirectory=getItemPathname(index);
830       }
831 
832     // See if dropdirectory is writable
833     if(FXStat::isAccessible(dropdirectory,FXIO::ReadOnly|FXIO::WriteOnly)){
834       acceptDrop(DRAG_ACCEPT);
835       }
836     return 1;
837     }
838   return 0;
839   }
840 
841 
842 // Handle drag-and-drop drop
onDNDDrop(FXObject * sender,FXSelector sel,void * ptr)843 long FXFileList::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){
844 
845   // Cancel open up timer
846   getApp()->removeTimeout(this,ID_OPENTIMER);
847 
848   // Stop scrolling
849   stopAutoScroll();
850 
851   // Restore start directory and scroll position
852   if(!startdirectory.empty()){
853     setDirectory(startdirectory,true);
854     startdirectory=FXString::null;
855     }
856 
857   // Perhaps target wants to deal with it
858   if(FXIconList::onDNDDrop(sender,sel,ptr)) return 1;
859 
860   // Get list of files as uri-list
861   if(getDNDData(FROM_DRAGNDROP,urilistType,dropfiles)){
862     if(!dropfiles.empty()){
863       switch(dropaction){
864         case DRAG_COPY:
865           getApp()->addChore(this,ID_DROPCOPY,ptr);
866           break;
867         case DRAG_MOVE:
868           getApp()->addChore(this,ID_DROPMOVE,ptr);
869           break;
870         case DRAG_LINK:
871           getApp()->addChore(this,ID_DROPLINK,ptr);
872           break;
873         default:
874           getApp()->addChore(this,ID_DROPASK,ptr);
875           break;
876         }
877       return 1;
878       }
879     }
880   return 0;
881   }
882 
883 
884 // Somebody wants our dragged data
onDNDRequest(FXObject * sender,FXSelector sel,void * ptr)885 long FXFileList::onDNDRequest(FXObject* sender,FXSelector sel,void* ptr){
886 
887   // Perhaps the target wants to supply its own data
888   if(FXIconList::onDNDRequest(sender,sel,ptr)) return 1;
889 
890   // Return list of filenames as a uri-list
891   if(((FXEvent*)ptr)->target==urilistType){
892     setDNDData(FROM_DRAGNDROP,urilistType,dragfiles);
893     return 1;
894     }
895 
896   // Delete selected files
897   if(((FXEvent*)ptr)->target==deleteType){
898     FXTRACE((100,"Delete files not yet implemented\n"));
899     return 1;
900     }
901 
902   return 0;
903   }
904 
905 
906 // Start a drag operation
onBeginDrag(FXObject * sender,FXSelector sel,void * ptr)907 long FXFileList::onBeginDrag(FXObject* sender,FXSelector sel,void* ptr){
908   if(!FXIconList::onBeginDrag(sender,sel,ptr)){
909     beginDrag(&urilistType,1);
910     dragfiles=getSelectedFiles();
911     }
912   return 1;
913   }
914 
915 
916 // Dragged stuff around
onDragged(FXObject * sender,FXSelector sel,void * ptr)917 long FXFileList::onDragged(FXObject* sender,FXSelector sel,void* ptr){
918   FXEvent* event=(FXEvent*)ptr;
919   if(!FXIconList::onDragged(sender,sel,ptr)){
920     FXDragAction action=DRAG_ASK;
921     if(event->state&CONTROLMASK) action=DRAG_COPY;
922     if(event->state&SHIFTMASK) action=DRAG_MOVE;
923     if(event->state&ALTMASK) action=DRAG_LINK;
924     handleDrag(event->root_x,event->root_y,action);
925     action=didAccept();
926     switch(action){
927       case DRAG_COPY:
928         setDragCursor(getApp()->getDefaultCursor(DEF_DNDCOPY_CURSOR));
929         break;
930       case DRAG_MOVE:
931         setDragCursor(getApp()->getDefaultCursor(DEF_DNDMOVE_CURSOR));
932         break;
933       case DRAG_LINK:
934         setDragCursor(getApp()->getDefaultCursor(DEF_DNDLINK_CURSOR));
935         break;
936       case DRAG_ASK:
937         setDragCursor(getApp()->getDefaultCursor(DEF_DNDASK_CURSOR));
938         break;
939       default:
940         setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
941         break;
942       }
943     }
944   return 1;
945   }
946 
947 
948 // End drag operation
onEndDrag(FXObject * sender,FXSelector sel,void * ptr)949 long FXFileList::onEndDrag(FXObject* sender,FXSelector sel,void* ptr){
950   if(!FXIconList::onEndDrag(sender,sel,ptr)){
951     endDrag((didAccept()!=DRAG_REJECT));
952     setDragCursor(getDefaultCursor());
953     dragfiles=FXString::null;
954     }
955   return 1;
956   }
957 
958 /*******************************************************************************/
959 
960 // Cycle through items that represent images
onPreviewChore(FXObject *,FXSelector,void * ptr)961 long FXFileList::onPreviewChore(FXObject*,FXSelector,void* ptr){
962   FXint index=(FXint)(FXival)ptr;
963   if(showImages() && iconloader && index<getNumItems()){
964     FXIcon *icon=iconloader->loadScaledIconFile(getApp(),getItemPathname(index),imagesize);;
965     if(icon){
966       icon->create();
967       setItemBigIcon(index,icon,true);
968       setItemMiniIcon(index,icon,false);
969       }
970    if(++index<getNumItems()){
971      getApp()->addChore(this,ID_PREVIEWCHORE,(void*)(FXival)index);
972      }
973    }
974   return 1;
975   }
976 
977 /*******************************************************************************/
978 
979 // Set value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)980 long FXFileList::onCmdSetValue(FXObject*,FXSelector,void* ptr){
981   setCurrentFile((const FXchar*)ptr,true);
982   return 1;
983   }
984 
985 
986 // Set current directory from dir part of filename
onCmdSetStringValue(FXObject *,FXSelector,void * ptr)987 long FXFileList::onCmdSetStringValue(FXObject*,FXSelector,void* ptr){
988   setCurrentFile(*((FXString*)ptr),true);
989   return 1;
990   }
991 
992 
993 // Get current file name (NULL if no current file)
onCmdGetStringValue(FXObject *,FXSelector,void * ptr)994 long FXFileList::onCmdGetStringValue(FXObject*,FXSelector,void* ptr){
995   *((FXString*)ptr)=getCurrentFile();
996   return 1;
997   }
998 
999 
1000 // Toggle hidden files display
onCmdToggleHidden(FXObject *,FXSelector,void *)1001 long FXFileList::onCmdToggleHidden(FXObject*,FXSelector,void*){
1002   showHiddenFiles(!showHiddenFiles(),true);
1003   return 1;
1004   }
1005 
1006 
1007 // Update toggle hidden files widget
onUpdToggleHidden(FXObject * sender,FXSelector,void *)1008 long FXFileList::onUpdToggleHidden(FXObject* sender,FXSelector,void*){
1009   sender->handle(this,showHiddenFiles()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1010   return 1;
1011   }
1012 
1013 
1014 // Show hidden files
onCmdShowHidden(FXObject *,FXSelector,void *)1015 long FXFileList::onCmdShowHidden(FXObject*,FXSelector,void*){
1016   showHiddenFiles(true,true);
1017   return 1;
1018   }
1019 
1020 
1021 // Update show hidden files widget
onUpdShowHidden(FXObject * sender,FXSelector,void *)1022 long FXFileList::onUpdShowHidden(FXObject* sender,FXSelector,void*){
1023   sender->handle(this,showHiddenFiles()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1024   return 1;
1025   }
1026 
1027 
1028 // Hide hidden files
onCmdHideHidden(FXObject *,FXSelector,void *)1029 long FXFileList::onCmdHideHidden(FXObject*,FXSelector,void*){
1030   showHiddenFiles(false,true);
1031   return 1;
1032   }
1033 
1034 
1035 // Update hide hidden files widget
onUpdHideHidden(FXObject * sender,FXSelector,void *)1036 long FXFileList::onUpdHideHidden(FXObject* sender,FXSelector,void*){
1037   sender->handle(this,showHiddenFiles()?FXSEL(SEL_COMMAND,ID_UNCHECK):FXSEL(SEL_COMMAND,ID_CHECK),NULL);
1038   return 1;
1039   }
1040 
1041 
1042 // Toggle image preview
onCmdToggleImages(FXObject *,FXSelector,void *)1043 long FXFileList::onCmdToggleImages(FXObject*,FXSelector,void*){
1044   showImages(!showImages(),true);
1045   return 1;
1046   }
1047 
1048 
1049 // Update image preview
onUpdToggleImages(FXObject * sender,FXSelector,void *)1050 long FXFileList::onUpdToggleImages(FXObject* sender,FXSelector,void*){
1051   sender->handle(this,showImages()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1052   return 1;
1053   }
1054 
1055 
1056 // Move up one level
onCmdDirectoryUp(FXObject *,FXSelector,void *)1057 long FXFileList::onCmdDirectoryUp(FXObject*,FXSelector,void*){
1058   setDirectory(FXPath::upLevel(directory),true);
1059   return 1;
1060   }
1061 
1062 
1063 // Determine if we can still go up more
onUpdDirectoryUp(FXObject * sender,FXSelector,void *)1064 long FXFileList::onUpdDirectoryUp(FXObject* sender,FXSelector,void*){
1065   sender->handle(this,FXPath::isTopDirectory(directory)?FXSEL(SEL_COMMAND,ID_DISABLE):FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
1066   return 1;
1067   }
1068 
1069 
1070 // Change pattern
onCmdSetPattern(FXObject *,FXSelector,void * ptr)1071 long FXFileList::onCmdSetPattern(FXObject*,FXSelector,void* ptr){
1072   setPattern((const char*)ptr,true);
1073   return 1;
1074   }
1075 
1076 
1077 // Update pattern
onUpdSetPattern(FXObject * sender,FXSelector,void *)1078 long FXFileList::onUpdSetPattern(FXObject* sender,FXSelector,void*){
1079   sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE),(void*)pattern.text());
1080   return 1;
1081   }
1082 
1083 
1084 // Change directory
onCmdSetDirectory(FXObject *,FXSelector,void * ptr)1085 long FXFileList::onCmdSetDirectory(FXObject*,FXSelector,void* ptr){
1086   setDirectory((const char*)ptr,true);
1087   return 1;
1088   }
1089 
1090 
1091 // Update directory
onUpdSetDirectory(FXObject * sender,FXSelector,void *)1092 long FXFileList::onUpdSetDirectory(FXObject* sender,FXSelector,void*){
1093   sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETVALUE),(void*)directory.text());
1094   return 1;
1095   }
1096 
1097 
1098 // Sort by name
onCmdSortByName(FXObject *,FXSelector,void *)1099 long FXFileList::onCmdSortByName(FXObject*,FXSelector,void*){
1100 #ifdef WIN32
1101   if(getSortFunc()==ascending) setSortFunc(descending);
1102   else if(getSortFunc()==ascendingCase) setSortFunc(descendingCase);
1103   else if(getSortFunc()==descending) setSortFunc(ascending);
1104   else setSortFunc(ascendingCase);
1105 #else
1106   if(getSortFunc()==ascending) setSortFunc(descending);
1107   else if(getSortFunc()==ascendingCase) setSortFunc(descendingCase);
1108   else if(getSortFunc()==descending) setSortFunc(ascending);
1109   else setSortFunc(ascending);
1110 #endif
1111   sortItems();
1112   return 1;
1113   }
1114 
1115 
1116 // Update sender
onUpdSortByName(FXObject * sender,FXSelector,void *)1117 long FXFileList::onUpdSortByName(FXObject* sender,FXSelector,void*){
1118   sender->handle(this,(getSortFunc()==ascending || getSortFunc()==descending || getSortFunc()==ascendingCase || getSortFunc()==descendingCase) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1119   return 1;
1120   }
1121 
1122 
1123 // Sort by type
onCmdSortByType(FXObject *,FXSelector,void *)1124 long FXFileList::onCmdSortByType(FXObject*,FXSelector,void*){
1125   setSortFunc((getSortFunc()==ascendingType) ? descendingType : ascendingType);
1126   sortItems();
1127   return 1;
1128   }
1129 
1130 
1131 // Update sender
onUpdSortByType(FXObject * sender,FXSelector,void *)1132 long FXFileList::onUpdSortByType(FXObject* sender,FXSelector,void*){
1133   sender->handle(this,(getSortFunc()==ascendingType || getSortFunc()==descendingType) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1134   return 1;
1135   }
1136 
1137 
1138 // Sort by size
onCmdSortBySize(FXObject *,FXSelector,void *)1139 long FXFileList::onCmdSortBySize(FXObject*,FXSelector,void*){
1140   setSortFunc((getSortFunc()==ascendingSize) ? descendingSize : ascendingSize);
1141   sortItems();
1142   return 1;
1143   }
1144 
1145 
1146 // Update sender
onUpdSortBySize(FXObject * sender,FXSelector,void *)1147 long FXFileList::onUpdSortBySize(FXObject* sender,FXSelector,void*){
1148   sender->handle(this,(getSortFunc()==ascendingSize || getSortFunc()==descendingSize) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1149   return 1;
1150   }
1151 
1152 
1153 // Sort by time
onCmdSortByTime(FXObject *,FXSelector,void *)1154 long FXFileList::onCmdSortByTime(FXObject*,FXSelector,void*){
1155   setSortFunc((getSortFunc()==ascendingTime) ? descendingTime : ascendingTime);
1156   sortItems();
1157   return 1;
1158   }
1159 
1160 
1161 // Update sender
onUpdSortByTime(FXObject * sender,FXSelector,void *)1162 long FXFileList::onUpdSortByTime(FXObject* sender,FXSelector,void*){
1163   sender->handle(this,(getSortFunc()==ascendingTime || getSortFunc()==descendingTime) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1164   return 1;
1165   }
1166 
1167 
1168 // Sort by user
onCmdSortByUser(FXObject *,FXSelector,void *)1169 long FXFileList::onCmdSortByUser(FXObject*,FXSelector,void*){
1170   setSortFunc((getSortFunc()==ascendingUser) ? descendingUser : ascendingUser);
1171   sortItems();
1172   return 1;
1173   }
1174 
1175 
1176 // Update sender
onUpdSortByUser(FXObject * sender,FXSelector,void *)1177 long FXFileList::onUpdSortByUser(FXObject* sender,FXSelector,void*){
1178   sender->handle(this,(getSortFunc()==ascendingUser || getSortFunc()==descendingUser) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1179   return 1;
1180   }
1181 
1182 
1183 // Sort by group
onCmdSortByGroup(FXObject *,FXSelector,void *)1184 long FXFileList::onCmdSortByGroup(FXObject*,FXSelector,void*){
1185   setSortFunc((getSortFunc()==ascendingGroup) ? descendingGroup : ascendingGroup);
1186   sortItems();
1187   return 1;
1188   }
1189 
1190 
1191 // Update sender
onUpdSortByGroup(FXObject * sender,FXSelector,void *)1192 long FXFileList::onUpdSortByGroup(FXObject* sender,FXSelector,void*){
1193   sender->handle(this,(getSortFunc()==ascendingGroup || getSortFunc()==descendingGroup) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1194   return 1;
1195   }
1196 
1197 
1198 // Reverse sort order
onCmdSortReverse(FXObject *,FXSelector,void *)1199 long FXFileList::onCmdSortReverse(FXObject*,FXSelector,void*){
1200   if(getSortFunc()==ascending) setSortFunc(descending);
1201   else if(getSortFunc()==descending) setSortFunc(ascending);
1202   else if(getSortFunc()==ascendingCase) setSortFunc(descendingCase);
1203   else if(getSortFunc()==descendingCase) setSortFunc(ascendingCase);
1204   else if(getSortFunc()==ascendingType) setSortFunc(descendingType);
1205   else if(getSortFunc()==descendingType) setSortFunc(ascendingType);
1206   else if(getSortFunc()==ascendingSize) setSortFunc(descendingSize);
1207   else if(getSortFunc()==descendingSize) setSortFunc(ascendingSize);
1208   else if(getSortFunc()==ascendingTime) setSortFunc(descendingTime);
1209   else if(getSortFunc()==descendingTime) setSortFunc(ascendingTime);
1210   else if(getSortFunc()==ascendingUser) setSortFunc(descendingUser);
1211   else if(getSortFunc()==descendingUser) setSortFunc(ascendingUser);
1212   else if(getSortFunc()==ascendingGroup) setSortFunc(descendingGroup);
1213   else if(getSortFunc()==descendingGroup) setSortFunc(ascendingGroup);
1214   sortItems();
1215   return 1;
1216   }
1217 
1218 
1219 // Update sender
onUpdSortReverse(FXObject * sender,FXSelector,void *)1220 long FXFileList::onUpdSortReverse(FXObject* sender,FXSelector,void*){
1221   FXSelector selector=FXSEL(SEL_COMMAND,ID_UNCHECK);
1222   if(getSortFunc()==descending) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1223   else if(getSortFunc()==descendingCase) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1224   else if(getSortFunc()==descendingType) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1225   else if(getSortFunc()==descendingSize) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1226   else if(getSortFunc()==descendingTime) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1227   else if(getSortFunc()==descendingUser) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1228   else if(getSortFunc()==descendingGroup) selector=FXSEL(SEL_COMMAND,ID_CHECK);
1229   sender->handle(this,selector,NULL);
1230   return 1;
1231   }
1232 
1233 
1234 // Toggle case sensitivity
onCmdSortCase(FXObject *,FXSelector,void *)1235 long FXFileList::onCmdSortCase(FXObject*,FXSelector,void*){
1236   if(getSortFunc()==ascending) setSortFunc(ascendingCase);
1237   else if(getSortFunc()==ascendingCase) setSortFunc(ascending);
1238   else if(getSortFunc()==descending) setSortFunc(descendingCase);
1239   else if(getSortFunc()==descendingCase) setSortFunc(descending);
1240   sortItems();
1241   return 1;
1242   }
1243 
1244 
1245 // Check if case sensitive
onUpdSortCase(FXObject * sender,FXSelector,void *)1246 long FXFileList::onUpdSortCase(FXObject* sender,FXSelector,void*){
1247   sender->handle(this,(getSortFunc()==ascendingCase || getSortFunc()==descendingCase) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1248   sender->handle(this,(getSortFunc()==ascendingCase || getSortFunc()==ascending || getSortFunc()==descendingCase || getSortFunc()==descending) ? FXSEL(SEL_COMMAND,ID_ENABLE) : FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1249   return 1;
1250   }
1251 
1252 
1253 // Clicked header button
onCmdHeader(FXObject *,FXSelector,void * ptr)1254 long FXFileList::onCmdHeader(FXObject*,FXSelector,void* ptr){
1255   if(((FXuint)(FXuval)ptr)<6) handle(this,FXSEL(SEL_COMMAND,(ID_SORT_BY_NAME+(FXuint)(FXuval)ptr)),NULL);
1256   return 1;
1257   }
1258 
1259 
1260 // Clicked header button
onUpdHeader(FXObject *,FXSelector,void *)1261 long FXFileList::onUpdHeader(FXObject*,FXSelector,void*){
1262   header->setArrowDir(0,(getSortFunc()==ascending || getSortFunc()==ascendingCase)  ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descending || getSortFunc()==descendingCase) ? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // Name
1263   header->setArrowDir(1,(getSortFunc()==ascendingType)  ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descendingType) ? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // Type
1264   header->setArrowDir(2,(getSortFunc()==ascendingSize)  ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descendingSize) ? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // Size
1265   header->setArrowDir(3,(getSortFunc()==ascendingTime)  ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descendingTime) ? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // Date
1266   header->setArrowDir(4,(getSortFunc()==ascendingUser)  ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descendingUser) ? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // User
1267   header->setArrowDir(5,(getSortFunc()==ascendingGroup) ? FXHeaderItem::ARROW_DOWN : (getSortFunc()==descendingGroup)? FXHeaderItem::ARROW_UP : FXHeaderItem::ARROW_NONE);   // Group
1268   return 1;
1269   }
1270 
1271 /*******************************************************************************/
1272 
1273 // Periodically check to see if directory was changed, and update the list if it was.
onRefreshTimer(FXObject *,FXSelector,void *)1274 long FXFileList::onRefreshTimer(FXObject*,FXSelector,void*){
1275   if(flags&FLAG_UPDATE){
1276     counter+=1;
1277     if(!listItems((counter==REFRESHCOUNT),true)){
1278       setDirectory(FXPath::validPath(directory),true);
1279       }
1280     }
1281   getApp()->addTimeout(this,ID_REFRESHTIMER,REFRESHINTERVAL);
1282   return 0;
1283   }
1284 
1285 
1286 // Force an immediate update of the list
onCmdRefresh(FXObject *,FXSelector,void *)1287 long FXFileList::onCmdRefresh(FXObject*,FXSelector,void*){
1288   listItems(true,true);
1289   return 1;
1290   }
1291 
1292 
1293 // Compare till '\t' or '\0'
fileequal(const FXchar * p1,const FXchar * p2)1294 static FXbool fileequal(const FXchar* p1,const FXchar* p2){
1295   FXint c1,c2;
1296   do{
1297     c1=*p1++;
1298     c2=*p2++;
1299     }
1300   while(c1==c2 && c1!='\0' && c1!='\t');
1301   return (c1=='\0' || c1=='\t') && (c2=='\0' || c2=='\t');
1302   }
1303 
1304 
1305 // List the items in the directory.
1306 // Regenerate the list if an update is forced or the directory timestamp was changed.
1307 // Add, remove, or update items as needed, generating the proper callbacks.
1308 // In addition, re-sort the items using current sort-function.
1309 // Return false if the directory can not be accessed, true otherwise.
listItems(FXbool force,FXbool notify)1310 FXbool FXFileList::listItems(FXbool force,FXbool notify){
1311   FXStat info;
1312 
1313   FXTRACE((100,"%s::listItems(%d,%d)\n",getClassName(),force,notify));
1314 
1315   // See if directory still there
1316   if(FXStat::statFile(directory,info)){
1317 
1318     // Last modified time of current directory
1319     FXTime time=info.modified();
1320 
1321     // Regenerate list if update forced or modified time changed
1322     if(force || time!=timestamp){
1323       FXFileItem  *oldlist=list;    // Old insert-order list
1324       FXFileItem  *newlist=NULL;    // New insert-order list
1325       FXFileItem **po=&oldlist;     // Head of old list
1326       FXFileItem **pn=&newlist;     // Head of new list
1327       FXFileItem  *olditem;
1328       FXFileItem  *newitem;
1329       FXFileItem  *link;
1330       FXString     pathname;
1331       FXString     extension;
1332       FXString     label;
1333       FXString     name;
1334       FXString     grpid;
1335       FXString     usrid;
1336       FXString     attrs;
1337       FXString     modtm;
1338       FXString     lnknm;
1339       FXuint       mode;
1340       FXbool       istop;
1341       FXDir        dir;
1342 
1343       // Get directory stream pointer
1344       if(dir.open(directory)){
1345 
1346         // Are we at the top directory?
1347         istop=FXPath::isTopDirectory(directory);
1348 
1349         // Loop over directory entries
1350         while(dir.next(name)){
1351 
1352           // Suppress '.' if not showing navigational items or not at top directory
1353           if(name[0]=='.'){
1354             if(name[1]=='\0'){
1355               if(!istop || (options&FILELIST_NO_PARENT)) continue;
1356               }
1357             else if(name[1]=='.' && name[2]=='\0'){
1358               if(istop || (options&FILELIST_NO_PARENT)) continue;
1359               }
1360             else{
1361               if(!(options&FILELIST_SHOWHIDDEN)) continue;
1362               }
1363             }
1364 
1365           // Build full pathname
1366           pathname=directory;
1367           if(!ISPATHSEP(pathname.tail())) pathname+=PATHSEPSTRING;
1368           pathname+=name;
1369 
1370 #ifdef WIN32
1371 
1372           // Get file/link info
1373           if(!FXStat::statFile(pathname,info)) continue;
1374 
1375           mode=info.mode();
1376 
1377           // Suppress hidden files or directories
1378           if((mode&FXIO::Hidden) && !(options&FILELIST_SHOWHIDDEN)) continue;
1379 #else
1380 
1381           // Get file/link info
1382           if(!FXStat::statLink(pathname,info)) continue;
1383 
1384           mode=info.mode();
1385 
1386           // If its a link, get file mode from target
1387           if(info.isLink()){
1388             mode=FXStat::mode(pathname) | FXIO::SymLink;
1389             }
1390 
1391 #endif
1392 
1393           // Skip item if it is a directory and we want only files, if it is a file and we want only directories,
1394           // or if it is a file and it fails to match the wildcard pattern.
1395           if(mode&FXIO::Directory){
1396             if(options&FILELIST_SHOWFILES) continue;
1397             }
1398           else{
1399             if(options&FILELIST_SHOWDIRS) continue;
1400             if(!FXPath::match(name,pattern,matchmode)) continue;
1401             }
1402 
1403           // Search for item in old list, unlink from old if found
1404           for(FXFileItem** pp=po; (olditem=*pp)!=NULL; pp=&olditem->link){
1405             if(fileequal(olditem->label.text(),name.text())){
1406               *pp=olditem->link; olditem->link=NULL;
1407               break;
1408               }
1409             }
1410 
1411           // Use a new item if forced, if there was no old item, or if the item information was changed
1412           if(force || !olditem || olditem->getDate()!=info.modified() || olditem->getSize()!=info.size() || olditem->getMode()!=mode){
1413 
1414             // Make new item
1415             newitem=(FXFileItem*)createItem(FXString::null,NULL,NULL,NULL);
1416 
1417             // Obtain user name
1418             usrid=FXSystem::userName(info.user());
1419 
1420             // Obtain group name
1421             grpid=FXSystem::groupName(info.group());
1422 
1423             // Permissions
1424             attrs=FXSystem::modeString(mode);
1425 
1426             // Mod time
1427             modtm=FXSystem::localTime(info.modified(),timeformat.text());
1428 
1429             // Link name, if any
1430             lnknm=FXString::null;
1431             if(info.isLink()) lnknm=FXFile::symlink(pathname);
1432 
1433             // Update item information
1434             newitem->setDraggable(draggable);
1435             newitem->setSize(info.size());
1436             newitem->setDate(info.modified());
1437             newitem->setMode(mode);
1438             newitem->setAssoc(NULL);
1439 
1440             // Determine icons and type
1441             if(newitem->isDirectory()){
1442               extension=tr("Folder");
1443               newitem->setBigIcon(big_folder);
1444               newitem->setMiniIcon(mini_folder);
1445               if(associations) newitem->setAssoc(associations->findDirBinding(pathname));
1446               }
1447             else if(newitem->isExecutable()){
1448               extension=tr("Application");
1449               newitem->setBigIcon(big_app);
1450               newitem->setMiniIcon(mini_app);
1451               if(associations) newitem->setAssoc(associations->findExecBinding(pathname));
1452               }
1453             else{
1454               extension=tr("Document");
1455               newitem->setBigIcon(big_doc);
1456               newitem->setMiniIcon(mini_doc);
1457               if(associations) newitem->setAssoc(associations->findFileBinding(pathname));
1458               }
1459 
1460             // If association is found, use it
1461             if(newitem->getAssoc()){
1462               extension=newitem->getAssoc()->extension;
1463               if(newitem->getAssoc()->bigicon) newitem->setBigIcon(newitem->getAssoc()->bigicon);
1464               if(newitem->getAssoc()->miniicon) newitem->setMiniIcon(newitem->getAssoc()->miniicon);
1465               }
1466 
1467             // Update item information
1468             label.format("%s\t%s\t%lld\t%s\t%s\t%s\t%s\t%s",name.text(),extension.text(),newitem->size,modtm.text(),usrid.text(),grpid.text(),attrs.text(),lnknm.text());
1469 
1470             // New label
1471             newitem->setText(label);
1472 
1473             // Create item
1474             if(id()) newitem->create();
1475 
1476             // Replace or add item
1477             if(olditem){
1478               setItem(items.find(olditem),newitem,notify);
1479               }
1480             else{
1481               appendItem(newitem,notify);
1482               }
1483             *pn=newitem; pn=&newitem->link;
1484             }
1485 
1486           // Keep old item if nothing changed
1487           else{
1488             *pn=olditem; pn=&olditem->link;
1489             }
1490           }
1491 
1492         // Show thumbnails
1493         if(showImages()){
1494           getApp()->addChore(this,ID_PREVIEWCHORE,(void*)(FXival)0);
1495           }
1496 
1497         // Close directory
1498         dir.close();
1499         }
1500 
1501       // Wipe items remaining in list:- they have disappeared!!
1502       for(olditem=oldlist; olditem; olditem=link){
1503         link=olditem->link;
1504         removeItem(items.find(olditem),notify);
1505         }
1506 
1507       // Remember new list
1508       list=newlist;
1509 
1510       // Update sort order
1511       sortItems();
1512 
1513       // Update timestamp
1514       timestamp=time;
1515 
1516       // Reset counter
1517       counter=0;
1518       }
1519     return true;
1520     }
1521   return false;
1522   }
1523 
1524 /*******************************************************************************/
1525 
1526 // Set current file; return true if success
setCurrentFile(const FXString & file,FXbool notify)1527 FXbool FXFileList::setCurrentFile(const FXString& file,FXbool notify){
1528   FXTRACE((100,"%s::setCurrentFile(%s)\n",getClassName(),file.text()));
1529   if(setDirectory(FXPath::directory(file),notify)){
1530     FXint index=findItem(FXPath::name(file));
1531     if(0<=index){
1532       makeItemVisible(index);
1533       setAnchorItem(index);
1534       setCurrentItem(index,notify);
1535       selectItem(index,notify);
1536       return true;
1537       }
1538     }
1539   return false;
1540   }
1541 
1542 
1543 // Get pathname to current file, if any
getCurrentFile() const1544 FXString FXFileList::getCurrentFile() const {
1545   if(0<=getCurrentItem()){
1546     return getItemPathname(getCurrentItem());
1547     }
1548   return FXString::null;
1549   }
1550 
1551 
1552 // Set current directory; return true if success
setDirectory(const FXString & pathname,FXbool notify)1553 FXbool FXFileList::setDirectory(const FXString& pathname,FXbool notify){
1554   FXTRACE((100,"%s::setDirectory(%s)\n",getClassName(),pathname.text()));
1555   FXString path(FXPath::absolute(directory,pathname));
1556   if(FXStat::isDirectory(path)){
1557     if(directory==path) return true;
1558     clearItems(notify);
1559     directory=path;
1560     list=NULL;
1561     if(listItems(true,notify)){
1562       if(getNumItems()){
1563         makeItemVisible(0);
1564         setAnchorItem(0);
1565         setCurrentItem(0,notify);
1566         }
1567       return true;
1568       }
1569     }
1570   return false;
1571   }
1572 
1573 /*******************************************************************************/
1574 
1575 // Get file name from item
getItemFilename(FXint index) const1576 FXString FXFileList::getItemFilename(FXint index) const {
1577   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemFilename: index out of range.\n",getClassName()); }
1578   return items[index]->label.section('\t',0);
1579   }
1580 
1581 
1582 // Get full pathname to item
getItemPathname(FXint index) const1583 FXString FXFileList::getItemPathname(FXint index) const {
1584   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemPathname: index out of range.\n",getClassName()); }
1585   return FXPath::absolute(directory,items[index]->label.section('\t',0));
1586   }
1587 
1588 
1589 // Is file
isItemFile(FXint index) const1590 FXbool FXFileList::isItemFile(FXint index) const {
1591   if(index<0 || getNumItems()<=index){ fxerror("%s::isItemFile: index out of range.\n",getClassName()); }
1592   return ((FXFileItem*)items[index])->isFile();
1593   }
1594 
1595 
1596 // Is directory
isItemDirectory(FXint index) const1597 FXbool FXFileList::isItemDirectory(FXint index) const {
1598   if(index<0 || getNumItems()<=index){ fxerror("%s::isItemDirectory: index out of range.\n",getClassName()); }
1599   return ((FXFileItem*)items[index])->isDirectory();
1600   }
1601 
1602 
1603 // Is executable
isItemExecutable(FXint index) const1604 FXbool FXFileList::isItemExecutable(FXint index) const {
1605   if(index<0 || getNumItems()<=index){ fxerror("%s::isItemExecutable: index out of range.\n",getClassName()); }
1606   return ((FXFileItem*)items[index])->isExecutable();
1607   }
1608 
1609 
1610 // Return true if this is a symbolic link item
isItemSymlink(FXint index) const1611 FXbool FXFileList::isItemSymlink(FXint index) const {
1612   if(index<0 || getNumItems()<=index){ fxerror("%s::isItemSymlink: index out of range.\n",getClassName()); }
1613   return ((FXFileItem*)items[index])->isSymlink();
1614   }
1615 
1616 
1617 // Return true if item is navigational item like '.' or '..'
isItemNavigational(FXint index) const1618 FXbool FXFileList::isItemNavigational(FXint index) const {
1619   if(index<0 || getNumItems()<=index){ fxerror("%s::isItemNavigational: index out of range.\n",getClassName()); }
1620   return ((FXFileItem*)items[index])->isNavigational();
1621   }
1622 
1623 
1624 // Get associations (if any) from the file
getItemAssoc(FXint index) const1625 FXFileAssoc* FXFileList::getItemAssoc(FXint index) const {
1626   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemAssoc: index out of range.\n",getClassName()); }
1627   return ((FXFileItem*)items[index])->getAssoc();
1628   }
1629 
1630 
1631 // Return the file size for this item
getItemSize(FXint index) const1632 FXlong FXFileList::getItemSize(FXint index) const {
1633   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemSize: index out of range.\n",getClassName()); }
1634   return ((FXFileItem*)items[index])->getSize();
1635   }
1636 
1637 
1638 // Return the date for this item, in nanoseconds
getItemDate(FXint index) const1639 FXTime FXFileList::getItemDate(FXint index) const {
1640   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemDate: index out of range.\n",getClassName()); }
1641   return ((FXFileItem*)items[index])->getDate();
1642   }
1643 
1644 
1645 // Return the mode bits for this item
getItemMode(FXint index) const1646 FXuint FXFileList::getItemMode(FXint index) const {
1647   if(index<0 || getNumItems()<=index){ fxerror("%s::getItemMode: index out of range.\n",getClassName()); }
1648   return ((FXFileItem*)items[index])->getMode();
1649   }
1650 
1651 /*******************************************************************************/
1652 
1653 // Set the pattern to filter
setPattern(const FXString & ptrn,FXbool notify)1654 void FXFileList::setPattern(const FXString& ptrn,FXbool notify){
1655   if(!ptrn.empty() && pattern!=ptrn){
1656     pattern=ptrn;
1657     listItems(true,notify);
1658     }
1659   }
1660 
1661 // Change file match mode
setMatchMode(FXuint mode,FXbool notify)1662 void FXFileList::setMatchMode(FXuint mode,FXbool notify){
1663   if(matchmode!=mode){
1664     matchmode=mode;
1665     listItems(true,notify);
1666     }
1667   }
1668 
1669 
1670 // Change show hidden files mode
showHiddenFiles(FXbool flag,FXbool notify)1671 void FXFileList::showHiddenFiles(FXbool flag,FXbool notify){
1672   FXuint opts=((-(FXint)flag^options)&FILELIST_SHOWHIDDEN)^options;
1673   if(opts!=options){
1674     options=opts;
1675     listItems(true,notify);
1676     }
1677   }
1678 
1679 
1680 // Return true if showing hidden files
showHiddenFiles() const1681 FXbool FXFileList::showHiddenFiles() const {
1682   return (options&FILELIST_SHOWHIDDEN)!=0;
1683   }
1684 
1685 
1686 // Change show directories only mode
showOnlyDirectories(FXbool flag,FXbool notify)1687 void FXFileList::showOnlyDirectories(FXbool flag,FXbool notify){
1688   FXuint opts=(((0-flag)^options)&FILELIST_SHOWDIRS)^options;
1689   if(opts!=options){
1690     options=opts;
1691     listItems(true,notify);
1692     }
1693   }
1694 
1695 
1696 // Return true if showing directories only
showOnlyDirectories() const1697 FXbool FXFileList::showOnlyDirectories() const {
1698   return (options&FILELIST_SHOWDIRS)!=0;
1699   }
1700 
1701 
1702 // Show files only
showOnlyFiles(FXbool flag,FXbool notify)1703 void FXFileList::showOnlyFiles(FXbool flag,FXbool notify){
1704   FXuint opts=(((0-flag)^options)&FILELIST_SHOWFILES)^options;
1705   if(opts!=options){
1706     options=opts;
1707     listItems(true,notify);
1708     }
1709   }
1710 
1711 
1712 // Return true if showing files only
showOnlyFiles() const1713 FXbool FXFileList::showOnlyFiles() const {
1714   return (options&FILELIST_SHOWFILES)!=0;
1715   }
1716 
1717 
1718 // Show parent directories
showParents(FXbool flag,FXbool notify)1719 void FXFileList::showParents(FXbool flag,FXbool notify){
1720   FXuint opts=(((flag-1)^options)&FILELIST_NO_PARENT)^options;
1721   if(opts!=options){
1722     options=opts;
1723     listItems(true,notify);
1724     }
1725   }
1726 
1727 
1728 // Return true if showing parent directories
showParents() const1729 FXbool FXFileList::showParents() const {
1730   return (options&FILELIST_NO_PARENT)==0;
1731   }
1732 
1733 
1734 // Change show image display mode
showImages(FXbool flag,FXbool notify)1735 void FXFileList::showImages(FXbool flag,FXbool notify){
1736   FXuint opts=(((0-flag)^options)&FILELIST_SHOWIMAGES)^options;
1737   if(opts!=options){
1738     options=opts;
1739     listItems(true,notify);
1740     }
1741   }
1742 
1743 
1744 // Return true if displaying image
showImages() const1745 FXbool FXFileList::showImages() const {
1746   return (options&FILELIST_SHOWIMAGES)!=0;
1747   }
1748 
1749 
1750 // Change images preview size
setImageSize(FXint size,FXbool notify)1751 void FXFileList::setImageSize(FXint size,FXbool notify){
1752   if(size!=imagesize){
1753     imagesize=size;
1754     listItems(true,notify);
1755     }
1756   }
1757 
1758 
1759 // Change file associations; delete the old one unless it was shared
setAssociations(FXFileAssociations * assocs,FXbool owned,FXbool notify)1760 void FXFileList::setAssociations(FXFileAssociations* assocs,FXbool owned,FXbool notify){
1761   FXuint opts=options;
1762   options^=((owned-1)^options)&FILELIST_NO_OWN_ASSOC;
1763   if(associations!=assocs){
1764     if(!(opts&FILELIST_NO_OWN_ASSOC)) delete associations;
1765     associations=assocs;
1766     listItems(true,notify);
1767     }
1768   }
1769 
1770 
1771 // Set draggable files
setDraggableFiles(FXbool flg,FXbool notify)1772 void FXFileList::setDraggableFiles(FXbool flg,FXbool notify){
1773   if(draggable!=flg){
1774     draggable=flg;
1775     listItems(true,notify);
1776     }
1777   }
1778 
1779 
1780 // Set file time format
setTimeFormat(const FXString & fmt,FXbool notify)1781 void FXFileList::setTimeFormat(const FXString& fmt,FXbool notify){
1782   if(timeformat!=fmt){
1783     timeformat=fmt;
1784     listItems(true,notify);
1785     }
1786   }
1787 
1788 /*******************************************************************************/
1789 
1790 // Save data
save(FXStream & store) const1791 void FXFileList::save(FXStream& store) const {
1792   FXIconList::save(store);
1793   store << associations;
1794   store << iconloader;
1795   store << big_folder;
1796   store << mini_folder;
1797   store << big_doc;
1798   store << mini_doc;
1799   store << big_app;
1800   store << mini_app;
1801   store << directory;
1802   store << pattern;
1803   store << timeformat;
1804   store << matchmode;
1805   store << imagesize;
1806   store << draggable;
1807   }
1808 
1809 
1810 // Load data
load(FXStream & store)1811 void FXFileList::load(FXStream& store){
1812   FXIconList::load(store);
1813   store >> associations;
1814   store >> iconloader;
1815   store >> big_folder;
1816   store >> mini_folder;
1817   store >> big_doc;
1818   store >> mini_doc;
1819   store >> big_app;
1820   store >> mini_app;
1821   store >> directory;
1822   store >> pattern;
1823   store >> timeformat;
1824   store >> matchmode;
1825   store >> imagesize;
1826   store >> draggable;
1827   }
1828 
1829 
1830 // Cleanup
~FXFileList()1831 FXFileList::~FXFileList(){
1832   getApp()->removeChore(this);
1833   getApp()->removeTimeout(this,ID_OPENTIMER);
1834   getApp()->removeTimeout(this,ID_REFRESHTIMER);
1835   if(!(options&FILELIST_NO_OWN_ASSOC)) delete associations;
1836   delete big_folder;
1837   delete mini_folder;
1838   delete big_doc;
1839   delete mini_doc;
1840   delete big_app;
1841   delete mini_app;
1842   associations=(FXFileAssociations*)-1L;
1843   iconloader=(FXIconSource*)-1L;
1844   list=(FXFileItem*)-1L;
1845   big_folder=(FXIcon*)-1L;
1846   mini_folder=(FXIcon*)-1L;
1847   big_doc=(FXIcon*)-1L;
1848   mini_doc=(FXIcon*)-1L;
1849   big_app=(FXIcon*)-1L;
1850   mini_app=(FXIcon*)-1L;
1851   }
1852 
1853 }
1854