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