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