Fixed major issues
[ede] / libede / src / FileSystemBox.ec
1 public import "ecere"
2 import "FileSystemCache"
3
4 #ifdef __WIN32__
5 static char * rootName = "Entire Computer";
6 static const char * msNetwork = "Microsoft Windows Network";
7 #else
8 static const char * rootName = "File System";
9 #endif
10
11 private:
12 define guiApp = (GuiApplication)((__thisModule).application);
13 define selectionColor = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
14
15 void MessageBoxTodo(const char * message)
16 {
17    PrintLn("MessageBoxTodo(char * message) -- ", message);
18    MessageBox { type = ok, text = "MessageBoxTodo(char * message)", contents = message }.Modal();
19 }
20
21 static const char * fileIconNames[] =
22 {
23    "<:ecere>mimeTypes/file.png",         /* none */
24
25    "<:ecere>mimeTypes/file.png",         /* normalFile */
26    "<:ecere>mimeTypes/textEcereWorkspace.png",          /* ewsFile */
27    "<:ecere>mimeTypes/textEcereProject.png",      /* epjFile */
28    "<:ecere>mimeTypes/textEcereSource.png",         /* ecFile */
29    "<:ecere>mimeTypes/textEcereHeader.png",         /* ehFile */
30    "<:ecere>mimeTypes/textCSource.png",          /* cFile */
31    "<:ecere>mimeTypes/textCHeader.png",          /* hFile */
32    "<:ecere>mimeTypes/textC++Source.png",        /* cppFile */
33    "<:ecere>mimeTypes/textC++Header.png",        /* hppFile */
34    "<:ecere>mimeTypes/text.png",         /* textFile */
35    "<:ecere>mimeTypes/textHyperTextMarkup.png",              /* webFile */
36    "<:ecere>mimeTypes/image.png",        /* pictureFile */
37    "<:ecere>status/audioVolumeHigh.png",         /* soundFile */
38    "<:ecere>mimeTypes/package.png",      /* archiveFile */
39    "<:ecere>mimeTypes/packageSoftware.png",     /* packageFile */
40    "<:ecere>mimeTypes/packageOpticalDisc.png", /* opticalMediaImageFile */
41
42    "<:ecere>places/folder.png",                    /* folder */
43    "<:ecere>status/folderOpen.png",               /* folderOpen */
44    "<:ecere>devices/computer.png",                 /* computer */
45    "<:ecere>devices/driveHardDisk.png",           /* drive */
46    "<:ecere>places/driveRemote.png",              /* netDrive */
47    "<:ecere>devices/mediaOptical.png",            /* cdrom */
48    "<:ecere>devices/driveRemovableMedia.png",    /* removable */
49    "<:ecere>devices/mediaFloppy.png",             /* floppy */
50    "<:ecere>places/networkWorkgroup.png",         /* network */
51    "<:ecere>places/networkServer.png",            /* server */
52    "<:ecere>places/folderRemote.png",             /* share */
53
54    "<:ecere>mimeTypes/package.png",      /* treeLoader */
55    "<:ecere>places/startHere.png",                /* lineNumbers */
56
57    ""
58 };
59
60 define countOfCompIconNames = 6;
61 static const char * compIconNames[] =
62 {
63 /*
64    "<:ede>a.png",
65    "<:ede>b.png",
66    "<:ede>c.png",
67    "<:ede>d.png",
68    "<:ede>not.png",
69 */
70    "<:ede>devices/media-optical.png",
71    "<:ede>devices/media-flash.png",
72    "<:ede>places/network-server.png",
73    "<:ede>places/folder-saved-search.png",
74    "<:ede>places/user-home.png",
75    "<:ede>emblem-not.png",
76    ""
77 };
78
79 public enum _FileType
80 {
81    none,
82
83    normalFile, ewsFile, epjFile, ecFile, ehFile, cFile, hFile, cppFile, hppFile,
84    textFile, webFile, pictureFile, soundFile,
85    archiveFile, packageFile, opticalMediaImageFile, /* these (all previous) are sort equal */
86
87    folder, folderOpen, computer,
88    drive, netDrive, cdrom, removable, floppy, network, server, share, // these are sort equal
89
90    // utilities
91    treeLoader,
92    lineNumbers;
93
94    /*property char *
95    {
96       set
97       {
98          this = SelectByExtension(value);
99       }
100    }*/
101
102    public property bool isFolder
103    {
104       get { return this >= folder && this <= share; }
105    }
106
107    public property bool isFile
108    {
109       get { return this >= normalFile && this <= opticalMediaImageFile; }
110    }
111
112    _FileType ::SelectByExtension(char * extension)
113    {
114       if(!strcmpi(extension, "ews"))
115          return ewsFile;
116       else if(!strcmpi(extension, "epj"))
117          return epjFile;
118       else if(!strcmpi(extension, "ec"))
119          return ecFile;
120       else if(!strcmpi(extension, "eh"))
121          return ehFile;
122       else if(!strcmpi(extension, "cpp") ||
123             !strcmpi(extension, "cc") || !strcmpi(extension, "cxx"))
124          return cppFile;
125       else if(!strcmpi(extension, "hpp") ||
126             !strcmpi(extension, "hh") || !strcmpi(extension, "hxx"))
127          return hppFile;
128       else if(!strcmpi(extension, "c"))
129          return cFile;
130       else if(!strcmpi(extension, "h"))
131          return hFile;
132       else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
133             !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
134          return textFile;
135       else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
136             !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
137             !strcmpi(extension, "js"))
138          return webFile;
139       else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
140             !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
141             !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
142             !strcmpi(extension, "ico"))
143          return pictureFile;
144       else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
145             !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
146          return soundFile;
147       else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
148             !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
149             !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
150             !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
151             !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
152             !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
153          return archiveFile;
154       else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
155             !strcmpi(extension, "rpm"))
156          return packageFile;
157       else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
158             !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
159             !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
160             !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
161          return opticalMediaImageFile;
162       return normalFile;
163    }
164 };
165
166 public enum FileSystemBoxMode { directory, list };
167
168 class FileSystemBoxBits
169 {
170    bool foldersOnly:1, filesOnly:1, details:1, pathColumn:1, treeBranches:1, previewPictures:1, navigateFolders:1, autoLoad:1;
171    bool preview:1;
172    bool columnsCompareStyle:1;
173    //bool header:1, freeSelect:1, fullRowSelect:1, multiSelect:1, autoScroll:1, alwaysHL : 1, moveRows:1, resizable:1;
174    //bool moveFields:1, clearHeader:1, alwaysEdit:1, collapse:1, treeBranch:1, rootCollapse:1, heightSet:1;
175    //bool sortable:1, noDragging:1, fillLastField:1, expandOnAdd:1;
176    bool textFileLinesStyle:1;
177
178    FileSystemBoxMode mode:2;
179 }
180
181 public class FileSystemBox : Window // should we not derive from ListBox instead?
182                                     // I say we should, but we can't right now...
183                                     // because ListBox (inside ecere library) is
184                                     // not exposing enough internal machinery...
185                                     // could we not have a different private and
186                                     // public mechanism when deriving a class than
187                                     // we do when simply instanciating a class?
188 /*
189    this stuff from the listbox would be nicely exposed...
190       fullRowSelect = false;
191       treeBranches = true;
192       collapseControl = true;
193       rootCollapseButton = true;
194       sortable = true;
195 */
196 {
197    borderStyle = deep;
198    hasHorzScroll = false;
199    hasVertScroll = false;
200
201    menu = Menu { };
202
203 public:
204    FileSystemNode root;
205    FileSystemBoxSelection selection { };
206
207    subclass(InterfaceFileSystemIterator) iteratorClass;
208    FileSystemCache cache;
209
210    virtual bool Window::NotifyNodeSelect(FileSystemBox box, FileSystemBoxSelection selection);
211    //virtual bool Window::NotifyNodeNavigate(FileSystemBox box, FileSystemNode node);
212    virtual bool Window::NotifyNodeOpen(FileSystemBox box, FileSystemBoxSelection selection);
213    virtual bool Window::NotifyNodeMenu(FileSystemBox box, Menu menu, FileSystemBoxSelection selection);
214    virtual bool Window::NotifyIteratorInit(FileSystemBox box, FileSystemIterator fileSystemIterator);
215
216    property const char * path
217    {
218       set
219       {
220          delete path;
221          if(value && value[0])
222             path = CopyString(value);
223          if(locationBox)
224             locationBox.path = value;
225          if(created)
226             Load();
227       }
228       get { return path; }
229       //isset { return path && path[0]; }
230    }
231
232    property Array<String> comparedPaths
233    {
234       set
235       {
236          if(comparedPaths)
237          {
238             comparedPaths.Free();
239             delete comparedPaths;
240          }
241          if(value && value.count)
242             comparedPaths = value;
243          if(locationBox)
244             locationBox.path = value[0];
245          if(created)
246             Load();
247       }
248       get { return comparedPaths; }
249       //isset { return comparedPaths && comparedPaths.count; }
250    }
251
252    property FileSystemBoxMode mode { set { bits.mode = value; } get { return bits.mode; } };
253    property bool foldersOnly { set { bits.foldersOnly = value; bits.filesOnly = !value; } get { return bits.foldersOnly; } };
254    property bool filesOnly { set { bits.filesOnly = value; bits.foldersOnly = !value; } get { return bits.filesOnly; } };
255    property bool previewPictures { set { bits.previewPictures = value; } get { return bits.previewPictures; } };
256    property char * extensions { set { delete extensions; if(value && value[0]) extensions = CopyString(value); } get { return extensions; } }
257    property bool details { set { bits.details = value; ChangeViewType(); } get { return bits.details; } };
258    property bool pathColumn { set { bits.pathColumn = value; ChangeViewType(); } get { return bits.pathColumn; } };
259    property bool treeBranches
260    {
261       set
262       {
263          bits.treeBranches = value;
264          list.treeBranches = value;
265          list.collapseControl = value;
266          list.rootCollapseButton = value;
267       }
268       get { return bits.treeBranches; }
269    };
270    property Color selectionColor { set { list.selectionColor = value; } get { return list.selectionColor; }/* isset { return selectionColor ? true : false; }*/ };
271    property Color selectionText  { set { list.selectionText = value; } get { return list.selectionText; }/* isset { return selectionText ? true : false; }*/ };
272    property bool navigateFolders { set { bits.navigateFolders = value; bits.filesOnly = !value; } get { return bits.navigateFolders; } };
273    property bool multiSelect { set { list.multiSelect = value; } get { return list.multiSelect; } };
274    property bool autoLoad { set { bits.autoLoad = value; } get { return bits.autoLoad; } };
275    property bool hasHeader { set { list.hasHeader = value; } get { return list.hasHeader; } };
276    property bool preview
277    {
278       set
279       {
280          bits.preview = value;
281          split.leftPane = value ? list : null;
282          split.visible = value;
283          show.visible = value;
284          if(!value)
285          {
286             list.parent = this;
287             list.anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
288          }
289       }
290       get { return bits.preview; }
291    };
292    property bool columnsCompareStyle { set { bits.columnsCompareStyle = value; } get { return bits.columnsCompareStyle; } };
293    property bool textFileLinesStyle { set { bits.textFileLinesStyle = value; } get { return bits.textFileLinesStyle; } };
294
295    property FileSystemNode node
296    {
297       get
298       {
299          if(!list)
300             return null;
301          if(!list.currentRow)
302             return null;
303          if(!list.currentRow.tag)
304             return null;
305          return (FileSystemNode)list.currentRow.tag;
306       }
307    }
308
309    PathBox locationBox;
310
311    void Select(FileSystemNode node)
312    {
313       if(node.row)
314       {
315          node.EnsureVisible(false);
316          list.SelectRow(node.row);
317       }
318    }
319
320    void SelectMultipleByPath(Array<String> paths)
321    {
322       DataRow row;
323       bool firstRow = false;
324       Map<String, bool> map { };
325       for(path : paths)
326          map[path] = true;
327       for(row = list.firstRow; row; row = row.next)
328       {
329          FileSystemNode node = (FileSystemNode)row.tag;
330          if(map[node.path])
331          {
332             if(!firstRow)
333             {
334                list.SelectRow(row);
335                firstRow = true;
336             }
337             else
338                row.selected = true;
339          }
340          else
341             row.selected = false;
342       }
343       delete map;
344    }
345
346    FileSystemNode SelectLocation(const char * location)
347    {
348       int c;
349       char * temp;
350       char step[MAX_LOCATION];
351
352       //StringArray steps { growingFactor = 4 };
353       Array<String> steps { };
354       FileSystemNode result = null;
355       FileSystemNode node = null;
356
357       temp = CopyString(location);
358       while(temp[0])
359       {
360          GetLastDirectory(temp, step);
361          StripLastDirectory(temp, temp);
362          steps.Add(CopyString(step));
363       }
364
365       for(c = steps.count - 1; c >= 0; c--)
366       {
367          //char * t = steps[c];
368          node = Find(steps[c], node);
369          if(!node)
370             break;
371          //Select(node);
372       }
373       if(node)
374       {
375          result = node;
376          Select(result);
377       }
378
379       steps.Free();
380       delete temp;
381       delete steps;
382
383       return result;
384    }
385
386    FileSystemNode Find(const char * name, FileSystemNode parent)
387    {
388       FileSystemNode node = null;
389       FileSystemNode result = null;
390       if(!parent/* && !strcmp(name, "/")*/)
391       {
392          DataRow row;
393          for(row = list.firstRow; row; row = row.next)
394          {
395             node = (FileSystemNode)row.tag;
396             if(node.name && !fstrcmp(node.name, name))
397                break;
398          }
399          if(node)
400             result = node;
401          //result = root;
402       }
403       else
404       {
405          FileSystemNode start = parent ? parent : root;
406          if(!start.bits.loaded || !start.bits.childrenLoaded)
407             LoadTreeNode(start);
408          for(node = start.children.first; node; node = node.next)
409             if(node.name && !fstrcmp(node.name, name))
410                break;
411          if(node)
412             result = node;
413       }
414       return result;
415    }
416
417    void Clear()
418    {
419       list.Clear();
420    }
421
422    void Refresh()
423    {
424       Load();
425    }
426
427    bool MenuOpen(MenuItem selection, Modifiers mods)
428    {
429       OpenNode();
430       return true;
431    }
432
433    bool MenuReplaceListItemByContainingDir(MenuItem selection, Modifiers mods)
434    {
435       bool result = true;
436       //FileSystemBoxSelection selection = this.selection.Copy(); // TOFIX compiler bug -- FileSystemBox.c -- ../libede/src/FileSystemBox.ec:420:49: error: â€˜selection’ redeclared as different kind of symbol        
437       FileSystemBoxSelection sel = this.selection.Copy();
438       FileSystemNode node = sel.node;
439       char newPath[MAX_LOCATION];
440       StripLastDirectory(node.path, newPath);
441       //node.path = newPath;
442       //node.
443       //if(node && node.type.isFolder && bits.navigateFolders)
444       //   property::path = node.path;
445       delete sel;
446       return result;
447    }
448
449    bool MenuReplaceListItemByChild(MenuItem selection, Modifiers mods)
450    {
451       bool result = true;
452       //FileSystemBoxSelection selection = this.selection.Copy(); // TOFIX compiler bug -- FileSystemBox.c -- ../libede/src/FileSystemBox.ec:420:49: error: â€˜selection’ redeclared as different kind of symbol        
453       FileSystemBoxSelection sel = this.selection.Copy();
454       FileSystemNode node = sel.node;
455       char newPath[MAX_LOCATION];
456       StripLastDirectory(node.path, newPath);
457       //node.path = newPath;
458       //node.
459       //if(node && node.type.isFolder && bits.navigateFolders)
460       //   property::path = node.path;
461       delete sel;
462       return result;
463    }
464
465 private:
466    FileSystemBoxBits bits;
467
468    char * path;
469    char * extensions;
470    Array<String> comparedPaths;
471
472    BitmapResource fileIcons[_FileType];
473    BitmapResource compIcons[countOfCompIconNames]; // todo: fix this limitation
474
475    Bitmap bitmap;
476    //BitmapArray bitmaps { growingFactor = 16 };
477
478    FileSystemBox()
479    {
480       char wd[MAX_LOCATION];
481       GetWorkingDir(wd, sizeof(wd));
482       property::path = wd;
483
484       InitFileIcons();
485       InitCompIcons(); // todo: these icons should not be initialize, they should be set
486                        //       or at least initalized when the comparison listing is requested
487                        //       and we know how many paths are being compared.
488       list.AddField(nameField);
489       bits.autoLoad = true;
490       //iteratorClass = class(FileSystemIterator);
491    }
492
493    ~FileSystemBox()
494    {
495       if(comparedPaths)
496       {
497          comparedPaths.Free();
498          delete comparedPaths;
499       }
500       delete extensions;
501       delete path;
502    }
503
504    watch(background)
505    {
506       list.background = background;
507    };
508
509    void InitFileIcons()
510    {
511       _FileType c;
512       for(c = 0; c < _FileType::enumSize; c++)
513       {
514          fileIcons[c] = BitmapResource { fileIconNames[c], alphaBlend = true };
515          AddResource(fileIcons[c]);
516       }
517    }
518
519    void InitCompIcons()
520    {
521       _FileType c;
522       for(c = 0; c < countOfCompIconNames; c++)
523       {
524          compIcons[c] = BitmapResource { compIconNames[c], alphaBlend = true };
525          AddResource(compIcons[c]);
526       }
527    }
528
529    DataField nameField { header = "Name", dataType = "FileSystemNode", width = 240, userData = this, freeData = false/*, editable = true*/; };
530    DataField pathField { header = "Location", dataType = /*"String"*/ "char *", width = 300, freeData = true };
531    DataField typeField { header = "Type", dataType = /*"String"*/ "char *", width = 40, freeData = false };
532    DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right, freeData = false };
533    DataField modifiedField { header = "Modified", dataType = "SecSince1970", width = 96, alignment = right, freeData = false };
534
535    bool OnPostCreate()
536    {
537       if(bits.autoLoad)
538       {
539          if(!path)
540          {
541             char wd[MAX_LOCATION];
542             GetWorkingDir(wd, sizeof(wd));
543             property::path = wd;
544          }
545          Load();
546       }
547       return true;
548    }
549
550    ListBox list
551    {
552       this;
553
554       borderStyle = none;
555       hasHorzScroll = true;
556       hasVertScroll = true;
557       fullRowSelect = false;
558       sortable = true;
559       alwaysHighLight = true;
560
561       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
562
563       // WHY is this not working ?
564       /*void OnResize(int width, int height)
565       {
566          if(vertScroll.visible)
567             nameField.width = width - vertScroll.size.w;
568          else
569             nameField.width = width;
570       }*/
571
572       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
573       {
574          if(row)
575          {
576             FileSystemNode node = (FileSystemNode)row.tag;
577             FileSystemNode child;
578             if(collapsed)
579             {
580                /*
581                for(child = node.children.last; child; child = node.children.last)
582                {
583                   listBox.DeleteRow(child.row);
584                   child.Free();
585                   delete child;
586                }
587                node.childrenLoaded = false;
588                */
589             }
590             else
591             {
592                if(!node.bits.loaded || !node.bits.childrenLoaded)
593                {
594                   LoadTreeNode(node);
595                   //list.Sort(nameField, 1);
596                   //node.
597                }
598                for(child = node.children.first; child && child.next; child = child.next);
599                if(child)
600                   child.EnsureVisible(false);
601             }
602          }
603          return true;
604       }
605
606       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
607       {
608          DataRow row = listBox.currentRow;
609          if(row)
610          {
611             FileSystemNode node = (FileSystemNode)row.tag;
612             if(node)
613             {
614                PopupMenu popup;
615                Menu menu { };
616
617                if(NotifyNodeMenu)
618                   NotifyNodeMenu(master, this, menu, selection);
619                else
620                {
621                   char * text;
622
623                   text = PrintString("Open ", node.name);
624                   MenuItem { menu, text, o, NotifySelect = MenuOpen, disabled = false }; //delete text;
625
626                   if(node.bits.isListItem/* && TODO: unless node is at root location*/)
627                   {
628                      MenuDivider { menu };
629                      MenuItem { menu, "Replace by Parent\tCtrl+R", r, NotifySelect = MenuReplaceListItemByContainingDir, disabled = false };
630                   }
631                   else if(bits.mode == list)
632                   {
633                      MenuDivider { menu };
634                      MenuItem { menu, "Replace List Item\tCtrl+R", r, NotifySelect = MenuReplaceListItemByChild, disabled = false };
635                   }
636                   MenuDivider { menu };
637                   MenuItem { menu, "Cut\tCtrl+X", t, NotifySelect = null, disabled = false };
638                   MenuItem { menu, "Copy\tCtrl+C", c, NotifySelect = null, disabled = false };
639                   MenuItem { menu, "Paste\tCtrl+V", p, NotifySelect = null, disabled = false /*!clipboard*/ };
640                   MenuItem { menu, "Delete\tDel", d, NotifySelect = null, disabled = false };
641                   //MenuDivider { menu };
642                }
643
644                popup = PopupMenu
645                   {
646                      master = this, menu = menu,
647                      position = {
648                         x + clientStart.x + absPosition.x - guiApp.desktop.position.x,
649                         y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
650                   };
651                popup.Create();
652             }
653          }
654          return true;
655       }
656
657       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
658       {
659          OldList rows;
660          OldLink item;
661
662          selection.nodes.Free();
663          list.GetMultiSelection(rows);
664          for(item = rows.first; item; item = item.next)
665          {
666             DataRow row = item.data;
667             FileSystemNode node = (FileSystemNode)row.tag;
668             selection.nodes.Add(node);
669             incref node;
670          }
671          rows.Free(null);
672          if(row)
673             selection.node = (FileSystemNode)row.tag;
674          else
675             selection.node = null;
676
677          if(selection.node && bits.preview)
678          {
679             //int pos;
680             FileSystemNode node = selection.node;
681             /*if(bitmap)
682                bitmap.Free();*/
683             delete bitmap;
684             if(node && node.type == pictureFile)
685             {
686                bitmap = Bitmap { };
687                bitmap.Load(node.path, null, displaySystem);
688             }
689
690             /*bitmaps.Clear();
691             bitmaps = BitmapArray { };
692             for(pos = 0; pos < selectedItems.count; pos++)
693             {
694                Bitmap bitmap { };
695                selItem = (ExplorerFileItem)selectedItems._[pos];
696                bitmap.Load(selItem.path, null, displaySystem);
697                //bitmaps.Add(bitmap);
698             }
699             if(node && node.type == pictureFile)
700             {
701                bitmap = Bitmap { };
702                bitmap.Load(node.path, null, displaySystem);
703             }*/
704
705             show.Update(null);
706             //NotifyItemSelect(master, view, item, selectedItems);
707          }
708
709          NotifyNodeSelect(listBox.parent.master, this, selection);
710          return true;
711       }
712
713       bool NotifyEditing(ListBox listBox, DataRow row)
714       {
715          if(row)
716          {
717             //FileSystemNode node = (FileSystemNode)row.tag;
718          }
719          return true;
720       }
721
722       bool NotifyEdited(ListBox listBox, DataRow row)
723       {
724          if(row)
725          {
726             //FileSystemNode node = (FileSystemNode)row.tag;
727          }
728          return true;
729       }
730
731       bool NotifyEditDone(ListBox listBox, DataRow row)
732       {
733          if(row)
734          {
735             //FileSystemNode node = (FileSystemNode)row.tag;
736          }
737          return true;
738       }
739
740       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
741       {
742          bool result = !(selection.node && selection.node.type.isFolder && bits.navigateFolders);
743          OpenNode();
744          return result;
745       }
746
747       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
748       {
749          //bool result = false;
750          if((SmartKey)key == enter)
751             /*result = */OpenNode();
752          #if 0
753          else if((SmartKey)key == f2)
754             /*result = */RenameNode();
755          #endif
756          else if((SmartKey)key == f2)
757          {
758             FileSystemNode node = selection.node;
759             node.row.Edit(nameField);
760          }
761          //else
762             //result = true;
763          return true;
764       }
765    };
766
767    PaneSplitter split
768    {
769       this;
770       //leftPane = list;
771       rightPane = show;
772       //split = 200;
773       scaleSplit = 0.5f;
774       tabCycle = true;
775       visible = false;
776    };
777
778    Window show
779    {
780       this;
781       visible = false;
782       borderStyle = none;
783       anchor = Anchor { top = 0, right = 0, bottom = 0 };
784
785       void OnRedraw(Surface surface)
786       {
787          FileSystemBox fsb = (FileSystemBox)parent;
788          if(fsb.bitmap)
789          {
790             int wBmp = fsb.bitmap.width;
791             int hBmp = fsb.bitmap.height;
792             int wWnd = fsb.show.clientSize.w;
793             int hWnd = fsb.show.clientSize.h;
794
795             //int wList = 0;//fsb.list.size.w + fsb.split.size.w;
796
797             //float scale = Min((float)(wWnd - 10) / wBmp, (float)(hWnd - 10) / hBmp);
798
799             //int wDraw = (int)(wBmp * scale);
800             //int hDraw = (int)(hBmp * scale);
801
802       #ifndef __linux__
803             surface.Filter(fsb.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw, wBmp, hBmp);
804       #else
805             // Until Filter / Stretch works with X
806             surface.Blit(fsb.bitmap, (wWnd - wBmp) / 2, (hWnd - hBmp) / 2, 0, 0, wBmp, hBmp);
807       #endif
808          }
809          else
810          {
811             surface.SetForeground(white);
812             surface.Area(0, 0, fsb.clientSize.w - 1, fsb.clientSize.h - 1);
813          }
814       }
815    };
816
817    bool OpenNode()
818    {
819       bool result = false;
820       FileSystemBoxSelection sel = this.selection.Copy();
821       //FileSystemNode node = selection.node;
822       for(node : sel.nodes)
823       {
824          sel.node = node;
825          if(node && node.type.isFolder && bits.navigateFolders)
826             property::path = node.path;
827          if(NotifyNodeOpen(this.master, this, sel) && !result)
828             result = true;
829       }
830       delete sel;
831       return result;
832    }
833
834    #if 0
835    bool RenameNode()
836    {
837       bool result;
838       //FileSystemBoxSelection selection = this.selection.Copy();
839       FileSystemNode node = selection.node;
840       //if(node && node.type.isFolder && bits.navigateFolders)
841       //   property::path = node.path;
842       // ------------------------------------------- working here ---------------------------
843       //node.row.Edit();
844       /*result = NotifyNodeRename(this.master, this, node);
845       if(result)
846       {
847          if(RenameFile(oldn, newn))
848          {
849             node.name = newn;
850          }
851       }*/
852       return result;
853    }
854    #endif
855
856    // Edit Menu
857    Menu editMenu { menu, "Edit", e };
858    MenuItem itemEditCut
859    {
860       editMenu, "Cut\tCtrl+X", t, disabled = true;
861
862       bool NotifySelect(MenuItem selection, Modifiers mods)
863       {
864          //EditCut();
865          return true;
866       }
867    };
868    MenuItem itemEditCopy
869    {
870       editMenu, "Copy\tCtrl+C", c, disabled = true;
871
872       bool NotifySelect(MenuItem selection, Modifiers mods)
873       {
874          //EditCopy();
875          return true;
876       }
877    };
878    MenuItem itemEditPaste
879    {
880       editMenu, "Paste\tCtrl+V", p;
881
882       bool NotifySelect(MenuItem selection, Modifiers mods)
883       {
884          //EditPaste();
885          return true;
886       }
887    };
888    MenuItem itemEditDelete
889    {
890       editMenu, "Delete\tDel", d, disabled = true;
891
892       bool NotifySelect(MenuItem selection, Modifiers mods)
893       {
894          //EditDelete();
895          return true;
896       }
897    };
898
899    // WHY is this crashing ?
900    /*void OnResize(int width, int height)
901    {
902       if(this && nameField)
903          nameField.width = width - 80;
904    }*/
905
906    void ChangeViewType()
907    {
908       list.Clear();
909       list.ClearFields();
910       list.resizable = bits.details || bits.pathColumn;
911       list.moveFields = bits.details || bits.pathColumn;
912       list.hasHeader = bits.details || bits.pathColumn;
913       list.AddField(nameField);
914       if(bits.pathColumn)
915          list.AddField(pathField);
916       if(bits.details)
917       {
918          list.AddField(typeField);
919          list.AddField(sizeField);
920          list.AddField(modifiedField);
921       }
922    }
923
924    void Load()
925    {
926       // TODO: fix this!
927       // this is crashing in for designer when details = true // can't save the file, always yields a crash
928       /*if(list && created)
929       {
930          list.ClearFields();
931          list.AddField(nameField);
932          if(bits.details)
933          {
934             list.AddField(typeField);
935             list.AddField(sizeField);
936          }
937       }*/
938       list.Clear();
939       if(comparedPaths && !bits.treeBranches)
940          LoadComparedList();
941       else
942       {
943          FileAttribs pathAttribs = FileExists(path);
944          if(pathAttribs)
945          {
946             if(pathAttribs.isDirectory)
947             {
948                if(bits.treeBranches)
949                   LoadTreeDirectory();
950                else
951                {
952                   if(iteratorClass)
953                      LoadListIterator();
954                   else
955                      LoadListDirectory();
956                }
957             }
958             else if(pathAttribs.isFile) // we assume this is a file list
959             {
960                File f = FileOpen(path, read);
961                if(f)
962                {
963                   if(bits.treeBranches)
964                      LoadTreeFileList(f);
965                   else
966                      LoadListFileList(f);
967                   delete f;
968                }
969                else
970                   MessageBoxTodo($"unable to open file list");
971             }
972             else
973                MessageBoxTodo($"path is not a directory nor is it a file");
974          }
975          else
976             MessageBoxTodo($"path does not exist");
977       }
978       list.Sort(nameField, 1);
979    }
980
981    void LoadTreeDirectory()
982    {
983       bool isRoot = !strcmp(path, "/");
984       char name[MAX_LOCATION];
985       //FileSystemNode parent;
986       //FileSystemNode node;
987       //FileListing listing { path, extensions = extensions };
988
989       if(!isRoot)
990          GetLastDirectory(path, name);
991       else
992          name[0] = '\0';
993
994       /*if(!path)
995          GetWorkingDir(startPath, sizeof(startPath));
996       else
997          strcpy(path, startPath);*/
998       bits.mode = directory;
999
1000       list.Clear();
1001
1002       delete root;
1003 #ifdef __WIN32__
1004       if(isRoot)
1005       {
1006          root = FileSystemNode { bits.loaded = true, bits.childrenLoaded = true };
1007          AddTreeNode(root, true, false, false, null);
1008          while(listing.Find())
1009          {
1010             int len = strlen(listing.name);
1011             char info[MAX_LOCATION];
1012             char name[MAX_LOCATION];
1013             if(listing.stats.attribs.isDrive &&
1014                   len > 3 && !strncmp(&listing.name[1], ": [", 3))
1015             {
1016                strncpy(name, listing.name, 2);
1017                name[2] = 0;
1018                strncpy(info, &listing.name[4], len - 5);
1019                info[len - 5] = 0;
1020             }
1021             else
1022             {
1023                strcpy(name, listing.name);
1024                info[0] = 0;
1025             }
1026
1027             parent =
1028                   MakeFileSystemNode(
1029                         listing.stats,
1030                         name,
1031                         listing.path,
1032                         false, bits.previewPictures, false,
1033                         displaySystem);
1034             if(info[0])
1035                parent.info = info; //CopyString(info);
1036             parent.bits.loaded = true;
1037             AddTreeNode(
1038                   parent,
1039                   !listing.stats.attribs.isDirectory,
1040                   false,
1041                   listing.stats.attribs.isDirectory,
1042                   root);
1043             if(!listing.stats.attribs.isDirectory)
1044                parent.bits.childrenLoaded = true;
1045          }
1046
1047          node = FileSystemNode { name = msNetwork, type = network };
1048          AddTreeNode(node, false, false, true, null);
1049          node.row.collapsed = true;
1050       }
1051       else
1052 #endif
1053       {
1054          FileStats stats;
1055          FileGetStats(path, stats);
1056          root = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, false, displaySystem);
1057          AddTreeNode(root, false, false, true, null);
1058          LoadTreeNode(root);
1059       }
1060
1061       if(isRoot)
1062       {
1063          root.type = computer;
1064          root.label = rootName;
1065       }
1066
1067       list.Sort(nameField, 1);
1068       list.SelectRow(root.row);
1069    }
1070
1071    void LoadListDirectory()
1072    {
1073       FileListing listing { path, extensions = extensions };
1074
1075       bits.mode = directory;
1076       while(listing.Find())
1077          ProcessListItem(listing.name, listing.path, listing.stats, false);
1078    }
1079
1080    void LoadListFileList(File f)
1081    {
1082       char line[65536];
1083       bits.mode = list;
1084       while(f.GetLine(line, 65536))
1085       {
1086          FileStats stats {};
1087          char name[MAX_FILENAME];
1088          FileGetStats(line, stats);
1089          GetLastDirectory(line, name);
1090          ProcessListItem(name, line, stats, true);
1091       }
1092    }
1093
1094    void LoadTreeFileList(File f)
1095    {
1096       char line[65536];
1097       bits.mode = list;
1098       while(f.GetLine(line, 65536))
1099       {
1100          FileStats stats {};
1101          char name[MAX_FILENAME];
1102          FileSystemNode node;
1103          FileGetStats(line, stats);
1104          GetLastDirectory(line, name);
1105          node = ProcessTreeItem(name, line, stats, node);
1106       }
1107    }
1108
1109    void LoadListIterator()
1110    {
1111       FileSystemIterator iterator = eInstance_New(iteratorClass);
1112       if(iterator)
1113       {
1114          iterator.owner = this;
1115          iterator.OnObject = ListIterator_OnObject;
1116          //iterator.OnLeavingDirectory = ListIterator_OnLeavingDirectory;
1117          NotifyIteratorInit(master, this, iterator);
1118          iterator.Iterate(path, true);
1119          delete iterator;
1120       }
1121    }
1122
1123    bool ListIterator_OnObject(const char * name, const char * path, FileStats stats, bool isRootObject)
1124    {
1125       ProcessListItem(name, path, stats, false);
1126       return false;
1127    }
1128
1129    //void ListIterator_OnLeavingDirectory(char * path) { }
1130
1131    void ProcessListItem(const char * name, const char * path, FileStats stats, bool isListItem)
1132    {
1133       if((!bits.foldersOnly && !bits.filesOnly) || (bits.foldersOnly && stats.attribs.isDirectory) || (bits.filesOnly && stats.attribs.isFile))
1134       {
1135          FileSystemNode node = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, isListItem, displaySystem);
1136          AddNode(node);
1137       }
1138    }
1139
1140    FileSystemNode ProcessTreeItem(char * name, char * path, FileStats stats, FileSystemNode parent)
1141    {
1142       FileSystemNode node = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, true, displaySystem);
1143       AddTreeNode(parent, false, false, true, null);
1144       //LoadTreeNode(node);
1145       return node;
1146    }
1147
1148    void LoadTreeNode(FileSystemNode node)
1149    {
1150       if(!node.bits.loaded)
1151       {
1152          char path[MAX_LOCATION];
1153          node.GetPath(path);
1154          {
1155             FileListing listing { path, extensions = extensions };
1156             if(node.children.count == 1)
1157                DeleteNode(node.children.first);
1158
1159             while(listing.Find())
1160             {
1161                char * test;
1162                FileSystemNode child = null;
1163                if(!listing.stats.attribs.isRemovable && ((!bits.foldersOnly && !bits.filesOnly) ||
1164                   (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
1165                   (bits.filesOnly && listing.stats.attribs.isFile)))
1166                   child = MakeAndAddToTreeFileSystemNodeFromFileListing(listing, node);
1167                if(child)
1168                   NodeChildLoad(child, node);
1169                test = child.name;
1170                if(!test)
1171                   PrintLn("error");
1172             }
1173          }
1174          node.bits.childrenLoaded = true;
1175          node.bits.loaded = true;
1176          node.row.SortSubRows(false);
1177       }
1178       else if(!node.bits.childrenLoaded)
1179       {
1180          FileSystemNode child;
1181          if(node.children.first)
1182          {
1183             for(child = node.children.first; child; child = child.next)
1184             {
1185                if(!child.bits.loaded)
1186                   LoadTreeNode(child);
1187                else if(!child.bits.childrenLoaded)
1188                   NodeChildLoad(child, node);
1189             }
1190             node.bits.childrenLoaded = true;
1191             node.row.SortSubRows(false);
1192          }
1193       }
1194    }
1195
1196    void NodeChildLoad(FileSystemNode parent, FileSystemNode node)
1197    {
1198       char path[MAX_LOCATION];
1199       parent.GetPath(path);
1200       if(bits.textFileLinesStyle && FileExists(path).isFile)
1201       {
1202          PrintLn("Test");
1203       }
1204       else
1205       {
1206          bool added = false;
1207          FileListing listing { path, extensions = extensions };
1208          while(listing.Find())
1209          {
1210             //char * test;
1211             FileSystemNode child = null;
1212             if((!bits.foldersOnly && !bits.filesOnly) ||
1213                (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
1214                (bits.filesOnly && listing.stats.attribs.isFile))
1215                child = MakeAndAddToTreeFileSystemNodeFromFileListing(listing, parent);
1216             if(child)
1217             {
1218                added = true;
1219                /*
1220                test = child.name;
1221                if(!test)
1222                   PrintLn("error");
1223                */
1224             }
1225          }
1226          if(!added)
1227             added = true;
1228       }
1229       parent.bits.childrenLoaded = true;
1230    }
1231
1232    void LoadComparedList()
1233    {
1234       int c/*, cmp*/ /*, smallest*/, icon;//, equalCount;
1235       int count = comparedPaths ? comparedPaths.count : 0;
1236       //bool allDone = false;
1237       bool not;
1238       FileStats stats;
1239       char path[MAX_LOCATION];
1240       //Array<ComparisonState> states { };
1241       //Array<FileListing> listings { };
1242       //Array<int> equals { };
1243       //Array<MapNode<String, int>> mapNodes { };
1244
1245       //Array<Map<String, int>> lists { };
1246       //Map<int, bool> equals{ };
1247
1248
1249       MapNode<String, /*Map<int, */Array<int>> na;
1250       //MapNode<int, int> nb;
1251       Map<String, /*Map<int, */Array<int>> names { };
1252       //Map<String, Array<bool>> names { };
1253       //Map<String, bool[16]> names { }; // does not seem to be working
1254       //Map<String, BoolArrayInt> names { };
1255       {
1256          for(c = 0; c < comparedPaths.count; c++)
1257          {
1258             FileListing listing { comparedPaths[c], extensions = extensions };
1259             while(listing.Find())
1260             {
1261                /*Map<int, int>*/Array<int> m = names[listing.name];
1262                if(!m)
1263                   m = { };
1264                names[listing.name] = m;
1265                /*/m[c] = */m.Add(c);
1266             }
1267          }
1268          /* // compiles and should work but better solution?
1269          for(c = 0; c < comparedPaths.count; c++)
1270          {
1271             FileListing listing { comparedPaths[c], extensions = extensions };
1272             while(listing.Find())
1273             {
1274                Array<bool> a = names[listing.name];
1275                if(!a)
1276                   a = { };
1277                names[listing.name] = a;
1278                a[c] = true;
1279             }
1280          }
1281          */
1282          /* // does not seem to be working
1283          for(c = 0; c < comparedPaths.count; c++)
1284          {
1285             FileListing listing { comparedPaths[c], extensions = extensions };
1286             while(listing.Find())
1287             {
1288                names[listing.name][c] = true;
1289             }
1290          }
1291          */
1292          /*
1293          if(comparedPaths.count > 0)
1294          {
1295             FileListing listing { comparedPaths[0], extensions = extensions };
1296             while(listing.Find())
1297             {
1298                // should be able to just do names[listing.name]._0 = true;
1299                BoolArrayInt bai = names[listing.name];
1300                bai._0 = true;
1301                names[listing.name] = bai;
1302             }
1303          }
1304          if(comparedPaths.count > 1)
1305          {
1306             FileListing listing { comparedPaths[1], extensions = extensions };
1307             while(listing.Find())
1308             {
1309                // should be able to just do names[listing.name]._1 = true;
1310                BoolArrayInt bai = names[listing.name];
1311                bai._1 = true;
1312                names[listing.name] = bai;
1313             }
1314          }
1315          */
1316          // and so on....
1317       }
1318
1319       /*
1320       for(dirPath : comparedPaths)
1321       {
1322          char * p = dirPath;
1323          if(FileExists(dirPath).isDirectory)
1324          {
1325             FileListing listing { dirPath, extensions = extensions };
1326             //MapNode<String, int> mn;
1327             Map<String, int> list { };
1328             //states.Add(listing.Find() == true ? matching : endOfListing);
1329             while(listing.Find())
1330                list[listing.name] = 0;
1331             //for(mn = ; mn; mn = mn.next)
1332             //mn = list.root.minimum;
1333             mapNodes.Add(/-*mn*-/list.root.minimum);
1334             lists.Add(list);
1335             //PrintLn(dirPath, " -- .Find() -- ", states[states.count-1] == matching ? listing.name : "endOfListing*");
1336             //listings.Add(listing);
1337
1338             {
1339                MapNode<String, int> mn;
1340                PrintLn("------------- DIR LISTING FOR ", dirPath);
1341                for(mn = list.root.minimum; mn; mn = mn.next)
1342                {
1343                   PrintLn(mn.key);
1344                }
1345             }
1346          }
1347       }
1348       */
1349
1350       for(na = names.root.minimum; na; na = na.next)
1351       {
1352          /*Map<int, */Array<int> equals = na.value;
1353          //MapNode<int, int> nb;
1354       /*
1355       while(!allDone)
1356       {
1357          smallest = 0;
1358          equals.Add(0);
1359          for(c = 1; c < count; c++)
1360          {
1361             //if(states[c] == endOfListing) continue;
1362             if(!mapNodes[c]) continue;
1363             // todo: use better comparison method
1364             //       it should compare file type (dir/file) before
1365             //       comparing file name.
1366             //       should also provide alternative methods
1367             //       of comparison including ones that consider
1368             //       date changes as differences of some kind.
1369             //       pethaps a OnCompare(a, b) to allow implementation
1370             //       of custom methods of comparison.
1371             // note: this (or these) method(s) depend on files
1372             //       being listed in sorted order by FileListing.
1373             //       different comparison methods should have
1374             //       appropriatly different sorting in FileListing.
1375             //
1376             //cmp = strcmp(listings[smallest].name, listings[c].name);
1377             cmp = fstrcmp(mapNodes[smallest].key, mapNodes[c].key);
1378             PrintLn("COMPARING - ", mapNodes[smallest].key, " and ", mapNodes[c].key);
1379             if(cmp == 0)
1380                equals.Add(c);
1381                //equals[c] = true;
1382             else if(cmp > 0)
1383             {
1384                smallest = c;
1385                equals.size = 0;
1386                equals.Add(c);
1387             }
1388          }
1389
1390       */
1391          if(equals.count == count) // all are equal, no diff icon
1392          {
1393             icon = 0;
1394             not = false;
1395          }
1396          else if(equals.count == count-1) // all are equal but one, not-sign icon for singled out missing
1397          {
1398             int i;
1399             /*
1400             for(nb = equals.root.minimum, i = 0; nb; nb = nb.next, i++)
1401             {
1402                if(i != nb.key)
1403                */
1404             for(i = 0; i < equals.count; i++)
1405             {
1406                if(i != equals[i])
1407                {
1408                   icon = i+1;
1409                   break;
1410                }
1411             }
1412             //if(!nb)
1413             if(i == equals.count)
1414                icon = count;
1415             not = true;
1416          }
1417          else if(equals.count == 1) // only one is present, all others missing, present-sign for singled out present
1418          {
1419             //icon = equals.root.minimum.key+1;
1420             icon = equals[0]+1;
1421             not = false;
1422          }
1423          else // mixed
1424          {
1425             icon = 0; // todo
1426             not = true;
1427          }
1428 #if 0
1429          // or
1430          if(equals.count == count) // all are equal, no diff icon
1431             ;
1432          else if(count/2 - equals.count < 0) // more than half are equal, use not-sign icons for all missing
1433             ;
1434          else // less than half are equal, use present-sign icons for all present
1435             ;
1436 #endif
1437
1438          /*if((!bits.foldersOnly && !bits.filesOnly) ||
1439             (bits.foldersOnly && listings[smallest].stats.attribs.isDirectory) ||
1440             (bits.filesOnly && listings[smallest].stats.attribs.isFile))*/
1441          strcpy(path, comparedPaths[/*smallest*/equals[0]]);
1442          PathCat(path, /*mapNodes[smallest].key*/na.key);
1443          FileGetStats(path, stats);
1444          if((!bits.foldersOnly && !bits.filesOnly) ||
1445             (bits.foldersOnly && stats.attribs.isDirectory) ||
1446             (bits.filesOnly && stats.attribs.isFile))
1447          {
1448             FileSystemNode node =
1449                   MakeComparedFileSystemNode(
1450                         stats,
1451                         /*mapNodes[smallest].key*/na.key,
1452                         path,
1453                         false, bits.previewPictures,
1454                         icon, not, equals,
1455                         displaySystem);
1456             AddNode(node);
1457          }
1458       /*
1459          for(equal : equals)
1460          {
1461             mapNodes[equal] = mapNodes[equal].next;
1462             //states[equal] = listings[equal].Find() == true ? matching : endOfListing;
1463             //PrintLn(comparedPaths[equal], " -- .Find() -- ", states[equal] == matching ? listings[equal].name : "endOfListing*");
1464          }
1465          equals.size = 0;
1466          //for(c = 0; c < count && states[c] == endOfListing; c++);
1467          for(c = 0; c < count && !mapNodes[c]; c++);
1468          if(c == count)
1469             allDone = true;
1470       */
1471       }
1472       list.Sort(nameField, 1);
1473    }
1474
1475    void AddNode(FileSystemNode node)
1476    {
1477       DataRow row = list.AddRow();
1478       row.tag = (intptr)node;
1479       node.row = row;
1480       incref node;
1481       row.SetData(nameField, node);
1482       if(bits.pathColumn)
1483       {
1484          char path[MAX_LOCATION];
1485          StripLastDirectory(node.path, path);
1486          row.SetData(pathField, CopyString(path));
1487       }
1488       if(bits.details)
1489       {
1490          if(node.type.isFile)
1491          {
1492             row.SetData(typeField, node.extension);
1493             row.SetData(sizeField, /*(void *)*/node.stats.size);
1494          }
1495          row.SetData(modifiedField, node.stats.modified);
1496       }
1497    }
1498
1499    FileSystemNode MakeAndAddToTreeFileSystemNodeFromFileListing(FileListing listing, FileSystemNode parent)
1500    {
1501       FileSystemNode result = null;
1502       /*if((!bits.foldersOnly && !bits.filesOnly) ||
1503          (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
1504          (bits.filesOnly && listing.stats.attribs.isFile))*/
1505       /*if(!listing.stats.attribs.isRemovable && ((!bits.foldersOnly && !bits.filesOnly) ||
1506          (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
1507          (bits.filesOnly && listing.stats.attribs.isFile)))*/
1508       {
1509          bool textFileLinesStyle = false;
1510          const char * test = listing.name;
1511          if(!test)
1512             PrintLn("error");
1513          result = MakeFileSystemNode(listing.stats, listing.name, listing.path, false, bits.previewPictures, false, displaySystem);
1514          test = result.name;
1515          if(!test)
1516             PrintLn("error");
1517          if(bits.textFileLinesStyle)
1518          {
1519             char ext[MAX_LOCATION];
1520             GetExtension(listing.name, ext);
1521             if(!strcmpi(ext, "txt") || !strcmpi(ext, "text"))
1522                textFileLinesStyle = true;
1523          }
1524          //AddTreeNode(result, true, false, textFileLinesStyle, parent);
1525          AddTreeNode(result, !textFileLinesStyle && listing.stats.attribs.isFile, false, !listing.stats.attribs.isFile || textFileLinesStyle, parent);
1526          test = result.name;
1527          if(!test)
1528             PrintLn("error");
1529       }
1530       return result;
1531    }
1532
1533    void AddTreeNode(
1534       FileSystemNode node,
1535       bool loaded,
1536       bool childrenLoaded,
1537       bool addLoader,
1538       FileSystemNode addTo)
1539    {
1540       DataRow row = (addTo && addTo.row) ? addTo.row.AddRow() : list.AddRow();
1541       if(addTo)
1542       {
1543          node.parent = addTo;
1544          node.indent = addTo.indent + 1;
1545          addTo.children.Add(node);
1546       }
1547       row.tag = (intptr)node;
1548       node.row = row;
1549       row.SetData(null, node);
1550       if(bits.pathColumn)
1551       {
1552          char path[MAX_LOCATION];
1553          StripLastDirectory(node.path, path);
1554          row.SetData(pathField, CopyString(path));
1555       }
1556       if(bits.details)
1557       {
1558          if(node.type.isFile)
1559          {
1560             row.SetData(typeField, node.extension);
1561             row.SetData(sizeField, /*(void *)*/node.stats.size);
1562          }
1563          row.SetData(modifiedField, node.stats.modified);
1564       }
1565
1566       node.bits.loaded = loaded;
1567       node.bits.childrenLoaded = childrenLoaded;
1568       if(addLoader)
1569          //AddTreeNode(FileSystemNode { }, false, false, node); // why would this create a compile error?
1570          AddTreeNode(FileSystemNode { type = none, name = "Loader" }, false, false, false, node);
1571
1572       if(node.indent > 0 || bits.mode == list)
1573          row.collapsed = true;
1574       else if(node.type == folder)
1575          node.type = folderOpen;
1576    }
1577
1578    void DeleteNode(FileSystemNode node)
1579    {
1580       FileSystemNode child;
1581       if(treeBranches)
1582       {
1583          for(; (child = node.children.first); )
1584             DeleteNode(child);
1585       }
1586       list.DeleteRow(node.row);
1587       node.Delete();
1588       //delete node;
1589       //Update(null);
1590    }
1591 }
1592
1593 enum ComparisonState { endOfListing, matching };
1594
1595 /*
1596 #if 0
1597 class ExplorerView : FileSystemBox
1598 {
1599    borderStyle = none;
1600    hasHorzScroll = false;
1601    hasVertScroll = false;
1602
1603    virtual void Load(FileSystemNode parent);
1604    virtual void Refresh();
1605
1606    virtual void LaunchNotifyItemSelect(Window master, ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
1607    {
1608       view.NotifyItemSelect(master, view, item, selectedItems);
1609    }
1610
1611    virtual bool Window::NotifyItemSelect(ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems);
1612    virtual bool Window::NotifyItemOpen(ExplorerView view, ExplorerFileItem item);
1613
1614    ListBox list
1615    {
1616       master = master, parent = this;
1617       //this, master;
1618       borderStyle = none;
1619       hasHorzScroll = true;
1620       hasVertScroll = true;
1621       resizable = true;
1622       sortable = true;
1623       fullRowSelect = false;
1624       multiSelect = true;
1625
1626       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
1627
1628       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
1629       {
1630          ExplorerView view = (ExplorerView)listBox.parent;
1631          if(listBox.currentRow)
1632          {
1633             DataRow listRow;
1634             ExplorerFileItemArray selectedItems { growingFactor = 16 };
1635             for(listRow = listBox.firstRow; listRow; listRow = listRow.next)
1636                if(listRow.selected)
1637                   selectedItems.Add((ExplorerFileItem)listRow.tag);
1638             //view.NotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
1639             view.LaunchNotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag, selectedItems);
1640          }
1641          return true;
1642       }
1643
1644       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
1645       {
1646          ExplorerView view = (ExplorerView)listBox.parent;
1647          view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
1648          return false;
1649       }
1650
1651       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
1652       {
1653          if((SmartKey)key == enter)
1654          {
1655             ExplorerView view = (ExplorerView)listBox.parent;
1656             view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
1657          }
1658          return true;
1659       }
1660    };
1661
1662    ExplorerView()
1663    {
1664    }
1665 }
1666 #endif
1667
1668 #if 0
1669 class ExplorerViewList : ExplorerView
1670 {
1671
1672    FileSystemNode location;
1673
1674 public:
1675
1676    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
1677
1678    ExplorerViewDetails()
1679    {
1680       list.AddField(nameField);
1681    }
1682
1683    void Refresh()
1684    {
1685       Load(location);
1686    }
1687
1688    void Load(FileSystemNode location)
1689    {
1690       char path[MAX_LOCATION];
1691       this.location = location;
1692       location.GetPath(path);
1693       {
1694          FileListing listing { path };
1695
1696          ExplorerFileItem item;
1697          DataRow row;
1698
1699          list.Clear();
1700
1701          while(listing.Find())
1702          {
1703             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
1704
1705             row = list.AddRow();
1706             row.tag = (int)item;
1707             row.SetData(nameField, item);
1708          }
1709          list.Sort(nameField, 1);
1710       }
1711    }
1712 }
1713 #endif
1714
1715 #if 0
1716 class ExplorerViewDetails : ExplorerView
1717 {
1718    list.hasHeader = true;
1719    list.moveFields = true;
1720    list.resizable = true;
1721    list.sortable = true;
1722
1723    FileSystemNode location;
1724
1725 public:
1726
1727    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
1728    DataField typeField { header = "Type", dataType = /-*"String"*-/ "char *", width = 40 };
1729    DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right };
1730
1731    ExplorerViewDetails()
1732    {
1733       list.AddField(nameField);
1734       list.AddField(typeField);
1735       list.AddField(sizeField);
1736    }
1737
1738    void Refresh()
1739    {
1740       Load(location);
1741    }
1742
1743    void Load(FileSystemNode location)
1744    {
1745       char path[MAX_LOCATION];
1746       this.location = location;
1747       location.GetPath(path);
1748       {
1749          FileListing listing { path };
1750
1751          ExplorerFileItem item;
1752          DataRow row;
1753
1754          list.Clear();
1755
1756          while(listing.Find())
1757          {
1758             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
1759
1760             row = list.AddRow();
1761             row.tag = (int)item;
1762             row.SetData(nameField, item);
1763             row.SetData(typeField, CopyString(item.extension));
1764             row.SetData(sizeField, (uint)listing.stats.size);
1765          }
1766          list.Sort(nameField, 1);
1767       }
1768    }
1769 }
1770 #endif
1771
1772 #if 0
1773 class ExplorerViewIcons : ExplorerView
1774 {
1775
1776    FileSystemNode location;
1777
1778 public:
1779
1780    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
1781
1782    ExplorerViewDetails()
1783    {
1784       list.AddField(nameField);
1785    }
1786
1787    void Refresh()
1788    {
1789       Load(location);
1790    }
1791
1792    void Load(FileSystemNode location)
1793    {
1794       char path[MAX_LOCATION];
1795       this.location = location;
1796       location.GetPath(path);
1797       {
1798          FileListing listing { path };
1799
1800          ExplorerFileItem item;
1801          DataRow row;
1802
1803          list.Clear();
1804
1805          while(listing.Find())
1806          {
1807             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
1808
1809             row = list.AddRow();
1810             row.tag = (int)item;
1811             row.SetData(nameField, item);
1812          }
1813          list.Sort(nameField, 1);
1814       }
1815    }
1816 }
1817 #endif
1818
1819 #if 0
1820 class ExplorerViewCards : ExplorerView
1821 {
1822
1823    FileSystemNode location;
1824
1825 public:
1826
1827    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
1828
1829    ExplorerViewDetails()
1830    {
1831       list.AddField(nameField);
1832    }
1833
1834    void Refresh()
1835    {
1836       Load(location);
1837    }
1838
1839    void Load(FileSystemNode location)
1840    {
1841       char path[MAX_LOCATION];
1842       this.location = location;
1843       location.GetPath(path);
1844       {
1845          FileListing listing { path };
1846
1847          ExplorerFileItem item;
1848          DataRow row;
1849
1850          list.Clear();
1851
1852          while(listing.Find())
1853          {
1854             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
1855
1856             row = list.AddRow();
1857             row.tag = (int)item;
1858             row.SetData(nameField, item);
1859          }
1860          list.Sort(nameField, 1);
1861       }
1862    }
1863 }
1864 #endif
1865
1866 #if 0
1867 public class BitmapArray : RedjArray
1868 {
1869    type = class(Bitmap);
1870 public:
1871    Bitmap * const _;
1872    Bitmap * Add(Bitmap bitmap)
1873    {
1874       uint pos = _count;
1875       Append(1);
1876       _[pos] = bitmap;
1877       return &_[pos];
1878    }
1879    Bitmap * AddBefore(uint position, Bitmap bitmap)
1880    {
1881       Insert(position, 1);
1882       _[position] = bitmap;
1883       return &_[position];
1884    }
1885    void Clear()
1886    {
1887       int c;
1888       for(c = 0; c < _count; c++)
1889       {
1890          _[c].Free();
1891          delete _[c];
1892       }
1893       count = 0;
1894       size = 0;
1895    }
1896 }
1897 #endif
1898
1899 #if 0
1900 class ExplorerViewShowcase : ExplorerView
1901 {
1902    list.anchor = Anchor { left = 0, top = 0, bottom = 0 };
1903    list.size = Size { w = 200 };
1904
1905    FileSystemNode location;
1906
1907 public:
1908
1909    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 180, editable = true, userData = this };
1910
1911    Bitmap bitmap;
1912    BitmapArray bitmaps { growingFactor = 16 };
1913
1914    Window show
1915    {
1916       this;
1917       borderStyle = none;
1918       anchor = Anchor { top = 0, right = 0, bottom = 0 };
1919
1920       void OnRedraw(Surface surface)
1921       {
1922          ExplorerViewShowcase view = (ExplorerViewShowcase)parent;
1923          if(view.bitmap)
1924          {
1925             int wBmp = view.bitmap.width;
1926             int hBmp = view.bitmap.height;
1927             int wWnd = clientSize.w;
1928             int hWnd = clientSize.h;
1929
1930             int wList = view.list.size.w + view.split.size.w;
1931
1932             float scale = Min((float)(wWnd - 10) / wBmp, (float)(hWnd - 10) / hBmp);
1933
1934             int wDraw = (int)(wBmp * scale);
1935             int hDraw = (int)(hBmp * scale);
1936
1937       #ifndef __linux__
1938             surface.Filter(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw, wBmp, hBmp);
1939       #else
1940             // Until Filter / Stretch works with X
1941             surface.Blit(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw);
1942       #endif
1943          }
1944          else
1945          {
1946             surface.SetForeground(white);
1947             surface.Area(0, 0, view.clientSize.w - 1, view.clientSize.h - 1);
1948          }
1949       }
1950    }
1951
1952    SplitWindow split
1953    {
1954       this;
1955       leftPane = list;
1956       rightPane = show;
1957       split = 200;
1958       tabCycle = true;
1959    };
1960
1961    ExplorerViewDetails()
1962    {
1963       list.AddField(nameField);
1964    }
1965
1966    void LaunchNotifyItemSelect(Window master, ExplorerViewShowcase view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
1967    {
1968       int pos;
1969       ExplorerFileItem selItem;
1970       if(view.bitmap)
1971          view.bitmap.Free();
1972       delete view.bitmap;
1973       if(item && item.type == pictureFile)
1974       {
1975          view.bitmap = Bitmap { };
1976          view.bitmap.Load(item.path, null, displaySystem);
1977       }
1978
1979       view.bitmaps.Clear();
1980       view.bitmaps = BitmapArray { };
1981       for(pos = 0; pos < selectedItems.count; pos++)
1982       {
1983          Bitmap bitmap { };
1984          selItem = (ExplorerFileItem)selectedItems._[pos];
1985          bitmap.Load(selItem.path, null, displaySystem);
1986          //view.bitmaps.Add(bitmap);
1987       }
1988       if(item && item.type == pictureFile)
1989       {
1990          view.bitmap = Bitmap { };
1991          view.bitmap.Load(item.path, null, displaySystem);
1992       }
1993
1994       view.show.Update(null);
1995       view.NotifyItemSelect(master, view, item, selectedItems);
1996    }
1997
1998    void Refresh()
1999    {
2000       Load(location);
2001    }
2002
2003    void Load(FileSystemNode location)
2004    {
2005       char path[MAX_LOCATION];
2006       this.location = location;
2007       location.GetPath(path);
2008       {
2009          FileListing listing { path };
2010
2011          ExplorerFileItem item;
2012          DataRow row;
2013
2014          list.Clear();
2015
2016          while(listing.Find())
2017          {
2018             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
2019
2020             row = list.AddRow();
2021             row.tag = (int)item;
2022             row.SetData(nameField, item);
2023          }
2024          list.Sort(nameField, 1);
2025       }
2026    }
2027 }
2028 #endif
2029
2030 #if 0
2031 class ExplorerTree : FileSystemBox
2032 {
2033    hasHorzScroll = false;
2034    hasVertScroll = false;
2035
2036    menu = Menu { };
2037
2038 public:
2039
2040    DataField nameField { dataType = "FileSystemNode", width = 240, userData = this };
2041
2042    FileSystemNode root;
2043    FileSystemNode selection;
2044
2045    virtual bool Window::NotifyNodeSelect(ExplorerTree tree, FileSystemNode node);
2046
2047    property FileSystemNode node
2048    {
2049       get
2050       {
2051          if(!tree)
2052             return null;
2053          if(!tree.currentRow)
2054             return null;
2055          if(!tree.currentRow.tag)
2056             return null;
2057          return (FileSystemNode)tree.currentRow.tag;
2058       }
2059    }
2060
2061    void Select(FileSystemNode node)
2062    {
2063       if(node.row)
2064       {
2065          node.EnsureVisible(false);
2066          tree.SelectRow(node.row);
2067       }
2068    }
2069
2070    FileSystemNode Find(const char * name, FileSystemNode parent)
2071    {
2072       FileSystemNode node;
2073       FileSystemNode start = parent ? parent : root;
2074       if(!start.loaded || !start.childrenLoaded)
2075          LoadTreeNode(start, tree);
2076       for(node = start.children.first; node; node = node.next)
2077          if(node.name && !strcmpi(node.name, name))
2078             return node;
2079       return null;
2080    }
2081
2082    ListBox tree
2083    {
2084       master = master, parent = this;
2085       //this, master;
2086       borderStyle = none;
2087       hasHorzScroll = true;
2088       hasVertScroll = true;
2089       fullRowSelect = false;
2090       treeNodees = true;
2091       collapseControl = true;
2092       rootCollapseButton = true;
2093
2094       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
2095
2096       // WHY is this not working ?
2097       /-*void OnResize(int width, int height)
2098       {
2099          if(vertScroll.visible)
2100             nameField.width = width - vertScroll.size.w;
2101          else
2102             nameField.width = width;
2103       }*-/
2104
2105       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
2106       {
2107          if(row)
2108          {
2109             FileSystemNode node = (FileSystemNode)row.tag;
2110             FileSystemNode child;
2111             if(collapsed)
2112             {
2113                /-*
2114                for(child = node.children.last; child; child = node.children.last)
2115                {
2116                   listBox.DeleteRow(child.row);
2117                   child.Free();
2118                   delete child;
2119                }
2120                node.childrenLoaded = false;
2121                *-/
2122             }
2123             else
2124             {
2125                if(!node.loaded || !node.childrenLoaded)
2126                   LoadTreeNode(node, tree);
2127                for(child = node.children.first; child && child.next; child = child.next);
2128                if(child)
2129                   child.EnsureVisible(false);
2130             }
2131          }
2132          return true;
2133       }
2134
2135       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
2136       {
2137          DataRow row = listBox.currentRow;
2138          if(row)
2139          {
2140             FileSystemNode node = (FileSystemNode)row.tag;
2141             if(node)
2142             {
2143                PopupMenu popup;
2144                Menu menu { };
2145
2146                MenuItem { menu, "Cut\tCtrl+X", t, NotifySelect = null, disabled = false };
2147                MenuItem { menu, "Copy\tCtrl+C", c, NotifySelect = null, disabled = false };
2148                MenuItem { menu, "Paste\tCtrl+V", p, NotifySelect = null, disabled = false /-*!clipboard*-/ };
2149                MenuItem { menu, "Delete\tDel", d, NotifySelect = null, disabled = false };
2150                //MenuDivider { menu };
2151
2152                popup = PopupMenu
2153                   {
2154                      master = this, menu = menu,
2155                      position = {
2156                         x + clientStart.x + absPosition.x - guiApp.desktop.position.x,
2157                         y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
2158                   };
2159                popup.Create();
2160             }
2161          }
2162          return true;
2163       }
2164
2165       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
2166       {
2167          if(row)
2168          {
2169             FileSystemNode node = (FileSystemNode)row.tag;
2170             NotifyNodeSelect(listBox.parent.master, this, node);
2171             selection = node;
2172          }
2173          return true;
2174       }
2175
2176       bool NotifyEditing(ListBox listBox, DataRow row)
2177       {
2178          if(row)
2179          {
2180             FileSystemNode node = (FileSystemNode)row.tag;
2181          }
2182          return true;
2183       }
2184
2185       bool NotifyEdited(ListBox listBox, DataRow row)
2186       {
2187          if(row)
2188          {
2189             FileSystemNode node = (FileSystemNode)row.tag;
2190          }
2191          return true;
2192       }
2193
2194       bool NotifyEditDone(ListBox listBox, DataRow row)
2195       {
2196          if(row)
2197          {
2198             FileSystemNode node = (FileSystemNode)row.tag;
2199          }
2200          return true;
2201       }
2202    };
2203
2204    // Edit Menu
2205    Menu editMenu { menu, "Edit", e };
2206    MenuItem itemEditCut
2207    {
2208       editMenu, "Cut\tCtrl+X", t, disabled = true;
2209
2210       bool NotifySelect(MenuItem selection, Modifiers mods)
2211       {
2212          //EditCut();
2213          return true;
2214       }
2215    };
2216    MenuItem itemEditCopy
2217    {
2218       editMenu, "Copy\tCtrl+C", c, disabled = true;
2219
2220       bool NotifySelect(MenuItem selection, Modifiers mods)
2221       {
2222          //EditCopy();
2223          return true;
2224       }
2225    };
2226    MenuItem itemEditPaste
2227    {
2228       editMenu, "Paste\tCtrl+V", p;
2229
2230       bool NotifySelect(MenuItem selection, Modifiers mods)
2231       {
2232          //EditPaste();
2233          return true;
2234       }
2235    };
2236    MenuItem itemEditDelete
2237    {
2238       editMenu, "Delete\tDel", d, disabled = true;
2239
2240       bool NotifySelect(MenuItem selection, Modifiers mods)
2241       {
2242          //EditDelete();
2243          return true;
2244       }
2245    };
2246
2247    // WHY is this crashing ?
2248    /-*void OnResize(int width, int height)
2249    {
2250       if(this && nameField)
2251          nameField.width = width - 80;
2252    }*-/
2253
2254    ExplorerTree()
2255    {
2256       tree.AddField(nameField);
2257    }
2258
2259    void Load()
2260    {
2261       FileSystemNode parent;
2262       FileSystemNode node;
2263       FileListing listing { "/" };
2264
2265       tree.Clear();
2266
2267       root = FileSystemNode { type = computer, loaded = true, childrenLoaded = true };
2268    #ifdef __WIN32__
2269       root.name = rootName;
2270    #else
2271       root.name = "/";
2272    #endif
2273       AddTreeNode(root, true, false, false, null, tree);
2274
2275    // How can this make sense for linux?
2276    #ifdef __WIN32__
2277       while(listing.Find())
2278       {
2279          int len = strlen(listing.name);
2280          char info[MAX_LOCATION];
2281          char name[MAX_LOCATION];
2282          if(listing.stats.attribs.isDrive &&
2283                len > 3 && !strncmp(&listing.name[1], ": [", 3))
2284          {
2285             strncpy(name, listing.name, 2);
2286             name[2] = 0;
2287             strncpy(info, &listing.name[4], len - 5);
2288             info[len - 5] = 0;
2289          }
2290          else
2291          {
2292             strcpy(name, listing.name);
2293             info[0] = 0;
2294          }
2295
2296          parent = MakeFileSystemNode(listing.stats, name);
2297          if(info[0])
2298             parent.info = CopyString(info);
2299          parent.loaded = true;
2300          AddTreeNode(parent, !listing.stats.attribs.isDirectory, false, listing.stats.attribs.isDirectory, root, tree);
2301          if(!listing.stats.attribs.isDirectory)
2302             parent.childrenLoaded = true;
2303       }
2304    #endif
2305       node = FileSystemNode { name = msNetwork, type = network };
2306       AddTreeNode(node, false, false, true, null, tree);
2307       node.row.collapsed = true;
2308       tree.Sort(nameField, 1);
2309       tree.SelectRow(root.row);
2310    }
2311 }
2312
2313 #if 0
2314 public class ClipBoardFiles
2315 {
2316
2317 public:
2318
2319    property
2320
2321 }
2322
2323    // CLIPBOARD
2324    void Copy()
2325    {
2326       if(this)
2327       {
2328          int size = SelSize();
2329          if(size)
2330          {
2331             // Try to allocate memory
2332             ClipBoard clipBoard { };
2333             if(clipBoard.Allocate(size+1))
2334             {
2335                GetSel(clipBoard.memory, true);
2336                // Save clipboard
2337                clipBoard.Save();
2338             }
2339             delete clipBoard;
2340          }
2341       }
2342    }
2343
2344    void Paste()
2345    {
2346       if(this)
2347       {
2348          ClipBoard clipBoard { };
2349          if(clipBoard.Load())
2350             PutS(clipBoard.memory);
2351          delete clipBoard;
2352       }
2353    }
2354
2355    void Cut()
2356    {
2357       if(this)
2358       {
2359          Copy();
2360          DelSel();
2361          SetViewToCursor(true);
2362          Modified();
2363       }
2364    }
2365
2366 /-*
2367 Private Type DROPFILES
2368    pFiles As Long
2369    pt As POINTAPI
2370    fNC As Long
2371    fWide As Long
2372 End Type
2373 For iCounter = 0 To filelist.ListCount - 1
2374   If filelist.Selected(iCounter) = True Then
2375     strFiles = strFiles & FixPath(filelist.Path) & filelist.List(iCounter) & vbNullChar
2376   End If
2377 Next
2378 'all selected items are now put in strFiles
2379
2380 hGlobal = GlobalAlloc(GHND, Len(DF) + Len(strFiles)) 'put all files to a exclusive number
2381 If hGlobal Then 'if the globalalloc worked
2382   lpGlobal = GlobalLock(hGlobal) 'lock the hGlobal
2383   DF.pFiles = Len(DF) 'set the size of the files
2384
2385   Call CopyMem(ByVal lpGlobal, DF, Len(DF)) 'copy df to the lpglobal
2386   Call CopyMem(ByVal (lpGlobal + Len(DF)), ByVal strFiles, Len(strFiles)) 'copy strfiles to lpglobal
2387   Call GlobalUnlock(hGlobal) 'unlock hglobal again
2388
2389   SetClipboardData CF_HDROP, hGlobal 'put files to the clipboard
2390 End If
2391 *-/
2392    bool SaveFile(const char * filePath)
2393    {
2394    }
2395 #endif
2396
2397 #if 0
2398 public class FileTreeNodeBSArray : ArrayBinarySorted
2399 {
2400    type = class(FileSystemNode);
2401 public:
2402    FileSystemNode * const _;
2403    BSloc Add(FileSystemNode item)
2404    {
2405       BSloc result = Find(item);
2406       if(!result.valid)
2407       {
2408          Insert(result.pos, 1);
2409          _[result.pos] = item;
2410       }
2411       return result;
2412    }
2413    BSloc Remove(FileSystemNode item)
2414    {
2415
2416    }
2417 }
2418 #endif
2419
2420 #if 0
2421 public class FileTreeNodeArray : RedjArray
2422 {
2423    type = class(FileSystemNode);
2424 public:
2425    FileSystemNode * const _;
2426    FileSystemNode * Add(FileSystemNode item)
2427    {
2428       uint pos = _count;
2429       Append(1);
2430       _[pos] = item;
2431       return &_[pos];
2432    }
2433    FileSystemNode * AddBefore(uint position, FileSystemNode item)
2434    {
2435       Insert(position, 1);
2436       _[position] = item;
2437       return &_[position];
2438    }
2439 }
2440 #endif
2441
2442 #if 0
2443 public class ExplorerFileItem : struct
2444 {
2445    char * path;
2446    char * name;
2447    char * info;
2448    char * extension;
2449    _FileType type;
2450    int indent;
2451
2452    Bitmap bitmap;
2453
2454    void OnDisplay(Surface surface, int x, int y, int width, FileSystemBox control, Alignment alignment, DataDisplayFlags displayFlags)
2455    {
2456       int indentSize = (displayFlags.dropBox) ? 0 : 10;
2457       int textOffset;
2458       int len;
2459       char label[MAX_FILENAME];
2460
2461       //float scale = Min((float)clientSize.w / (float)bitmap.width, (float)clientSize.h / (float)bitmap.height);
2462       int w = 16; //(int)(bitmap.width * scale);
2463       int h = 16; //(int)(bitmap.height * scale);
2464
2465       Bitmap icon;
2466
2467       icon = control.fileIcons[type].bitmap;
2468       if(!icon)
2469       {
2470          if(type == folder || type == folderOpen)
2471             surface.SetForeground(red); //Color { 170, 170, 0 } // REDJ What is that color?
2472          indentSize = 8;
2473       }
2474       textOffset = indent * indentSize + (icon ? (icon.width + 6) : 0);
2475
2476       if(info)
2477          sprintf(label, "%s [%s]", name, info);
2478       else
2479          strcpy(label, name);
2480       len = strlen(label);
2481
2482       surface.WriteTextDots
2483          (alignment, x + textOffset, y + 2, width - textOffset, label, len);
2484       if(type == pictureFile && control.previewPictures && bitmap)
2485       {
2486 #ifndef __linux__
2487          //surface.Filter(bitmap, (clientSize.w - w) / 2,(clientSize.h - h) / 2, 0,0, w, h, bitmap.width, bitmap.height);
2488          surface.Filter(bitmap, x + indent * indentSize + 2, y, 0, 0, w, h, bitmap.width, bitmap.height);
2489 #else
2490          // Until Filter / Stretch works with X
2491          //surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2,(clientSize.h - bitmap.height) / 2, 0,0, bitmap.width, bitmap.height);
2492          surface.blend = true;
2493          surface.Blit(bitmap, x + indent * indentSize + 2, y,0,0, w, h);
2494 #endif
2495          //bitmap.Free();
2496          //delete bitmap;
2497       }
2498       else if(icon)
2499          surface.Blit(icon, x + indent * indentSize + 2, y,0,0, icon.width, icon.height);
2500    }
2501
2502    int OnCompare(ExplorerFileItem b)
2503    {
2504       int result;
2505       if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
2506          result = strcmpi(name, b.name);
2507       else
2508       {
2509          if(type == folder && b.type < folder) result = -1;
2510          else if(type < folder && b.type == folder) result = 1;
2511       }
2512       return result;
2513    }
2514
2515    void OnCopy(ExplorerFileItem newData)
2516    {
2517       type = newData.type;
2518       indent = newData.indent;
2519       if(newData.name)
2520       {
2521          int len = strlen(newData.name) + 1;
2522          name = new char[len];
2523          CopyBytes(name, newData.name, len);
2524       }
2525    }
2526
2527    bool OnGetDataFromString(char * string)
2528    {
2529       int len = strlen(string) + 1;
2530       name = new char[len];
2531       CopyBytes(name, string, len);
2532       return true;
2533    }
2534
2535    void OnFree()
2536    {
2537       delete path;
2538       delete name;
2539       delete info;
2540       delete extension;
2541       if(bitmap)
2542          bitmap.Free();
2543    }
2544
2545    char * OnGetString(char * string, void * fieldData, bool * needClass)
2546    {
2547       return name;
2548    }
2549 };
2550
2551 public class ExplorerFileItemArray : RedjArray
2552 {
2553    type = class(ExplorerFileItem);
2554 public:
2555    ExplorerFileItem * const _;
2556    ExplorerFileItem * Add(ExplorerFileItem item)
2557    {
2558       uint pos = _count;
2559       Append(1);
2560       _[pos] = item;
2561       return &_[pos];
2562    }
2563    ExplorerFileItem * AddBefore(uint position, ExplorerFileItem item)
2564    {
2565       Insert(position, 1);
2566       _[position] = item;
2567       return &_[position];
2568    }
2569    void Clear()
2570    {
2571       int c;
2572       for(c = 0; c < _count; c++)
2573       {
2574          //_[c].Free()
2575          delete _[c];
2576       }
2577       count = 0;
2578       size = 0;
2579    }
2580 }
2581
2582 ExplorerFileItem MakeFileItem(const FileAttribs attribs, const char * fileName, const char * filePath, const bool previewPicture, const DisplaySystem displaySystem)
2583 {
2584    int len = strlen(fileName);
2585    char info[MAX_LOCATION];
2586    char name[MAX_LOCATION];
2587    char extension[MAX_EXTENSION];
2588
2589    ExplorerFileItem item { };
2590
2591    //if(stats.attribs.isFile) // -- should work now
2592    if(attribs.isDirectory)
2593    {
2594       extension[0] = 0;
2595
2596       item.type = (attribs.isDrive) ? drive : folder;
2597       if(attribs.isServer)
2598          item.type = server;
2599       if(attribs.isShare)
2600          item.type = share;
2601       if(attribs.isCDROM)
2602          item.type = cdrom;
2603       if(attribs.isRemote)
2604          item.type = netDrive;
2605       if(attribs.isRemovable)
2606       {
2607          if(fileName[0] == 'A' || fileName[0] == 'B')
2608             item.type = floppy;
2609          else
2610             item.type = removable;
2611       }
2612    }
2613    else
2614    {
2615       GetExtension(fileName, extension);
2616       //strupr(extension);
2617       strlwr(extension);
2618
2619       item.type = _FileType::SelectByExtension(extension);
2620    }
2621
2622    if(attribs.isDrive &&
2623          len > 3 && !strncmp(&fileName[1], ": [", 3))
2624    {
2625       strncpy(name, fileName, 2);
2626       name[2] = 0;
2627       strncpy(info, &fileName[4], len - 5);
2628       info[len - 5] = 0;
2629    }
2630    else
2631    {
2632       strcpy(name, fileName);
2633       info[0] = 0;
2634    }
2635
2636    item.path = CopyString(filePath);
2637    item.name = CopyString(name);
2638    if(info[0])
2639       item.info = CopyString(info);
2640    item.extension = CopyString(extension);
2641
2642    if(item.type == pictureFile && previewPicture)
2643    {
2644       item.bitmap = Bitmap { };
2645       item.bitmap.Load(filePath, null, displaySystem);
2646    }
2647
2648    return item;
2649 }
2650 #endif
2651 */
2652
2653 public class FileSystemBoxSelection
2654 {
2655 public:
2656    FileSystemNode node;
2657    Array<FileSystemNode> nodes { };
2658
2659 private:
2660    FileSystemBoxSelection Copy()
2661    {
2662       FileSystemBoxSelection copy { node = node };
2663       for(node : nodes)
2664       {
2665          copy.nodes.Add(node);
2666          incref node;
2667       }
2668       return copy;
2669    }
2670
2671    ~FileSystemBoxSelection()
2672    {
2673       nodes.Free();
2674    }
2675 }
2676
2677 class FileSystemNodeBits
2678 {
2679    bool loaded:1, childrenLoaded:1, isListItem:1;
2680 };
2681
2682 public class FileSystemNode
2683 {
2684
2685 private:
2686    FileSystemNodeBits bits;
2687    char * path;
2688    char * name;
2689    char * extension;
2690    char * label;
2691    char * info;
2692
2693    ~FileSystemNode()
2694    {
2695       Free();
2696    }
2697
2698 public:
2699    /* LinkElement<FileSystemNode> link;
2700    FileSystemNode parent;
2701
2702    FileSystemNodeType type;
2703
2704    char * name;*/
2705
2706    FileSystemNode prev, next;
2707
2708    int indent;
2709
2710    property bool isListItem { set { bits.isListItem = value; } get { return bits.isListItem; } };
2711
2712    property const char * path
2713    {
2714       set { delete path; if(value && value[0]) path = CopyString(value); }
2715       get { return path; } isset { return path && path[0]; }
2716    }
2717    property const char * name
2718    {
2719       set { delete name; if(value && value[0]) name = CopyString(value); }
2720       get { return name; } isset { return name && name[0]; }
2721    }
2722    property char * extension
2723    {
2724       set { delete extension; if(value && value[0]) extension = CopyString(value); }
2725       get { return extension; } isset { return extension && extension[0]; }
2726    }
2727    property const char * label
2728    {
2729       set { delete label; if(value && value[0]) label = CopyString(value); }
2730       get { return label; } isset { return label && label[0]; }
2731    }
2732    property const char * info
2733    {
2734       set { delete info; if(value && value[0]) info = CopyString(value); }
2735       get { return info; } isset { return info && info[0]; }
2736    }
2737
2738    DataRow row;
2739    OldList children;
2740    _FileType type;
2741    FileSystemNode parent;
2742
2743    FileStats stats;
2744
2745    Bitmap bitmap;
2746
2747    int cmpIcon;
2748    bool cmpNot;
2749    Array<int> exists; // would use (see) BoolArrayInt to pack this into an int if could be accessed as an array
2750
2751    void GetPath(String outputPath)
2752    {
2753       if(path)
2754          strcpy(outputPath, path);
2755       else if(parent)
2756       {
2757          FileSystemNode up;
2758          if(name)
2759             strcpy(outputPath, name);
2760          for(up = parent; up; up = up.parent)
2761          {
2762             char temp[MAX_LOCATION];
2763             strcpy(temp, up.name);
2764             PathCat(temp, outputPath);
2765             strcpy(outputPath, temp);
2766          }
2767       }
2768       else
2769          strcpy(outputPath, name ? name : "");
2770    }
2771
2772    bool IsChildOf(FileSystemNode node)
2773    {
2774       FileSystemNode test;
2775       for(test = parent; test; test = test.parent)
2776          if(test == node)
2777             return true;
2778       return false;
2779    }
2780
2781    void DuplicateChildren(bool recursive, bool forceExpanded, FileSystemNode addTo, FileSystemBox fsb)
2782    {
2783       if(children.first)
2784       {
2785          FileSystemNode child;
2786
2787          for(child = children.first; child; child = child.next)
2788          {
2789             FileSystemNode copy { };
2790             copy.name = child.name; //CopyString(child.name);
2791             copy.type = child.type;
2792             fsb.AddTreeNode(copy, child.bits.loaded, false, false, addTo);
2793             if(forceExpanded)
2794                copy.row.collapsed = false;
2795             if(recursive)
2796                child.DuplicateChildren(recursive, forceExpanded, copy, fsb);
2797          }
2798       }
2799    }
2800
2801    void EnsureVisible(bool expand)
2802    {
2803       if(parent)
2804          parent.EnsureVisible(true);
2805       if(expand)
2806          row.collapsed = false;
2807       // TODO: row.EnsureVisible(); // making the row visible by scrolling
2808    }
2809
2810    void OnFree()
2811    {
2812       //delete name;
2813    }
2814
2815    void Free()
2816    {
2817       FileSystemNode child;
2818       for(; (child = children.first); )
2819       {
2820          child.Free();
2821          children.Delete(child);
2822       }
2823       //if(name)
2824       delete path;
2825       delete name;
2826       delete extension;
2827       delete label;
2828       delete info;
2829    }
2830
2831    void Delete()
2832    {
2833       Free();
2834       if(parent)
2835          parent.children.Delete(this);
2836    }
2837
2838    void OnDisplay(Surface surface, int x, int y, int width, FileSystemBox fsb, Alignment alignment, DataDisplayFlags displayFlags)
2839    {
2840       //int indentSize = (displayFlags.dropBox) ? 0 : 10;
2841       //int indent = 16;
2842       int xStart;
2843       int len;
2844       int w, h;
2845       //int textOffset;
2846       char * alt;
2847       char text[MAX_LOCATION];
2848       bool comp;
2849
2850       Bitmap icon;
2851       Bitmap diffIcon;
2852       Bitmap notIcon;
2853
2854       if(!this)
2855          return;
2856
2857       comp = fsb.comparedPaths && fsb.comparedPaths.count > 1;
2858
2859       icon = fsb.fileIcons[type].bitmap;
2860       alt = bits.isListItem ? path : name;
2861       if(comp && !fsb.bits.columnsCompareStyle && cmpIcon)
2862       {
2863          /*
2864          diffIcon = Bitmap { };
2865          diffIcon.AllocateDD(
2866                surface.display.displaySystem,
2867                fsb.compIcons[cmpIcon].bitmap.width,
2868                fsb.compIcons[cmpIcon].bitmap.height);
2869          if(fsb.compIcons[cmpIcon].bitmap)
2870             diffIcon.Copy(fsb.compIcons[cmpIcon].bitmap);
2871          */
2872          diffIcon = fsb.compIcons[cmpIcon-1].bitmap;
2873          notIcon = fsb.compIcons[countOfCompIconNames-1].bitmap;
2874       }
2875       //xStart = indent * indent + x + (icon ? (icon.width + 5) : 0);
2876       xStart = x + (icon ? (icon.width + 5) : 0) + (comp ? 18*(fsb.bits.columnsCompareStyle ? fsb.comparedPaths.count : 1) : 0);
2877
2878       if(!alt)
2879          return;
2880
2881       if(info)
2882          sprintf(text, "%s [%s]", label ? label : alt, info);
2883       else
2884          strcpy(text, label ? label : alt); //"%d-%d/%s", stats.inode, stats.nlink
2885          //sprintf(text, "%d-%d/%s", stats.inode, stats.nlink, label ? label : alt);
2886       len = strlen(text);
2887
2888       if(!icon)
2889       {
2890          if(type == folder || type == folderOpen)
2891             surface.SetForeground(yellow);
2892          //indentSize = 8;
2893          indent = 8;
2894       }
2895       //textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
2896
2897       surface.SetForeground(displayFlags.selected ? fsb.selectionText : fsb.foreground);
2898       surface.TextOpacity(false);
2899       surface.TextExtent(text, len, &w, &h);
2900       h = Max(h, 16);
2901
2902       // Draw the current row stipple
2903       if(displayFlags.selected)
2904          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
2905          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
2906          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
2907       //surface.WriteTextDots(alignment, x + textOffset, y + 2, width - textOffset, alt, strlen(alt));
2908       surface.WriteTextDots(alignment, xStart, y + 2, width, text, len);
2909
2910       if(!guiApp.textMode)
2911       {
2912          if(displayFlags.current)
2913          {
2914             if(displayFlags.active)
2915             {
2916                surface.LineStipple(0x5555);
2917                if(displayFlags.selected)
2918                   surface.SetForeground(0xFFFFFF80);
2919                else
2920                   surface.SetForeground(black);
2921             }
2922             else
2923             {
2924                surface.SetForeground(selectionColor);
2925             }
2926             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
2927             surface.LineStipple(0);
2928          }
2929
2930          if(comp)
2931          {
2932             if(!fsb.bits.columnsCompareStyle && diffIcon)
2933             {
2934                w = diffIcon.width;
2935                h = diffIcon.height;
2936                /*if(cmpNot && notIcon)
2937                {
2938                   Surface s = diffIcon.GetSurface(0,0, {w,h});
2939                   s.SetForeground(white);
2940                   s.Blit(notIcon, x,y, 0,0, w,h);
2941                   delete s;
2942                }*/
2943                surface.SetForeground(white);
2944                surface.Blit(diffIcon, x,y, 0,0, w,h);
2945                if(cmpNot && notIcon)
2946                   surface.Blit(notIcon, x,y, 0,0, w,h);
2947                x+=18;
2948                //delete diffIcon;
2949             }
2950             else if(fsb.bits.columnsCompareStyle && exists && exists.count)
2951             {
2952                int c, d;
2953                for(c = d = 0; c < fsb.comparedPaths.count; c++)
2954                {
2955                   if(d == exists.count || exists[d] != c)
2956                      x+=18;
2957                   else
2958                   {
2959                      diffIcon = fsb.compIcons[c].bitmap;
2960                      if(diffIcon)
2961                      {
2962                         w = diffIcon.width;
2963                         h = diffIcon.height;
2964                         surface.SetForeground(white);
2965                         surface.Blit(diffIcon, x,y, 0,0, w,h);
2966                      }
2967                      x+=18;
2968                      d++;
2969                   }
2970                }
2971                /*
2972                for(c = d = 0; c < exists.count; c++)
2973                {
2974                   if(exists[c] != d)
2975                   {
2976                      d = exists[c]+1;
2977                      x+=18*(exists[c]-d);
2978                   }
2979                   else
2980                   {
2981                      diffIcon = fsb.compIcons[exists[c]].bitmap;
2982                      if(diffIcon)
2983                      {
2984                         w = diffIcon.width;
2985                         h = diffIcon.height;
2986                         surface.SetForeground(white);
2987                         surface.Blit(diffIcon, x,y, 0,0, w,h);
2988                      }
2989                      d++;
2990                      x+=18;
2991                   }
2992                }
2993                if(exists.count < fsb.comparedPaths.count && exists[exists.count-1] != d)
2994                {
2995                   x+=18*(exists[exists.count-1]-d);
2996                }
2997                */
2998             }
2999             else if(fsb.bits.columnsCompareStyle)
3000             {
3001                x+=18*fsb.comparedPaths.count;
3002             }
3003          }
3004          if(icon)
3005          {
3006             w = icon.width;
3007             h = icon.height;
3008          }
3009          if(type == pictureFile && fsb.previewPictures && bitmap)
3010          {
3011             surface.SetForeground(white);
3012             surface.blend = true;
3013    //#ifndef __linux__
3014             //surface.Filter(bitmap, (clientSize.w - w) / 2,(clientSize.h - h) / 2, 0,0, w, h, bitmap.width, bitmap.height);
3015             //surface.Filter(bitmap, x + indent/* * indentSize*/ + 2, y, 0, 0, w, h, bitmap.width, bitmap.height);
3016             surface.Filter(bitmap, x,y,0,0, w, h, bitmap.width, bitmap.height);
3017    //#else
3018             // Until Filter / Stretch works with X
3019             //surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2,(clientSize.h - bitmap.height) / 2, 0,0, bitmap.width, bitmap.height);
3020    //         surface.blend = true;
3021             //surface.Blit(bitmap, x + indent/* * indentSize*/ + 2, y,0,0, w, h);
3022             //surface.Blit(bitmap, x,y,0,0, bitmap.width, bitmap.height);
3023    //#endif
3024             //bitmap.Free();
3025             //delete bitmap;
3026          }
3027          else if(icon)
3028          {
3029             //surface.blend = true;
3030             //surface.alphaWrite = blend;
3031             surface.SetForeground(white);
3032             //surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
3033             surface.Blit(icon, x,y, 0,0, w,h);
3034          }
3035       }
3036    }
3037
3038    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
3039    {
3040       EditBox editBox
3041       {
3042          dataBox, anchor = { 0, 0, 0, 0 };
3043          borderStyle = none;
3044          //borderStyle = contour;
3045          opacity = 1.0f;
3046          //background = white;
3047          //autoSize = true;
3048          contents = name;
3049       };
3050       //dataBox.borderStyle = none;
3051       dataBox.borderStyle = contour;
3052       dataBox.background = white;
3053       //dataBox.opacity = 0.0f;
3054       editBox.Create();
3055       return editBox;
3056    }
3057
3058    bool OnSaveEdit(EditBox editBox, void * object)
3059    {
3060       bool changed = false;
3061       if(editBox.modifiedDocument)
3062       {
3063 #if 0
3064          // how the heck did this work for PathBox? :S
3065          //String::OnFree();
3066          //changed = _class._vTbl[__ecereVMethodID_class_OnGetDataFromString](_class, &this, editBox.contents);
3067          PrintLn(name);
3068          //if(strcmp(editBox.contents, this.name))
3069          ;
3070          /*{
3071             //changed = NotifyNodeRename(this.master, this, node);
3072             changed = true;
3073             if(changed && RenameFile(name, editBox.contents))
3074             {
3075                name = editBox.contents;
3076             }
3077             else
3078             {
3079                changed = false;
3080             }
3081          }*/
3082 #endif
3083       }
3084       return changed;
3085    }
3086
3087    int OnCompare(FileSystemNode b)
3088    {
3089       int result;
3090       FileSystemNode a = this;
3091       if(a.type == b.type || (a.type < folder && b.type < folder) || (a.type >= drive))
3092       {
3093          if(!a.name || !b.name)
3094             PrintLn("error: FileSystemNode::OnCompare -- null-named node");
3095          result = strcmpi(a.name, b.name);
3096       }
3097       else
3098       {
3099          if(a.type == folder && b.type < folder) result = -1;
3100          else if(a.type < folder && b.type == folder) result = 1;
3101          else result = 0;
3102       }
3103       return result;
3104    }
3105
3106 #if 0
3107    //int OnCompare(FileSystemNode b)
3108    //{
3109       //int result;
3110       //FileSystemNode a = this;
3111       //if(a.parent < b.parent) result = -1;
3112       //else if(a.parent > b.parent) result = 1;
3113       //else
3114          //result = fstrcmp(a.name, b.name);
3115       //return result;
3116    //}
3117 #endif
3118
3119    bool OnGetDataFromString(const char * string)
3120    {
3121 #if 0
3122       if(string && *string)
3123       {
3124          int len = strlen(string) + 1;
3125          name = new char[len];
3126          CopyBytes(name, string, len);
3127          return true;
3128       }
3129 #endif
3130       return false;
3131    }
3132
3133    const char * OnGetString(char * tempString, void * unused /*FileSystemToolWindow fileSysToolWnd*/, bool * needClass)
3134    {
3135       return name ? name : "";
3136    }
3137 }
3138
3139 /*FileSystemNode MakeFileSystemNode(const FileStats stats, const char * name)
3140 {
3141    FileSystemNode node { stats = stats };
3142    node.name = CopyString(name);
3143    if(!node.name)
3144       node.name = null;
3145    if(stats.attribs.isDirectory)
3146    {
3147       node.type = (stats.attribs.isDrive) ? drive : folder;
3148       if(stats.attribs.isServer) node.type = server;
3149       if(stats.attribs.isShare) node.type = share;
3150       if(stats.attribs.isCDROM) node.type = cdrom;
3151       if(stats.attribs.isRemote) node.type = netDrive;
3152       if(stats.attribs.isRemovable)
3153       {
3154          if(name[0] == 'A' || name[0] == 'B')
3155             node.type = floppy;
3156          else
3157             node.type = removable;
3158       }
3159    }
3160    else
3161    {
3162       char extension[MAX_EXTENSION];
3163       GetExtension(node.name, extension);
3164       node.type = _FileType::SelectByExtension(extension);
3165    }
3166    return node;
3167 }*/
3168
3169 FileSystemNode MakeFileSystemNode(
3170    const FileStats stats,
3171    const char * name,
3172    const char * path,
3173    const bool pathAddName,
3174    const bool previewPicture,
3175    const bool isListItem,
3176    /*const */DisplaySystem displaySystem)
3177 {
3178    int len = strlen(name);
3179    char info[MAX_LOCATION];
3180    char name2[MAX_LOCATION];
3181    char extension[MAX_EXTENSION];
3182
3183    FileSystemNode node { stats = stats };
3184
3185    /*if(!pathAddName)
3186    {
3187       char o[MAX_LOCATION];
3188       //char r[MAX_LOCATION];
3189       //StripLastDirectory(path, o);
3190       GetLastDirectory(path, o);
3191       if(fstrcmp(name, o))
3192       //if(!FileExists(path))
3193          PrintLn("Stop!");
3194    }*/
3195    //if(stats.attribs.isFile) // TODO fix this in ecere -- WTH -- this has been fixed :/
3196    if(stats.attribs.isDirectory)
3197    {
3198       extension[0] = '\0';
3199
3200       node.type = (stats.attribs.isDrive) ? drive : folder;
3201       if(stats.attribs.isServer) node.type = server;
3202       if(stats.attribs.isShare) node.type = share;
3203       if(stats.attribs.isCDROM) node.type = cdrom;
3204       if(stats.attribs.isRemote) node.type = netDrive;
3205       if(stats.attribs.isRemovable)
3206       {
3207          if(name[0] == 'A' || name[0] == 'B')
3208             node.type = floppy;
3209          else
3210             node.type = removable;
3211       }
3212    }
3213    else
3214    {
3215       GetExtension(name, extension);
3216       strlwr(extension);
3217
3218       node.type = _FileType::SelectByExtension(extension);
3219    }
3220
3221    if(stats.attribs.isDrive &&
3222          len > 3 && !strncmp(&name[1], ": [", 3))
3223    {
3224       strncpy(name2, name, 2);
3225       name2[2] = 0;
3226       strncpy(info, &name[4], len - 5);
3227       info[len - 5] = 0;
3228    }
3229    else
3230    {
3231       strcpy(name2, name);
3232       info[0] = 0;
3233    }
3234
3235    if(pathAddName)
3236    {
3237       //bool isFile = stats.attribs.isFile;
3238       //bool isFolder = stats.attribs.isDirectory;
3239       char full[MAX_LOCATION];
3240       strcpy(full, path);
3241       PathCat(full, name);
3242       node.path = full; //CopyString(full);
3243    }
3244    else
3245       node.path = path; //CopyString(path);
3246    node.name = name2; //CopyString(name2);
3247    if(info[0])
3248       node.info = info; //CopyString(info);
3249    node.extension = extension; //CopyString(extension);
3250
3251    if(node.type == pictureFile && previewPicture)
3252    {
3253       node.bitmap = Bitmap { alphaBlend = true };
3254       node.bitmap.Load(path, null, displaySystem);
3255    }
3256
3257    if(isListItem)
3258       node.bits.isListItem = true;
3259
3260    return node;
3261 }
3262
3263 FileSystemNode MakeComparedFileSystemNode(
3264    const FileStats stats,
3265    const char * name,
3266    const char * path,
3267    const bool pathAddName,
3268    const bool previewPicture,
3269    const int cmpIcon,
3270    const bool cmpNot,
3271    /*const */Array<int> exists,
3272    /*const */DisplaySystem displaySystem)
3273 {
3274    FileSystemNode node = MakeFileSystemNode(stats, name, path, pathAddName, previewPicture, false, displaySystem);
3275    if(node)
3276    {
3277       node.cmpIcon = cmpIcon;
3278       node.cmpNot = cmpNot;
3279       node.exists = exists;
3280    }
3281    return node;
3282 }
3283
3284 #if 0 // could we do this?
3285 class BoolArrayInt : int
3286 {
3287    // packing 32 bools in one int exposing them as an array
3288    bool [32]:1; // the :1 specifies the size of each element
3289    // byte [4]:8; // packing 4 bytes in an int exposing them as an arrat
3290 }
3291 // allowing you to access each 32 bits with the following notation:
3292 static void Dummy()
3293 {
3294    int c;
3295    BoolArrayInt a;
3296    a[0] = true;
3297    a[31] = false;
3298    for(c = 0; c < 32; c++)
3299       a[c] = SomFunction(...);
3300 }
3301 #endif
3302 class BoolArrayInt : int
3303 {
3304    bool  _0:1;
3305    bool  _1:1;
3306    bool  _2:1;
3307    bool  _3:1;
3308    bool  _4:1;
3309    bool  _5:1;
3310    bool  _6:1;
3311    bool  _7:1;
3312    bool  _8:1;
3313    bool  _9:1;
3314    bool _10:1;
3315    bool _11:1;
3316    bool _12:1;
3317    bool _13:1;
3318    bool _14:1;
3319    bool _15:1;
3320 }