+ /*property char *
+ {
+ set
+ {
+ this = SelectByExtension(value);
+ }
+ }*/
+
+ public property bool isFolder
+ {
+ get { return this >= folder && this <= share; }
+ }
+
+ public property bool isFile
+ {
+ get { return this >= normalFile && this <= opticalMediaImageFile; }
+ }
+
+ _FileType ::SelectByExtension(char * extension)
+ {
+ if(!strcmpi(extension, "ews"))
+ return ewsFile;
+ else if(!strcmpi(extension, "epj"))
+ return epjFile;
+ else if(!strcmpi(extension, "ec"))
+ return ecFile;
+ else if(!strcmpi(extension, "eh"))
+ return ehFile;
+ else if(!strcmpi(extension, "cpp") ||
+ !strcmpi(extension, "cc") || !strcmpi(extension, "cxx"))
+ return cppFile;
+ else if(!strcmpi(extension, "hpp") ||
+ !strcmpi(extension, "hh") || !strcmpi(extension, "hxx"))
+ return hppFile;
+ else if(!strcmpi(extension, "c"))
+ return cFile;
+ else if(!strcmpi(extension, "h"))
+ return hFile;
+ else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
+ !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
+ return textFile;
+ else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
+ !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
+ !strcmpi(extension, "js"))
+ return webFile;
+ else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
+ !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
+ !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
+ !strcmpi(extension, "ico"))
+ return pictureFile;
+ else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
+ !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
+ return soundFile;
+ else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
+ !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
+ !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
+ !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
+ !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
+ !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
+ return archiveFile;
+ else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
+ !strcmpi(extension, "rpm"))
+ return packageFile;
+ else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
+ !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
+ !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
+ !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
+ return opticalMediaImageFile;
+ return normalFile;
+ }
+};
+
+class FileSystemBoxBits
+{
+ bool foldersOnly:1, filesOnly:1, details:1, pathColumn:1, treeBranches:1, previewPictures:1, navigateFolders:1, autoLoad:1;
+ bool preview: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;
+};
+
+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 { };
+
+ virtual bool Window::NotifyNodeSelect(FileSystemBox box, FileSystemBoxSelection selection);
+ //virtual bool Window::NotifyNodeNavigate(FileSystemBox box, FileSystemNode node);
+ virtual bool Window::NotifyNodeOpen(FileSystemBox box, FileSystemBoxSelection selection);
+
+ property 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 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;
+ }
+ get { return bits.preview; }
+ };
+
+ 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(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();
+ }
+
+private:
+ FileSystemBoxBits bits;
+
+ char * path;
+ char * extensions;
+
+ BitmapResource fileIcons[_FileType];
+
+ Bitmap bitmap;
+ //BitmapArray bitmaps { growingFactor = 16 };
+
+ FileSystemBox()
+ {
+ char wd[MAX_LOCATION];
+ GetWorkingDir(wd, sizeof(wd));
+ property::path = wd;
+
+ InitFileIcons();
+ list.AddField(nameField);
+ bits.autoLoad = true;
+ }
+
+ ~FileSystemBox()
+ {
+ 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]);
+ }
+ }
+
+ DataField nameField { header = "Name", dataType = "FileSystemNode", width = 240, userData = this, freeData = false };
+ DataField pathField { header = "Location", dataType = /*"String"*/ "char *", width = 300, freeData = false };
+ 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)
+ 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)
+ {
+ char * text;
+
+ PopupMenu popup;
+ Menu menu { };
+
+ text = PrintString("Open ", node.path);
+
+ MenuItem { menu, text, o, NotifySelect = MenuOpen, disabled = false };
+ 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;
+ if((SmartKey)key == enter)
+ result = OpenNode();
+ 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 MenuOpen(MenuItem selection, Modifiers mods)
+ {
+ OpenNode();
+ }
+
+ bool OpenNode()
+ {
+ bool result;
+ FileSystemBoxSelection selection = this.selection.Copy();
+ FileSystemNode node = selection.node;
+ if(node && node.type.isFolder && bits.navigateFolders)
+ property::path = node.path;
+ result = NotifyNodeOpen(this.master, this, selection);
+ return result;
+ }
+
+ // 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);
+ }
+ }*/
+ if(bits.treeBranches)
+ LoadTree();
+ else
+ LoadList();
+ }
+
+ void LoadList()
+ {
+ FileListing listing { path, extensions = extensions };
+
+ list.Clear();
+ while(listing.Find())
+ {
+ if((!bits.foldersOnly && !bits.filesOnly) ||
+ (bits.foldersOnly && listing.stats.attribs.isDirectory) ||
+ (bits.filesOnly && listing.stats.attribs.isFile))
+ {
+ FileSystemNode node = MakeFileSystemNode(listing.stats, listing.name, listing.path, false, bits.previewPictures, displaySystem);
+ AddNode(node);
+ }
+ }
+ list.Sort(nameField, 1);
+ }
+
+ void LoadTree()
+ {
+ 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);*/
+
+ list.Clear();
+
+ delete root;
+ #ifdef __WIN32__
+ if(isRoot)
+ {
+ root = FileSystemNode { bits.loaded = true, bits.childrenLoaded = true };
+ AddTreeNode(root, true, 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, displaySystem);
+ if(info[0])
+ parent.info = info; //CopyString(info);
+ parent.bits.loaded = true;
+ AddTreeNode(parent, !listing.stats.attribs.isDirectory, listing.stats.attribs.isDirectory, root);
+ if(!listing.stats.attribs.isDirectory)
+ parent.bits.childrenLoaded = true;
+ }
+
+ node = FileSystemNode { name = msNetwork, type = network };
+ AddTreeNode(node, false, true, null);
+ node.row.collapsed = true;
+ }
+ else
+ #endif
+ {
+ root = MakeFileSystemNode(FileStats { attribs = FileExists(path)}, name, path, false, bits.previewPictures, displaySystem);
+ AddTreeNode(root, false, true, null);
+ LoadTreeNode(root);
+ }