+};
+
+public enum FileSystemBoxMode { directory, list };
+
+class FileSystemBoxBits
+{
+ bool foldersOnly:1, filesOnly:1, details:1, pathColumn:1, treeBranches:1, previewPictures:1, navigateFolders:1, autoLoad:1;
+ bool preview:1;
+ bool columnsCompareStyle:1;
+ //bool header:1, freeSelect:1, fullRowSelect:1, multiSelect:1, autoScroll:1, alwaysHL : 1, moveRows:1, resizable:1;
+ //bool moveFields:1, clearHeader:1, alwaysEdit:1, collapse:1, treeBranch:1, rootCollapse:1, heightSet:1;
+ //bool sortable:1, noDragging:1, fillLastField:1, expandOnAdd:1;
+ bool textFileLinesStyle:1;
+
+ FileSystemBoxMode mode:2;
+}
+
+public class FileSystemBox : Window // should we not derive from ListBox instead?
+ // I say we should, but we can't right now...
+ // because ListBox (inside ecere library) is
+ // not exposing enough internal machinery...
+ // could we not have a different private and
+ // public mechanism when deriving a class than
+ // we do when simply instanciating a class?
+/*
+ this stuff from the listbox would be nicely exposed...
+ fullRowSelect = false;
+ treeBranches = true;
+ collapseControl = true;
+ rootCollapseButton = true;
+ sortable = true;
+*/
+{
+ borderStyle = deep;
+ hasHorzScroll = false;
+ hasVertScroll = false;
+
+ menu = Menu { };
+
+public:
+ FileSystemNode root;
+ FileSystemBoxSelection selection { };
+
+ subclass(InterfaceFileSystemIterator) iteratorClass;
+ FileSystemCache cache;
+
+ virtual bool Window::NotifyNodeSelect(FileSystemBox box, FileSystemBoxSelection selection);
+ //virtual bool Window::NotifyNodeNavigate(FileSystemBox box, FileSystemNode node);
+ virtual bool Window::NotifyNodeOpen(FileSystemBox box, FileSystemBoxSelection selection);
+ virtual bool Window::NotifyNodeMenu(FileSystemBox box, Menu menu, FileSystemBoxSelection selection);
+ virtual bool Window::NotifyIteratorInit(FileSystemBox box, FileSystemIterator fileSystemIterator);
+
+ property const char * path
+ {
+ set
+ {
+ delete path;
+ if(value && value[0])
+ path = CopyString(value);
+ if(locationBox)
+ locationBox.path = value;
+ if(created)
+ Load();
+ }
+ get { return path; }
+ //isset { return path && path[0]; }
+ }
+
+ property Array<String> comparedPaths
+ {
+ set
+ {
+ if(comparedPaths)
+ {
+ comparedPaths.Free();
+ delete comparedPaths;
+ }
+ if(value && value.count)
+ comparedPaths = value;
+ if(locationBox)
+ locationBox.path = value[0];
+ if(created)
+ Load();
+ }
+ get { return comparedPaths; }
+ //isset { return comparedPaths && comparedPaths.count; }
+ }
+
+ property FileSystemBoxMode mode { set { bits.mode = value; } get { return bits.mode; } };
+ property bool foldersOnly { set { bits.foldersOnly = value; bits.filesOnly = !value; } get { return bits.foldersOnly; } };
+ property bool filesOnly { set { bits.filesOnly = value; bits.foldersOnly = !value; } get { return bits.filesOnly; } };
+ property bool previewPictures { set { bits.previewPictures = value; } get { return bits.previewPictures; } };
+ property char * extensions { set { delete extensions; if(value && value[0]) extensions = CopyString(value); } get { return extensions; } }
+ property bool details { set { bits.details = value; ChangeViewType(); } get { return bits.details; } };
+ property bool pathColumn { set { bits.pathColumn = value; ChangeViewType(); } get { return bits.pathColumn; } };
+ property bool treeBranches
+ {
+ set
+ {
+ bits.treeBranches = value;
+ list.treeBranches = value;
+ list.collapseControl = value;
+ list.rootCollapseButton = value;
+ }
+ get { return bits.treeBranches; }
+ };
+ property Color selectionColor { set { list.selectionColor = value; } get { return list.selectionColor; }/* isset { return selectionColor ? true : false; }*/ };
+ property Color selectionText { set { list.selectionText = value; } get { return list.selectionText; }/* isset { return selectionText ? true : false; }*/ };
+ property bool navigateFolders { set { bits.navigateFolders = value; bits.filesOnly = !value; } get { return bits.navigateFolders; } };
+ property bool multiSelect { set { list.multiSelect = value; } get { return list.multiSelect; } };
+ property bool autoLoad { set { bits.autoLoad = value; } get { return bits.autoLoad; } };
+ property bool hasHeader { set { list.hasHeader = value; } get { return list.hasHeader; } };
+ property bool preview
+ {
+ set
+ {
+ bits.preview = value;
+ split.leftPane = value ? list : null;
+ split.visible = value;
+ show.visible = value;
+ if(!value)
+ {
+ list.parent = this;
+ list.anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+ }
+ }
+ get { return bits.preview; }
+ };
+ property bool columnsCompareStyle { set { bits.columnsCompareStyle = value; } get { return bits.columnsCompareStyle; } };
+ property bool textFileLinesStyle { set { bits.textFileLinesStyle = value; } get { return bits.textFileLinesStyle; } };
+
+ property FileSystemNode node
+ {
+ get
+ {
+ if(!list)
+ return null;
+ if(!list.currentRow)
+ return null;
+ if(!list.currentRow.tag)
+ return null;
+ return (FileSystemNode)list.currentRow.tag;
+ }
+ }
+
+ PathBox locationBox;
+
+ void Select(FileSystemNode node)
+ {
+ if(node.row)
+ {
+ node.EnsureVisible(false);
+ list.SelectRow(node.row);
+ }
+ }
+
+ void SelectMultipleByPath(Array<String> paths)
+ {
+ DataRow row;
+ bool firstRow = false;
+ Map<String, bool> map { };
+ for(path : paths)
+ map[path] = true;
+ for(row = list.firstRow; row; row = row.next)
+ {
+ FileSystemNode node = (FileSystemNode)row.tag;
+ if(map[node.path])
+ {
+ if(!firstRow)
+ {
+ list.SelectRow(row);
+ firstRow = true;
+ }
+ else
+ row.selected = true;
+ }
+ else
+ row.selected = false;
+ }
+ delete map;
+ }
+
+ FileSystemNode SelectLocation(const char * location)
+ {
+ int c;
+ char * temp;
+ char step[MAX_LOCATION];
+
+ //StringArray steps { growingFactor = 4 };
+ Array<String> steps { };
+ FileSystemNode result = null;
+ FileSystemNode node = null;
+
+ temp = CopyString(location);
+ while(temp[0])
+ {
+ GetLastDirectory(temp, step);
+ StripLastDirectory(temp, temp);
+ steps.Add(CopyString(step));
+ }
+
+ for(c = steps.count - 1; c >= 0; c--)
+ {
+ //char * t = steps[c];
+ node = Find(steps[c], node);
+ if(!node)
+ break;
+ //Select(node);
+ }
+ if(node)
+ {
+ result = node;
+ Select(result);
+ }
+
+ steps.Free();
+ delete temp;
+ delete steps;
+
+ return result;
+ }
+
+ FileSystemNode Find(const char * name, FileSystemNode parent)
+ {
+ FileSystemNode node = null;
+ FileSystemNode result = null;
+ if(!parent/* && !strcmp(name, "/")*/)
+ {
+ DataRow row;
+ for(row = list.firstRow; row; row = row.next)
+ {
+ node = (FileSystemNode)row.tag;
+ if(node.name && !fstrcmp(node.name, name))
+ break;
+ }
+ if(node)
+ result = node;
+ //result = root;
+ }
+ else
+ {
+ FileSystemNode start = parent ? parent : root;
+ if(!start.bits.loaded || !start.bits.childrenLoaded)
+ LoadTreeNode(start);
+ for(node = start.children.first; node; node = node.next)
+ if(node.name && !fstrcmp(node.name, name))
+ break;
+ if(node)
+ result = node;
+ }
+ return result;
+ }
+
+ void Clear()
+ {
+ list.Clear();
+ }
+
+ void Refresh()
+ {
+ Load();
+ }
+
+ bool MenuOpen(MenuItem selection, Modifiers mods)
+ {
+ OpenNode();
+ return true;
+ }
+
+ bool MenuReplaceListItemByContainingDir(MenuItem selection, Modifiers mods)
+ {
+ bool result = true;
+ //FileSystemBoxSelection selection = this.selection.Copy(); // TOFIX compiler bug -- FileSystemBox.c -- ../libede/src/FileSystemBox.ec:420:49: error: ‘selection’ redeclared as different kind of symbol
+ FileSystemBoxSelection sel = this.selection.Copy();
+ FileSystemNode node = sel.node;
+ char newPath[MAX_LOCATION];
+ StripLastDirectory(node.path, newPath);
+ //node.path = newPath;
+ //node.
+ //if(node && node.type.isFolder && bits.navigateFolders)
+ // property::path = node.path;
+ delete sel;
+ return result;
+ }
+
+ bool MenuReplaceListItemByChild(MenuItem selection, Modifiers mods)
+ {
+ bool result = true;
+ //FileSystemBoxSelection selection = this.selection.Copy(); // TOFIX compiler bug -- FileSystemBox.c -- ../libede/src/FileSystemBox.ec:420:49: error: ‘selection’ redeclared as different kind of symbol
+ FileSystemBoxSelection sel = this.selection.Copy();
+ FileSystemNode node = sel.node;
+ char newPath[MAX_LOCATION];
+ StripLastDirectory(node.path, newPath);
+ //node.path = newPath;
+ //node.
+ //if(node && node.type.isFolder && bits.navigateFolders)
+ // property::path = node.path;
+ delete sel;
+ return result;
+ }
+
+private:
+ FileSystemBoxBits bits;
+
+ char * path;
+ char * extensions;
+ Array<String> comparedPaths;
+
+ BitmapResource fileIcons[_FileType];
+ BitmapResource compIcons[countOfCompIconNames]; // todo: fix this limitation
+
+ Bitmap bitmap;
+ //BitmapArray bitmaps { growingFactor = 16 };
+
+ FileSystemBox()
+ {
+ char wd[MAX_LOCATION];
+ GetWorkingDir(wd, sizeof(wd));
+ property::path = wd;
+
+ InitFileIcons();
+ InitCompIcons(); // todo: these icons should not be initialize, they should be set
+ // or at least initalized when the comparison listing is requested
+ // and we know how many paths are being compared.
+ list.AddField(nameField);
+ bits.autoLoad = true;
+ //iteratorClass = class(FileSystemIterator);
+ }
+
+ ~FileSystemBox()
+ {
+ if(comparedPaths)
+ {
+ comparedPaths.Free();
+ delete comparedPaths;
+ }
+ delete extensions;
+ delete path;
+ }
+
+ watch(background)
+ {
+ list.background = background;
+ };
+
+ void InitFileIcons()
+ {
+ _FileType c;
+ for(c = 0; c < _FileType::enumSize; c++)
+ {
+ fileIcons[c] = BitmapResource { fileIconNames[c], alphaBlend = true };
+ AddResource(fileIcons[c]);
+ }
+ }
+
+ void InitCompIcons()
+ {
+ _FileType c;
+ for(c = 0; c < countOfCompIconNames; c++)
+ {
+ compIcons[c] = BitmapResource { compIconNames[c], alphaBlend = true };
+ AddResource(compIcons[c]);
+ }
+ }
+
+ DataField nameField { header = "Name", dataType = "FileSystemNode", width = 240, userData = this, freeData = false/*, editable = true*/; };
+ DataField pathField { header = "Location", dataType = /*"String"*/ "char *", width = 300, freeData = true };
+ DataField typeField { header = "Type", dataType = /*"String"*/ "char *", width = 40, freeData = false };
+ DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right, freeData = false };
+ DataField modifiedField { header = "Modified", dataType = "SecSince1970", width = 96, alignment = right, freeData = false };
+
+ bool OnPostCreate()
+ {
+ if(bits.autoLoad)
+ {
+ if(!path)
+ {
+ char wd[MAX_LOCATION];
+ GetWorkingDir(wd, sizeof(wd));
+ property::path = wd;
+ }
+ Load();
+ }
+ return true;
+ }
+
+ ListBox list
+ {
+ this;
+
+ borderStyle = none;
+ hasHorzScroll = true;
+ hasVertScroll = true;
+ fullRowSelect = false;
+ sortable = true;
+ alwaysHighLight = true;
+
+ anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+
+ // WHY is this not working ?
+ /*void OnResize(int width, int height)
+ {
+ if(vertScroll.visible)
+ nameField.width = width - vertScroll.size.w;
+ else
+ nameField.width = width;
+ }*/
+
+ bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
+ {
+ if(row)
+ {
+ FileSystemNode node = (FileSystemNode)row.tag;
+ FileSystemNode child;
+ if(collapsed)
+ {
+ /*
+ for(child = node.children.last; child; child = node.children.last)
+ {
+ listBox.DeleteRow(child.row);
+ child.Free();
+ delete child;
+ }
+ node.childrenLoaded = false;
+ */
+ }
+ else
+ {
+ if(!node.bits.loaded || !node.bits.childrenLoaded)
+ {
+ LoadTreeNode(node);
+ //list.Sort(nameField, 1);
+ //node.
+ }
+ for(child = node.children.first; child && child.next; child = child.next);
+ if(child)
+ child.EnsureVisible(false);
+ }
+ }
+ return true;
+ }
+
+ bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
+ {
+ DataRow row = listBox.currentRow;
+ if(row)
+ {
+ FileSystemNode node = (FileSystemNode)row.tag;
+ if(node)
+ {
+ PopupMenu popup;
+ Menu menu { };
+
+ if(NotifyNodeMenu)
+ NotifyNodeMenu(master, this, menu, selection);
+ else
+ {
+ char * text;
+
+ text = PrintString("Open ", node.name);
+ MenuItem { menu, text, o, NotifySelect = MenuOpen, disabled = false }; //delete text;
+
+ if(node.bits.isListItem/* && TODO: unless node is at root location*/)
+ {
+ MenuDivider { menu };
+ MenuItem { menu, "Replace by Parent\tCtrl+R", r, NotifySelect = MenuReplaceListItemByContainingDir, disabled = false };
+ }
+ else if(bits.mode == list)
+ {
+ MenuDivider { menu };
+ MenuItem { menu, "Replace List Item\tCtrl+R", r, NotifySelect = MenuReplaceListItemByChild, disabled = false };
+ }
+ MenuDivider { menu };
+ MenuItem { menu, "Cut\tCtrl+X", t, NotifySelect = null, disabled = false };
+ MenuItem { menu, "Copy\tCtrl+C", c, NotifySelect = null, disabled = false };
+ MenuItem { menu, "Paste\tCtrl+V", p, NotifySelect = null, disabled = false /*!clipboard*/ };
+ MenuItem { menu, "Delete\tDel", d, NotifySelect = null, disabled = false };
+ //MenuDivider { menu };
+ }
+
+ popup = PopupMenu
+ {
+ master = this, menu = menu,
+ position = {
+ x + clientStart.x + absPosition.x - guiApp.desktop.position.x,
+ y + clientStart.y + absPosition.y - guiApp.desktop.position.y }
+ };
+ popup.Create();
+ }
+ }
+ return true;
+ }
+
+ bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
+ {
+ OldList rows;
+ OldLink item;
+
+ selection.nodes.Free();
+ list.GetMultiSelection(rows);
+ for(item = rows.first; item; item = item.next)
+ {
+ DataRow row = item.data;
+ FileSystemNode node = (FileSystemNode)row.tag;
+ selection.nodes.Add(node);
+ incref node;
+ }
+ rows.Free(null);
+ if(row)
+ selection.node = (FileSystemNode)row.tag;
+ else
+ selection.node = null;
+
+ if(selection.node && bits.preview)
+ {
+ //int pos;
+ FileSystemNode node = selection.node;
+ /*if(bitmap)
+ bitmap.Free();*/
+ delete bitmap;
+ if(node && node.type == pictureFile)
+ {
+ bitmap = Bitmap { };
+ bitmap.Load(node.path, null, displaySystem);
+ }
+
+ /*bitmaps.Clear();
+ bitmaps = BitmapArray { };
+ for(pos = 0; pos < selectedItems.count; pos++)
+ {
+ Bitmap bitmap { };
+ selItem = (ExplorerFileItem)selectedItems._[pos];
+ bitmap.Load(selItem.path, null, displaySystem);
+ //bitmaps.Add(bitmap);
+ }
+ if(node && node.type == pictureFile)
+ {
+ bitmap = Bitmap { };
+ bitmap.Load(node.path, null, displaySystem);
+ }*/
+
+ show.Update(null);
+ //NotifyItemSelect(master, view, item, selectedItems);
+ }
+
+ NotifyNodeSelect(listBox.parent.master, this, selection);
+ return true;
+ }
+
+ bool NotifyEditing(ListBox listBox, DataRow row)
+ {
+ if(row)
+ {
+ //FileSystemNode node = (FileSystemNode)row.tag;
+ }
+ return true;
+ }
+
+ bool NotifyEdited(ListBox listBox, DataRow row)
+ {
+ if(row)
+ {
+ //FileSystemNode node = (FileSystemNode)row.tag;
+ }
+ return true;
+ }
+
+ bool NotifyEditDone(ListBox listBox, DataRow row)
+ {
+ if(row)
+ {
+ //FileSystemNode node = (FileSystemNode)row.tag;
+ }
+ return true;
+ }
+
+ bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
+ {
+ bool result = !(selection.node && selection.node.type.isFolder && bits.navigateFolders);
+ OpenNode();
+ return result;
+ }
+
+ bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
+ {
+ //bool result = false;
+ if((SmartKey)key == enter)
+ /*result = */OpenNode();
+ #if 0
+ else if((SmartKey)key == f2)
+ /*result = */RenameNode();
+ #endif
+ else if((SmartKey)key == f2)
+ {
+ FileSystemNode node = selection.node;
+ node.row.Edit(nameField);
+ }
+ //else
+ //result = true;
+ return true;
+ }
+ };
+
+ PaneSplitter split
+ {
+ this;
+ //leftPane = list;
+ rightPane = show;
+ //split = 200;
+ scaleSplit = 0.5f;
+ tabCycle = true;
+ visible = false;
+ };
+
+ Window show
+ {
+ this;
+ visible = false;
+ borderStyle = none;
+ anchor = Anchor { top = 0, right = 0, bottom = 0 };
+
+ void OnRedraw(Surface surface)
+ {
+ FileSystemBox fsb = (FileSystemBox)parent;
+ if(fsb.bitmap)
+ {
+ int wBmp = fsb.bitmap.width;
+ int hBmp = fsb.bitmap.height;
+ int wWnd = fsb.show.clientSize.w;
+ int hWnd = fsb.show.clientSize.h;
+
+ //int wList = 0;//fsb.list.size.w + fsb.split.size.w;
+
+ //float scale = Min((float)(wWnd - 10) / wBmp, (float)(hWnd - 10) / hBmp);
+
+ //int wDraw = (int)(wBmp * scale);
+ //int hDraw = (int)(hBmp * scale);
+
+ #ifndef __linux__
+ surface.Filter(fsb.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw, wBmp, hBmp);
+ #else
+ // Until Filter / Stretch works with X
+ surface.Blit(fsb.bitmap, (wWnd - wBmp) / 2, (hWnd - hBmp) / 2, 0, 0, wBmp, hBmp);
+ #endif
+ }
+ else
+ {
+ surface.SetForeground(white);
+ surface.Area(0, 0, fsb.clientSize.w - 1, fsb.clientSize.h - 1);
+ }
+ }
+ };
+
+ bool OpenNode()
+ {
+ bool result = false;
+ FileSystemBoxSelection sel = this.selection.Copy();
+ //FileSystemNode node = selection.node;
+ for(node : sel.nodes)
+ {
+ sel.node = node;
+ if(node && node.type.isFolder && bits.navigateFolders)
+ property::path = node.path;
+ if(NotifyNodeOpen(this.master, this, sel) && !result)
+ result = true;
+ }
+ delete sel;
+ return result;
+ }
+
+ #if 0
+ bool RenameNode()
+ {
+ bool result;
+ //FileSystemBoxSelection selection = this.selection.Copy();
+ FileSystemNode node = selection.node;
+ //if(node && node.type.isFolder && bits.navigateFolders)
+ // property::path = node.path;
+ // ------------------------------------------- working here ---------------------------
+ //node.row.Edit();
+ /*result = NotifyNodeRename(this.master, this, node);
+ if(result)
+ {
+ if(RenameFile(oldn, newn))
+ {
+ node.name = newn;
+ }
+ }*/
+ return result;
+ }
+ #endif
+
+ // Edit Menu
+ Menu editMenu { menu, "Edit", e };
+ MenuItem itemEditCut
+ {
+ editMenu, "Cut\tCtrl+X", t, disabled = true;
+
+ bool NotifySelect(MenuItem selection, Modifiers mods)
+ {
+ //EditCut();
+ return true;
+ }
+ };
+ MenuItem itemEditCopy
+ {
+ editMenu, "Copy\tCtrl+C", c, disabled = true;
+
+ bool NotifySelect(MenuItem selection, Modifiers mods)
+ {
+ //EditCopy();
+ return true;
+ }
+ };
+ MenuItem itemEditPaste
+ {
+ editMenu, "Paste\tCtrl+V", p;
+
+ bool NotifySelect(MenuItem selection, Modifiers mods)
+ {
+ //EditPaste();
+ return true;
+ }
+ };
+ MenuItem itemEditDelete
+ {
+ editMenu, "Delete\tDel", d, disabled = true;
+
+ bool NotifySelect(MenuItem selection, Modifiers mods)
+ {
+ //EditDelete();
+ return true;
+ }
+ };
+
+ // WHY is this crashing ?
+ /*void OnResize(int width, int height)
+ {
+ if(this && nameField)
+ nameField.width = width - 80;
+ }*/
+
+ void ChangeViewType()
+ {
+ list.Clear();
+ list.ClearFields();
+ list.resizable = bits.details || bits.pathColumn;
+ list.moveFields = bits.details || bits.pathColumn;
+ list.hasHeader = bits.details || bits.pathColumn;
+ list.AddField(nameField);
+ if(bits.pathColumn)
+ list.AddField(pathField);
+ if(bits.details)
+ {
+ list.AddField(typeField);
+ list.AddField(sizeField);
+ list.AddField(modifiedField);
+ }
+ }
+
+ void Load()
+ {
+ // TODO: fix this!
+ // this is crashing in for designer when details = true // can't save the file, always yields a crash
+ /*if(list && created)
+ {
+ list.ClearFields();
+ list.AddField(nameField);
+ if(bits.details)
+ {
+ list.AddField(typeField);
+ list.AddField(sizeField);
+ }
+ }*/
+ list.Clear();
+ if(comparedPaths && !bits.treeBranches)
+ LoadComparedList();
+ else
+ {
+ FileAttribs pathAttribs = FileExists(path);
+ if(pathAttribs)
+ {
+ if(pathAttribs.isDirectory)
+ {
+ if(bits.treeBranches)
+ LoadTreeDirectory();
+ else
+ {
+ if(iteratorClass)
+ LoadListIterator();
+ else
+ LoadListDirectory();
+ }
+ }
+ else if(pathAttribs.isFile) // we assume this is a file list
+ {
+ File f = FileOpen(path, read);
+ if(f)
+ {
+ if(bits.treeBranches)
+ LoadTreeFileList(f);
+ else
+ LoadListFileList(f);
+ delete f;
+ }
+ else
+ MessageBoxTodo($"unable to open file list");
+ }
+ else
+ MessageBoxTodo($"path is not a directory nor is it a file");
+ }
+ else
+ MessageBoxTodo($"path does not exist");
+ }
+ list.Sort(nameField, 1);
+ }
+
+ void LoadTreeDirectory()
+ {
+ bool isRoot = !strcmp(path, "/");
+ char name[MAX_LOCATION];
+ //FileSystemNode parent;
+ //FileSystemNode node;
+ //FileListing listing { path, extensions = extensions };
+
+ if(!isRoot)
+ GetLastDirectory(path, name);
+ else
+ name[0] = '\0';
+
+ /*if(!path)
+ GetWorkingDir(startPath, sizeof(startPath));
+ else
+ strcpy(path, startPath);*/
+ bits.mode = directory;
+
+ list.Clear();
+
+ delete root;
+#ifdef __WIN32__
+ if(isRoot)
+ {
+ root = FileSystemNode { bits.loaded = true, bits.childrenLoaded = true };
+ AddTreeNode(root, true, false, false, null);
+ while(listing.Find())
+ {
+ int len = strlen(listing.name);
+ char info[MAX_LOCATION];
+ char name[MAX_LOCATION];
+ if(listing.stats.attribs.isDrive &&
+ len > 3 && !strncmp(&listing.name[1], ": [", 3))
+ {
+ strncpy(name, listing.name, 2);
+ name[2] = 0;
+ strncpy(info, &listing.name[4], len - 5);
+ info[len - 5] = 0;
+ }
+ else
+ {
+ strcpy(name, listing.name);
+ info[0] = 0;
+ }
+
+ parent =
+ MakeFileSystemNode(
+ listing.stats,
+ name,
+ listing.path,
+ false, bits.previewPictures, false,
+ displaySystem);
+ if(info[0])
+ parent.info = info; //CopyString(info);
+ parent.bits.loaded = true;
+ AddTreeNode(
+ parent,
+ !listing.stats.attribs.isDirectory,
+ false,
+ listing.stats.attribs.isDirectory,
+ root);
+ if(!listing.stats.attribs.isDirectory)
+ parent.bits.childrenLoaded = true;
+ }
+
+ node = FileSystemNode { name = msNetwork, type = network };
+ AddTreeNode(node, false, false, true, null);
+ node.row.collapsed = true;
+ }
+ else
+#endif
+ {
+ FileStats stats;
+ FileGetStats(path, stats);
+ root = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, false, displaySystem);
+ AddTreeNode(root, false, false, true, null);
+ LoadTreeNode(root);
+ }
+
+ if(isRoot)
+ {
+ root.type = computer;
+ root.label = rootName;
+ }
+
+ list.Sort(nameField, 1);
+ list.SelectRow(root.row);
+ }
+
+ void LoadListDirectory()
+ {
+ FileListing listing { path, extensions = extensions };
+
+ bits.mode = directory;
+ while(listing.Find())
+ ProcessListItem(listing.name, listing.path, listing.stats, false);
+ }
+
+ void LoadListFileList(File f)
+ {
+ char line[65536];
+ bits.mode = list;
+ while(f.GetLine(line, 65536))
+ {
+ FileStats stats {};
+ char name[MAX_FILENAME];
+ FileGetStats(line, stats);
+ GetLastDirectory(line, name);
+ ProcessListItem(name, line, stats, true);
+ }
+ }
+
+ void LoadTreeFileList(File f)
+ {
+ char line[65536];
+ bits.mode = list;
+ while(f.GetLine(line, 65536))
+ {
+ FileStats stats {};
+ char name[MAX_FILENAME];
+ FileSystemNode node;
+ FileGetStats(line, stats);
+ GetLastDirectory(line, name);
+ node = ProcessTreeItem(name, line, stats, node);
+ }
+ }
+
+ void LoadListIterator()
+ {
+ FileSystemIterator iterator = eInstance_New(iteratorClass);
+ if(iterator)
+ {
+ iterator.owner = this;
+ iterator.OnObject = ListIterator_OnObject;
+ //iterator.OnLeavingDirectory = ListIterator_OnLeavingDirectory;
+ NotifyIteratorInit(master, this, iterator);
+ iterator.Iterate(path, true);
+ delete iterator;
+ }
+ }
+
+ bool ListIterator_OnObject(const char * name, const char * path, FileStats stats, bool isRootObject)
+ {
+ ProcessListItem(name, path, stats, false);
+ return false;
+ }
+
+ //void ListIterator_OnLeavingDirectory(char * path) { }
+
+ void ProcessListItem(const char * name, const char * path, FileStats stats, bool isListItem)
+ {
+ if((!bits.foldersOnly && !bits.filesOnly) || (bits.foldersOnly && stats.attribs.isDirectory) || (bits.filesOnly && stats.attribs.isFile))
+ {
+ FileSystemNode node = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, isListItem, displaySystem);
+ AddNode(node);
+ }
+ }
+
+ FileSystemNode ProcessTreeItem(char * name, char * path, FileStats stats, FileSystemNode parent)
+ {
+ FileSystemNode node = MakeFileSystemNode(stats, name, path, false, bits.previewPictures, true, displaySystem);
+ AddTreeNode(parent, false, false, true, null);
+ //LoadTreeNode(node);
+ return node;
+ }
+
+ void LoadTreeNode(FileSystemNode node)
+ {
+ if(!node.bits.loaded)
+ {
+ char path[MAX_LOCATION];
+ node.GetPath(path);
+ {
+ FileListing listing { path, extensions = extensions };
+ if(node.children.count == 1)
+ DeleteNode(node.children.first);
+
+ while(listing.Find())
+ {
+ char * test;
+ FileSystemNode child = null;
+ if(!listing.stats.attribs.isRemovable && ((!bits.foldersOnly && !bits.filesOnly) ||
+ (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
+ (bits.filesOnly && listing.stats.attribs.isFile)))
+ child = MakeAndAddToTreeFileSystemNodeFromFileListing(listing, node);
+ if(child)
+ NodeChildLoad(child, node);
+ test = child.name;
+ if(!test)
+ PrintLn("error");
+ }
+ }
+ node.bits.childrenLoaded = true;
+ node.bits.loaded = true;
+ node.row.SortSubRows(false);
+ }
+ else if(!node.bits.childrenLoaded)
+ {
+ FileSystemNode child;
+ if(node.children.first)
+ {
+ for(child = node.children.first; child; child = child.next)
+ {
+ if(!child.bits.loaded)
+ LoadTreeNode(child);
+ else if(!child.bits.childrenLoaded)
+ NodeChildLoad(child, node);
+ }
+ node.bits.childrenLoaded = true;
+ node.row.SortSubRows(false);
+ }
+ }
+ }
+
+ void NodeChildLoad(FileSystemNode parent, FileSystemNode node)
+ {
+ char path[MAX_LOCATION];
+ parent.GetPath(path);
+ if(bits.textFileLinesStyle && FileExists(path).isFile)
+ {
+ PrintLn("Test");
+ }
+ else
+ {
+ bool added = false;
+ FileListing listing { path, extensions = extensions };
+ while(listing.Find())
+ {
+ //char * test;
+ FileSystemNode child = null;
+ if((!bits.foldersOnly && !bits.filesOnly) ||
+ (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
+ (bits.filesOnly && listing.stats.attribs.isFile))
+ child = MakeAndAddToTreeFileSystemNodeFromFileListing(listing, parent);
+ if(child)
+ {
+ added = true;
+ /*
+ test = child.name;
+ if(!test)
+ PrintLn("error");
+ */
+ }
+ }
+ if(!added)
+ added = true;
+ }
+ parent.bits.childrenLoaded = true;
+ }
+
+ void LoadComparedList()
+ {
+ int c/*, cmp*/ /*, smallest*/, icon;//, equalCount;
+ int count = comparedPaths ? comparedPaths.count : 0;
+ //bool allDone = false;
+ bool not;
+ FileStats stats;
+ char path[MAX_LOCATION];
+ //Array<ComparisonState> states { };
+ //Array<FileListing> listings { };
+ //Array<int> equals { };
+ //Array<MapNode<String, int>> mapNodes { };
+
+ //Array<Map<String, int>> lists { };
+ //Map<int, bool> equals{ };
+
+
+ MapNode<String, /*Map<int, */Array<int>> na;
+ //MapNode<int, int> nb;
+ Map<String, /*Map<int, */Array<int>> names { };
+ //Map<String, Array<bool>> names { };
+ //Map<String, bool[16]> names { }; // does not seem to be working
+ //Map<String, BoolArrayInt> names { };
+ {
+ for(c = 0; c < comparedPaths.count; c++)
+ {
+ FileListing listing { comparedPaths[c], extensions = extensions };
+ while(listing.Find())
+ {
+ /*Map<int, int>*/Array<int> m = names[listing.name];
+ if(!m)
+ m = { };
+ names[listing.name] = m;
+ /*/m[c] = */m.Add(c);
+ }
+ }
+ /* // compiles and should work but better solution?
+ for(c = 0; c < comparedPaths.count; c++)
+ {
+ FileListing listing { comparedPaths[c], extensions = extensions };
+ while(listing.Find())
+ {
+ Array<bool> a = names[listing.name];
+ if(!a)
+ a = { };
+ names[listing.name] = a;
+ a[c] = true;
+ }
+ }
+ */
+ /* // does not seem to be working
+ for(c = 0; c < comparedPaths.count; c++)
+ {
+ FileListing listing { comparedPaths[c], extensions = extensions };
+ while(listing.Find())
+ {
+ names[listing.name][c] = true;
+ }
+ }
+ */
+ /*
+ if(comparedPaths.count > 0)
+ {
+ FileListing listing { comparedPaths[0], extensions = extensions };
+ while(listing.Find())
+ {
+ // should be able to just do names[listing.name]._0 = true;
+ BoolArrayInt bai = names[listing.name];
+ bai._0 = true;
+ names[listing.name] = bai;
+ }
+ }
+ if(comparedPaths.count > 1)
+ {
+ FileListing listing { comparedPaths[1], extensions = extensions };
+ while(listing.Find())
+ {
+ // should be able to just do names[listing.name]._1 = true;
+ BoolArrayInt bai = names[listing.name];
+ bai._1 = true;
+ names[listing.name] = bai;
+ }
+ }
+ */
+ // and so on....
+ }
+
+ /*
+ for(dirPath : comparedPaths)
+ {
+ char * p = dirPath;
+ if(FileExists(dirPath).isDirectory)
+ {
+ FileListing listing { dirPath, extensions = extensions };
+ //MapNode<String, int> mn;
+ Map<String, int> list { };
+ //states.Add(listing.Find() == true ? matching : endOfListing);
+ while(listing.Find())
+ list[listing.name] = 0;
+ //for(mn = ; mn; mn = mn.next)
+ //mn = list.root.minimum;
+ mapNodes.Add(/-*mn*-/list.root.minimum);
+ lists.Add(list);
+ //PrintLn(dirPath, " -- .Find() -- ", states[states.count-1] == matching ? listing.name : "endOfListing*");
+ //listings.Add(listing);
+
+ {
+ MapNode<String, int> mn;
+ PrintLn("------------- DIR LISTING FOR ", dirPath);
+ for(mn = list.root.minimum; mn; mn = mn.next)
+ {
+ PrintLn(mn.key);
+ }
+ }
+ }
+ }
+ */
+
+ for(na = names.root.minimum; na; na = na.next)
+ {
+ /*Map<int, */Array<int> equals = na.value;
+ //MapNode<int, int> nb;
+ /*
+ while(!allDone)
+ {
+ smallest = 0;
+ equals.Add(0);
+ for(c = 1; c < count; c++)
+ {
+ //if(states[c] == endOfListing) continue;
+ if(!mapNodes[c]) continue;
+ // todo: use better comparison method
+ // it should compare file type (dir/file) before
+ // comparing file name.
+ // should also provide alternative methods
+ // of comparison including ones that consider
+ // date changes as differences of some kind.
+ // pethaps a OnCompare(a, b) to allow implementation
+ // of custom methods of comparison.
+ // note: this (or these) method(s) depend on files
+ // being listed in sorted order by FileListing.
+ // different comparison methods should have
+ // appropriatly different sorting in FileListing.
+ //
+ //cmp = strcmp(listings[smallest].name, listings[c].name);
+ cmp = fstrcmp(mapNodes[smallest].key, mapNodes[c].key);
+ PrintLn("COMPARING - ", mapNodes[smallest].key, " and ", mapNodes[c].key);
+ if(cmp == 0)
+ equals.Add(c);
+ //equals[c] = true;
+ else if(cmp > 0)
+ {
+ smallest = c;
+ equals.size = 0;
+ equals.Add(c);
+ }
+ }
+
+ */
+ if(equals.count == count) // all are equal, no diff icon
+ {
+ icon = 0;
+ not = false;
+ }
+ else if(equals.count == count-1) // all are equal but one, not-sign icon for singled out missing
+ {
+ int i;
+ /*
+ for(nb = equals.root.minimum, i = 0; nb; nb = nb.next, i++)
+ {
+ if(i != nb.key)
+ */
+ for(i = 0; i < equals.count; i++)
+ {
+ if(i != equals[i])
+ {
+ icon = i+1;
+ break;
+ }
+ }
+ //if(!nb)
+ if(i == equals.count)
+ icon = count;
+ not = true;
+ }
+ else if(equals.count == 1) // only one is present, all others missing, present-sign for singled out present
+ {
+ //icon = equals.root.minimum.key+1;
+ icon = equals[0]+1;
+ not = false;
+ }
+ else // mixed
+ {
+ icon = 0; // todo
+ not = true;
+ }
+#if 0
+ // or
+ if(equals.count == count) // all are equal, no diff icon
+ ;
+ else if(count/2 - equals.count < 0) // more than half are equal, use not-sign icons for all missing
+ ;
+ else // less than half are equal, use present-sign icons for all present
+ ;
+#endif
+
+ /*if((!bits.foldersOnly && !bits.filesOnly) ||
+ (bits.foldersOnly && listings[smallest].stats.attribs.isDirectory) ||
+ (bits.filesOnly && listings[smallest].stats.attribs.isFile))*/
+ strcpy(path, comparedPaths[/*smallest*/equals[0]]);
+ PathCat(path, /*mapNodes[smallest].key*/na.key);
+ FileGetStats(path, stats);
+ if((!bits.foldersOnly && !bits.filesOnly) ||
+ (bits.foldersOnly && stats.attribs.isDirectory) ||
+ (bits.filesOnly && stats.attribs.isFile))
+ {
+ FileSystemNode node =
+ MakeComparedFileSystemNode(
+ stats,
+ /*mapNodes[smallest].key*/na.key,
+ path,
+ false, bits.previewPictures,
+ icon, not, equals,
+ displaySystem);
+ AddNode(node);
+ }
+ /*
+ for(equal : equals)
+ {
+ mapNodes[equal] = mapNodes[equal].next;
+ //states[equal] = listings[equal].Find() == true ? matching : endOfListing;
+ //PrintLn(comparedPaths[equal], " -- .Find() -- ", states[equal] == matching ? listings[equal].name : "endOfListing*");
+ }
+ equals.size = 0;
+ //for(c = 0; c < count && states[c] == endOfListing; c++);
+ for(c = 0; c < count && !mapNodes[c]; c++);
+ if(c == count)
+ allDone = true;
+ */
+ }
+ list.Sort(nameField, 1);
+ }
+
+ void AddNode(FileSystemNode node)
+ {
+ DataRow row = list.AddRow();
+ row.tag = (intptr)node;
+ node.row = row;
+ incref node;
+ row.SetData(nameField, node);
+ if(bits.pathColumn)
+ {
+ char path[MAX_LOCATION];
+ StripLastDirectory(node.path, path);
+ row.SetData(pathField, CopyString(path));
+ }
+ if(bits.details)
+ {
+ if(node.type.isFile)
+ {
+ row.SetData(typeField, node.extension);
+ row.SetData(sizeField, /*(void *)*/node.stats.size);
+ }
+ row.SetData(modifiedField, node.stats.modified);
+ }
+ }