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