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