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