tests:libede: added navigateFolders option to all tests
[ede] / libede / src / FileSystemBox.ec
1 public import "ecere"
2
3 #ifdef __WIN32__
4 static char * rootName = "Entire Computer";
5 static char * msNetwork = "Microsoft Windows Network";
6 #else
7 static char * rootName = "File System";
8 #endif
9
10 private:
11 define guiApp = ((GuiApplication)__thisModule);  // how to do this in a dll?
12 define selectionColor = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
13
14 static char * fileIconNames[] = 
15 {
16    "<:ecere>mimeTypes/file.png",         /* none */
17
18    "<:ecere>mimeTypes/file.png",         /* normalFile */
19    "<:ecere>mimeTypes/textEcereWorkspace.png",          /* ewsFile */
20    "<:ecere>mimeTypes/textEcereProject.png",      /* epjFile */
21    "<:ecere>mimeTypes/textEcereSource.png",         /* ecFile */
22    "<:ecere>mimeTypes/textEcereHeader.png",         /* ehFile */
23    "<:ecere>mimeTypes/textCSource.png",          /* cFile */
24    "<:ecere>mimeTypes/textCHeader.png",          /* hFile */
25    "<:ecere>mimeTypes/textC++Source.png",        /* cppFile */
26    "<:ecere>mimeTypes/textC++Header.png",        /* hppFile */
27    "<:ecere>mimeTypes/text.png",         /* textFile */
28    "<:ecere>mimeTypes/textHyperTextMarkup.png",              /* webFile */
29    "<:ecere>mimeTypes/image.png",        /* pictureFile */
30    "<:ecere>status/audioVolumeHigh.png",         /* soundFile */
31    "<:ecere>mimeTypes/package.png",      /* archiveFile */
32    "<:ecere>mimeTypes/packageSoftware.png",     /* packageFile */
33    "<:ecere>mimeTypes/packageOpticalDisc.png", /* opticalMediaImageFile */
34
35    "<:ecere>places/folder.png",                    /* folder */
36    "<:ecere>status/folderOpen.png",               /* folderOpen */
37    "<:ecere>devices/computer.png",                 /* computer */
38    "<:ecere>devices/driveHardDisk.png",           /* drive */
39    "<:ecere>places/driveRemote.png",              /* netDrive */
40    "<:ecere>devices/mediaOptical.png",            /* cdrom */
41    "<:ecere>devices/driveRemovableMedia.png",    /* removable */
42    "<:ecere>devices/mediaFloppy.png",             /* floppy */
43    "<:ecere>places/networkWorkgroup.png",         /* network */
44    "<:ecere>places/networkServer.png",            /* server */
45    "<:ecere>places/folderRemote.png",             /* share */
46
47    "<:ecere>mimeTypes/package.png",      /* treeLoader */
48    "<:ecere>places/startHere.png",                /* lineNumbers */
49    
50    ""
51 };
52
53 public enum _FileType
54 {
55    none,
56    
57    normalFile, ewsFile, epjFile, ecFile, ehFile, cFile, hFile, cppFile, hppFile,
58    textFile, webFile, pictureFile, soundFile,
59    archiveFile, packageFile, opticalMediaImageFile, /* these (all previous) are sort equal */
60    
61    folder, folderOpen, computer,
62    drive, netDrive, cdrom, removable, floppy, network, server, share, // these are sort equal
63    
64    // utilities
65    treeLoader,
66    lineNumbers;
67
68    /*property char * 
69    {
70       set
71       {
72          this = SelectByExtension(value);
73       }
74    }*/
75
76    public property bool isFolder
77    {
78       get { return this >= folder && this <= share; }
79    }
80
81    public property bool isFile
82    {
83       get { return this >= normalFile && this <= opticalMediaImageFile; }
84    }
85
86    _FileType ::SelectByExtension(char * extension)
87    {
88       if(!strcmpi(extension, "ews"))
89          return ewsFile;
90       else if(!strcmpi(extension, "epj"))
91          return epjFile;
92       else if(!strcmpi(extension, "ec"))
93          return ecFile;
94       else if(!strcmpi(extension, "eh"))
95          return ehFile;
96       else if(!strcmpi(extension, "cpp") ||
97             !strcmpi(extension, "cc") || !strcmpi(extension, "cxx"))
98          return cppFile;
99       else if(!strcmpi(extension, "hpp") ||
100             !strcmpi(extension, "hh") || !strcmpi(extension, "hxx"))
101          return hppFile;
102       else if(!strcmpi(extension, "c"))
103          return cFile;
104       else if(!strcmpi(extension, "h"))
105          return hFile;
106       else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
107             !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
108          return textFile;
109       else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
110             !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
111             !strcmpi(extension, "js"))
112          return webFile;
113       else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
114             !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
115             !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
116             !strcmpi(extension, "ico"))
117          return pictureFile;
118       else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
119             !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
120          return soundFile;
121       else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
122             !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
123             !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
124             !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
125             !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
126             !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
127          return archiveFile;
128       else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
129             !strcmpi(extension, "rpm"))
130          return packageFile;
131       else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
132             !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
133             !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
134             !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
135          return opticalMediaImageFile;
136       return normalFile;
137    }
138 };
139
140 class FileSystemBoxBits
141 {
142    bool foldersOnly:1, filesOnly:1, details:1, treeBranches:1, previewPictures:1, navigateFolders:1;
143    //bool header:1, freeSelect:1, fullRowSelect:1, multiSelect:1, autoScroll:1, alwaysHL : 1, moveRows:1, resizable:1;
144    //bool moveFields:1, clearHeader:1, alwaysEdit:1, collapse:1, treeBranch:1, rootCollapse:1, heightSet:1;
145    //bool sortable:1, noDragging:1, fillLastField:1, expandOnAdd:1;
146 };
147
148 public class FileSystemBox : Window // should we not derive from ListBox instead?
149 /*
150    this stuff from the listbox would be nicely exposed...
151       fullRowSelect = false;
152       treeBranches = true;
153       collapseControl = true;
154       rootCollapseButton = true;
155       sortable = true;
156 */
157 {
158    borderStyle = deep;
159    hasHorzScroll = false;
160    hasVertScroll = false;
161
162    menu = Menu { };
163
164 public:
165    FileSystemNode root;
166    FileSystemNode selection;
167
168    virtual bool Window::NotifyNodeSelect(FileSystemBox box, FileSystemNode node);
169    virtual bool Window::NotifyNodeOpen(FileSystemBox box, FileSystemNode node);
170    
171    property char * path
172    {
173       set
174       {
175          delete path;
176          if(value && value[0])
177             path = CopyString(value);
178          if(locationBox)
179             locationBox.path = value;
180          if(created)
181             Load();
182       }
183
184       get { return path; }
185       //isset { return path && path[0]; }
186    }
187
188    property bool foldersOnly { set { bits.foldersOnly = value; bits.filesOnly = !value; } get { return bits.foldersOnly; } };
189    property bool filesOnly { set { bits.filesOnly = value; bits.foldersOnly = !value; } get { return bits.filesOnly; } };
190    property bool previewPictures { set { bits.previewPictures = value; } get { return bits.previewPictures; } };
191    property char * extensions { set { delete extensions; if(value && value[0]) extensions = CopyString(value); } get { return extensions; } }
192    property bool details { set { bits.details = value; } get { return bits.details; } };
193    property bool treeBranches
194    {
195       set
196       {
197          bits.treeBranches = value;
198          list.treeBranches = value;
199          list.collapseControl = value;
200          list.rootCollapseButton = value;
201       }
202       get { return bits.treeBranches; }
203    };
204    property bool navigateFolders { set { bits.navigateFolders = value; bits.foldersOnly = !value; } get { return bits.navigateFolders; } };
205    
206    property FileSystemNode node
207    {
208       get
209       {
210          if(!list)
211             return null;
212          if(!list.currentRow)
213             return null;
214          if(!list.currentRow.tag)
215             return null;
216          return (FileSystemNode)list.currentRow.tag;
217       }
218    }
219
220    PathBox locationBox;
221
222    void Select(FileSystemNode node)
223    {
224       if(node.row)
225       {
226          node.EnsureVisible(false);
227          list.SelectRow(node.row);
228       }
229    }
230
231    FileSystemNode Find(const char * name, FileSystemNode parent)
232    {
233       FileSystemNode node;
234       FileSystemNode start = parent ? parent : root;
235       if(!start.loaded || !start.childrenLoaded)
236          LoadTreeNode(start);
237       for(node = start.children.first; node; node = node.next)
238          if(node.name && !strcmpi(node.name, name))
239             return node;
240       return null;
241    }
242
243    void Refresh()
244    {
245       Load();
246    }
247
248 private:
249    FileSystemBoxBits bits;
250
251    char * path;
252    char * extensions;
253
254    BitmapResource fileIcons[_FileType];
255
256    FileSystemBox()
257    {
258       char wd[MAX_LOCATION];
259       GetWorkingDir(wd, sizeof(wd));
260       property::path = wd;
261       
262       InitFileIcons();
263       list.AddField(nameField);
264    }
265    ~FileSystemBox()
266    {
267       delete extensions;
268       delete path;
269    }
270    void InitFileIcons()
271    {
272       _FileType c;
273       for(c = 0; c < _FileType::enumSize; c++)
274       {
275          fileIcons[c] = BitmapResource { fileIconNames[c], alphaBlend = true };
276          AddResource(fileIcons[c]);
277       }
278    }
279
280    DataField nameField { dataType = "FileSystemNode", width = 240, userData = this, freeData = false };
281    DataField typeField { header = "Type", dataType = /*"String"*/ "char *", width = 40, freeData = false };
282    DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right, freeData = false };
283
284    bool OnPostCreate()
285    {
286       Load();
287       return true;
288    }
289
290    ListBox list
291    {
292       this;
293
294       borderStyle = none;
295       hasHorzScroll = true;
296       hasVertScroll = true;
297       fullRowSelect = false;
298       sortable = true;
299
300       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
301
302       // WHY is this not working ?
303       /*void OnResize(int width, int height)
304       {
305          if(vertScroll.visible)
306             nameField.width = width - vertScroll.size.w;
307          else
308             nameField.width = width;
309       }*/
310
311       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
312       {
313          if(row)
314          {
315             FileSystemNode node = (FileSystemNode)row.tag;
316             FileSystemNode child;
317             if(collapsed)
318             {
319                /*
320                for(child = node.children.last; child; child = node.children.last)
321                {
322                   listBox.DeleteRow(child.row);
323                   child.Free();
324                   delete child;
325                }
326                node.childrenLoaded = false;
327                */
328             }
329             else
330             {
331                if(!node.loaded || !node.childrenLoaded)
332                {
333                   LoadTreeNode(node);
334                   //list.Sort(nameField, 1);
335                   //node.
336                }
337                for(child = node.children.first; child && child.next; child = child.next);
338                if(child)
339                   child.EnsureVisible(false);
340             }
341          }
342          return true;
343       }
344       
345       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
346       {
347          DataRow row = listBox.currentRow;
348          if(row)
349          {
350             FileSystemNode node = (FileSystemNode)row.tag;
351             if(node)
352             {
353                PopupMenu popup;
354                Menu menu { };
355
356                MenuItem { menu, "Cut\tCtrl+X", t, NotifySelect = null, disabled = false };
357                MenuItem { menu, "Copy\tCtrl+C", c, NotifySelect = null, disabled = false };
358                MenuItem { menu, "Paste\tCtrl+V", p, NotifySelect = null, disabled = false /*!clipboard*/ };
359                MenuItem { menu, "Delete\tDel", d, NotifySelect = null, disabled = false };
360                //MenuDivider { menu };
361
362                popup = PopupMenu
363                   {
364                      master = this, menu = menu,
365                      position = { 
366                         x + clientStart.x + absPosition.x - guiApp.desktop.position.x, 
367                         y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
368                   };
369                popup.Create();
370             }
371          }
372          return true;
373       }
374
375       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
376       {
377          if(row)
378          {
379             FileSystemNode node = (FileSystemNode)row.tag;
380             NotifyNodeSelect(listBox.parent.master, this, node);
381             selection = node;
382          }
383          return true;
384       }
385
386       bool NotifyEditing(ListBox listBox, DataRow row)
387       {
388          if(row)
389          {
390             FileSystemNode node = (FileSystemNode)row.tag;
391          }
392          return true;
393       }
394
395       bool NotifyEdited(ListBox listBox, DataRow row)
396       {
397          if(row)
398          {
399             FileSystemNode node = (FileSystemNode)row.tag;
400          }
401          return true;
402       }
403
404       bool NotifyEditDone(ListBox listBox, DataRow row)
405       {
406          if(row)
407          {
408             FileSystemNode node = (FileSystemNode)row.tag;
409          }
410          return true;
411       }
412
413       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
414       {
415          return OpenNode();
416       }
417
418       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
419       {
420          bool result;
421          if((SmartKey)key == enter)
422             result = OpenNode();
423          else
424             result = true;
425          return true;
426       }
427    };
428
429    bool OpenNode()
430    {
431       bool result;
432       if(selection && selection.type.isFolder && bits.navigateFolders)
433       {
434          property::path = selection.path;
435          result = true;
436       }
437       else
438          result = NotifyNodeOpen(this.master, this, selection);
439       return result;
440    }
441
442    // Edit Menu
443    Menu editMenu { menu, "Edit", e };
444    MenuItem itemEditCut
445    {
446       editMenu, "Cut\tCtrl+X", t, disabled = true;
447
448       bool NotifySelect(MenuItem selection, Modifiers mods)
449       {
450          //EditCut();
451          return true;
452       }
453    };
454    MenuItem itemEditCopy
455    {
456       editMenu, "Copy\tCtrl+C", c, disabled = true;
457
458       bool NotifySelect(MenuItem selection, Modifiers mods)
459       {
460          //EditCopy();
461          return true;
462       }
463    };
464    MenuItem itemEditPaste
465    {
466       editMenu, "Paste\tCtrl+V", p;
467    
468       bool NotifySelect(MenuItem selection, Modifiers mods)
469       {
470          //EditPaste();
471          return true;
472       }
473    };
474    MenuItem itemEditDelete
475    {
476       editMenu, "Delete\tDel", d, disabled = true;
477
478       bool NotifySelect(MenuItem selection, Modifiers mods)
479       {
480          //EditDelete();
481          return true;
482       }
483    };
484
485    // WHY is this crashing ? 
486    /*void OnResize(int width, int height)
487    {
488       if(this && nameField)
489          nameField.width = width - 80;
490    }*/
491
492    void Load()
493    {
494       // TODO: fix this!
495       // this is crashing in for designer when details = true // can't save the file, always yields a crash
496       /*if(list && created)
497       {
498          list.ClearFields();
499          list.AddField(nameField);
500          if(bits.details)
501          {
502             list.AddField(typeField);
503             list.AddField(sizeField);
504          }
505       }*/
506       if(bits.treeBranches)
507          LoadTree();
508       else
509          LoadList();
510    }
511
512    void LoadList()
513    {
514       FileListing listing { path, extensions = extensions };
515
516       list.Clear();
517       while(listing.Find())
518       {
519          if((!bits.foldersOnly && !bits.filesOnly) ||
520             (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
521             (bits.filesOnly && !listing.stats.attribs.isDirectory/*listing.stats.attribs.isFile*/)) // TOCHECK: isFile broken?
522          {
523             FileSystemNode node = MakeFileSystemNode(listing.stats, listing.name, listing.path, bits.previewPictures, displaySystem);
524             DataRow row = list.AddRow();
525             row.tag = (int)node;
526             row.SetData(nameField, node);
527             if(bits.details)
528             {
529                row.SetData(typeField, node.extension);
530                row.SetData(sizeField, (void *)node.stats.size);
531             }
532          }
533       }
534       list.Sort(nameField, 1);
535    }
536
537    void LoadTree()
538    {
539       bool isRoot = !strcmp(path, "/");
540       //char startPath[MAX_LOCATION];
541       FileSystemNode parent;
542       FileSystemNode node;
543       FileListing listing { path, extensions = extensions };
544       
545       /*if(!path)
546          GetWorkingDir(startPath, sizeof(startPath));
547       else
548          strcpy(path, startPath);*/
549       
550       list.Clear();
551
552       delete root;
553    #ifdef __WIN32__
554       if(isRoot)
555       {
556          root = FileSystemNode { loaded = true, childrenLoaded = true };
557          AddTreeNode(root, true, false, null);
558          while(listing.Find())
559          {
560             int len = strlen(listing.name);
561             char info[MAX_LOCATION];
562             char name[MAX_LOCATION];
563             if(listing.stats.attribs.isDrive &&
564                   len > 3 && !strncmp(&listing.name[1], ": [", 3))
565             {
566                strncpy(name, listing.name, 2);
567                name[2] = 0;
568                strncpy(info, &listing.name[4], len - 5);
569                info[len - 5] = 0;
570             }
571             else
572             {
573                strcpy(name, listing.name);
574                info[0] = 0;
575             }
576
577             parent = MakeFileSystemNode(listing.stats, name, listing.path, bits.previewPictures, displaySystem);
578             if(info[0])
579                parent.info = CopyString(info);
580             parent.loaded = true;
581             AddTreeNode(parent, !listing.stats.attribs.isDirectory, listing.stats.attribs.isDirectory, root);
582             if(!listing.stats.attribs.isDirectory)
583                parent.childrenLoaded = true;
584          }
585
586          node = FileSystemNode { name = msNetwork, type = network };
587          AddTreeNode(node, false, true, null);
588          node.row.collapsed = true;
589       }
590       else
591    #endif
592       {
593          root = MakeFileSystemNode(FileStats { attribs = FileExists(path)}, path, path, bits.previewPictures, displaySystem);
594          AddTreeNode(root, false, true, null);
595          LoadTreeNode(root);
596       }
597
598       if(isRoot)
599       {
600          root.type = computer;
601          root.name = rootName;
602       }
603
604       list.Sort(nameField, 1);
605       list.SelectRow(root.row);
606    }
607
608    void LoadTreeNode(FileSystemNode node)
609    {
610       if(!node.loaded)
611       {
612          char path[MAX_LOCATION];
613          node.GetPath(path);
614          {
615             FileListing listing { path, extensions = extensions };
616             if(node.children.count == 1)
617             DeleteNode(node.children.first);
618
619             while(listing.Find())
620             {
621                if((!bits.foldersOnly && !bits.filesOnly) ||
622                   (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
623                   (bits.filesOnly && !listing.stats.attribs.isDirectory/*listing.stats.attribs.isFile*/)) // TOCHECK: isFile broken?
624                {
625                   FileSystemNode child = MakeFileSystemNode(listing.stats, listing.name, listing.path, bits.previewPictures, displaySystem);
626                   AddTreeNode(child, true, false, node);
627                   NodeChildLoad(child, node);
628                }
629             }
630          }
631          node.childrenLoaded = true;
632          node.loaded = true;
633          node.row.SortSubRows(false);
634       }
635       else if(!node.childrenLoaded)
636       {
637          FileSystemNode child;
638          if(node.children.first)
639          {
640             for(child = node.children.first; child; child = child.next)
641             {
642                if(!child.loaded)
643                   LoadTreeNode(child);
644                else if(!child.childrenLoaded)
645                   NodeChildLoad(child, node);
646             }
647             node.childrenLoaded = true;
648             node.row.SortSubRows(false);
649          }
650       }
651    }
652
653    void NodeChildLoad(FileSystemNode parent, FileSystemNode node)
654    {
655       char path[MAX_LOCATION];
656       parent.GetPath(path);
657       {
658          bool added = false;
659          FileListing listing { path, extensions = extensions };
660          while(listing.Find())
661          {
662             if((!bits.foldersOnly && !bits.filesOnly) ||
663                (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
664                (bits.filesOnly && !listing.stats.attribs.isDirectory/*listing.stats.attribs.isFile*/)) // TOCHECK: isFile broken?
665             {
666                FileSystemNode child = MakeFileSystemNode(listing.stats, listing.name, listing.path, bits.previewPictures, displaySystem);
667                AddTreeNode(child, true, false, parent);
668                added = true;
669             }
670          }
671          if(!added)
672             added = true;
673       }
674       //parent.childrenLoaded = true;
675    }
676
677    void AddTreeNode(FileSystemNode node, bool loaded, bool addLoader, FileSystemNode addTo)
678    {
679       DataRow row = (addTo && addTo.row) ? addTo.row.AddRow() : list.AddRow();
680       if(addTo)
681       {
682          node.parent = addTo;
683          node.indent = addTo.indent + 1;
684          addTo.children.Add(node);
685       }
686       row.tag = (int)node;
687       node.row = row;
688       row.SetData(null, node);
689
690       node.loaded = loaded;
691       if(addLoader)
692          //AddTreeNode(FileSystemNode { }, false, false, node); // why would this create a compile error?
693          AddTreeNode(FileSystemNode { type = none }, false, false, node);
694
695       if(node.indent > 0)
696          row.collapsed = true;
697       else if(node.type == folder)
698          node.type = folderOpen;
699    }
700
701    void DeleteNode(FileSystemNode node)
702    {
703       FileSystemNode child;
704       for(; (child = node.children.first); )
705          DeleteNode(child);
706       list.DeleteRow(node.row);
707       node.Delete();
708    }
709 }
710
711 /*
712 #if 0
713 class ExplorerView : FileSystemBox
714 {
715    borderStyle = none;
716    hasHorzScroll = false;
717    hasVertScroll = false;
718
719    virtual void Load(FileSystemNode parent);
720    virtual void Refresh();
721
722    virtual void LaunchNotifyItemSelect(Window master, ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
723    {
724       view.NotifyItemSelect(master, view, item, selectedItems);
725    }
726
727    virtual bool Window::NotifyItemSelect(ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems);
728    virtual bool Window::NotifyItemOpen(ExplorerView view, ExplorerFileItem item);
729
730    ListBox list
731    {
732       master = master, parent = this;
733       //this, master;
734       borderStyle = none;
735       hasHorzScroll = true;
736       hasVertScroll = true;
737       resizable = true;
738       sortable = true;
739       fullRowSelect = false;
740       multiSelect = true;
741
742       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
743
744       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
745       {
746          ExplorerView view = (ExplorerView)listBox.parent;
747          if(listBox.currentRow)
748          {
749             DataRow listRow;
750             ExplorerFileItemArray selectedItems { growingFactor = 16 };
751             for(listRow = listBox.firstRow; listRow; listRow = listRow.next)
752                if(listRow.selected)
753                   selectedItems.Add((ExplorerFileItem)listRow.tag);
754             //view.NotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
755             view.LaunchNotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag, selectedItems);
756          }
757          return true;
758       }
759
760       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
761       {
762          ExplorerView view = (ExplorerView)listBox.parent;
763          view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
764          return false;
765       }
766
767       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
768       {
769          if((SmartKey)key == enter)
770          {
771             ExplorerView view = (ExplorerView)listBox.parent;
772             view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
773          }
774          return true;
775       }
776    };
777
778    ExplorerView()
779    {
780    }
781 }
782 #endif
783
784 #if 0
785 class ExplorerViewList : ExplorerView
786 {
787
788    FileSystemNode location;
789
790 public:
791
792    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
793
794    ExplorerViewDetails()
795    {
796       list.AddField(nameField);
797    }
798
799    void Refresh()
800    {
801       Load(location);
802    }
803
804    void Load(FileSystemNode location)
805    {
806       char path[MAX_LOCATION];
807       this.location = location;
808       location.GetPath(path);
809       {
810          FileListing listing { path };
811          
812          ExplorerFileItem item;
813          DataRow row;
814
815          list.Clear();
816
817          while(listing.Find())
818          {
819             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
820
821             row = list.AddRow();
822             row.tag = (int)item;
823             row.SetData(nameField, item);
824          }
825          list.Sort(nameField, 1);
826       }
827    }
828 }
829 #endif
830
831 #if 0
832 class ExplorerViewDetails : ExplorerView
833 {
834    list.hasHeader = true;
835    list.moveFields = true;
836    list.resizable = true;
837    list.sortable = true;
838
839    FileSystemNode location;
840
841 public:
842
843    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
844    DataField typeField { header = "Type", dataType = /-*"String"*-/ "char *", width = 40 };
845    DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right };
846
847    ExplorerViewDetails()
848    {
849       list.AddField(nameField);
850       list.AddField(typeField);
851       list.AddField(sizeField);
852    }
853
854    void Refresh()
855    {
856       Load(location);
857    }
858
859    void Load(FileSystemNode location)
860    {
861       char path[MAX_LOCATION];
862       this.location = location;
863       location.GetPath(path);
864       {
865          FileListing listing { path };
866          
867          ExplorerFileItem item;
868          DataRow row;
869
870          list.Clear();
871
872          while(listing.Find())
873          {
874             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
875
876             row = list.AddRow();
877             row.tag = (int)item;
878             row.SetData(nameField, item);
879             row.SetData(typeField, CopyString(item.extension));
880             row.SetData(sizeField, (uint)listing.stats.size);
881          }
882          list.Sort(nameField, 1);
883       }
884    }
885 }
886 #endif
887
888 #if 0
889 class ExplorerViewIcons : ExplorerView
890 {
891
892    FileSystemNode location;
893
894 public:
895
896    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
897
898    ExplorerViewDetails()
899    {
900       list.AddField(nameField);
901    }
902
903    void Refresh()
904    {
905       Load(location);
906    }
907
908    void Load(FileSystemNode location)
909    {
910       char path[MAX_LOCATION];
911       this.location = location;
912       location.GetPath(path);
913       {
914          FileListing listing { path };
915          
916          ExplorerFileItem item;
917          DataRow row;
918
919          list.Clear();
920
921          while(listing.Find())
922          {
923             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
924
925             row = list.AddRow();
926             row.tag = (int)item;
927             row.SetData(nameField, item);
928          }
929          list.Sort(nameField, 1);
930       }
931    }
932 }
933 #endif
934
935 #if 0
936 class ExplorerViewCards : ExplorerView
937 {
938
939    FileSystemNode location;
940
941 public:
942
943    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
944
945    ExplorerViewDetails()
946    {
947       list.AddField(nameField);
948    }
949
950    void Refresh()
951    {
952       Load(location);
953    }
954
955    void Load(FileSystemNode location)
956    {
957       char path[MAX_LOCATION];
958       this.location = location;
959       location.GetPath(path);
960       {
961          FileListing listing { path };
962          
963          ExplorerFileItem item;
964          DataRow row;
965
966          list.Clear();
967
968          while(listing.Find())
969          {
970             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
971
972             row = list.AddRow();
973             row.tag = (int)item;
974             row.SetData(nameField, item);
975          }
976          list.Sort(nameField, 1);
977       }
978    }
979 }
980 #endif
981
982 #if 0
983 public class BitmapArray : RedjArray
984 {
985    type = class(Bitmap);
986 public:
987    Bitmap * const _;
988    Bitmap * Add(Bitmap bitmap)
989    {
990       uint pos = _count;
991       Append(1);
992       _[pos] = bitmap;
993       return &_[pos];
994    }
995    Bitmap * AddBefore(uint position, Bitmap bitmap)
996    {
997       Insert(position, 1);
998       _[position] = bitmap;
999       return &_[position];
1000    }
1001    void Clear()
1002    {
1003       int c;
1004       for(c = 0; c < _count; c++)
1005       {
1006          _[c].Free();
1007          delete _[c];
1008       }  
1009       count = 0;
1010       size = 0;
1011    }
1012 }
1013 #endif
1014
1015 #if 0
1016 class ExplorerViewShowcase : ExplorerView
1017 {
1018    list.anchor = Anchor { left = 0, top = 0, bottom = 0 };
1019    list.size = Size { w = 200 };
1020
1021    FileSystemNode location;
1022
1023 public:
1024
1025    DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 180, editable = true, userData = this };
1026
1027    Bitmap bitmap;
1028    BitmapArray bitmaps { growingFactor = 16 };
1029
1030    Window show
1031    {
1032       this;
1033       borderStyle = none;
1034       anchor = Anchor { top = 0, right = 0, bottom = 0 };
1035
1036       void OnRedraw(Surface surface)
1037       {
1038          ExplorerViewShowcase view = (ExplorerViewShowcase)parent;
1039          if(view.bitmap)
1040          {
1041             int wBmp = view.bitmap.width;
1042             int hBmp = view.bitmap.height;
1043             int wWnd = clientSize.w;
1044             int hWnd = clientSize.h;
1045
1046             int wList = view.list.size.w + view.split.size.w;
1047             
1048             float scale = Min((float)(wWnd - 10) / wBmp, (float)(hWnd - 10) / hBmp);
1049             
1050             int wDraw = (int)(wBmp * scale);
1051             int hDraw = (int)(hBmp * scale);
1052
1053       #ifndef __linux__
1054             surface.Filter(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw, wBmp, hBmp);
1055       #else
1056             // Until Filter / Stretch works with X
1057             surface.Blit(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw);
1058       #endif
1059          }
1060          else
1061          {
1062             surface.SetForeground(white);
1063             surface.Area(0, 0, view.clientSize.w - 1, view.clientSize.h - 1);
1064          }
1065       }
1066    }
1067
1068    SplitWindow split
1069    {
1070       this;
1071       leftPane = list;
1072       rightPane = show;
1073       split = 200;
1074       tabCycle = true;
1075    };
1076
1077    ExplorerViewDetails()
1078    {
1079       list.AddField(nameField);
1080    }
1081
1082    void LaunchNotifyItemSelect(Window master, ExplorerViewShowcase view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
1083    {
1084       int pos;
1085       ExplorerFileItem selItem;
1086       if(view.bitmap)
1087          view.bitmap.Free();
1088       delete view.bitmap;
1089       if(item && item.type == pictureFile)
1090       {
1091          view.bitmap = Bitmap { };
1092          view.bitmap.Load(item.path, null, displaySystem);
1093       }
1094
1095       view.bitmaps.Clear();
1096       view.bitmaps = BitmapArray { };
1097       for(pos = 0; pos < selectedItems.count; pos++)
1098       {
1099          Bitmap bitmap { };
1100          selItem = (ExplorerFileItem)selectedItems._[pos]; 
1101          bitmap.Load(selItem.path, null, displaySystem);
1102          //view.bitmaps.Add(bitmap);
1103       }  
1104       if(item && item.type == pictureFile)
1105       {
1106          view.bitmap = Bitmap { };
1107          view.bitmap.Load(item.path, null, displaySystem);
1108       }
1109
1110       view.show.Update(null);
1111       view.NotifyItemSelect(master, view, item, selectedItems);
1112    }
1113
1114    void Refresh()
1115    {
1116       Load(location);
1117    }
1118
1119    void Load(FileSystemNode location)
1120    {
1121       char path[MAX_LOCATION];
1122       this.location = location;
1123       location.GetPath(path);
1124       {
1125          FileListing listing { path };
1126          
1127          ExplorerFileItem item;
1128          DataRow row;
1129
1130          list.Clear();
1131
1132          while(listing.Find())
1133          {
1134             item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
1135
1136             row = list.AddRow();
1137             row.tag = (int)item;
1138             row.SetData(nameField, item);
1139          }
1140          list.Sort(nameField, 1);
1141       }
1142    }
1143 }
1144 #endif
1145
1146 #if 0
1147 class ExplorerTree : FileSystemBox
1148 {
1149    hasHorzScroll = false;
1150    hasVertScroll = false;
1151
1152    menu = Menu { };
1153
1154 public:
1155
1156    DataField nameField { dataType = "FileSystemNode", width = 240, userData = this };
1157
1158    FileSystemNode root;
1159    FileSystemNode selection;
1160
1161    virtual bool Window::NotifyNodeSelect(ExplorerTree tree, FileSystemNode node);
1162    
1163    property FileSystemNode node
1164    {
1165       get
1166       {
1167          if(!tree)
1168             return null;
1169          if(!tree.currentRow)
1170             return null;
1171          if(!tree.currentRow.tag)
1172             return null;
1173          return (FileSystemNode)tree.currentRow.tag;
1174       }
1175    }
1176
1177    void Select(FileSystemNode node)
1178    {
1179       if(node.row)
1180       {
1181          node.EnsureVisible(false);
1182          tree.SelectRow(node.row);
1183       }
1184    }
1185
1186    FileSystemNode Find(const char * name, FileSystemNode parent)
1187    {
1188       FileSystemNode node;
1189       FileSystemNode start = parent ? parent : root;
1190       if(!start.loaded || !start.childrenLoaded)
1191          LoadTreeNode(start, tree);
1192       for(node = start.children.first; node; node = node.next)
1193          if(node.name && !strcmpi(node.name, name))
1194             return node;
1195       return null;
1196    }
1197
1198    ListBox tree
1199    {
1200       master = master, parent = this;
1201       //this, master;
1202       borderStyle = none;
1203       hasHorzScroll = true;
1204       hasVertScroll = true;
1205       fullRowSelect = false;
1206       treeNodees = true;
1207       collapseControl = true;
1208       rootCollapseButton = true;
1209
1210       anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
1211
1212       // WHY is this not working ?
1213       /-*void OnResize(int width, int height)
1214       {
1215          if(vertScroll.visible)
1216             nameField.width = width - vertScroll.size.w;
1217          else
1218             nameField.width = width;
1219       }*-/
1220
1221       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
1222       {
1223          if(row)
1224          {
1225             FileSystemNode node = (FileSystemNode)row.tag;
1226             FileSystemNode child;
1227             if(collapsed)
1228             {
1229                /-*
1230                for(child = node.children.last; child; child = node.children.last)
1231                {
1232                   listBox.DeleteRow(child.row);
1233                   child.Free();
1234                   delete child;
1235                }
1236                node.childrenLoaded = false;
1237                *-/
1238             }
1239             else
1240             {
1241                if(!node.loaded || !node.childrenLoaded)
1242                   LoadTreeNode(node, tree);
1243                for(child = node.children.first; child && child.next; child = child.next);
1244                if(child)
1245                   child.EnsureVisible(false);
1246             }
1247          }
1248          return true;
1249       }
1250       
1251       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
1252       {
1253          DataRow row = listBox.currentRow;
1254          if(row)
1255          {
1256             FileSystemNode node = (FileSystemNode)row.tag;
1257             if(node)
1258             {
1259                PopupMenu popup;
1260                Menu menu { };
1261
1262                MenuItem { menu, "Cut\tCtrl+X", t, NotifySelect = null, disabled = false };
1263                MenuItem { menu, "Copy\tCtrl+C", c, NotifySelect = null, disabled = false };
1264                MenuItem { menu, "Paste\tCtrl+V", p, NotifySelect = null, disabled = false /-*!clipboard*-/ };
1265                MenuItem { menu, "Delete\tDel", d, NotifySelect = null, disabled = false };
1266                //MenuDivider { menu };
1267
1268                popup = PopupMenu
1269                   {
1270                      master = this, menu = menu,
1271                      position = { 
1272                         x + clientStart.x + absPosition.x - guiApp.desktop.position.x, 
1273                         y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
1274                   };
1275                popup.Create();
1276             }
1277          }
1278          return true;
1279       }
1280
1281       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
1282       {
1283          if(row)
1284          {
1285             FileSystemNode node = (FileSystemNode)row.tag;
1286             NotifyNodeSelect(listBox.parent.master, this, node);
1287             selection = node;
1288          }
1289          return true;
1290       }
1291
1292       bool NotifyEditing(ListBox listBox, DataRow row)
1293       {
1294          if(row)
1295          {
1296             FileSystemNode node = (FileSystemNode)row.tag;
1297          }
1298          return true;
1299       }
1300
1301       bool NotifyEdited(ListBox listBox, DataRow row)
1302       {
1303          if(row)
1304          {
1305             FileSystemNode node = (FileSystemNode)row.tag;
1306          }
1307          return true;
1308       }
1309
1310       bool NotifyEditDone(ListBox listBox, DataRow row)
1311       {
1312          if(row)
1313          {
1314             FileSystemNode node = (FileSystemNode)row.tag;
1315          }
1316          return true;
1317       }
1318    };
1319
1320    // Edit Menu
1321    Menu editMenu { menu, "Edit", e };
1322    MenuItem itemEditCut
1323    {
1324       editMenu, "Cut\tCtrl+X", t, disabled = true;
1325
1326       bool NotifySelect(MenuItem selection, Modifiers mods)
1327       {
1328          //EditCut();
1329          return true;
1330       }
1331    };
1332    MenuItem itemEditCopy
1333    {
1334       editMenu, "Copy\tCtrl+C", c, disabled = true;
1335
1336       bool NotifySelect(MenuItem selection, Modifiers mods)
1337       {
1338          //EditCopy();
1339          return true;
1340       }
1341    };
1342    MenuItem itemEditPaste
1343    {
1344       editMenu, "Paste\tCtrl+V", p;
1345    
1346       bool NotifySelect(MenuItem selection, Modifiers mods)
1347       {
1348          //EditPaste();
1349          return true;
1350       }
1351    };
1352    MenuItem itemEditDelete
1353    {
1354       editMenu, "Delete\tDel", d, disabled = true;
1355
1356       bool NotifySelect(MenuItem selection, Modifiers mods)
1357       {
1358          //EditDelete();
1359          return true;
1360       }
1361    };
1362
1363    // WHY is this crashing ? 
1364    /-*void OnResize(int width, int height)
1365    {
1366       if(this && nameField)
1367          nameField.width = width - 80;
1368    }*-/
1369
1370    ExplorerTree()
1371    {
1372       tree.AddField(nameField);
1373    }
1374
1375    void Load()
1376    {
1377       FileSystemNode parent;
1378       FileSystemNode node;
1379       FileListing listing { "/" };
1380
1381       tree.Clear();
1382
1383       root = FileSystemNode { type = computer, loaded = true, childrenLoaded = true };
1384    #ifdef __WIN32__
1385       root.name = rootName;
1386    #else
1387       root.name = "/";
1388    #endif
1389       AddTreeNode(root, true, false, null, tree);
1390
1391    // How can this make sense for linux? 
1392    #ifdef __WIN32__
1393       while(listing.Find())
1394       {
1395          int len = strlen(listing.name);
1396          char info[MAX_LOCATION];
1397          char name[MAX_LOCATION];
1398          if(listing.stats.attribs.isDrive && 
1399                len > 3 && !strncmp(&listing.name[1], ": [", 3))
1400          {
1401             strncpy(name, listing.name, 2);
1402             name[2] = 0;
1403             strncpy(info, &listing.name[4], len - 5);
1404             info[len - 5] = 0;
1405          }
1406          else
1407          {
1408             strcpy(name, listing.name);
1409             info[0] = 0;
1410          }
1411
1412          parent = MakeFileSystemNode(listing.stats, name);
1413          if(info[0])
1414             parent.info = CopyString(info);
1415          parent.loaded = true;
1416          AddTreeNode(parent, !listing.stats.attribs.isDirectory, listing.stats.attribs.isDirectory, root, tree);
1417          if(!listing.stats.attribs.isDirectory)
1418             parent.childrenLoaded = true;
1419       }
1420    #endif
1421       node = FileSystemNode { name = msNetwork, type = network };
1422       AddTreeNode(node, false, true, null, tree);
1423       node.row.collapsed = true;
1424       tree.Sort(nameField, 1);
1425       tree.SelectRow(root.row);
1426    }
1427 }
1428
1429 #if 0
1430 public class ClipBoardFiles
1431 {
1432
1433 public:
1434
1435    property
1436
1437 }
1438
1439    // CLIPBOARD
1440    void Copy()
1441    {
1442       if(this)
1443       {
1444          int size = SelSize();
1445          if(size)
1446          {
1447             // Try to allocate memory
1448             ClipBoard clipBoard { };
1449             if(clipBoard.Allocate(size+1))
1450             {
1451                GetSel(clipBoard.memory, true);   
1452                // Save clipboard
1453                clipBoard.Save();
1454             }
1455             delete clipBoard;
1456          }
1457       }
1458    }
1459
1460    void Paste()
1461    {
1462       if(this)
1463       {
1464          ClipBoard clipBoard { };
1465          if(clipBoard.Load())
1466             PutS(clipBoard.memory);
1467          delete clipBoard;
1468       }
1469    }
1470
1471    void Cut()
1472    {
1473       if(this)
1474       {
1475          Copy();
1476          DelSel();
1477          SetViewToCursor(true);
1478          Modified();
1479       }
1480    }
1481
1482 /-*
1483 Private Type DROPFILES
1484    pFiles As Long
1485    pt As POINTAPI
1486    fNC As Long
1487    fWide As Long
1488 End Type
1489 For iCounter = 0 To filelist.ListCount - 1
1490   If filelist.Selected(iCounter) = True Then
1491     strFiles = strFiles & FixPath(filelist.Path) & filelist.List(iCounter) & vbNullChar
1492   End If
1493 Next
1494 'all selected items are now put in strFiles
1495
1496 hGlobal = GlobalAlloc(GHND, Len(DF) + Len(strFiles)) 'put all files to a exclusive number
1497 If hGlobal Then 'if the globalalloc worked
1498   lpGlobal = GlobalLock(hGlobal) 'lock the hGlobal
1499   DF.pFiles = Len(DF) 'set the size of the files
1500   
1501   Call CopyMem(ByVal lpGlobal, DF, Len(DF)) 'copy df to the lpglobal
1502   Call CopyMem(ByVal (lpGlobal + Len(DF)), ByVal strFiles, Len(strFiles)) 'copy strfiles to lpglobal
1503   Call GlobalUnlock(hGlobal) 'unlock hglobal again
1504   
1505   SetClipboardData CF_HDROP, hGlobal 'put files to the clipboard
1506 End If
1507 *-/
1508    bool SaveFile(const char * filePath)
1509    {
1510    }
1511 #endif
1512
1513 #if 0
1514 public class FileTreeNodeBSArray : ArrayBinarySorted
1515 {
1516    type = class(FileSystemNode);
1517 public:
1518    FileSystemNode * const _;
1519    BSloc Add(FileSystemNode item)
1520    {
1521       BSloc result = Find(item);
1522       if(!result.valid)
1523       {
1524          Insert(result.pos, 1);
1525          _[result.pos] = item;
1526       }
1527       return result;
1528    }
1529    BSloc Remove(FileSystemNode item)
1530    {
1531       
1532    }
1533 }
1534 #endif
1535
1536 #if 0
1537 public class FileTreeNodeArray : RedjArray
1538 {
1539    type = class(FileSystemNode);
1540 public:
1541    FileSystemNode * const _;
1542    FileSystemNode * Add(FileSystemNode item)
1543    {
1544       uint pos = _count;
1545       Append(1);
1546       _[pos] = item;
1547       return &_[pos];
1548    }
1549    FileSystemNode * AddBefore(uint position, FileSystemNode item)
1550    {
1551       Insert(position, 1);
1552       _[position] = item;
1553       return &_[position];
1554    }
1555 }
1556 #endif
1557
1558 #if 0
1559 public class ExplorerFileItem : struct
1560 {
1561    char * path;
1562    char * name;
1563    char * info;
1564    char * extension;
1565    _FileType type;
1566    int indent;
1567
1568    Bitmap bitmap;
1569
1570    void OnDisplay(Surface surface, int x, int y, int width, FileSystemBox control, Alignment alignment, DataDisplayFlags displayFlags)
1571    {
1572       int indentSize = (displayFlags.dropBox) ? 0 : 10;
1573       int textOffset;
1574       int len;
1575       char label[MAX_FILENAME];
1576
1577       //float scale = Min((float)clientSize.w / (float)bitmap.width, (float)clientSize.h / (float)bitmap.height);
1578       int w = 16; //(int)(bitmap.width * scale);
1579       int h = 16; //(int)(bitmap.height * scale);
1580    
1581       Bitmap icon;
1582
1583       icon = control.fileIcons[type].bitmap;
1584       if(!icon)
1585       {
1586          if(type == folder || type == folderOpen)
1587             surface.SetForeground(red); //Color { 170, 170, 0 } // REDJ What is that color?
1588          indentSize = 8;
1589       }
1590       textOffset = indent * indentSize + (icon ? (icon.width + 6) : 0);
1591       
1592       if(info)
1593          sprintf(label, "%s [%s]", name, info);
1594       else
1595          strcpy(label, name);
1596       len = strlen(label);
1597
1598       surface.WriteTextDots
1599          (alignment, x + textOffset, y + 2, width - textOffset, label, len);
1600       if(type == pictureFile && control.previewPictures && bitmap)
1601       {
1602 #ifndef __linux__
1603          //surface.Filter(bitmap, (clientSize.w - w) / 2,(clientSize.h - h) / 2, 0,0, w, h, bitmap.width, bitmap.height);
1604          surface.Filter(bitmap, x + indent * indentSize + 2, y, 0, 0, w, h, bitmap.width, bitmap.height);
1605 #else
1606          // Until Filter / Stretch works with X
1607          //surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2,(clientSize.h - bitmap.height) / 2, 0,0, bitmap.width, bitmap.height);
1608          surface.blend = true;
1609          surface.Blit(bitmap, x + indent * indentSize + 2, y,0,0, w, h);
1610 #endif
1611          //bitmap.Free();
1612          //delete bitmap;
1613       }
1614       else if(icon)
1615          surface.Blit(icon, x + indent * indentSize + 2, y,0,0, icon.width, icon.height);
1616    }
1617
1618    int OnCompare(ExplorerFileItem b)
1619    {
1620       int result;
1621       if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
1622          result = strcmpi(name, b.name);
1623       else
1624       {
1625          if(type == folder && b.type < folder) result = -1;
1626          else if(type < folder && b.type == folder) result = 1;
1627       }
1628       return result;
1629    }
1630
1631    void OnCopy(ExplorerFileItem newData)
1632    {
1633       type = newData.type;
1634       indent = newData.indent;
1635       if(newData.name)
1636       {
1637          int len = strlen(newData.name) + 1;
1638          name = new char[len];
1639          CopyBytes(name, newData.name, len);
1640       }
1641    }
1642
1643    bool OnGetDataFromString(char * string)
1644    {
1645       int len = strlen(string) + 1;
1646       name = new char[len];
1647       CopyBytes(name, string, len);
1648       return true;
1649    }
1650
1651    void OnFree()
1652    {
1653       delete path;
1654       delete name;
1655       delete info;
1656       delete extension;
1657       if(bitmap)
1658          bitmap.Free();
1659    }
1660
1661    char * OnGetString(char * string, void * fieldData, bool * needClass)
1662    {
1663       return name;
1664    }
1665 };
1666
1667 public class ExplorerFileItemArray : RedjArray
1668 {
1669    type = class(ExplorerFileItem);
1670 public:
1671    ExplorerFileItem * const _;
1672    ExplorerFileItem * Add(ExplorerFileItem item)
1673    {
1674       uint pos = _count;
1675       Append(1);
1676       _[pos] = item;
1677       return &_[pos];
1678    }
1679    ExplorerFileItem * AddBefore(uint position, ExplorerFileItem item)
1680    {
1681       Insert(position, 1);
1682       _[position] = item;
1683       return &_[position];
1684    }
1685    void Clear()
1686    {
1687       int c;
1688       for(c = 0; c < _count; c++)
1689       {
1690          //_[c].Free()
1691          delete _[c];
1692       }  
1693       count = 0;
1694       size = 0;
1695    }
1696 }
1697
1698 ExplorerFileItem MakeFileItem(const FileAttribs attribs, const char * fileName, const char * filePath, const bool previewPicture, const DisplaySystem displaySystem)
1699 {
1700    int len = strlen(fileName);
1701    char info[MAX_LOCATION];
1702    char name[MAX_LOCATION];
1703    char extension[MAX_EXTENSION];
1704    
1705    ExplorerFileItem item { };
1706
1707    //if(attribs.isFile) // TODO fix this in ecere
1708    if(attribs.isDirectory)
1709    {
1710       extension[0] = 0;
1711
1712       item.type = (attribs.isDrive) ? drive : folder;
1713       if(attribs.isServer)
1714          item.type = server;
1715       if(attribs.isShare)
1716          item.type = share;
1717       if(attribs.isCDROM)
1718          item.type = cdrom;
1719       if(attribs.isRemote)
1720          item.type = netDrive;
1721       if(attribs.isRemovable) 
1722       {
1723          if(fileName[0] == 'A' || fileName[0] == 'B')
1724             item.type = floppy;
1725          else
1726             item.type = removable;
1727       }
1728    }
1729    else
1730    {
1731       GetExtension(fileName, extension);
1732       //strupr(extension);
1733       strlwr(extension);
1734       
1735       item.type = _FileType::SelectByExtension(extension);
1736    }
1737
1738    if(attribs.isDrive && 
1739          len > 3 && !strncmp(&fileName[1], ": [", 3))
1740    {
1741       strncpy(name, fileName, 2);
1742       name[2] = 0;
1743       strncpy(info, &fileName[4], len - 5);
1744       info[len - 5] = 0;
1745    }
1746    else
1747    {
1748       strcpy(name, fileName);
1749       info[0] = 0;
1750    }
1751
1752    item.path = CopyString(filePath);
1753    item.name = CopyString(name);
1754    if(info[0])
1755       item.info = CopyString(info);
1756    item.extension = CopyString(extension);
1757
1758    if(item.type == pictureFile && previewPicture)
1759    {
1760       item.bitmap = Bitmap { };
1761       item.bitmap.Load(filePath, null, displaySystem);
1762    }
1763
1764    return item;
1765 }
1766 #endif
1767 */
1768
1769 public class FileSystemNode : struct
1770 {
1771 public:
1772    /*//LinkElement<FileSystemNode> link;
1773    FileSystemNode parent;
1774
1775    FileSystemNodeType type;
1776
1777    char * name;*/
1778
1779    FileSystemNode prev, next;
1780
1781    bool loaded, childrenLoaded;
1782    int indent;
1783    char * path;
1784    char * name;
1785    char * extension;
1786    char * info;
1787    DataRow row;
1788    OldList children;
1789    _FileType type;
1790    FileSystemNode parent;
1791
1792    FileStats stats;
1793
1794    Bitmap bitmap;
1795
1796    void GetPath(String outputPath)
1797    {  
1798       FileSystemNode up;
1799       if(parent)
1800       {
1801          strcpy(outputPath, name);
1802          for(up = parent; up; up = up.parent)
1803          {
1804             char temp[MAX_LOCATION];
1805             strcpy(temp, up.name);
1806             PathCat(temp, outputPath);
1807             strcpy(outputPath, temp);
1808          }
1809       }
1810       else
1811 /*#ifdef __WIN32__
1812          strcpy(outputPath, "/");
1813 #else*/
1814          strcpy(outputPath, name);
1815 //#endif
1816
1817    }
1818
1819    bool IsChildOf(FileSystemNode node)
1820    {
1821       FileSystemNode test;
1822       for(test = parent; test; test = test.parent)
1823          if(test == node)
1824             return true;
1825       return false;
1826    }
1827
1828    void DuplicateChildren(bool recursive, bool forceExpanded, FileSystemNode addTo, FileSystemBox fsb)
1829    {
1830       if(children.first)
1831       {
1832          FileSystemNode child;
1833          
1834          for(child = children.first; child; child = child.next)
1835          {
1836             FileSystemNode copy { };
1837             copy.name = CopyString(child.name);
1838             copy.type = child.type;
1839             fsb.AddTreeNode(copy, child.loaded, false, addTo);
1840             if(forceExpanded)
1841                copy.row.collapsed = false;
1842             if(recursive)
1843                child.DuplicateChildren(recursive, forceExpanded, copy, fsb);
1844          }
1845       }
1846    }
1847    
1848    void EnsureVisible(bool expand)
1849    {
1850       if(parent)
1851          parent.EnsureVisible(true);
1852       if(expand)
1853          row.collapsed = false;
1854       // TODO: row.EnsureVisible(); // making the row visible by scrolling
1855    }
1856
1857    void OnFree()
1858    {
1859       //delete name;
1860    }
1861
1862    void Free()
1863    {
1864       FileSystemNode child;
1865       for(; (child = children.first); )
1866       {
1867          child.Free();
1868          children.Delete(child);
1869       }
1870       //if(name)
1871       delete name;
1872       delete info;
1873    }
1874
1875    void Delete()
1876    {
1877       Free();
1878       if(parent)
1879          parent.children.Delete(this);
1880    }
1881
1882    void OnDisplay(Surface surface, int x, int y, int width, FileSystemBox fsb, Alignment alignment, DataDisplayFlags displayFlags)
1883    {
1884       //int indentSize = (displayFlags.dropBox) ? 0 : 10;
1885       int indent = 16;
1886       int xStart;
1887       int len;
1888       int w, h;
1889       //int textOffset;
1890       char label[MAX_FILENAME];
1891
1892       Bitmap icon;
1893
1894       if(!this)
1895          return;
1896       
1897       icon = fsb.fileIcons[type].bitmap;
1898       //xStart = indent * indent + x + (icon ? (icon.width + 5) : 0);
1899       xStart = x + (icon ? (icon.width + 5) : 0);
1900
1901       if(!name)
1902          return;
1903
1904       if(info)
1905          sprintf(label, "%s [%s]", name, info);
1906       else
1907          strcpy(label, name);
1908       len = strlen(label);
1909       
1910       if(!icon)
1911       {
1912          if(type == folder || type == folderOpen)
1913             surface.SetForeground(yellow);
1914          //indentSize = 8;
1915          indent = 8;
1916       }
1917       //textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
1918       
1919       surface.TextOpacity(false);
1920       surface.TextExtent(label, len, &w, &h);
1921       h = Max(h, 16);
1922     
1923       // Draw the current row stipple
1924       if(displayFlags.selected)
1925          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
1926          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
1927          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1928       
1929       //surface.WriteTextDots(alignment, x + textOffset, y + 2, width - textOffset, name, strlen(name));
1930       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1931
1932       //if(!guiApp.textMode) -- how to do this in a dll?
1933       {
1934          if(displayFlags.current)
1935          {
1936             if(displayFlags.active)
1937             {
1938                surface.LineStipple(0x5555);
1939                if(displayFlags.selected)
1940                   surface.SetForeground(0xFFFFFF80);
1941                else
1942                   surface.SetForeground(black);
1943             }
1944             else
1945             {
1946                surface.SetForeground(selectionColor);
1947             }
1948             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1949             surface.LineStipple(0);
1950          }
1951
1952          if(icon)
1953          {
1954             w = icon.width;
1955             h = icon.height;
1956          }
1957          if(type == pictureFile && fsb.previewPictures && bitmap)
1958          {
1959             surface.SetForeground(white);
1960             surface.blend = true;
1961    //#ifndef __linux__
1962             //surface.Filter(bitmap, (clientSize.w - w) / 2,(clientSize.h - h) / 2, 0,0, w, h, bitmap.width, bitmap.height);
1963             //surface.Filter(bitmap, x + indent/* * indentSize*/ + 2, y, 0, 0, w, h, bitmap.width, bitmap.height);
1964             surface.Filter(bitmap, x,y,0,0, w, h, bitmap.width, bitmap.height);
1965    //#else
1966             // Until Filter / Stretch works with X
1967             //surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2,(clientSize.h - bitmap.height) / 2, 0,0, bitmap.width, bitmap.height);
1968    //         surface.blend = true;
1969             //surface.Blit(bitmap, x + indent/* * indentSize*/ + 2, y,0,0, w, h);
1970             //surface.Blit(bitmap, x,y,0,0, bitmap.width, bitmap.height);
1971    //#endif
1972             //bitmap.Free();
1973             //delete bitmap;
1974          }
1975          else if(icon)
1976          {
1977             //surface.blend = true;
1978             //surface.alphaWrite = blend;
1979             surface.SetForeground(white);
1980             //surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
1981             surface.Blit(icon, x,y,0,0, icon.width, icon.height);
1982          }
1983       }
1984    }
1985
1986    int OnCompare(FileSystemNode b)
1987    {
1988       int result;
1989       FileSystemNode a = this;
1990       if(a.type == b.type || (a.type < folder && b.type < folder) || (a.type >= drive))
1991          result = strcmpi(a.name, b.name);
1992       else
1993       {
1994          if(a.type == folder && b.type < folder) result = -1;
1995          else if(a.type < folder && b.type == folder) result = 1;
1996          else result = 0;
1997       }
1998       return result;
1999    }
2000
2001    /*int OnCompare(FileSystemNode b)
2002    {
2003       int result;
2004       FileSystemNode a = this;
2005       if(a.parent < b.parent) result = -1;
2006       else if(a.parent > b.parent) result = 1;
2007       else
2008          result = fstrcmp(a.name, b.name);
2009       return result;
2010    }*/
2011
2012    char * OnGetString(char * tempString, FileSystemToolWindow fileSysToolWnd, bool * needClass)
2013    {
2014       return name ? name : "";
2015    }
2016 }
2017
2018 /*FileSystemNode MakeFileSystemNode(const FileStats stats, const char * name)
2019 {
2020    FileSystemNode node { stats = stats };
2021    node.name = CopyString(name);
2022    if(!node.name)
2023       node.name = null;
2024    if(stats.attribs.isDirectory)
2025    {
2026       node.type = (stats.attribs.isDrive) ? drive : folder;
2027       if(stats.attribs.isServer) node.type = server;
2028       if(stats.attribs.isShare) node.type = share;
2029       if(stats.attribs.isCDROM) node.type = cdrom;
2030       if(stats.attribs.isRemote) node.type = netDrive;
2031       if(stats.attribs.isRemovable) 
2032       {
2033          if(name[0] == 'A' || name[0] == 'B')
2034             node.type = floppy;
2035          else
2036             node.type = removable;
2037       }
2038    }
2039    else
2040    {
2041       char extension[MAX_EXTENSION];
2042       GetExtension(node.name, extension);
2043       node.type = _FileType::SelectByExtension(extension);
2044    }
2045    return node;
2046 }*/
2047 static FileSystemNode MakeFileSystemNode(const FileStats stats,
2048       const char * fileName, const char * filePath,
2049       const bool previewPicture, const DisplaySystem displaySystem)
2050 {
2051    int len = strlen(fileName);
2052    char info[MAX_LOCATION];
2053    char name[MAX_LOCATION];
2054    char extension[MAX_EXTENSION];
2055    
2056    FileSystemNode node { stats = stats };
2057
2058    //if(stats.attribs.isFile) // TODO fix this in ecere
2059    if(stats.attribs.isDirectory)
2060    {
2061       extension[0] = '\0';
2062
2063       node.type = (stats.attribs.isDrive) ? drive : folder;
2064       if(stats.attribs.isServer) node.type = server;
2065       if(stats.attribs.isShare) node.type = share;
2066       if(stats.attribs.isCDROM) node.type = cdrom;
2067       if(stats.attribs.isRemote) node.type = netDrive;
2068       if(stats.attribs.isRemovable) 
2069       {
2070          if(fileName[0] == 'A' || fileName[0] == 'B')
2071             node.type = floppy;
2072          else
2073             node.type = removable;
2074       }
2075    }
2076    else
2077    {
2078       GetExtension(fileName, extension);
2079       strlwr(extension);
2080       
2081       node.type = _FileType::SelectByExtension(extension);
2082    }
2083
2084    if(stats.attribs.isDrive && 
2085          len > 3 && !strncmp(&fileName[1], ": [", 3))
2086    {
2087       strncpy(name, fileName, 2);
2088       name[2] = 0;
2089       strncpy(info, &fileName[4], len - 5);
2090       info[len - 5] = 0;
2091    }
2092    else
2093    {
2094       strcpy(name, fileName);
2095       info[0] = 0;
2096    }
2097
2098    node.path = CopyString(filePath);
2099    node.name = CopyString(name);
2100    if(info[0])
2101       node.info = CopyString(info);
2102    node.extension = CopyString(extension);
2103
2104    if(node.type == pictureFile && previewPicture)
2105    {
2106       node.bitmap = Bitmap { alphaBlend = true };
2107       node.bitmap.Load(filePath, null, displaySystem);
2108    }
2109
2110    return node;
2111 }