Initial Git commit
authorJerome St-Louis <jerome@ecere.com>
Tue, 17 May 2011 04:44:03 +0000 (00:44 -0400)
committerJerome St-Louis <jerome@ecere.com>
Tue, 17 May 2011 04:44:03 +0000 (00:44 -0400)
30 files changed:
explorer/Explorer.epj [new file with mode: 0644]
explorer/res/browse.png [new file with mode: 0644]
explorer/res/panel-tree.png [new file with mode: 0644]
explorer/res/view-cards.png [new file with mode: 0644]
explorer/res/view-details.png [new file with mode: 0644]
explorer/res/view-icons.png [new file with mode: 0644]
explorer/res/view-list.png [new file with mode: 0644]
explorer/res/view-showcase-right.png [new file with mode: 0644]
explorer/src/DeleteBox.ec [new file with mode: 0644]
explorer/src/Explorer.ec [new file with mode: 0644]
explorer/src/ExplorerTree.ec [new file with mode: 0644]
explorer/src/ExplorerWindow.ec [new file with mode: 0644]
explorer/src/FileListItem.ec [new file with mode: 0644]
explorer/src/FileTreeBranch.ec [new file with mode: 0644]
explorer/src/Finder.ec [new file with mode: 0644]
explorer/src/NotificationBox.ec [new file with mode: 0644]
explorer/src/Panels.ec [new file with mode: 0644]
explorer/src/Search.ec [new file with mode: 0644]
explorer/src/Skin.ec [new file with mode: 0644]
explorer/src/SplitWindow.ec [new file with mode: 0644]
explorer/src/Structures/ArrayBasic.ec [new file with mode: 0644]
explorer/src/Structures/ArrayBinaryIndexed.ec [new file with mode: 0644]
explorer/src/Structures/ArrayBinarySorted.ec [new file with mode: 0644]
explorer/src/Structures/ArrayFactoredGrowth.ec [new file with mode: 0644]
explorer/src/Structures/ArrayNotes.ec [new file with mode: 0644]
explorer/src/Structures/ArrayTypes.ec [new file with mode: 0644]
explorer/src/Structures/ArrayUtilities.ec [new file with mode: 0644]
explorer/src/Structures/Stack.ec [new file with mode: 0644]
explorer/src/Structures/Temp.ec [new file with mode: 0644]
explorer/tools/FinderMenuCommand.reg [new file with mode: 0644]

diff --git a/explorer/Explorer.epj b/explorer/Explorer.epj
new file mode 100644 (file)
index 0000000..df6b66d
--- /dev/null
@@ -0,0 +1,80 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "Explorer",
+   "Options" : {
+      "Warnings" : "All",
+      "MemoryGuard" : false,
+      "Profile" : false,
+      "StrictNameSpaces" : false,
+      "TargetType" : "Executable",
+      "Libraries" : [
+         "ecere"
+      ],
+      "Console" : false
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ],
+            "TargetFileName" : "Explorer",
+            "TargetDirectory" : "debug",
+            "ObjectsDirectory" : "debug"
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed",
+            "TargetFileName" : "Explorer",
+            "TargetDirectory" : "release",
+            "ObjectsDirectory" : "release"
+         }
+      }
+   ],
+   "Files" : [
+      {
+         "Folder" : "notes",
+         "Files" : [
+            "Info.txt",
+            "Todo.txt"
+         ]
+      },
+      {
+         "Folder" : "src",
+         "Files" : [
+            "Explorer.ec",
+            "ExplorerTree.ec",
+            "DeleteBox.ec",
+            "Finder.ec",
+            "NotificationBox.ec",
+            "Panels.ec",
+            "Search.ec"
+         ]
+      },
+      {
+         "Folder" : "outside",
+         "Files" : [
+            "src/SplitWindow.ec",
+            "src/Structures/ArrayFactoredGrowth.ec",
+            "src/Structures/ArrayUtilities.ec",
+            "src/Skin.ec"
+         ]
+      }
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+      "res/panel-tree.png",
+      "res/view-cards.png",
+      "res/view-details.png",
+      "res/view-icons.png",
+      "res/view-list.png",
+      "res/view-showcase-right.png",
+      "res/browse.png"
+   ]
+}
\ No newline at end of file
diff --git a/explorer/res/browse.png b/explorer/res/browse.png
new file mode 100644 (file)
index 0000000..f227989
Binary files /dev/null and b/explorer/res/browse.png differ
diff --git a/explorer/res/panel-tree.png b/explorer/res/panel-tree.png
new file mode 100644 (file)
index 0000000..11a5108
Binary files /dev/null and b/explorer/res/panel-tree.png differ
diff --git a/explorer/res/view-cards.png b/explorer/res/view-cards.png
new file mode 100644 (file)
index 0000000..8638b8f
Binary files /dev/null and b/explorer/res/view-cards.png differ
diff --git a/explorer/res/view-details.png b/explorer/res/view-details.png
new file mode 100644 (file)
index 0000000..0a52090
Binary files /dev/null and b/explorer/res/view-details.png differ
diff --git a/explorer/res/view-icons.png b/explorer/res/view-icons.png
new file mode 100644 (file)
index 0000000..334c518
Binary files /dev/null and b/explorer/res/view-icons.png differ
diff --git a/explorer/res/view-list.png b/explorer/res/view-list.png
new file mode 100644 (file)
index 0000000..43dddab
Binary files /dev/null and b/explorer/res/view-list.png differ
diff --git a/explorer/res/view-showcase-right.png b/explorer/res/view-showcase-right.png
new file mode 100644 (file)
index 0000000..721e705
Binary files /dev/null and b/explorer/res/view-showcase-right.png differ
diff --git a/explorer/src/DeleteBox.ec b/explorer/src/DeleteBox.ec
new file mode 100644 (file)
index 0000000..d9c2d08
--- /dev/null
@@ -0,0 +1,139 @@
+public import "ecere"
+
+import "ExplorerTree"
+
+public class DeleteBox : Window
+{
+   background = activeBorder;
+   hasClose = true;
+   tabCycle = true;
+   size = Size { 400, 300 };
+   text = "Delete Confirmation";
+
+public:
+   ListBox source;
+
+private:
+   
+   ListBox list
+   {
+      parent = this, borderStyle = deep, hasVertScroll = true, hasHorzScroll = true;
+      fullRowSelect = false, treeBranches = true, collapseControl = true, rootCollapseButton = true;
+      multiSelect = true;
+      size = Size { 624, 268 }, anchor = Anchor { left = 248, right = 8, bottom = 8 };
+      text = "Browser", hotKey = Key { e, alt = true };
+
+      /*hasHeader = true, moveFields = true, resizable = true, sortable = true;*/
+      anchor = Anchor { left = 8, top = 8, right = 8, bottom = 48 };
+   };
+   DataField listNameField { header = "Name", dataType = "ExplorerFileBranch", width = 304, userData = this }; // editable = true
+
+   DeleteBox()
+   {
+      list.AddField(listNameField);
+   }
+
+   ~DeleteBox()
+   {
+   }
+
+   /*
+   bool OnPostCreate()
+   {
+      if(source)
+      {
+         Link item;
+         OldList selection;
+         FileTreeBranchArray copies { growingFactor = 2 };
+         FileTreeBranchBSArray branches { };
+         FileTreeBranchArray parents { growingFactor = 4 };
+
+         source.GetMultiSelection(selection);
+
+         for(item = selection.first; item; item = item.next)
+         {
+            DataRow row = item.data;
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+            if(parents.count)
+            {
+               uint c;
+               for(c = 0; c < parents.count; c++)
+               {
+                  if(branch.IsChildOf(parents._[c]))
+                     break;
+               }
+               if(c == parents.count)
+               {
+                  parents.Add(branch);
+               }
+            }
+            else
+            {
+               parents.Add(branch);
+            }
+            // what if a child of some parent was added before that parent, should check to remove
+            // or is it imposible to get the backwards order from GetMultiSelection
+            
+            //for(branch = (ExplorerFileBranch)row.tag; branch; branch = branch.parent)
+            //   branches.Add(branch);
+         }
+         selection.Free(null);
+
+         {
+            uint c;
+            char path[MAX_LOCATION];
+            for(c = 0; c < parents.count; c++)
+            {
+               ExplorerFileBranch parent = parents._[c];
+               ExplorerFileBranch root { };
+               parent.GetPath(path);
+               root.name = CopyString(path);
+               root.childrenLoaded = true;
+               root.type = parent.type;
+               AddBranch(root, true, false, null, list);
+               parent.DuplicateChildren(true, true, root, list);
+            }
+         }
+         
+         */
+         /*
+         copies.count = branches.count;
+         
+         {
+            bool added = true;
+            uint b;
+            int i = 0;
+            while(added)
+            {
+               for(b = 0; b < branches.count; b++)
+               {
+                  if(branches._[b].indent == i)
+                  {
+                     ExplorerFileBranch copy;
+                     //copy = branches._[b].Copy();
+                     added = true;
+                  }
+               }
+               i++;
+            }
+         }
+         */
+         /*
+
+            */
+            /*
+            char path[MAX_LOCATION];
+            DataRow row = item.data;
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+            branch.GetPath(path);
+            if(FileExists(path))
+               DeleteFile(path);
+            if(!FileExists(path))
+               DeleteBranch(branch, source);
+            */
+            /*
+
+      }
+   }
+   */
+};
diff --git a/explorer/src/Explorer.ec b/explorer/src/Explorer.ec
new file mode 100644 (file)
index 0000000..6933999
--- /dev/null
@@ -0,0 +1,624 @@
+public import "ecere"
+import "SplitWindow"
+
+import "ExplorerTree" // meant to be called ExplorerTools
+import "Finder"
+import "Search"       // meant to be called ExplorerSearch
+import "Panels"
+
+/*
+#ifdef __WIN32__
+#include <direct.h>
+#else
+#include <unistd.h>
+#endif
+*/
+
+enum ExplorerToolId
+{
+   none,
+   newWindow, goBack, goForward, goUp, goHome,
+   browse,
+   panelTree, panelSearch,
+   addressBar,
+   refreshView,
+   viewList, viewDetails, viewIcons, viewCards, viewShowcase, viewCustom,
+   previewPictures
+};
+
+static char * toolIconNames[] = 
+{
+   "<:ecere>emblems/unreadable.png",     /* none */
+
+   "<:ecere>actions/windowNew.png",            /* newWindow */
+   "<:ecere>actions/goPrevious.png",           /* goBack */
+   "<:ecere>actions/goNext.png",               /* goForward */
+   "<:ecere>actions/goUp.png",                 /* goUp */
+   "<:ecere>actions/goHome.png",               /* goHome */
+
+   ":browse.png",                                           /* browse */
+
+   ":panel-tree.png",                                       /* panelTree */
+   "<:ecere>actions/editFind.png",             /* panelSearch */
+
+   "<:ecere>emblems/unreadable.png",     /* addressBar */
+
+   "<:ecere>actions/viewRefresh.png",          /* refreshView */
+
+   ":view-list.png",                                        /* viewList */
+   ":view-details.png",                                     /* viewDetails */
+   ":view-icons.png",                                       /* viewIcons */
+   ":view-cards.png",                                       /* viewCards */
+   ":view-showcase-right.png",                              /* viewShowcase */
+   ":view-custom.png",                                      /* viewCustom */
+   
+   "<:ecere>mimetypes/image.png",     /* previewPictures */
+   
+   ""
+};
+
+class ToolButton : Button
+{
+   size = Size { 24, 24 };
+
+   property int toolId
+   {
+      set
+      {
+         bitmap = BitmapResource { fileName = toolIconNames[value], alphaBlend = true };
+         id = value;
+      }
+   }
+
+   bool TestToolBar::NotifyClicked(ToolButton button, int x, int y, Modifiers mods)
+   {
+      NotifyToolClick(this.parent, this, button.id);
+      return true;
+   }
+}
+
+class ToggleToolButton : ToolButton
+{
+   toggle = true;
+   size = Size { 24, 24 };
+   
+   bool TestToolBar::NotifyClicked(ToggleToolButton button, int x, int y, Modifiers mods)
+   {
+      NotifyToolClick(this.parent, this, button.id);
+      return true;
+   }
+}
+
+class GroupToggleToolButton : ToolButton
+{
+   toggle = true;
+   size = Size { 24, 24 };
+   GroupToggleToolButton * selected;
+   bool TestToolBar::NotifyClicked(GroupToggleToolButton button, int x, int y, Modifiers mods)
+   {
+      bool configured = (bool)button.selected;
+      bool preselection = (configured && (*button.selected));
+      bool reclick = preselection ? (*button.selected == button) : false;
+      if(configured && preselection && !reclick)
+      {
+         (*button.selected).checked = false;
+         *button.selected = button;
+      }
+      NotifyToolClick(this.parent, this, button.id);
+      return true;
+   }
+}
+
+class OptionToolButton : ToolButton
+{
+   toggle = true;
+   size = Size { 24, 24 };
+   OptionToolButton * selected;
+   bool TestToolBar::NotifyClicked(OptionToolButton button, int x, int y, Modifiers mods)
+   {
+      bool configured = (bool)button.selected;
+      bool preselection = (configured && (*button.selected));
+      bool reclick = preselection ? (*button.selected == button) : false;
+      if(configured && !preselection)
+         *button.selected = button;
+      else if(configured && !reclick)
+      {
+         (*button.selected).checked = false;
+         *button.selected = button;
+      }
+      button.checked = true;
+      if(!reclick)
+         NotifyToolClick(this.parent, this, button.id);
+      return true;
+   }
+}
+
+class TestToolBar : Window
+{
+   int xL, xR;
+
+   borderStyle = bevel;
+   inactive = true;
+   background = activeBorder;
+   size = Size { h = 32 };
+   anchor = Anchor { left = 0, top = 0, right = 0 };
+
+   virtual void Window::NotifyToolClick(TestToolBar toolBar, int id);
+
+   xL = 0;
+
+   ToolButton newWindow { this, anchor = { left = (xL += 20) }, toolId = ExplorerToolId::newWindow };
+
+   ToolButton goBack    { this, anchor = { left = (xL += 46) }, toolId = ExplorerToolId::goBack };
+   ToolButton goForward { this, anchor = { left = (xL += 26) }, toolId = ExplorerToolId::goForward };
+   ToolButton goUp      { this, anchor = { left = (xL += 30) }, toolId = ExplorerToolId::goUp };
+   ToolButton goHome    { this, anchor = { left = (xL += 30) }, toolId = ExplorerToolId::goHome };
+
+   GroupToggleToolButton panelTree   { this, anchor = { left = (xL += 46) }, toolId = ExplorerToolId::panelTree, selected = &selectedPanel, checked = true };
+   GroupToggleToolButton panelSearch { this, anchor = { left = (xL += 26) }, toolId = ExplorerToolId::panelSearch, selected = &selectedPanel };
+   GroupToggleToolButton selectedPanel;
+   selectedPanel = panelTree;
+
+   xR = 0;
+
+   OptionToolButton viewList     { this, anchor = { right = (xR += 20) }, toolId = ExplorerToolId::viewList, selected = &selectedView, checked = true };
+   OptionToolButton viewDetails  { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewDetails, selected = &selectedView };
+   OptionToolButton viewIcons    { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewIcons, selected = &selectedView };
+   OptionToolButton viewTiles    { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewCards, selected = &selectedView };
+   OptionToolButton viewShowcase { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewShowcase, selected = &selectedView };
+   OptionToolButton selectedView;
+   selectedView = viewList;
+
+   ToggleToolButton previewPictures { this, anchor = { right = (xR += 46) }, toolId = ExplorerToolId::previewPictures };
+
+   ToolButton refreshView { this, anchor = { right = (xR += 46) }, toolId = ExplorerToolId::refreshView };
+
+   EditBox addressBar
+   {
+      this, master;
+      size = { w = 250 }, anchor = { left = (xL += 30), right = (xR += 30) }, id = ExplorerToolId::addressBar;
+   };
+
+   /*DropBox tileSet
+   {
+      this,
+      anchor = { left = (xPos += 88) };
+      inactive = true;
+
+      bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
+      {
+         return true;
+      }
+   };*/
+
+   TestToolBar()
+   {
+      /*tileSet.AddString("Forest").tag = 0;
+      tileSet.AddString("Winter").tag = 0;
+      tileSet.AddString("Wasteland").tag = 0;*/
+   }
+};
+
+class ExplorerWindow : Window
+{
+   text = "Ecere Explorer";
+   background = activeBorder;
+   borderStyle = sizable;
+   hasMaximize = true;
+   hasMinimize = true;
+   hasClose = true;
+   hasMenuBar = true;
+   tabCycle = true;
+   size = Size { 888, 840 };
+
+   bool userMode;
+   bool clipboard;
+
+   int treeSplit;
+   int searchSplit;
+   
+   ExplorerToolId lastViewId;
+
+   menu = Menu { };
+   
+   Menu fileMenu { menu, "File", f };
+   Menu windowMenu { menu, "Window", w };
+      MenuItem newWindow
+      {
+         windowMenu, "New Window", n;
+         
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            ExplorerWindow { }.Create();
+            return true;
+         }
+      };
+
+   TestToolBar toolBar
+   {
+      this;
+
+      void NotifyToolClick(TestToolBar toolBar, int id)
+      {
+         ExplorerToolId toolId = (ExplorerToolId)id;
+         switch(toolId)
+         {
+            case none:
+               break;
+            case newWindow:
+               ExplorerWindow { }.Create();
+               break;
+            case goBack:
+            case goForward:
+               break;
+            case goUp:
+            {
+               ExplorerFileBranch branch = tree.branch;
+               if(branch && branch.parent)
+                  tree.Select(branch.parent);
+               break;
+            }
+            case panelTree:
+               if(tree.visible)
+               {
+                  split.leftPane = null;
+                  split.visible = false;
+                  tree.visible = false;
+                  treeSplit = split.split;
+                  split.OnResize(split.size.w, split.size.h);
+               }
+               else
+               {
+                  if(search.visible)
+                  {
+                     search.visible = false;
+                     searchSplit = split.split;
+                  }
+                  tree.visible = true;
+                  split.leftPane = tree;
+                  split.visible = true;
+                  split.split = treeSplit;
+                  split.OnResize(split.size.w, split.size.h);
+                  SwitchViews(lastViewId);
+               }
+               break;
+            case panelSearch:
+               if(search.visible)
+               {
+                  split.leftPane = null;
+                  split.visible = false;
+                  search.visible = false;
+                  searchSplit = split.split;
+                  split.OnResize(split.size.w, split.size.h);
+                  SwitchViews(lastViewId);
+               }
+               else
+               {
+                  if(tree.visible)
+                  {
+                     tree.visible = false;
+                     treeSplit = split.split;
+                  }
+                  search.visible = true;
+                  split.leftPane = search;
+                  split.visible = true;
+                  split.split = searchSplit;
+
+                  view = ExplorerSearchViewTree { viewHolder, this };
+                  view.previewPictures = toolBar.previewPictures.checked;
+                  view.anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
+                  view.NotifyItemOpen = ViewNotifyItemOpen;
+                  view.Create();
+                  search.view = (ExplorerSearchViewTree)view;
+
+                  split.OnResize(split.size.w, split.size.h);
+               }
+               break;
+            case refreshView:
+               if(view)
+                  view.Refresh();
+               break;
+            case previewPictures:
+               if(view)
+                  view.previewPictures = toolBar.previewPictures.checked;
+               break;
+            case viewList:
+            case viewDetails:
+            case viewIcons:
+            case viewCards:
+            case viewShowcase:
+               SwitchViews(toolId);
+               break;
+         }
+      }
+   };
+
+   void SwitchViews(ExplorerToolId viewId)
+   {
+      ExplorerFileBranch branch = tree.branch;
+      view.Destroy(0);
+      switch(viewId)
+      {
+         case viewList:     view = ExplorerViewList     { parent = viewHolder, master = this }; break;
+         case viewDetails:  view = ExplorerViewDetails  { parent = viewHolder, master = this }; break;
+         case viewIcons:    view = ExplorerViewIcons    { parent = viewHolder, master = this }; break;
+         case viewCards:    view = ExplorerViewCards    { parent = viewHolder, master = this }; break;
+         case viewShowcase: view = ExplorerViewShowcase { parent = viewHolder, master = this }; break;
+      }
+      view.tabCycle = true;
+      view.previewPictures = toolBar.previewPictures.checked;
+      view.anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
+      view.NotifyItemOpen = ViewNotifyItemOpen;
+      view.Create();
+      view.Load(branch);
+      lastViewId = viewId;
+   }
+
+   Window deep
+   {
+      this;
+      borderStyle = deep;
+      tabCycle = true;
+      anchor = Anchor { left = 0, top = 34, right = 0, bottom = 0 };
+   };
+
+   ExplorerTree tree
+   {
+      deep, this;
+      //visible = false;
+      tabCycle = true;
+      size = Size { 624, 268 };
+      anchor = Anchor { left = 0, top = 0, bottom = 0 };
+      text = "Browser", hotKey = Key { e, alt = true };
+      NotifyBranchSelect = TreeNotifyBranchSelect;
+   };
+   
+   ExplorerView view;
+
+   ExplorerSearch search
+   {
+      deep, this;
+      visible = false;
+      tabCycle = true;
+      size = Size { 624, 268 };
+      anchor = Anchor { left = 0, top = 0, bottom = 0 };
+   };
+
+   ExplorerViewSearch results;
+
+   bool TreeNotifyBranchSelect(ExplorerTree tree, ExplorerFileBranch branch)
+   {
+      if(branch)
+      {
+         char path[MAX_LOCATION];
+         branch.GetPath(path);
+         toolBar.addressBar.contents = path;
+         view.Load(branch);
+      }
+      return true;
+   }
+   
+   bool ViewNotifyItemOpen(ExplorerView view, ExplorerFileItem item)
+   {
+      ExplorerFileBranch branch = tree.branch;
+      if(item && branch)
+      {
+         if(item.type.isFolderType)
+         {
+            ExplorerFileBranch child;
+            
+            if(!branch.loaded || !branch.childrenLoaded)
+               BranchLoad(branch, tree.tree);
+
+            for(child = branch.children.first; child; child = child.next)
+               if(!strcmp(child.name, item.name))
+                  break;
+            
+            if(child)
+            {
+               if(branch.row.collapsed)
+                  child.row.collapsed = true;
+               child.EnsureVisible(false);
+               tree.Select(child);
+            }
+         }
+         else
+         {
+            char path[MAX_LOCATION];
+            branch.GetPath(path);
+            PathCat(path, item.name);
+            ShellOpen(path);
+         }
+      }
+   }
+
+   Window viewHolder
+   {
+      parent = deep, master = this;
+      tabCycle = true;
+      anchor = Anchor { top = 0, bottom = 0, right = 0 };
+   };
+
+   SplitWindow split
+   {
+      deep, this;
+      leftPane = tree, rightPane = viewHolder;
+      split = 300;
+   };
+
+   void GoToLocation(char * location)
+   {
+      int c;
+      char * temp;
+      char step[MAX_LOCATION];
+      
+      StringArray steps { growingFactor = 4 };
+      ExplorerFileBranch last = null;
+      
+      temp = CopyString(location);
+      while(strlen(temp))
+      {
+         GetLastDirectory(temp, step);
+         StripLastDirectory(temp, temp);
+         steps.Add(CopyString(step));
+      }
+      
+      for(c = steps._count - 1; c >= 0; c--)
+      {
+         last = tree.Find(steps._[c], last);
+         if(!last)
+            break;
+         tree.Select(last);
+      }
+      
+      delete temp;
+      delete steps;
+   }
+
+   void SearchLocation(char * location)
+   {
+      GoToLocation(location);
+      search.location.editBox.contents = location;
+   }
+
+   bool OnPostCreate()
+   {
+      userMode = true;
+      return true;
+   }
+
+   ExplorerWindow()
+   {
+      userMode = false;
+
+      treeSplit = 300;
+      searchSplit = 200;
+
+      view = ExplorerViewList
+      {
+         parent = viewHolder, master = this;
+         tabCycle = true;
+         previewPictures = toolBar.previewPictures.checked;
+         anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
+         NotifyItemOpen = ViewNotifyItemOpen;
+      };
+      lastViewId = viewList;
+      
+      tree.Load();
+      view.Load(tree.root);
+   }
+
+   void InitTree()
+   {
+   }
+
+   void InitSearch()
+   {
+   }
+}
+
+class ExplorerApp : GuiApplication
+{
+   //skin = "Acovel";
+
+   bool Init()
+   {
+      QuickPathTool goPath { };
+      QuickPathTool searchPath { };
+      char * findWhat = null;
+      SetLoggingMode(debug, null);
+      if(argc > 1)
+      {
+         if(!strcmpi(argv[1], "go") && argc > 2)
+            goPath = argv[2];
+         else if(!strcmpi(argv[1], "find") && argc > 2)
+         {
+            char * unquoted;
+            if(argv[2][0] == '\"')
+               StripQuotes(argv[2], unquoted);
+            else
+               unquoted = argv[2];
+            findWhat = CopyString(unquoted);
+            if(argc > 3)
+            {
+               if(!strcmpi(argv[3], "in") && argc > 4)
+                  searchPath = argv[4];
+               else
+                  searchPath = ""; // this should make it current dir
+            }
+            else
+               searchPath = ""; // same
+         } 
+         else if(!strcmpi(argv[1], "search") && argc > 2)
+            searchPath = argv[2];
+         else if(!strcmpi(argv[1], "image") && argc > 2)
+            ;
+         else if(!strcmpi(argv[1], "slides") && argc > 2)
+            ;
+         else
+            goPath = argv[1];
+      }
+      else
+         goPath = "";
+      if(goPath)
+      {
+         ExplorerWindow explorerWnd { };
+         explorerWnd.Create();
+         explorerWnd.GoToLocation(goPath);
+      }
+      else if(searchPath)
+      {
+         ExplorerWindow explorerWnd { };
+         explorerWnd.Create();
+         explorerWnd.SearchLocation(searchPath);
+      }
+      return true;
+   }
+}
+
+struct QuickPathTool
+{
+   char path[MAX_LOCATION];
+
+   property char * 
+   {
+      set
+      {
+         char * unquoted;
+         GetWorkingDir(path, MAX_LOCATION);
+         if(value[0] == '\"')
+            StripQuotes(value, unquoted);
+         else
+            unquoted = value;
+         PathCat(path, unquoted);
+         if(!FileExists(path))
+         {
+            // this incomplete functionality is not quite at it's place in this class
+            int len;
+            char * original = CopyString(path);
+            while((len = strlen(path)))
+            {
+               StripLastDirectory(path, path);
+               if(FileExists(path))
+               {
+                  // TODO: message location does not exist, 
+                  //       this higher location exists though
+                  //       go there?
+                  break;
+               }
+            }
+            if(!len)
+            {
+               // TODO: message location does not exist, 
+               //       unable to select alternate location
+            }
+            path[0] = '\0';
+            delete original;
+         }
+      }
+      get { return path[0] ? path : null; }
+   }
+   property bool { get { return (bool)path[0]; } }
+};
+
+define app = ((ExplorerApp)__thisModule);
diff --git a/explorer/src/ExplorerTree.ec b/explorer/src/ExplorerTree.ec
new file mode 100644 (file)
index 0000000..d3993bb
--- /dev/null
@@ -0,0 +1,1527 @@
+public import "ecere"
+import "SplitWindow"
+import "ArrayFactoredGrowth"
+
+//import "ArrayFactoredGrowth"
+//import "ArrayBinarySorted"
+
+#ifdef __WIN32__
+static char * rootName = "Entire Computer";
+static char * msNetwork = "Microsoft Windows Network";
+#else
+static char * rootName = "File System";
+#endif
+
+private:
+define guiApp = ((GuiApplication)__thisModule);
+define selectionColor = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
+
+static char * fileIconNames[] = 
+{
+   "<:ecere>mimetypes/file.png",         /* none */
+
+   "<:ecere>mimetypes/file.png",         /* normalFile */
+   "<:ecere>mimetypes/textEcereWorkspace.png",          /* ewsFile */
+   "<:ecere>mimetypes/textEcereProject.png",      /* epjFile */
+   "<:ecere>mimetypes/textEcereSource.png",         /* ecFile */
+   "<:ecere>mimetypes/textEcereHeader.png",         /* ehFile */
+   "<:ecere>mimetypes/textCSource.png",          /* cFile */
+   "<:ecere>mimetypes/textCHeader.png",          /* hFile */
+   "<:ecere>mimetypes/textC++Source.png",        /* cppFile */
+   "<:ecere>mimetypes/textC++Header.png",        /* hppFile */
+   "<:ecere>mimetypes/text.png",         /* textFile */
+   "<:ecere>mimetypes/textHyperTextMarkup.png",              /* webFile */
+   "<:ecere>mimetypes/image.png",        /* pictureFile */
+   "<:ecere>status/audioVolumeHigh.png",         /* soundFile */
+   "<:ecere>mimetypes/package.png",      /* archiveFile */
+   "<:ecere>mimetypes/packageSoftware.png",     /* packageFile */
+   "<:ecere>mimetypes/packageOpticalDisc.png", /* opticalMediaImageFile */
+
+   "<:ecere>places/folder.png",                    /* folder */
+   "<:ecere>status/folderOpen.png",               /* folderOpen */
+   "<:ecere>devices/computer.png",                 /* computer */
+   "<:ecere>devices/driveHardDisk.png",           /* drive */
+   "<:ecere>places/driveRemote.png",              /* netDrive */
+   "<:ecere>devices/mediaOptical.png",            /* cdrom */
+   "<:ecere>devices/driveRemovableMedia.png",    /* removable */
+   "<:ecere>devices/mediaFloppy.png",             /* floppy */
+   "<:ecere>places/networkWorkgroup.png",         /* network */
+   "<:ecere>places/networkServer.png",            /* server */
+   "<:ecere>places/folderRemote.png",             /* share */
+
+   "<:ecere>mimetypes/package.png",      /* treeLoader */
+   "<:ecere>places/startHere.png",                /* lineNumbers */
+   
+   ""
+};
+
+public enum ExplorerFileType
+{
+   none,
+   
+   normalFile, ewsFile, epjFile, ecFile, ehFile, cFile, hFile, cppFile, hppFile,
+   textFile, webFile, pictureFile, soundFile,
+   archiveFile, packageFile, opticalMediaImageFile, /* these (all previous) are sort equal */
+   
+   folder, folderOpen, computer,
+   drive, netDrive, cdrom, removable, floppy, network, server, share, // these are sort equal
+   
+   // utilities
+   treeLoader,
+   lineNumbers;
+
+
+   /*property char * 
+   {
+      set
+      {
+         this = SelectByExtension(value);
+      }
+   }*/
+
+   public property bool isFolderType
+   {
+      get { return this >= folder && this <= share; }
+   }
+
+   public property bool isFileType
+   {
+      get { return this >= normalFile && this <= opticalMediaImageFile; }
+   }
+
+   ExplorerFileType ::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 ExplorerControl : Window
+{
+   bool previewPictures;
+
+   BitmapResource fileIcons[ExplorerFileType];
+
+   ExplorerControl()
+   {
+      ExplorerFileType c;
+      for(c = 0; c < ExplorerFileType::enumSize; c++)
+      {
+         fileIcons[c] = BitmapResource { fileIconNames[c], alphaBlend = true };
+         AddResource(fileIcons[c]);
+      }
+   }
+}
+
+class ExplorerView : ExplorerControl
+{
+   borderStyle = none;
+   hasHorzScroll = false;
+   hasVertScroll = false;
+
+   virtual void Load(ExplorerFileBranch parent);
+   virtual void Refresh();
+
+   virtual void LaunchNotifyItemSelect(Window master, ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
+   {
+      view.NotifyItemSelect(master, view, item, selectedItems);
+   }
+
+   virtual bool Window::NotifyItemSelect(ExplorerView view, ExplorerFileItem item, ExplorerFileItemArray selectedItems);
+   virtual bool Window::NotifyItemOpen(ExplorerView view, ExplorerFileItem item);
+
+   ListBox list
+   {
+      master = master, parent = this;
+      //this, master;
+      borderStyle = none;
+      hasHorzScroll = true;
+      hasVertScroll = true;
+      resizable = true;
+      sortable = true;
+      fullRowSelect = false;
+      multiSelect = true;
+
+      anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+
+      bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
+      {
+         ExplorerView view = (ExplorerView)listBox.parent;
+         if(listBox.currentRow)
+         {
+            DataRow listRow;
+            ExplorerFileItemArray selectedItems { growingFactor = 16 };
+            for(listRow = listBox.firstRow; listRow; listRow = listRow.next)
+               if(listRow.selected)
+                  selectedItems.Add((ExplorerFileItem)listRow.tag);
+            //view.NotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
+            view.LaunchNotifyItemSelect(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag, selectedItems);
+         }
+         return true;
+      }
+
+      bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
+      {
+         ExplorerView view = (ExplorerView)listBox.parent;
+         view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
+         return false;
+      }
+
+      bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
+      {
+         if((SmartKey)key == enter)
+         {
+            ExplorerView view = (ExplorerView)listBox.parent;
+            view.NotifyItemOpen(listBox.parent.master, view, (ExplorerFileItem)listBox.currentRow.tag);
+         }
+         return true;
+      }
+   };
+
+   ExplorerView()
+   {
+   }
+}
+
+class ExplorerViewList : ExplorerView
+{
+
+   ExplorerFileBranch location;
+
+public:
+
+   DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
+
+   ExplorerViewDetails()
+   {
+      list.AddField(nameField);
+   }
+
+   void Refresh()
+   {
+      Load(location);
+   }
+
+   void Load(ExplorerFileBranch location)
+   {
+      char path[MAX_LOCATION];
+      this.location = location;
+      location.GetPath(path);
+      {
+         FileListing listing { path };
+         
+         ExplorerFileItem item;
+         DataRow row;
+
+         list.Clear();
+
+         while(listing.Find())
+         {
+            item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
+
+            row = list.AddRow();
+            row.tag = (int)item;
+            row.SetData(nameField, item);
+         }
+         list.Sort(nameField, 1);
+      }
+   }
+}
+
+class ExplorerViewDetails : ExplorerView
+{
+   list.hasHeader = true;
+   list.moveFields = true;
+   list.resizable = true;
+   list.sortable = true;
+
+   ExplorerFileBranch location;
+
+public:
+
+   DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
+   DataField typeField { header = "Type", dataType = /*"String"*/ "char *", width = 40 };
+   DataField sizeField { header = "Size", dataType = "FileSize", width = 96, alignment = right };
+
+   ExplorerViewDetails()
+   {
+      list.AddField(nameField);
+      list.AddField(typeField);
+      list.AddField(sizeField);
+   }
+
+   void Refresh()
+   {
+      Load(location);
+   }
+
+   void Load(ExplorerFileBranch location)
+   {
+      char path[MAX_LOCATION];
+      this.location = location;
+      location.GetPath(path);
+      {
+         FileListing listing { path };
+         
+         ExplorerFileItem item;
+         DataRow row;
+
+         list.Clear();
+
+         while(listing.Find())
+         {
+            item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
+
+            row = list.AddRow();
+            row.tag = (int)item;
+            row.SetData(nameField, item);
+            row.SetData(typeField, CopyString(item.extension));
+            row.SetData(sizeField, (void *)listing.stats.size);
+         }
+         list.Sort(nameField, 1);
+      }
+   }
+}
+
+class ExplorerViewIcons : ExplorerView
+{
+
+   ExplorerFileBranch location;
+
+public:
+
+   DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
+
+   ExplorerViewDetails()
+   {
+      list.AddField(nameField);
+   }
+
+   void Refresh()
+   {
+      Load(location);
+   }
+
+   void Load(ExplorerFileBranch location)
+   {
+      char path[MAX_LOCATION];
+      this.location = location;
+      location.GetPath(path);
+      {
+         FileListing listing { path };
+         
+         ExplorerFileItem item;
+         DataRow row;
+
+         list.Clear();
+
+         while(listing.Find())
+         {
+            item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
+
+            row = list.AddRow();
+            row.tag = (int)item;
+            row.SetData(nameField, item);
+         }
+         list.Sort(nameField, 1);
+      }
+   }
+}
+
+class ExplorerViewCards : ExplorerView
+{
+
+   ExplorerFileBranch location;
+
+public:
+
+   DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 304, editable = true, userData = this };
+
+   ExplorerViewDetails()
+   {
+      list.AddField(nameField);
+   }
+
+   void Refresh()
+   {
+      Load(location);
+   }
+
+   void Load(ExplorerFileBranch location)
+   {
+      char path[MAX_LOCATION];
+      this.location = location;
+      location.GetPath(path);
+      {
+         FileListing listing { path };
+         
+         ExplorerFileItem item;
+         DataRow row;
+
+         list.Clear();
+
+         while(listing.Find())
+         {
+            item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
+
+            row = list.AddRow();
+            row.tag = (int)item;
+            row.SetData(nameField, item);
+         }
+         list.Sort(nameField, 1);
+      }
+   }
+}
+
+public class BitmapArray : RedjArray
+{
+   type = class(Bitmap);
+public:
+   Bitmap * const _;
+   Bitmap * Add(Bitmap bitmap)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = bitmap;
+      return &_[pos];
+   }
+   Bitmap * AddBefore(uint position, Bitmap bitmap)
+   {
+      Insert(position, 1);
+      _[position] = bitmap;
+      return &_[position];
+   }
+   void Clear()
+   {
+      int c;
+      for(c = 0; c < _count; c++)
+      {
+         _[c].Free();
+         delete _[c];
+      }  
+      count = 0;
+      size = 0;
+   }
+}
+
+class ExplorerViewShowcase : ExplorerView
+{
+   list.anchor = Anchor { left = 0, top = 0, bottom = 0 };
+   list.size = Size { w = 200 };
+
+   ExplorerFileBranch location;
+
+public:
+
+   DataField nameField { header = "Name", dataType = "ExplorerFileItem", width = 180, editable = true, userData = this };
+
+   Bitmap bitmap;
+   BitmapArray bitmaps { growingFactor = 16 };
+
+   Window show
+   {
+      this;
+      borderStyle = none;
+      anchor = Anchor { top = 0, right = 0, bottom = 0 };
+
+      void OnRedraw(Surface surface)
+      {
+         ExplorerViewShowcase view = (ExplorerViewShowcase)parent;
+         if(view.bitmap)
+         {
+            int wBmp = view.bitmap.width;
+            int hBmp = view.bitmap.height;
+            int wWnd = clientSize.w;
+            int hWnd = clientSize.h;
+
+            int wList = view.list.size.w + view.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(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw, wBmp, hBmp);
+      #else
+            // Until Filter / Stretch works with X
+            surface.Blit(view.bitmap, (wWnd - wDraw) / 2, (hWnd - hDraw) / 2, 0, 0, wDraw, hDraw);
+      #endif
+         }
+         else
+         {
+            surface.SetForeground(white);
+            surface.Area(0, 0, view.clientSize.w - 1, view.clientSize.h - 1);
+         }
+      }
+   }
+
+   SplitWindow split
+   {
+      this;
+      leftPane = list;
+      rightPane = show;
+      split = 200;
+      tabCycle = true;
+   };
+
+   ExplorerViewDetails()
+   {
+      list.AddField(nameField);
+   }
+
+   void LaunchNotifyItemSelect(Window master, ExplorerViewShowcase view, ExplorerFileItem item, ExplorerFileItemArray selectedItems)
+   {
+      int pos;
+      ExplorerFileItem selItem;
+      if(view.bitmap)
+         view.bitmap.Free();
+      delete view.bitmap;
+      if(item && item.type == pictureFile)
+      {
+         view.bitmap = Bitmap { };
+         view.bitmap.Load(item.path, null, displaySystem);
+      }
+
+      view.bitmaps.Clear();
+      view.bitmaps = BitmapArray { };
+      for(pos = 0; pos < selectedItems.count; pos++)
+      {
+         Bitmap bitmap { };
+         selItem = (ExplorerFileItem)selectedItems._[pos]; 
+         bitmap.Load(selItem.path, null, displaySystem);
+         //view.bitmaps.Add(bitmap);
+      }  
+      if(item && item.type == pictureFile)
+      {
+         view.bitmap = Bitmap { };
+         view.bitmap.Load(item.path, null, displaySystem);
+      }
+
+      view.show.Update(null);
+      view.NotifyItemSelect(master, view, item, selectedItems);
+   }
+
+   void Refresh()
+   {
+      Load(location);
+   }
+
+   void Load(ExplorerFileBranch location)
+   {
+      char path[MAX_LOCATION];
+      this.location = location;
+      location.GetPath(path);
+      {
+         FileListing listing { path };
+         
+         ExplorerFileItem item;
+         DataRow row;
+
+         list.Clear();
+
+         while(listing.Find())
+         {
+            item = MakeFileItem(listing.stats.attribs, listing.name, listing.path, previewPictures, displaySystem);
+
+            row = list.AddRow();
+            row.tag = (int)item;
+            row.SetData(nameField, item);
+         }
+         list.Sort(nameField, 1);
+      }
+   }
+}
+
+class ExplorerTree : ExplorerControl
+{
+   hasHorzScroll = false;
+   hasVertScroll = false;
+
+   menu = Menu { };
+
+public:
+
+   DataField nameField { dataType = "ExplorerFileBranch", width = 240, userData = this };
+
+   ExplorerFileBranch root;
+   ExplorerFileBranch selection;
+
+   virtual bool Window::NotifyBranchSelect(ExplorerTree tree, ExplorerFileBranch branch);
+   
+   property ExplorerFileBranch branch
+   {
+      get
+      {
+         if(!tree)
+            return null;
+         if(!tree.currentRow)
+            return null;
+         if(!tree.currentRow.tag)
+            return null;
+         return (ExplorerFileBranch)tree.currentRow.tag;
+      }
+   }
+
+   void Select(ExplorerFileBranch branch)
+   {
+      if(branch.row)
+      {
+         branch.EnsureVisible(false);
+         tree.SelectRow(branch.row);
+      }
+   }
+
+   ExplorerFileBranch Find(const char * name, ExplorerFileBranch parent)
+   {
+      ExplorerFileBranch branch;
+      ExplorerFileBranch start = parent ? parent : root;
+      if(!start.loaded || !start.childrenLoaded)
+         BranchLoad(start, tree);
+      for(branch = start.children.first; branch; branch = branch.next)
+         if(branch.name && !strcmpi(branch.name, name))
+            return branch;
+      return null;
+   }
+
+   ListBox tree
+   {
+      master = master, parent = this;
+      //this, master;
+      borderStyle = none;
+      hasHorzScroll = true;
+      hasVertScroll = true;
+      fullRowSelect = false;
+      treeBranches = true;
+      collapseControl = true;
+      rootCollapseButton = 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)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+            ExplorerFileBranch child;
+            if(collapsed)
+            {
+               /*
+               for(child = branch.children.last; child; child = branch.children.last)
+               {
+                  listBox.DeleteRow(child.row);
+                  child.Free();
+                  delete child;
+               }
+               branch.childrenLoaded = false;
+               */
+            }
+            else
+            {
+               if(!branch.loaded || !branch.childrenLoaded)
+                  BranchLoad(branch, tree);
+               for(child = branch.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)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+            if(branch)
+            {
+               PopupMenu popup;
+               Menu 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)
+      {
+         if(row)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+            NotifyBranchSelect(listBox.parent.master, this, branch);
+            selection = branch;
+         }
+         return true;
+      }
+
+      bool NotifyEditing(ListBox listBox, DataRow row)
+      {
+         if(row)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+         }
+         return true;
+      }
+
+      bool NotifyEdited(ListBox listBox, DataRow row)
+      {
+         if(row)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+         }
+         return true;
+      }
+
+      bool NotifyEditDone(ListBox listBox, DataRow row)
+      {
+         if(row)
+         {
+            ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+         }
+         return true;
+      }
+   };
+
+   // 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;
+   }*/
+
+   ExplorerTree()
+   {
+      tree.AddField(nameField);
+   }
+
+   void Load()
+   {
+      ExplorerFileBranch parent;
+      ExplorerFileBranch branch;
+      FileListing listing { "/" };
+
+      tree.Clear();
+
+      root = ExplorerFileBranch { type = computer, loaded = true, childrenLoaded = true };
+   #ifdef __WIN32__
+      root.name = rootName;
+   #else
+      root.name = "/";
+   #endif
+      AddBranch(root, true, false, null, tree);
+
+   // How can this make sense for linux? 
+   #ifdef __WIN32__
+      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 = MakeFileBranch(listing.stats, name);
+         if(info[0])
+            parent.info = CopyString(info);
+         parent.loaded = true;
+         AddBranch(parent, !listing.stats.attribs.isDirectory, listing.stats.attribs.isDirectory, root, tree);
+         if(!listing.stats.attribs.isDirectory)
+            parent.childrenLoaded = true;
+      }
+
+      branch = ExplorerFileBranch { name = msNetwork, type = network };
+      AddBranch(branch, false, true, null, tree);
+      branch.row.collapsed = true;
+      tree.Sort(nameField, 1);
+      tree.SelectRow(root.row);
+   #endif
+   }
+
+/*
+public class ClipBoardFiles
+{
+
+public:
+
+   property
+
+}
+
+   // CLIPBOARD
+   void Copy()
+   {
+      if(this)
+      {
+         int size = SelSize();
+         if(size)
+         {
+            // Try to allocate memory
+            ClipBoard clipBoard { };
+            if(clipBoard.Allocate(size+1))
+            {
+               GetSel(clipBoard.memory, true);   
+               // Save clipboard
+               clipBoard.Save();
+            }
+            delete clipBoard;
+         }
+      }
+   }
+
+   void Paste()
+   {
+      if(this)
+      {
+         ClipBoard clipBoard { };
+         if(clipBoard.Load())
+            PutS(clipBoard.memory);
+         delete clipBoard;
+      }
+   }
+
+   void Cut()
+   {
+      if(this)
+      {
+         Copy();
+         DelSel();
+         SetViewToCursor(true);
+         Modified();
+      }
+   }
+
+Private Type DROPFILES
+   pFiles As Long
+   pt As POINTAPI
+   fNC As Long
+   fWide As Long
+End Type
+For iCounter = 0 To filelist.ListCount - 1
+  If filelist.Selected(iCounter) = True Then
+    strFiles = strFiles & FixPath(filelist.Path) & filelist.List(iCounter) & vbNullChar
+  End If
+Next
+'all selected items are now put in strFiles
+
+hGlobal = GlobalAlloc(GHND, Len(DF) + Len(strFiles)) 'put all files to a exclusive number
+If hGlobal Then 'if the globalalloc worked
+  lpGlobal = GlobalLock(hGlobal) 'lock the hGlobal
+  DF.pFiles = Len(DF) 'set the size of the files
+  
+  Call CopyMem(ByVal lpGlobal, DF, Len(DF)) 'copy df to the lpglobal
+  Call CopyMem(ByVal (lpGlobal + Len(DF)), ByVal strFiles, Len(strFiles)) 'copy strfiles to lpglobal
+  Call GlobalUnlock(hGlobal) 'unlock hglobal again
+  
+  SetClipboardData CF_HDROP, hGlobal 'put files to the clipboard
+End If
+
+   bool SaveFile(const char * filePath)
+   {
+   }
+*/
+
+}
+
+/*public class FileTreeBranchBSArray : ArrayBinarySorted
+{
+   type = class(ExplorerFileBranch);
+public:
+   ExplorerFileBranch * const _;
+   BSloc Add(ExplorerFileBranch item)
+   {
+      BSloc result = Find(item);
+      if(!result.valid)
+      {
+         Insert(result.pos, 1);
+         _[result.pos] = item;
+      }
+      return result;
+   }
+   BSloc Remove(ExplorerFileBranch item)
+   {
+      
+   }
+}*/
+
+/*public class FileTreeBranchArray : RedjArray
+{
+   type = class(ExplorerFileBranch);
+public:
+   ExplorerFileBranch * const _;
+   ExplorerFileBranch * Add(ExplorerFileBranch item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   ExplorerFileBranch * AddBefore(uint position, ExplorerFileBranch item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}*/
+
+public class ExplorerFileItem : struct
+{
+   char * path;
+   char * name;
+   char * info;
+   char * extension;
+   ExplorerFileType type;
+   int indent;
+
+   Bitmap bitmap;
+
+   void OnDisplay(Surface surface, int x, int y, int width, ExplorerControl control, Alignment alignment, DataDisplayFlags displayFlags)
+   {
+      int indentSize = (displayFlags.dropBox) ? 0 : 10;
+      int textOffset;
+      int len;
+      char label[MAX_FILENAME];
+
+      //float scale = Min((float)clientSize.w / (float)bitmap.width, (float)clientSize.h / (float)bitmap.height);
+      int w = 16; //(int)(bitmap.width * scale);
+      int h = 16; //(int)(bitmap.height * scale);
+   
+      Bitmap icon;
+
+      icon = control.fileIcons[type].bitmap;
+      if(!icon)
+      {
+         if(type == folder || type == folderOpen)
+            surface.SetForeground(red); //Color { 170, 170, 0 } // REDJ What is that color?
+         indentSize = 8;
+      }
+      textOffset = indent * indentSize + (icon ? (icon.width + 6) : 0);
+      
+      if(info)
+         sprintf(label, "%s [%s]", name, info);
+      else
+         strcpy(label, name);
+      len = strlen(label);
+
+      surface.WriteTextDots
+         (alignment, x + textOffset, y + 2, width - textOffset, label, len);
+      if(type == pictureFile && control.previewPictures && bitmap)
+      {
+#ifndef __linux__
+         //surface.Filter(bitmap, (clientSize.w - w) / 2,(clientSize.h - h) / 2, 0,0, w, h, bitmap.width, bitmap.height);
+         surface.Filter(bitmap, x + indent * indentSize + 2, y, 0, 0, w, h, bitmap.width, bitmap.height);
+#else
+         // Until Filter / Stretch works with X
+         //surface.Blit(bitmap, (clientSize.w - bitmap.width) / 2,(clientSize.h - bitmap.height) / 2, 0,0, bitmap.width, bitmap.height);
+         surface.blend = true;
+         surface.Blit(bitmap, x + indent * indentSize + 2, y,0,0, w, h);
+#endif
+         //bitmap.Free();
+         //delete bitmap;
+      }
+      else if(icon)
+         surface.Blit(icon, x + indent * indentSize + 2, y,0,0, icon.width, icon.height);
+   }
+
+   int OnCompare(ExplorerFileItem b)
+   {
+      int result;
+      if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
+         result = strcmpi(name, b.name);
+      else
+      {
+         if(type == folder && b.type < folder) result = -1;
+         else if(type < folder && b.type == folder) result = 1;
+      }
+      return result;
+   }
+
+   void OnCopy(ExplorerFileItem newData)
+   {
+      type = newData.type;
+      indent = newData.indent;
+      if(newData.name)
+      {
+         int len = strlen(newData.name) + 1;
+         name = new char[len];
+         CopyBytes(name, newData.name, len);
+      }
+   }
+
+   bool OnGetDataFromString(char * string)
+   {
+      int len = strlen(string) + 1;
+      name = new char[len];
+      CopyBytes(name, string, len);
+      return true;
+   }
+
+   void OnFree()
+   {
+      delete path;
+      delete name;
+      delete info;
+      delete extension;
+      if(bitmap)
+         bitmap.Free();
+   }
+
+   char * OnGetString(char * string, void * fieldData, bool * needClass)
+   {
+      return name;
+   }
+};
+
+public class ExplorerFileItemArray : RedjArray
+{
+   type = class(ExplorerFileItem);
+public:
+   ExplorerFileItem * const _;
+   ExplorerFileItem * Add(ExplorerFileItem item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   ExplorerFileItem * AddBefore(uint position, ExplorerFileItem item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+   void Clear()
+   {
+      int c;
+      for(c = 0; c < _count; c++)
+      {
+         //_[c].Free()
+         delete _[c];
+      }  
+      count = 0;
+      size = 0;
+   }
+}
+
+ExplorerFileItem MakeFileItem(const FileAttribs attribs, const char * fileName, const char * filePath, const bool previewPicture, const DisplaySystem displaySystem)
+{
+   int len = strlen(fileName);
+   char info[MAX_LOCATION];
+   char name[MAX_LOCATION];
+   char extension[MAX_EXTENSION];
+   
+   ExplorerFileItem item { };
+
+   //if(attribs.isFile) // TODO fix this in ecere
+   if(attribs.isDirectory)
+   {
+      extension[0] = 0;
+
+      item.type = (attribs.isDrive) ? drive : folder;
+      if(attribs.isServer)
+         item.type = server;
+      if(attribs.isShare)
+         item.type = share;
+      if(attribs.isCDROM)
+         item.type = cdrom;
+      if(attribs.isRemote)
+         item.type = netDrive;
+      if(attribs.isRemovable) 
+      {
+         if(fileName[0] == 'A' || fileName[0] == 'B')
+            item.type = floppy;
+         else
+            item.type = removable;
+      }
+   }
+   else
+   {
+      GetExtension(fileName, extension);
+      //strupr(extension);
+      strlwr(extension);
+      
+      item.type = ExplorerFileType::SelectByExtension(extension);
+   }
+
+   if(attribs.isDrive && 
+         len > 3 && !strncmp(&fileName[1], ": [", 3))
+   {
+      strncpy(name, fileName, 2);
+      name[2] = 0;
+      strncpy(info, &fileName[4], len - 5);
+      info[len - 5] = 0;
+   }
+   else
+   {
+      strcpy(name, fileName);
+      info[0] = 0;
+   }
+
+   item.path = CopyString(filePath);
+   item.name = CopyString(name);
+   if(info[0])
+      item.info = CopyString(info);
+   item.extension = CopyString(extension);
+
+   if(item.type == pictureFile && previewPicture)
+   {
+      item.bitmap = Bitmap { };
+      item.bitmap.Load(filePath, null, displaySystem);
+   }
+
+   return item;
+}
+
+public class ExplorerFileBranch : struct
+{
+   ExplorerFileBranch prev, next;
+
+   bool loaded, childrenLoaded;
+   int indent;
+   char * name;
+   char * info;
+   DataRow row;
+   OldList children;
+   ExplorerFileType type;
+   ExplorerFileBranch parent;
+
+   FileStats stats;
+
+   void GetPath(String outputPath)
+   {  
+      ExplorerFileBranch up;
+      if(parent)
+      {
+         strcpy(outputPath, name);
+         for(up = parent; up; up = up.parent)
+         {
+            char temp[MAX_LOCATION];
+            strcpy(temp, up.name);
+            PathCat(temp, outputPath);
+            strcpy(outputPath, temp);
+         }
+      }
+      else
+#ifdef __WIN32__
+         strcpy(outputPath, "/");
+#else
+         strcpy(outputPath, name);
+#endif
+
+   }
+
+   bool IsChildOf(ExplorerFileBranch branch)
+   {
+      ExplorerFileBranch test;
+      for(test = parent; test; test = test.parent)
+         if(test == branch)
+            return true;
+      return false;
+   }
+
+   void DuplicateChildren(bool recursive, bool forceExpanded, ExplorerFileBranch addTo, ListBox tree)
+   {
+      if(children.first)
+      {
+         ExplorerFileBranch child;
+         
+         for(child = children.first; child; child = child.next)
+         {
+            ExplorerFileBranch copy { };
+            copy.name = CopyString(child.name);
+            copy.type = child.type;
+            AddBranch(copy, child.loaded, false, addTo, tree);
+            if(forceExpanded)
+               copy.row.collapsed = false;
+            if(recursive)
+               child.DuplicateChildren(recursive, forceExpanded, copy, tree);
+         }
+      }
+   }
+   
+   void EnsureVisible(bool expand)
+   {
+      if(parent)
+         parent.EnsureVisible(true);
+      if(expand)
+         row.collapsed = false;
+      // TODO: row.EnsureVisible(); // making the row visible by scrolling
+   }
+
+   void OnFree()
+   {
+      //delete name;
+   }
+
+   void Free()
+   {
+      ExplorerFileBranch child;
+      for(; (child = children.first); )
+      {
+         child.Free();
+         children.Delete(child);
+      }
+      //if(name)
+      delete name;
+      delete info;
+   }
+
+   void Delete()
+   {
+      Free();
+      if(parent)
+         parent.children.Delete(this);
+   }
+
+   void OnDisplay(Surface surface, int x, int y, int width, ExplorerControl control, Alignment alignment, DataDisplayFlags displayFlags)
+   {
+      //int indentSize = (displayFlags.dropBox) ? 0 : 10;
+      int indent = 16;
+      int xStart;
+      int len;
+      int w, h;
+      //int textOffset;
+      char label[MAX_FILENAME];
+
+      Bitmap icon;
+
+      if(!this)
+         return;
+      
+      icon = control.fileIcons[type].bitmap;
+      //xStart = indent * indent + x + (icon ? (icon.width + 5) : 0);
+      xStart = x + (icon ? (icon.width + 5) : 0);
+
+      if(!name)
+         return;
+
+      if(info)
+         sprintf(label, "%s [%s]", name, info);
+      else
+         strcpy(label, name);
+      len = strlen(label);
+      
+      if(!icon)
+      {
+         if(type == folder || type == folderOpen)
+            surface.SetForeground(yellow);
+         //indentSize = 8;
+         indent = 8;
+      }
+      //textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
+      
+      surface.TextOpacity(false);
+      surface.TextExtent(label, len, &w, &h);
+      h = Max(h, 16);
+    
+      // Draw the current row stipple
+      if(displayFlags.selected)
+         //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
+         //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
+         surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
+      
+      //surface.WriteTextDots(alignment, x + textOffset, y + 2, width - textOffset, name, strlen(name));
+      surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
+
+      if(!guiApp.textMode)
+      {
+         if(displayFlags.current)
+         {
+            if(displayFlags.active)
+            {
+               surface.LineStipple(0x5555);
+               if(displayFlags.selected)
+                  surface.SetForeground(0xFFFFFF80);
+               else
+                  surface.SetForeground(black);
+            }
+            else
+            {
+               surface.SetForeground(selectionColor);
+            }
+            surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
+            surface.LineStipple(0);
+         }
+
+         if(icon)
+         {
+            //surface.blend = true;
+            //surface.alphaWrite = blend;
+            surface.SetForeground(white);
+            //surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
+            surface.Blit(icon, x,y,0,0, icon.width, icon.height);
+         }
+      }
+   }
+
+   int OnCompare(ExplorerFileBranch b)
+   {
+      int result;
+      if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
+         result = strcmpi(name, b.name);
+      else
+      {
+         if(type == folder && b.type < folder) result = -1;
+         else if(type < folder && b.type == folder) result = 1;
+      }
+      return result;
+   }
+
+   char * OnGetString(char * tempString, FileSystemToolWindow fileSysToolWnd, bool * needClass)
+   {
+      return name ? name : "";
+   }
+};
+
+ExplorerFileBranch MakeFileBranch(const FileStats stats, const char * name)
+{
+   ExplorerFileBranch fileTreeBranch { stats = stats };
+   fileTreeBranch.name = CopyString(name);
+   if(!fileTreeBranch.name)
+      fileTreeBranch.name = null;
+   if(stats.attribs.isDirectory)
+   {
+      fileTreeBranch.type = (stats.attribs.isDrive) ? drive : folder;
+      if(stats.attribs.isServer) fileTreeBranch.type = server;
+      if(stats.attribs.isShare) fileTreeBranch.type = share;
+      if(stats.attribs.isCDROM) fileTreeBranch.type = cdrom;
+      if(stats.attribs.isRemote) fileTreeBranch.type = netDrive;
+      if(stats.attribs.isRemovable) 
+      {
+         if(name[0] == 'A' || name[0] == 'B')
+            fileTreeBranch.type = floppy;
+         else
+            fileTreeBranch.type = removable;
+      }
+   }
+   else
+   {
+      char extension[MAX_EXTENSION];
+      GetExtension(fileTreeBranch.name, extension);
+      fileTreeBranch.type = ExplorerFileType::SelectByExtension(extension);
+   }
+   return fileTreeBranch;
+}
+
+void AddBranch(ExplorerFileBranch branch, bool loaded, bool addLoader, ExplorerFileBranch addTo, ListBox tree)
+{
+   DataRow row = (addTo && addTo.row) ? addTo.row.AddRow() : tree.AddRow();
+   if(addTo)
+   {
+      branch.parent = addTo;
+      branch.indent = addTo.indent + 1;
+      addTo.children.Add(branch);
+   }
+   row.tag = (int)branch;
+   branch.row = row;
+   row.SetData(null, branch);
+
+   branch.loaded = loaded;
+   if(addLoader)
+      //AddBranch(ExplorerFileBranch { }, false, false, branch, tree); // why would this create a compile error?
+      AddBranch(ExplorerFileBranch { type = none }, false, false, branch, tree);
+
+   if(branch.indent > 0)
+      row.collapsed = true;
+   else if(branch.type == folder)
+      branch.type = folderOpen;
+}
+
+void BranchLoad(ExplorerFileBranch branch, ListBox tree)
+{
+   if(!branch.loaded)
+   {
+      char path[MAX_LOCATION];
+      branch.GetPath(path);
+      {
+         FileListing listing { path };
+         if(branch.children.count == 1)
+         DeleteBranch(branch.children.first, tree);
+
+         while(listing.Find())
+         {
+            if(listing.stats.attribs.isDirectory)
+            {
+               ExplorerFileBranch child = MakeFileBranch(listing.stats, listing.name);
+               AddBranch(child, true, false, branch, tree);
+               BranchChildLoad(child, branch, tree);
+            }
+         }
+      }
+      branch.childrenLoaded = true;
+      branch.loaded = true;
+      branch.row.SortSubRows(false);
+   }
+   else if(!branch.childrenLoaded)
+   {
+      ExplorerFileBranch child;
+      if(branch.children.first)
+      {
+         for(child = branch.children.first; child; child = child.next)
+         {
+            if(!child.loaded)
+               BranchLoad(child, tree);
+            else if(!child.childrenLoaded)
+               BranchChildLoad(child, branch, tree);
+         }
+         branch.childrenLoaded = true;
+         branch.row.SortSubRows(false);
+      }
+   }
+}
+
+static void BranchChildLoad(ExplorerFileBranch parent, ExplorerFileBranch branch, ListBox tree)
+{
+   char path[MAX_LOCATION];
+   parent.GetPath(path);
+   {
+      bool added = false;
+      FileListing listing { path };
+      while(listing.Find())
+      {
+         if(listing.stats.attribs.isDirectory)
+         {
+            ExplorerFileBranch child = MakeFileBranch(listing.stats, listing.name);
+            AddBranch(child, true, false, parent, tree);
+            added = true;
+         }
+      }
+      if(!added)
+         added = true;
+   }
+   //parent.childrenLoaded = true;
+}
+
+void DeleteBranch(ExplorerFileBranch branch, ListBox tree)
+{
+   ExplorerFileBranch child;
+   for(; (child = branch.children.first); )
+      DeleteBranch(child, tree);
+   tree.DeleteRow(branch.row);
+   branch.Delete();
+}
+
diff --git a/explorer/src/ExplorerWindow.ec b/explorer/src/ExplorerWindow.ec
new file mode 100644 (file)
index 0000000..5a5b37f
--- /dev/null
@@ -0,0 +1,58 @@
+public import "ecere"
+
+private:
+define guiApp = ((GuiApplication)__thisModule);
+define selectionColor = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
+
+static char * iconNames[] = 
+{
+   "<:ecere>tango/16x16/mimetypes/file-x-generic.png",         /*none*/
+
+   "<:ecere>tango/16x16/mimetypes/file-x-generic.png",         /*normalFile*/
+   "<:ecere>tango/16x16/mimetypes/text-ews-work.png",          /*ewsFile*/
+   "<:ecere>tango/16x16/mimetypes/text-epj-assembly.png",      /*epjFile*/
+   "<:ecere>tango/16x16/mimetypes/text-ec-source.png",         /*ecFile*/
+   "<:ecere>tango/16x16/mimetypes/text-eh-header.png",         /*ehFile*/
+   "<:ecere>tango/16x16/mimetypes/text-c-source.png",          /*cFile*/
+   "<:ecere>tango/16x16/mimetypes/text-h-header.png",          /*hFile*/
+   "<:ecere>tango/16x16/mimetypes/text-cpp-source.png",        /*cppFile*/
+   "<:ecere>tango/16x16/mimetypes/text-hpp-header.png",        /*hppFile*/
+   "<:ecere>tango/16x16/mimetypes/text-x-generic.png",         /*textFile*/
+   "<:ecere>tango/16x16/mimetypes/text-html.png",              /*webFile*/
+   "<:ecere>tango/16x16/mimetypes/image-x-generic.png",        /*pictureFile*/
+   "<:ecere>tango/16x16/status/audio-volume-high.png",         /*soundFile*/
+   "<:ecere>tango/16x16/mimetypes/package-x-generic.png",      /*archiveFile*/
+   "<:ecere>tango/16x16/mimetypes/package-x-software.png",     /*packageFile*/
+   "<:ecere>tango/16x16/mimetypes/package-x-optical-disc.png", /*opticalMediaImageFile*/
+
+   "<:ecere>tango/16x16/places/folder.png",
+   "<:ecere>tango/16x16/status/folder-open.png",
+   "<:ecere>tango/16x16/devices/computer.png",
+   "<:ecere>tango/16x16/devices/drive-harddisk.png",
+   "<:ecere>tango/16x16/places/folder-remote.png",
+   "<:ecere>tango/16x16/devices/media-optical.png",
+   "<:ecere>tango/16x16/devices/drive-removable-media.png",
+   "<:ecere>tango/16x16/devices/media-floppy.png",
+   "<:ecere>tango/16x16/places/network-workgroup.png",
+   "<:ecere>tango/16x16/places/network-server.png",
+   "<:ecere>tango/16x16/places/folder-remote.png",
+
+   "<:ecere>tango/16x16/mimetypes/package-x-generic.png",      /*treeLoader*/
+   "<:ecere>tango/16x16/places/start-here.png"                 /*lineNumbers*/
+};
+
+public class FileSystemToolWindow : public Window
+{
+   BitmapResource icons[FileItemType];
+
+   FileSystemToolWindow()
+   {
+      FileItemType c;
+      for(c = 0; c < FileItemType::enumSize; c++)
+      {
+         icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
+         AddResource(icons[c]);
+      }
+   }
+}
+
diff --git a/explorer/src/FileListItem.ec b/explorer/src/FileListItem.ec
new file mode 100644 (file)
index 0000000..7a354c2
--- /dev/null
@@ -0,0 +1,108 @@
+public import "ecere"
+
+import "ExplorerTree"
+
+public class FileListItem : struct
+{
+   char * name;
+   char * info;
+   FileItemType type;
+   int indent;
+
+   void OnDisplay(Surface surface, int x, int y, int width, ExplorerListBox icons, Alignment alignment, DataDisplayFlags displayFlags)
+   {
+      int indentSize = (displayFlags.dropBox) ? 0 : 10;
+      int textOffset;
+      int len;
+      char label[MAX_FILENAME];
+
+      Bitmap icon = icons.icons[type].bitmap;
+      if(!icon)
+      {
+         if(type == folder || type == folderOpen)
+            surface.SetForeground(red); //Color { 170, 170, 0 } // REDJ What is that color?
+         indentSize = 8;
+      }
+      textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
+      
+      if(info)
+         sprintf(label, "%s [%s]", name, info);
+      else
+         strcpy(label, name);
+      len = strlen(label);
+
+      surface.WriteTextDots
+         (alignment, x + textOffset, y + 2, width - textOffset, label, len);
+      if(icon)
+         surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
+   }
+
+   int OnCompare(FileListItem b)
+   {
+      int result;
+      if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
+         result = strcmpi(name, b.name);
+      else
+      {
+         if(type == folder && b.type < folder) result = -1;
+         else if(type < folder && b.type == folder) result = 1;
+      }
+      return result;
+   }
+
+   void OnCopy(FileListItem newData)
+   {
+      type = newData.type;
+      indent = newData.indent;
+      if(newData.name)
+      {
+         int len = strlen(newData.name) + 1;
+         name = new char[len];
+         CopyBytes(name, newData.name, len);
+      }
+   }
+
+   bool OnGetDataFromString(char * string)
+   {
+      int len = strlen(string) + 1;
+      name = new char[len];
+      CopyBytes(name, string, len);
+      return true;
+   }
+
+   void OnFree()
+   {
+      delete name;
+      delete info;
+   }
+
+   char * OnGetString(char * string, void * fieldData, bool * needClass)
+   {
+      return name;
+   }
+};
+
+FileListItem MakeFileListItem(const FileAttribs attribs, const char * name, const char * extension)
+{
+   FileListItem fileListRow { };
+   fileListRow.name = CopyString(name);
+   if(attribs.isDirectory)
+   {
+      fileListRow.type = (attribs.isDrive) ? drive : folder;
+      if(attribs.isServer) fileListRow.type = server;
+      if(attribs.isShare) fileListRow.type = share;
+      if(attribs.isCDROM) fileListRow.type = cdrom;
+      if(attribs.isRemote) fileListRow.type = netDrive;
+      if(attribs.isRemovable) 
+      {
+         if(name[0] == 'A' || name[0] == 'B')
+            fileListRow.type = floppy;
+         else
+            fileListRow.type = removable;
+      }
+   }
+   else
+      fileListRow.type = FileItemType::SelectByExtension(extension);
+   return fileListRow;
+}
+
diff --git a/explorer/src/FileTreeBranch.ec b/explorer/src/FileTreeBranch.ec
new file mode 100644 (file)
index 0000000..3c2ef27
--- /dev/null
@@ -0,0 +1,365 @@
+public import "ecere"
+
+import "ArrayFactoredGrowth"
+import "ArrayBinarySorted"
+
+import "ExplorerTree"
+
+public class FileTreeBranchBSArray : ArrayBinarySorted
+{
+   type = class(FileTreeBranch);
+public:
+   FileTreeBranch * const _;
+   BSloc Add(FileTreeBranch item)
+   {
+      BSloc result = Find(item);
+      if(!result.valid)
+      {
+         Insert(result.pos, 1);
+         _[result.pos] = item;
+      }
+      return result;
+   }
+   BSloc Remove(FileTreeBranch item)
+   {
+      
+   }
+}
+
+public class FileTreeBranchArray : Array
+{
+   type = class(FileTreeBranch);
+public:
+   FileTreeBranch * const _;
+   FileTreeBranch * Add(FileTreeBranch item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   FileTreeBranch * AddBefore(uint position, FileTreeBranch item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class FileTreeBranch : struct
+{
+   FileTreeBranch prev, next;
+
+   bool loaded, childrenLoaded;
+   int indent;
+   char * name;
+   char * info;
+   DataRow row;
+   List children;
+   FileItemType type;
+   FileTreeBranch parent;
+
+   FileStats stats;
+
+   void GetPath(String outputPath)
+   {  
+      FileTreeBranch up;
+      strcpy(outputPath, name);
+      if(parent)
+      {
+         for(up = parent; up; up = up.parent)
+         {
+            char temp[MAX_LOCATION];
+            strcpy(temp, up.name);
+            PathCat(temp, outputPath);
+            strcpy(outputPath, temp);
+         }
+      }
+   }
+
+   bool IsChildOf(FileTreeBranch branch)
+   {
+      FileTreeBranch test;
+      for(test = parent; test; test = test.parent)
+         if(test == branch)
+            return true;
+      return false;
+   }
+
+   void DuplicateChildren(bool recursive, bool forceExpanded, FileTreeBranch addTo, ListBox tree)
+   {
+      if(children.first)
+      {
+         FileTreeBranch child;
+         
+         for(child = children.first; child; child = child.next)
+         {
+            FileTreeBranch copy { };
+            copy.name = CopyString(child.name);
+            copy.type = child.type;
+            AddBranch(copy, child.loaded, false, addTo, tree);
+            if(forceExpanded)
+               copy.row.collapsed = false;
+            if(recursive)
+               child.DuplicateChildren(recursive, forceExpanded, copy, tree);
+         }
+      }
+   }
+   
+   void EnsureVisible(bool expand)
+   {
+      if(parent)
+         parent.EnsureVisible(true);
+      if(expand)
+         row.collapsed = false;
+   }
+
+   void OnFree()
+   {
+   }
+
+   void Free()
+   {
+      FileTreeBranch child;
+      for(; (child = children.first); )
+      {
+         child.Free();
+         children.Delete(child);
+      }
+      //if(name)
+      delete name;
+      delete info;
+   }
+
+   void Delete()
+   {
+      Free();
+      if(parent)
+         parent.children.Delete(this);
+   }
+
+   void OnDisplay(Surface surface, int x, int y, int width, ExplorerListBox icons, Alignment alignment, DataDisplayFlags displayFlags)
+   {
+      //int indentSize = (displayFlags.dropBox) ? 0 : 10;
+      int indent = 16;
+      int xStart;
+      int len;
+      int w, h;
+      //int textOffset;
+      char label[MAX_FILENAME];
+
+      Bitmap icon;
+      if(!this)
+         return;
+      
+      /*Bitmap*/ 
+      icon = icons.icons[type].bitmap;
+      //xStart = indent * indent + x + (icon ? (icon.width + 5) : 0);
+      xStart = x + (icon ? (icon.width + 5) : 0);
+
+      if(!name)
+         return;
+
+      if(info)
+         sprintf(label, "%s [%s]", name, info);
+      else
+         strcpy(label, name);
+      len = strlen(label);
+      
+      if(!icon)
+      {
+         if(type == folder || type == folderOpen)
+            surface.SetForeground(yellow);
+         //indentSize = 8;
+         indent = 8;
+      }
+      //textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
+      
+      surface.TextOpacity(false);
+      surface.TextExtent(label, len, &w, &h);
+      h = Max(h, 16);
+    
+      // Draw the current row stipple
+      if(displayFlags.selected)
+         //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
+         //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
+         surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
+      
+      //surface.WriteTextDots(alignment, x + textOffset, y + 2, width - textOffset, name, strlen(name));
+      surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
+
+      if(!guiApp.textMode)
+      {
+         if(displayFlags.current)
+         {
+            if(displayFlags.active)
+            {
+               surface.LineStipple(0x5555);
+               if(displayFlags.selected)
+                  surface.SetForeground(0xFFFFFF80);
+               else
+                  surface.SetForeground(black);
+            }
+            else
+            {
+               surface.SetForeground(selectionColor);
+            }
+            surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
+            surface.LineStipple(0);
+         }
+
+         if(icon)
+         {
+            //surface.blend = true;
+            //surface.alphaWrite = blend;
+            surface.SetForeground(white);
+            //surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
+            surface.Blit(icon, x,y,0,0, icon.width, icon.height);
+         }
+      }
+   }
+
+   int OnCompare(FileTreeBranch b)
+   {
+      int result;
+      if(type == b.type || (type < folder && b.type < folder) || (type >= drive))
+         result = strcmpi(name, b.name);
+      else
+      {
+         if(type == folder && b.type < folder) result = -1;
+         else if(type < folder && b.type == folder) result = 1;
+      }
+      return result;
+   }
+
+   char * OnGetString(char * tempString, FileSystemToolWindow fileSysToolWnd, bool * needClass)
+   {
+      return name ? name : "";
+   }
+};
+
+FileTreeBranch MakeFileTreeBranch(const FileStats stats, const char * name)
+{
+   FileTreeBranch fileTreeBranch { stats = stats };
+   fileTreeBranch.name = CopyString(name);
+   if(!fileTreeBranch.name)
+      fileTreeBranch.name = null;
+   if(stats.attribs.isDirectory)
+   {
+      fileTreeBranch.type = (stats.attribs.isDrive) ? drive : folder;
+      if(stats.attribs.isServer) fileTreeBranch.type = server;
+      if(stats.attribs.isShare) fileTreeBranch.type = share;
+      if(stats.attribs.isCDROM) fileTreeBranch.type = cdrom;
+      if(stats.attribs.isRemote) fileTreeBranch.type = netDrive;
+      if(stats.attribs.isRemovable) 
+      {
+         if(name[0] == 'A' || name[0] == 'B')
+            fileTreeBranch.type = floppy;
+         else
+            fileTreeBranch.type = removable;
+      }
+   }
+   else
+   {
+      char extension[MAX_EXTENSION];
+      GetExtension(fileTreeBranch.name, extension);
+      fileTreeBranch.type = FileItemType::SelectByExtension(extension);
+   }
+   return fileTreeBranch;
+}
+
+void AddBranch(FileTreeBranch branch, bool loaded, bool addLoader, FileTreeBranch addTo, ListBox tree)
+{
+   DataRow row = (addTo && addTo.row) ? addTo.row.AddRow() : tree.AddRow();
+   if(addTo)
+   {
+      branch.parent = addTo;
+      branch.indent = addTo.indent + 1;
+      addTo.children.Add(branch);
+   }
+   row.tag = (int)branch;
+   branch.row = row;
+   row.SetData(null, branch);
+
+   branch.loaded = loaded;
+   if(addLoader)
+      //AddBranch(FileTreeBranch { }, false, false, branch, tree); // why would this create a compile error?
+      AddBranch(FileTreeBranch { type = none }, false, false, branch, tree);
+
+   if(branch.indent > 0)
+      row.collapsed = true;
+   else if(branch.type == folder)
+      branch.type = folderOpen;
+}
+
+void BranchLoad(FileTreeBranch branch, ListBox tree)
+{
+   if(!branch.loaded)
+   {
+      char path[MAX_LOCATION];
+      branch.GetPath(path);
+      {
+         FileListing listing { path };
+         if(branch.children.count == 1)
+         DeleteBranch(branch.children.first, tree);
+
+         while(listing.Find())
+         {
+            if(listing.stats.attribs.isDirectory)
+            {
+               FileTreeBranch child = MakeFileTreeBranch(listing.stats, listing.name);
+               AddBranch(child, true, false, branch, tree);
+               BranchChildLoad(child, branch, tree);
+            }
+         }
+      }
+      branch.childrenLoaded = true;
+      branch.loaded = true;
+   }
+   else if(!branch.childrenLoaded)
+   {
+      FileTreeBranch child;
+      if(branch.children.first)
+      {
+         for(child = branch.children.first; child; child = child.next)
+         {
+            if(!child.loaded)
+               BranchLoad(child, tree);
+            else if(!child.childrenLoaded)
+               BranchChildLoad(child, branch, tree);
+         }
+         branch.childrenLoaded = true;
+      }
+   }
+}
+
+static void BranchChildLoad(FileTreeBranch parent, FileTreeBranch branch, ListBox tree)
+{
+   char path[MAX_LOCATION];
+   parent.GetPath(path);
+   {
+      bool added = false;
+      FileListing listing { path };
+      while(listing.Find())
+      {
+         if(listing.stats.attribs.isDirectory)
+         {
+            FileTreeBranch child = MakeFileTreeBranch(listing.stats, listing.name);
+            AddBranch(child, true, false, parent, tree);
+            added = true;
+         }
+      }
+      if(!added)
+         added = true;
+   }
+   //parent.childrenLoaded = true;
+}
+
+void DeleteBranch(FileTreeBranch branch, ListBox tree)
+{
+   FileTreeBranch child;
+   for(; (child = branch.children.first); )
+      DeleteBranch(child, tree);
+   tree.DeleteRow(branch.row);
+   branch.Delete();
+}
diff --git a/explorer/src/Finder.ec b/explorer/src/Finder.ec
new file mode 100644 (file)
index 0000000..c65367f
--- /dev/null
@@ -0,0 +1,300 @@
+public import "ecere"
+
+import "ExplorerTree"
+import "Panels"
+import "DeleteBox"
+
+class ExplorerSearch : Window
+{
+   hasHorzScroll = false;
+   hasVertScroll = true;
+
+   background = activeBorder;
+
+   //menu = Menu { };
+
+   ExplorerSearchViewTree view;
+
+   SearchThread searchThread { searchPanel = this };
+
+public:
+
+   void OptTree(bool on)
+   {
+      view.results.Clear();
+      view.results.ClearFields();
+      view.results.AddField(DataField { editable = true });
+      if(on)
+         view.results.AddField(view.resultsFieldPath);
+   }
+
+   void SearchStart()
+   {
+      char text[2048];
+      
+      searchThread.active = true;
+
+      searchThread.optionSubdirs = options.subdirs.checked;
+      searchThread.optionTree = (options.subdirs.checked && options.tree.checked);
+      searchThread.optionBrowser = (options.subdirs.checked && options.browser.checked);
+      searchThread.optionNameMatchCase = findName.optionMatchCase.checked;
+      searchThread.optionNameMatchWord = findName.optionMatchWord.checked;
+      searchThread.optionContentMatchCase = findTextContent.optionMatchCase.checked;
+      searchThread.optionContentMatchWord = findTextContent.optionMatchWord.checked;
+
+      strcpy(searchThread.location, location.GetText());
+      strcpy(searchThread.nameSearch, findName.GetText());
+      strcpy(searchThread.contentSearch, findTextContent.GetText());
+      
+      actions.startStop.text = "Stop Search";
+      actions.clear.disabled = false;
+      view.results.Clear();
+      view.results.hasHeader = !searchThread.optionTree;
+      view.results.treeBranches = searchThread.optionTree;
+      view.browser.Clear();
+      ToggleBrowserDisplay(searchThread.optionBrowser);
+
+      view.browser.text = "Browser";
+      sprintf(text, "Search Results (Searching %s)", location.GetText());
+      view.results.text = text;
+      
+      searchThread.Create();
+   }
+
+   bool SearchStop()
+   {
+      if(searchThread.active)
+      {
+         searchThread.terminate = true;
+         app.Unlock();
+            searchThread.Wait();
+         app.Lock();
+         return true;
+      }
+      return false;
+   }
+
+   void SearchUpdateLabel(char * path)
+   {
+      char text[2048];
+      sprintf(text, "Search Results (Searching %s)", path);
+      view.results.text = text;
+   }
+
+   void AddResult(ExplorerFileBranch branch, ExplorerFileBranch addTo)
+   {
+      AddBranch(branch, true, false, addTo, view.results);
+   }
+
+   void SortResults()
+   {
+      //view.results.Sort(resultsNameField, 1);
+   }
+
+   void AddBrowse(ExplorerFileBranch branch, ExplorerFileBranch addTo)
+   {
+      AddBranch(branch, true, false, addTo, view.browser);
+   }
+
+   void SortBrowser()
+   {
+      //view.browser.Sort(browserNameField, 1);
+   }
+
+   void SearchTerminate()
+   {
+      char text[1024];
+      
+      if(searchThread.terminate)
+         sprintf(text, "Search Results (%d item(s) found), Search was aborted.", searchThread.matchCount);
+      else
+         sprintf(text, "Search Results (%d item(s) found), Search completed successfuly.", searchThread.matchCount);
+      view.results.text = text;
+      sprintf(text, "Browser (%d item(s) searched)", searchThread.count);
+      view.browser.text = text;
+         
+      actions.startStop.text = "Start Search";
+   }
+
+   void ListDirectory(DataRow addTo, char * path)
+   {
+      FileListing listing { path };
+      while(listing.Find())
+      {
+         DataRow row = addTo.AddString(listing.name);
+         row.collapsed = true;
+         if(listing.stats.attribs.isDirectory)
+            ListDirectory(row, listing.path);
+      }
+   }
+
+   void ToggleBrowserDisplay(bool visible)
+   {
+      //view.labelBrowser.visible = visible;
+      view.browser.visible = visible;
+      if(visible)
+         view.results.anchor.bottom = 304;
+      else
+         view.results.anchor.bottom = 4;
+   }
+
+   PanelFileName findName
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = 0, right = 4 };
+   };
+   /*PanelFileSize findSize
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = (int)(findName.position.y + findName.size.h + 4), right = 4 };
+   };*/
+   PanelFileTextContent findTextContent
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = (int)(findName.position.y + findName.size.h + 4), right = 4 };
+   };
+   PanelLocation location
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = (int)(findTextContent.position.y + findTextContent.size.h + 4), right = 4 };
+#ifdef _DEBUG
+      editBox.contents = "D:\\Projects\\Ecere";
+#endif
+   };
+   PanelOptions options
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = (int)(location.position.y + location.size.h + 4), right = 4 };
+   };
+   PanelActions actions
+   {
+      this, master;
+      anchor = Anchor { left = 4, top = (int)(options.position.y + options.size.h + 4), right = 4 };
+   };
+
+   ExplorerSearch()
+   {
+   }
+
+   void OnDestroy()
+   {
+      SearchStop();
+   }
+
+}
+
+class ExplorerSearchView : ExplorerView
+{
+   virtual void AddItem(const FileStats stats, const char * name, bool match);
+}
+
+class ExplorerSearchViewTree : ExplorerSearchView
+{
+   
+   DataField resultsFieldPath { "char *" };
+
+   ListBox results
+   {
+      this, master;
+
+      borderStyle = none; // deep
+      hasHorzScroll = true;
+      hasVertScroll = true;
+      fullRowSelect = false;
+      treeBranches = true;
+      collapseControl = true;
+      rootCollapseButton = true;
+      multiSelect = true;
+
+      size = Size { 600, 200 };
+      //anchor = Anchor { left = 0, top = 0, right = 0, bottom = 0 };
+      //anchor = Anchor { left = 8, top = 24, right = 4, bottom = 304 };
+      anchor = Anchor { left = 0, top = 0, right = 0, bottom = 304 };
+      text = "Search Results", hotKey = Key { r, alt = true };
+
+      /*hasHeader = true, moveFields = true, resizable = true, sortable = true;*/
+
+      NotifyKeyHit = CommonNotifyKeyHit;
+      NotifyDoubleClick = Lists_NotifyDoubleClick;
+   };
+   DataField resultsNameField { header = "Name", dataType = "ExplorerFileBranch", width = 304, userData = this };
+   ListBox browser
+   {
+      this, master;
+
+      borderStyle = none; // deep
+      hasHorzScroll = true;
+      hasVertScroll = true;
+      fullRowSelect = false;
+      treeBranches = true;
+      collapseControl = true;
+      rootCollapseButton = true;
+      multiSelect = true;
+
+      size = Size { 624, 268 };
+      anchor = Anchor { left = 8, right = 4, bottom = 4 };
+      text = "Browser", hotKey = Key { e, alt = true };
+
+      /*hasHeader = true, moveFields = true, resizable = true, sortable = true;*/
+
+      NotifyKeyHit = CommonNotifyKeyHit;
+      NotifyDoubleClick = Lists_NotifyDoubleClick;
+   };
+   DataField browserNameField { header = "Name", dataType = "ExplorerFileBranch", width = 304, userData = this }; // editable = true
+
+   bool CommonNotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch)
+   {
+      if((SmartKey)key == del)
+      {
+         //DeleteBox { this, source = listBox }.Modal();
+         /*
+         if(MessageBox { finderWnd, text = "text", contents = "contents", type = yesNo }.Modal() == yes)
+         {
+            OldList selection;
+            Link item;
+            
+            listBox.GetMultiSelection(selection);
+            
+            for(item = selection.first; item; item = item.next)
+            {
+               char path[MAX_LOCATION];
+               DataRow row = item.data;
+               ExplorerFileBranch branch = (ExplorerFileBranch)row.tag;
+               branch.GetPath(path);
+               if(FileExists(path))
+                  DeleteFile(path);
+               if(!FileExists(path))
+                  DeleteBranch(branch, listBox);
+            }
+            selection.Free(null);
+         }
+         */
+      }
+      return true;
+   }
+
+   bool Lists_NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
+   {  
+      if(listBox.currentRow)
+      {
+         char path[MAX_LOCATION];
+         ExplorerFileBranch branch = (ExplorerFileBranch)listBox.currentRow.tag;
+         //if(listBox != view.results || searchThread.tree)
+         //   GetRowPath(listBox.currentRow, path);
+         //else
+         //   strcpy(path, *(char **)listBox.currentRow.GetData(resultsFieldPath));
+         branch.GetPath(path);
+         ShellOpen(path);
+         return false;
+      }
+      return true;
+   }
+
+   ExplorerSearchViewTree()
+   {
+      results.AddField(resultsNameField);
+      browser.AddField(browserNameField);
+   }
+
+}
+
diff --git a/explorer/src/NotificationBox.ec b/explorer/src/NotificationBox.ec
new file mode 100644 (file)
index 0000000..47d3339
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+public import "ecere"
+
+static class MsgLine : struct
+{
+   MsgLine prev, next;
+   char * string;
+   int len;
+};
+
+public class NotificationBox : Window
+{
+   background = activeBorder;
+   borderStyle = contour;
+   //hasClose = true;
+   //tabCycle = true;
+   
+   AutoDestroyThread autoDestroy { box = this };
+
+public:
+   property char * contents
+   {
+      set
+      {
+         FreeLines();
+         if(value)
+         {
+            int len = strlen(value);
+            int start = 0;
+            int c;
+            for(c = 0; c <= len; c++)
+            {
+               if(c == len || value[c] == '\n')
+               {
+                  MsgLine line { };
+                  lines.Add(line);
+                  if(line)
+                  {
+                     line.len = c - start;
+                     line.string = new char[line.len+1];
+                     CopyBytes(line.string, value + start, line.len);
+                     line.string[line.len] = '\0';
+                     start = c+1;
+                  }
+               }
+            }
+         }
+      }
+   };
+   Seconds delay;
+   GuiApplication app;
+   Window reactivate;
+
+private:
+   OldList lines;
+   int totalWidth, totalHeight, lineHeight;
+
+   ~NotificationBox()
+   {
+      FreeLines();
+   }
+
+   */
+   /*
+   bool ButtonActivate(Window control, bool active, Window previous)
+   {
+      control.isDefault = true;
+      return true;
+   }
+   */
+   /*
+
+   bool OnPostCreate()
+   {
+      if(!delay)
+         autoDestroy.delay = 1;
+      else
+         autoDestroy.delay = delay;
+      autoDestroy.app = app;
+      autoDestroy.Create();
+      return true;
+   }
+
+   */
+   /*bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
+   {
+      if(reactivate)
+         reactivate.Activate();
+      return true;
+   }*/
+   /*
+
+   bool OnResizing(int *w, int *h)
+   {
+      *w = Max(*w, Max(totalWidth, 144) + 24);
+
+      *h = Max(*h, Max(totalHeight, 33));// + 40);
+      return true;
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      MsgLine line;
+      //int y = (clientSize.h - 33 - totalHeight) / 2;
+      int y = (clientSize.h - totalHeight) / 2;
+      for(line = lines.first; line; line = line.next)
+      {
+         surface.WriteText((clientSize.w - totalWidth) / 2, y, line.string, line.len);
+         y += lineHeight;
+      }
+   }
+
+   bool OnLoadGraphics()
+   {
+      MsgLine line;
+
+      totalHeight = 0;
+      for(line = lines.first; line; line = line.next)
+      {
+         Size size;
+         if(!line.string[0])
+            display.FontExtent(fontObject, " ", 1, (int *)&size.w, (int *)&size.h);
+         else
+            display.FontExtent(fontObject, line.string, strlen(line.string), (int *)&size.w, (int *)&size.h);
+         if(size.h)
+            lineHeight = size.h;
+         totalWidth = Max(totalWidth, size.w);
+         totalHeight += size.h;
+      }
+      return true;
+   }
+
+   void FreeLines()
+   {
+      MsgLine line;
+      for(line = lines.first; line; line = line.next)
+         delete line.string;
+      lines.Free(null);
+   }
+};
+
+class AutoDestroyThread : Thread
+{
+   GuiApplication app;
+   NotificationBox box;
+   Seconds delay;
+
+   unsigned int Main()
+   {
+      int c;
+      int r = 5;
+      int w, h;
+      Sleep(delay);
+      app.Lock();
+         w = box.size.w;
+         h = box.size.h;
+      app.Unlock();
+      for(c = 0; c < r; c--)
+      {
+         //int vw = w / r;
+         int vh = h / r / 2 - c * h / (r * 2);
+         app.Lock();
+            //box.position.x += vw / 2;
+            box.position.y += vh / 2;
+            //box.size.w -= vw;
+            box.size.h -= vh;
+            box.Update(null);
+         app.Unlock();
+         Sleep(0.5 / r);
+      }
+      app.Lock();
+         box.Destroy(0);
+      app.Unlock();
+      return 0;
+   }
+}
+
+*/
diff --git a/explorer/src/Panels.ec b/explorer/src/Panels.ec
new file mode 100644 (file)
index 0000000..9e5df0e
--- /dev/null
@@ -0,0 +1,386 @@
+import "Explorer"
+//import "ecere"
+//import "Finder"
+
+class Panel : Label
+{
+   //background = activeBorder;
+   size = { 200, 40 };
+   isGroupBox = true;
+   //background = activeBorder;
+
+   bool OnMoving(int * x, int * y, int w, int h)
+   {
+      //if(((ExplorerWindow)master).userMode)
+      //   return false;
+      return true; 
+   }
+}
+
+class PanelText : Panel
+{
+   // IDEA: Would it be totally unreasonable for a group box to use client size
+   //       to set the actual size considering the size of the border?
+   //       Also, to have anchors be relative to the client size?
+   clientSize = { 200, 40 };
+   size = { 200, 64 };
+   //background = activeBorder;
+   
+   EditBox editBox
+   {
+      this;
+      //borderStyle = none;
+      size = { 160, 20 };
+      anchor = { left = 8, top = 16, right = 8 };
+   };
+   
+   char * GetText()
+   {
+      return editBox.contents;
+   }
+}
+
+class PanelTextMatch : PanelText
+{
+   clientSize = { 200, 40 };
+   size = { 200, 64 };
+
+   Button optionMatchCase
+   {
+      this;
+      //toggle = true, inactive = true;
+      isCheckbox = true;
+      text = "Match Case", hotKey = Key { c, alt = true };
+      //size = { 40, 18 };
+      //anchor = { left = 4, top = 40, right = 0.508f };
+      anchor = { top = 38, right = 8 };
+   };
+   Button optionMatchWord
+   {
+      this;
+      //toggle = true, inactive = true;
+      isCheckbox = true;
+      text = "Match Whole Word", hotKey = Key { w, alt = true };
+      //size = { 40, 18 };
+      position = Point { y = 62 };
+      //anchor = { left = 0.508f, top = 40, right = 4 };
+      anchor = { top = 38, right = 8 };
+   };
+
+   bool OnPostCreate()
+   {
+      optionMatchCase.anchor.right = (int)optionMatchWord.size.w + 8 + 2;
+      return true;
+   }
+
+   /*void OnResize(int width, int height)
+   {
+      optionMatchCase.anchor.right = (int)optionMatchWord.size.w + 8 + 2;
+   }*/
+}
+
+class PanelFileName : PanelTextMatch
+{
+   clientSize = { 200, 40 };
+   size = { 200, 64 };
+   text = "File Name";
+   hotKey = Key { n, alt = true };
+
+#ifdef _DEBUG
+   editBox.contents = "Ecere";
+#endif
+}
+
+class PanelFileSize : Panel
+{
+   clientSize = { 200, 40 };
+   size = { 200, 64 };
+
+   text = "File Size";
+   hotKey = Key { s, alt = true };
+   
+   uint compare;
+
+   EditBox editBox 
+   {      
+      this;
+      //borderStyle = none;
+      contents = "1024";
+      size = { 102, 18 };
+      anchor = { left = 8, top = 16, right = 130 };
+
+      bool NotifyModified(EditBox editBox)
+      {
+         UpdateCompare();
+         return true;
+      }
+   };
+   Button optionGB
+   {
+      this;
+      toggle = true, inactive = true;
+      text = "GB";
+      size = { 30, 18 };
+      anchor = { top = 16, right = 98 };
+      NotifyClicked = optionScale_NotifyClicked;
+   };
+   Button optionMB
+   {
+      this;
+      toggle = true, inactive = true;
+      text = "MB";
+      size = { 30, 18 };
+      anchor = { top = 16, right = 66 };
+      NotifyClicked = optionScale_NotifyClicked;
+   };
+   Button optionKB
+   {
+      this;
+      toggle = true, checked = true, inactive = true;
+      text = "KB";
+      size = { 30, 18 };
+      anchor = { top = 16, right = 34 };
+      NotifyClicked = optionScale_NotifyClicked;
+   };
+   Button optionB
+   {
+      this;
+      toggle = true, inactive = true;
+      text = "B";
+      size = { 30, 18 };
+      anchor = { top = 16, right = 4 };
+      NotifyClicked = optionScale_NotifyClicked;
+   };
+
+   Button optionSmaller
+   {
+      this;
+      toggle = true, inactive = true;
+      text = "Smaller";
+      size = { 74, 18 };
+      anchor = { left = 4, top = 41, right = 0.67f };
+      NotifyClicked = optionCompare_NotifyClicked
+   };
+   Button optionEqual
+   {
+      this;
+      toggle = true, inactive = true;
+      text = "Equal";
+      size = { 74, 18 };
+      anchor = { left = 0.34f, top = 41, right = 0.34f };
+      NotifyClicked = optionCompare_NotifyClicked;
+   };
+   Button optionGreater
+   {
+      this;
+      toggle = true, checked = true, inactive = true;
+      text = "Greater";
+      size = { 74, 18 };
+      anchor = { left = 0.67f, top = 41, right = 4 };
+      NotifyClicked = optionCompare_NotifyClicked;
+   };
+
+   bool optionScale_NotifyClicked(Button button, int x, int y, Modifiers mods)
+   {
+      optionGB.checked = (button == optionGB);
+      optionMB.checked = (button == optionMB);
+      optionKB.checked = (button == optionKB);
+      optionB.checked = (button == optionB);
+      UpdateCompare();
+      return true;
+   }
+
+   bool optionCompare_NotifyClicked(Button button, int x, int y, Modifiers mods)
+   {
+      optionSmaller.checked = (button == optionSmaller);
+      optionEqual.checked = (button == optionEqual);
+      optionGreater.checked = (button == optionGreater);
+      return true;
+   }
+
+   bool Match(uint size)
+   {
+      if(optionSmaller.checked)
+         return size < compare;
+      else if(optionEqual.checked)
+         return size = compare;
+      else if(optionGreater.checked)
+         return size > compare;
+      else
+         return false;
+   }
+
+   void UpdateCompare()
+   {
+      if(optionGB.checked)
+         compare = atoi(editBox.contents) * 1024 * 1024 * 1024;
+      else if(optionMB.checked)
+         compare = atoi(editBox.contents) * 1024 * 1024;
+      else if(optionKB.checked)
+         compare = atoi(editBox.contents) * 1024;
+      else if(optionB.checked)
+         compare = atoi(editBox.contents);
+   }
+
+}
+
+class PanelFileTextContent : PanelTextMatch
+{
+   clientSize = { 200, 40 };
+   size = { 200, 64 };
+   text = "Text Content";
+   hotKey = Key { x, alt = true };
+}
+
+class PanelLocation : Panel
+{
+   clientSize = { 200, 40 };
+   size = { 200, 44 };
+   text = "Location";
+   hotKey = Key { l, alt = true };
+
+   FileDialog fileDialog
+   {
+      master, master;
+      type = selectDir;
+      text = "Select Search Location...";
+   };
+
+   EditBox editBox
+   {
+      this;
+      //borderStyle = none;
+      contents = "/";
+      size = { 160, 20 };
+      anchor = { left = 8, top = 16, right = 2 + 24 + 8 };
+   };
+   
+   ToolButton browse
+   {
+      this;
+      anchor = { top = 14, right = 8 };
+      toolId = ExplorerToolId::browse;
+      
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         fileDialog.currentDirectory = editBox.contents;
+         if(fileDialog.Modal() == ok)
+            editBox.contents = fileDialog.filePath;
+         return true;
+      }
+   };
+
+   /*Button buttonBrowse
+   {
+      this;
+      text = "Browse", hotKey = Key { b, alt = true };
+      size = { 160, 29 };
+      anchor = { left = 8, top = 41, right = 8 };
+
+   };*/
+
+   char * GetText()
+   {
+      return editBox.contents;
+   }
+}
+
+class PanelOptions : Panel 
+{
+   clientSize = { 200, 60 };
+   size = { 200, 80 };
+   text = "Options";
+   hotKey = Key { o, alt = true };
+   
+   Button subdirs
+   {
+      this;
+      toggle = true, checked = true;
+      text = "Search Sub Directories", hotKey = Key { d, alt = true };
+      size = { 160, 18 };
+      anchor = { left = 8, top = 16, right = 8 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         if(button.checked)
+         {
+            tree.disabled = false;
+            browser.disabled = false;
+         }
+         else
+         {
+            // I need a way to show this unchecked while keeping it original checked value
+            tree.disabled = true;
+            browser.disabled = true;
+         }
+         return true;
+      }
+   };
+   Button tree
+   {
+      this;
+      toggle = true, checked = true, inactive = true;
+      text = "Display Results With Tree", hotKey = Key { t, alt = true };
+      size = { 160, 18 };
+      anchor = { left = 8, top = 40, right = 8 };
+   
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         ExplorerSearch search = (ExplorerSearch)parent;
+         search.OptTree(button.checked);
+         return true;
+      }
+   };
+   Button browser
+   {
+      this;
+      toggle = true, checked = true, inactive = true;
+      text = "Show Browser", hotKey = Key { h, alt = true };
+      size = { 160, 18 };
+      anchor = { left = 8, top = 60, right = 8 };
+   };
+}
+
+class PanelActions : Panel 
+{
+   clientSize = { 200, 38 };
+   size = { 200, 48 };
+   text = "Actions";
+   hotKey = Key { o, alt = true };
+   
+   Button startStop
+   {
+      this;
+      isDefault = true;
+      text = "Start Search", hotKey = Key { s, alt = true };
+      size = { 80, 26 };
+      anchor = { top = 16, right = 92 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         ExplorerSearch search = (ExplorerSearch)parent;
+         //--//if(!finderWnd.SearchStop())
+            search.SearchStart();
+         return true;
+      }
+   };
+   Button clear
+   {
+      this;
+      disabled = true;
+      text = "Clear Results", hotKey = Key { c, alt = true };
+      size = { 80, 26 };
+      anchor = { top = 16, right = 8 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         ExplorerSearch search = (ExplorerSearch)parent;
+         search.SearchStop();
+         search.view.results.Clear();
+         search.view.browser.Clear();
+         button.disabled = true;
+         return true;
+      }
+   };
+}
+
diff --git a/explorer/src/Search.ec b/explorer/src/Search.ec
new file mode 100644 (file)
index 0000000..df805b0
--- /dev/null
@@ -0,0 +1,347 @@
+import "ExplorerTree"
+import "Finder"
+
+class NumberLink : struct
+{
+   NumberLink prev, next;
+   int num;
+};
+
+struct SearchStackFrame
+{
+   int tag;
+   char path[MAX_LOCATION];
+   FileListing listing;
+   
+   bool branched;
+   //DataRow result;
+   //DataRow browse;
+   ExplorerFileBranch result;
+   ExplorerFileBranch browse;
+};
+
+class SearchThread : Thread
+{
+   bool active, terminate, hasNameSearch, hasSizeSearch, hasContentSearch, listLines;
+   int count, matchCount;
+   char location[MAX_LOCATION], nameSearch[1024], contentSearch[1024];
+   bool optionTree, optionBrowser, optionSubdirs;
+   bool optionNameMatchCase, optionNameMatchWord;
+   bool optionContentMatchCase, optionContentMatchWord;
+   OldList lines;
+   //DataField resultsNameField;
+   //DataField browserNameField;
+
+   ExplorerSearch searchPanel;
+
+   SearchThread()
+   {
+      active = false;
+      terminate = false;
+   }
+
+   bool SearchFileContent(String path)
+   {
+      bool match = false;
+      File file = FileOpen(path, read);
+      if(file)
+      {
+         //
+         //   char line[65536];
+         //   char * find = null;
+         //   for( ; !find && file.GetLine(line, 65536) ; )
+         //      find = SearchString(line, 0, contentSearch, optionContentMatchCase, optionContentMatchWord);
+         //   //   find = strstr(line, contentSearch);
+         //
+
+         uint readCount = 16383;
+         char * find = null;
+         char buffer[16384];
+         int seekBack = 0 - strlen(contentSearch) - 2;
+         if(listLines)
+         {
+            uint line = 1;
+            while(readCount == 16383)
+            {
+               uint start = 0, len = 0;
+               readCount = file.Read(buffer, 1, 16383);
+               buffer[readCount] = '\0';
+               for( ; start < readCount; start += len ? len : 1)
+               {
+                  if( (len = strlen(&buffer[start])) )
+                  {
+                     char * newLine = buffer;
+                     uint bstart;
+                     find = SearchString(buffer, start, contentSearch, optionContentMatchCase, optionContentMatchWord);
+
+                     // todo add a maximum line length possibility as param
+                     for(bstart = start; 
+                           bstart < readCount && newLine && (!find || newLine < find); 
+                              bstart += (newLine - &buffer[bstart]) / sizeof(char))
+                     {
+                        newLine = strstr(&buffer[bstart], "\n");
+                        newLine++;
+                        if(bstart + (newLine - &buffer[bstart]) / sizeof(char) < readCount && newLine && (!find || newLine < find))
+                           line++;
+                     }
+
+                     if(find)
+                     {
+                        len = (find - &buffer[start]) / sizeof(char) + strlen(contentSearch);
+                        match = true;
+                        if(!lines.first || ((NumberLink)lines.last).num != line)
+                           lines.Add(NumberLink { num = line });
+                     }
+
+                  }
+               }
+               file.Seek(seekBack, current);
+            }
+         }
+         else
+         {
+            for( ; !find && readCount == 16383; )
+            {
+               uint start = 0, len = 0;
+               readCount = file.Read(buffer, 1, 16383);
+               buffer[readCount] = '\0';
+               for( ; !find && start < readCount; start += len + 1)
+                  if( (len = strlen(&buffer[start])) )
+                     find = SearchString(buffer, start, contentSearch, optionContentMatchCase, optionContentMatchWord);
+               file.Seek(seekBack, current);
+            }
+            match = (bool)find;
+         }
+         delete file;
+      }
+      return match;
+   }
+   
+   // I wonder if this is optimized at the c level to be compiled inline 
+   // and to use in-place in-stack memory for both the return value and the parameters
+   // if not, c should be more optimized...
+   // would the const have any impact on optimization?
+   unsigned int Main()
+   {
+      bool match;
+      int frame, stackTop = 0, treeTop = 0;
+      double lastTime = GetTime();
+      SearchStackFrame stack[1024];
+      //to be used for content replace... EditBox edit { multiLine = true, textHorzScroll = true, textVertScroll = true, maxLineSize = 65536 };
+
+      ExplorerFileBranch fileTreeBranch;
+      // This won't give drive attribs for c: or c:\ and other drives as well
+      // \\Nateus\data\ is not remote, etc...
+      // How to?
+      FileStats stats;
+      
+      terminate = false;
+      count = 0;
+      matchCount = 0;
+      
+      hasNameSearch = (strlen(nameSearch) != 0);
+      hasSizeSearch = false;  // this is temporary
+      hasContentSearch = (strlen(contentSearch) != 0);
+
+      listLines = true;
+      lines = OldList { };
+      
+      //SearchDir(location);
+
+      FileGetStats(location, stats);
+
+      strcpy(stack[0].path, location);
+      stack[0].listing = FileListing { stack[0].path };  // there should be a sorted = true/false 
+                                                         // Binary Search sorting...
+      if(optionTree)
+         stack[0].branched = false;
+      if(optionBrowser)
+      {
+         app.Lock();
+         {
+            //stack[0].browse = searchPanel.AddBrowserRow();
+            //fileTreeBranch = MakeFileBranch(attribs, stack[0].path);
+            //stack[0].browse.SetData(browserNameField, fileTreeBranch);
+            //stack[0].browse.SetData(typeField, null);
+            //stack[0].browse.SetData(sizeField, null);
+
+            stack[0].browse = MakeFileBranch(stats, stack[0].path);
+            searchPanel.AddBrowse(stack[0].browse, null);
+         }
+         app.Unlock();
+      }
+      
+      for(frame = 0; frame >= 0 && !terminate; )
+      {
+         if(stack[frame].listing.Find())
+         {
+            count++;
+            
+            //match = (strcmp(stack[frame].listing.name, nameSearch) == 0);
+            //match = (stack[frame].listing.name[0] == nameSearch[0]);
+            //match = (bool)strstr(stack[frame].listing.name, nameSearch);
+               
+            if(hasNameSearch)
+               match = (bool)SearchString(stack[frame].listing.name, 0, nameSearch, optionNameMatchCase, optionNameMatchWord);
+            else
+               match = true;
+
+            if(match && hasContentSearch && !stack[frame].listing.stats.attribs.isDirectory)
+               if(!SearchFileContent(stack[frame].listing.path))
+                  match = false;
+
+            if(match)
+            {
+               matchCount++;
+               if(optionTree)
+               {
+                  for(frame = treeTop; frame <= stackTop; frame++)
+                  {
+                     if(!stack[frame].branched)
+                     {
+                        app.Lock();
+                           if(frame)
+                           {
+                              stack[frame].result = MakeFileBranch(stack[frame - 1].listing.stats, stack[frame - 1].listing.name);
+                              searchPanel.AddResult(stack[frame].result, stack[frame - 1].result);
+                              stack[frame].result.row.collapsed = false;
+                           }
+                           else
+                           {
+                              stack[0].result = MakeFileBranch(stats, stack[0].path);
+                              searchPanel.AddResult(stack[0].result, null);
+                              stack[0].result.row.collapsed = false;
+                           }
+                           stack[frame].branched = true;
+                        app.Unlock();
+                     }
+                  }
+                  treeTop = stackTop;
+                  frame--;
+               }
+            }
+            if(optionSubdirs && stack[frame].listing.stats.attribs.isDirectory)
+            {
+               app.Lock();
+                  {
+                     double thisTime = GetTime();
+                     if(thisTime - lastTime > 0.25)
+                     {
+                        searchPanel.SearchUpdateLabel(stack[stackTop].listing.path);
+                        lastTime = thisTime;
+                     }
+                  }
+                  //searchPanel.SearchUpdateLabel(stack[stackTop].listing.path);
+                  frame++;
+                  if(optionBrowser)
+                  {
+                     stack[frame].browse = MakeFileBranch(stack[stackTop].listing.stats, stack[stackTop].listing.name);
+                     searchPanel.AddBrowse(stack[frame].browse, stack[stackTop].browse);
+
+                     if(frame)
+                        stack[frame].browse.row.collapsed = true;
+                     //if(frame == 1)
+                        //searchPanel.SortBrowser();  // this can be very bad for performance in some situations
+                                                // there should be a way to sort the nodes as they are added
+                                                // BinarySearch sorting implementation in listBox's Sort?
+                  }
+               app.Unlock();
+               strcpy(stack[frame].path, stack[stackTop].listing.path);
+               stack[frame].listing = FileListing { stack[frame].path };
+               if(optionTree)
+                  stack[frame].branched = false;
+               if(match)
+               {
+                  app.Lock();
+                     if(optionTree)
+                     {
+                        stack[frame].result = MakeFileBranch(stack[stackTop].listing.stats, stack[stackTop].listing.name);
+                        searchPanel.AddResult(stack[frame].result, stack[stackTop].result);
+                        stack[frame].result.row.collapsed = false;
+                        //searchPanel.SortResults();  // this can be very bad for performance in some situations
+                                                // there should be a way to sort the nodes as they are added
+                                                // BinarySearch sorting implementation in listBox's Sort?
+                        stack[frame].branched = true;
+                     }
+                     else
+                     {
+                        //searchPanel.SearchAddResultsItem(stack[stackTop].listing.name, stack[stackTop].listing.path);
+                     }
+                  app.Unlock();
+               }
+               stackTop++;
+            }
+            else
+            {
+               app.Lock();
+                  if(optionBrowser)
+                  {
+                     ExplorerFileBranch item;
+                     item = MakeFileBranch(stack[frame].listing.stats, stack[frame].listing.name);
+                     searchPanel.AddBrowse(item, stack[frame].browse);
+                     //if(frame == 1)
+                        //searchPanel.SortBrowser();  // this can be very bad for performance in some situations
+                                                // there should be a way to sort the nodes as they are added
+                                                // BinarySearch sorting implementation in listBox's Sort?
+                  }
+                  if(match)
+                  {
+                     if(optionTree)
+                     {
+                        ExplorerFileBranch item;
+                        item = MakeFileBranch(stack[frame].listing.stats, stack[frame].listing.name);
+                        searchPanel.AddResult(item, stack[frame].result);
+                        item.row.collapsed = false;
+                        //searchPanel.SortResults();  // this can be very bad for performance in some situations
+                                                // there should be a way to sort the nodes as they are added
+                                                // BinarySearch sorting implementation in listBox's Sort?
+
+                        if(listLines && lines.first)
+                        {
+                           NumberLink lin = lines.first;
+                           char temp[MAX_F_STRING];
+                           sprintf(temp, "lines %d", lin.num);
+                           for(lin = lin.next; lin && strlen(temp) < MAX_F_STRING; lin = lin.next)
+                              strcatf(temp, ", %d", lin.num);
+                           fileTreeBranch = ExplorerFileBranch { name = CopyString(temp), type = lineNumbers };
+                           searchPanel.AddResult(fileTreeBranch, item);
+                           //row.AddRow().SetData(resultsNameField, fileTreeBranch);
+                           lines.Free(null);
+                        }
+                     }
+                     else
+                        ;//searchPanel.SearchAddResultsItem(stack[frame].listing.name, stack[frame].listing.path);
+                  }
+               app.Unlock();
+            }
+         }
+         else
+         {
+            if(optionTree && stack[frame].branched && frame == treeTop)
+            {
+               stack[frame].branched = false;
+               treeTop--;
+            }
+            frame--;
+            stackTop--;
+         }
+      }
+      if(terminate)
+         for( ; frame >= 0 ; frame--)
+            stack[frame].listing.Stop();
+
+      app.Lock();
+         if(optionTree)
+            searchPanel.SortResults();
+         if(optionBrowser)
+            searchPanel.SortBrowser();
+      app.Unlock();
+
+      active = false;
+
+      searchPanel.SearchTerminate();
+      return 0;
+   }
+}
+
+
diff --git a/explorer/src/Skin.ec b/explorer/src/Skin.ec
new file mode 100644 (file)
index 0000000..fe5ba20
--- /dev/null
@@ -0,0 +1,657 @@
+import "ecere"
+//import "Finder"
+
+#define BORDER       2
+#define TOP          2
+#define BOTTOM       2
+#define CORNER       (BORDER * 2)
+#define CAPTION      12
+#define DEAD_BORDER  2
+#define MIN_WIDTH    60
+#define MIN_HEIGHT   3
+#define BUTTON_OFFSET   0
+#define NAME_OFFSET   0
+#define NAME_OFFSETX  4
+
+#define SB_WIDTH  16
+#define SB_HEIGHT 16
+
+#define MENU_HEIGHT     25
+#define STATUS_HEIGHT   18
+
+#define TEXT_COLOR         skinForeground
+#define TEXT_INACTIVE      darkGray
+
+define skinBackground = Color { r = 40, g = 40, b = 40 };
+define skinForeground = white;
+define skinTextForeground = lightGray;
+define evenRowBackground = Color { 80, 70, 60 };
+define selectionColor = steelBlue;
+
+ColorKey skinGradient[3] =
+{
+   { Color { 40,40,40 }, 0.0f },
+   { darkGray, 0.5f },
+   { Color { 40,40,40 }, 1.0f },
+};
+
+/*
+define skinBackground = white; //Color { 255, 255, 255 };
+define skinForeground = black;
+define skinTextForeground = black;
+define evenRowBackground = lavender;
+define selectionColor = cornflowerBlue;
+
+ColorKey skinGradient[2] =
+{
+   { lightGray, 0.0f }
+   { white, 1.0f },
+};
+*/
+
+class AcovelSkin_Window : Window
+{
+   void ShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving)
+   {
+      bool isNormal = (state == normal || state == maximized);
+      int top = 0, border = 0, bottom = 0;
+      if(state == minimized)
+         top = border = bottom = DEAD_BORDER;
+      else if(((BorderBits)borderStyle).sizable)
+      {
+         top = isNormal ? TOP : 0;
+         border = isNormal ? BORDER : 0;
+         bottom = BOTTOM;
+      }
+      else if(((BorderBits)borderStyle).fixed)
+      {
+         top = 2; //DEAD_BORDER;
+         border = DEAD_BORDER;
+         bottom = DEAD_BORDER;
+      }
+      else if(((BorderBits)borderStyle).contour)
+      {
+         top = 1;
+         border = 1;
+         bottom = 1;
+      }
+
+      if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
+      {
+         int deepTop = 0, deepBottom = 0, deepBorder = 0;
+         if(((BorderBits)borderStyle).contour)
+         {
+            deepBorder = border;
+            deepTop = (((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar)) ? (top + CAPTION) : top;
+            deepBottom = (((BorderBits)borderStyle).sizable && isNormal) ? bottom : border;
+         }
+
+         surface.Bevel(((BorderBits)borderStyle).bevel ? false : true, deepBorder, deepTop, 
+            size.w - deepBorder - deepBorder, size.h - deepBottom - deepTop);
+      }
+
+      if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
+      {
+         /*
+         if(state != maximized || !((BorderBits)borderStyle).sizable)
+         {
+            // Frame for ES_CAPTION windows
+            surface.Bevel(false, 0, 0, size.w, size.h);
+            surface.SetForeground(activeBorder);
+            surface.Rectangle(2, 2, size.w-3, size.h-3);
+
+            // Resizeable frame is 1 pixel thicker 
+            if(((BorderBits)borderStyle).sizable && isNormal)
+               surface.Rectangle(3, 3, size.w - 4, size.h - 4);
+         }
+
+         // Caption
+         if(active)
+            surface.Gradient(gradient, sizeof(gradient) / sizeof(ColorKey), GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
+               border, top, size.w - border - 1, top + CAPTION - 2);
+         else
+            surface.Gradient(gradientInactive, sizeof(gradientInactive) / sizeof(ColorKey), 
+            GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
+               border, top, size.w - border - 1, top + CAPTION - 2);
+
+         surface.SetForeground(activeBorder);
+         if(state != minimized)
+            surface.HLine(border, size.w-border-1, top + CAPTION-1);
+         */
+
+         surface.SetForeground(skinBackground);
+         surface.Rectangle(0,0, size.w-1, size.h-1);
+         surface.SetForeground(active ? TEXT_COLOR : TEXT_INACTIVE /*skinForeground*/);
+         surface.Rectangle(1,1, size.w-2, size.h-2);
+         surface.SetBackground(skinBackground);
+         surface.Area(2, 2, size.w-3, CAPTION + 2);
+
+         // surface.TextFont(captionFont);
+         // surface.WriteText(4,2, name, strlen(name));
+
+         surface.SetForeground((active ? TEXT_COLOR : TEXT_INACTIVE));
+         surface.TextOpacity(false);
+         surface.TextFont(captionFont);
+         if(name)
+         {
+            int buttonsSize = border +
+               ((hasMaximize || hasMinimize) ? 52 : 18);
+            surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET, 
+               size.w - (buttonsSize + border + 4), name, strlen(name));
+         }
+      }
+      if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
+      {
+         surface.SetForeground(skinForeground /*black*/);
+         surface.Rectangle(0, 0, size.w - 1, size.h - 1);
+      }
+
+      if(state != minimized && hasHorzScroll && hasVertScroll)
+      {
+         if(sbh && sbh.visible && sbv && sbv.visible)
+         {
+            surface.SetBackground(activeBorder);
+            surface.Area(
+               clientStart.x + clientSize.w,
+               clientStart.y + clientSize.h,
+               clientStart.x + clientSize.w + SB_WIDTH - 1,
+               clientStart.y + clientSize.h + SB_HEIGHT - 1);
+         }
+      }
+   }
+
+   void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
+   {
+      *w = *h = 0;
+      if((((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel) && state != minimized)
+      {
+         *w += 4;
+         *h += 4;
+      }
+      if(((BorderBits)borderStyle).sizable && (state == normal || state == maximized))
+      {
+         *w += 2 * BORDER;
+         *h += TOP + BOTTOM;
+      }
+      if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
+      {
+         *h += CAPTION;
+         if(!((BorderBits)borderStyle).sizable || state == minimized)
+         {
+            *h += 2*DEAD_BORDER;
+            *w += 2*DEAD_BORDER;
+         }
+      }
+      if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
+      {
+         *w += 2;
+         *h += 2;
+      }
+      if(hasMenuBar && state != minimized)
+      {
+         *h += MENU_HEIGHT;
+      }
+      if(statusBar && state != minimized)
+      {
+         *h += STATUS_HEIGHT;
+      }
+   }
+
+   bool IsMouseMoving(int x, int y, int w, int h)
+   {
+      BorderBits style = (BorderBits)borderStyle; // TOFIX: borderStyle.fixed doesn't work
+      if(style.fixed)
+      {
+         bool resizeX, resizeY, resizeEndX, resizeEndY;
+         if(!IsMouseResizing(x, y, w, h, &resizeX, &resizeY, &resizeEndX, &resizeEndY))
+            return true;
+      }
+      return false;
+   }
+
+   bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
+   {
+      bool result = false;
+
+      *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
+
+      if(((BorderBits)borderStyle).sizable && (state == normal))
+      {
+         // TopLeft Corner
+         if(Box { 0, 0,CORNER-1, TOP-1 }.IsPointInside({x, y}))
+            result = *resizeX = *resizeY = true;
+         // TopRight Corner
+         if(Box { w-CORNER-1, 0, w-1, TOP-1 }.IsPointInside({x, y}))
+            result = *resizeEndX = *resizeY = true;
+         // BottomLeft Corner
+         if(Box { 0, h-BOTTOM-1, CORNER-1, h-1 }.IsPointInside({x, y}))
+            result = *resizeX = *resizeEndY = true;
+         // BottomRight Corner
+         if(Box { w-CORNER-1, h-BOTTOM-1, w-1, h-1 }.IsPointInside({x, y}))
+            result = *resizeEndX = *resizeEndY = true;
+         // Left Border
+         if(Box { 0,TOP, BORDER, h-BOTTOM-1 }.IsPointInside({x, y}))
+            result = *resizeX = true;
+         // Right Border
+         if(Box { w-BORDER-1, TOP, w-1, h-BOTTOM-1 }.IsPointInside({x, y}))
+            result = *resizeEndX = true;
+         // Top Border
+         if(Box { CORNER, 0, w-CORNER-1, TOP-1 }.IsPointInside({x, y}))
+            result = *resizeY = true;
+         // Bottom Border
+         if(Box { CORNER, h-BOTTOM-1, w-CORNER-1, h-1 }.IsPointInside({x, y}))
+            result = *resizeEndY = true;
+      }
+      return result;
+   }
+
+   void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh)
+   {
+      bool isNormal = (state == normal || state == maximized);
+      if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
+      {
+         *mw = MIN_WIDTH;
+         *mh = MIN_HEIGHT;
+      }
+      else
+         *mw = *mh = 0;
+      /*
+      if(((BorderBits)borderStyle).sizable && isNormal)
+         *mw += 2*CORNER;
+      // GetDecorationsSize(window, mw, mh);
+      */
+
+      if(hasVertScroll)
+         *mw += SB_WIDTH;
+      if(hasHorzScroll)
+         *mh += SB_HEIGHT;
+      if(hasVertScroll && hasHorzScroll)
+      {
+         *mw += 2 * SB_WIDTH + SB_WIDTH;
+         *mh += 2 * SB_HEIGHT + SB_HEIGHT;
+         /*
+         if(((BorderBits)borderStyle).sizable && isNormal)
+            *mw -= 2*CORNER;
+         */
+      }
+   }
+
+   void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
+   {
+      bool isNormal = (state == normal || state == maximized);
+      MinMaxValue aw = 0, ah = 0;
+
+      *x = *y = 0;
+
+      GetDecorationsSize(&aw, &ah);
+
+      // Compute client area start
+      if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
+      {
+         *x += 2;
+         *y += 2;
+      }
+
+      if(((BorderBits)borderStyle).sizable && isNormal)
+      {
+         *x += BORDER;
+         *y += TOP;
+      }
+
+      if(hasMenuBar)
+      {
+         *y += MENU_HEIGHT;
+      }
+
+      if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
+      {
+         *y += CAPTION;
+         if(!((BorderBits)borderStyle).sizable || state == minimized)
+         {
+            *y += DEAD_BORDER;
+            *x += DEAD_BORDER;
+         }
+      }
+
+      if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
+      {
+         *x += 1;
+         *y += 1;
+      }
+
+      // Reduce client area
+      *cw = *w - aw;
+      *ch = *h - ah;
+
+      *cw = Max(*cw, 0);
+      *ch = Max(*ch, 0);
+   }
+
+   void UpdateNonClient()
+   {
+      bool isNormal = (state == normal || state == maximized);
+      int top = 0, border = 0;
+      int insideBorder;
+      if(state == minimized)
+         top = border = DEAD_BORDER;
+      else if(((BorderBits)borderStyle).sizable)
+      {
+         if(state == maximized && parent.menuBar)
+         {
+            top = 2;
+            border = 2;
+         }
+         else
+         {
+            top = isNormal ? TOP : 0;
+            border = isNormal ? BORDER : 0;
+         }
+      }
+      else if(((BorderBits)borderStyle).fixed)
+      {
+         top = 2;
+         border = 2;
+      }
+      else if(((BorderBits)borderStyle).contour)
+      {
+         top = 1;
+         border = 1;
+      }
+      insideBorder = border;
+      if(((BorderBits)borderStyle).deep)
+         insideBorder += 2;
+
+      if(menuBar)
+      {
+         if(state == minimized)
+            menuBar.visible = false;
+         else
+            menuBar.visible = true;
+         menuBar.Move(clientStart.x, clientStart.y - MENU_HEIGHT, size.w - insideBorder * 2, MENU_HEIGHT);
+      }
+      if(statusBar)
+      {
+         if(state == minimized)
+            statusBar.visible = false;
+         else
+         {
+            statusBar.visible = true;
+            statusBar.anchor = { left = clientStart.x, bottom = border };
+            statusBar.size.w = size.w - insideBorder * 2;
+         }
+      }
+
+      if(minimizeButton)
+      {
+         minimizeButton.anchor = { right = ((maximizeButton && !maximizeButton.disabled) ? 49 : 24) + border, top = top + BUTTON_OFFSET };
+         minimizeButton.size = { 20, 10 };
+         minimizeButton.foreground = skinForeground;
+         minimizeButton.background = skinBackground;
+         minimizeButton.bevel = false;
+         minimizeButton.OnRedraw = Minimize_OnRedraw;
+         minimizeButton.visible = true;
+      }
+      if(maximizeButton && !maximizeButton.disabled)
+      {
+         maximizeButton.anchor = { right = 24 + border, top = top + BUTTON_OFFSET };
+         maximizeButton.size = { 20, 10 };
+         maximizeButton.bevel = false;
+         maximizeButton.foreground = skinForeground;
+         maximizeButton.background = skinBackground;
+         maximizeButton.OnRedraw = Maximize_OnRedraw;
+         maximizeButton.visible = true;
+      }
+      if(closeButton)
+      {
+         closeButton.anchor = { right = -1 + border, top = top + BUTTON_OFFSET };
+         closeButton.size = { 20, 10 };
+         closeButton.bevel = false;
+         closeButton.foreground = skinForeground;
+         closeButton.background = skinBackground;
+         closeButton.OnRedraw = Close_OnRedraw;
+         closeButton.visible = true;
+      }
+   }
+
+   void OnApplyGraphics()
+   {
+      background = skinBackground;
+      foreground = skinForeground;
+   }
+}
+
+static void Button::SmallButton_OnRedraw(Surface surface)
+{
+   surface.SetForeground(parent.active ? TEXT_COLOR : TEXT_INACTIVE);
+   surface.VLine(0, clientSize.h-2, 0);
+   surface.VLine(0, clientSize.h-2, clientSize.w-1);
+   surface.HLine(1,clientSize.w-2, clientSize.h-1);
+}
+
+static void Button::Minimize_OnRedraw(Surface surface)
+{
+   SmallButton_OnRedraw(this, surface);
+   surface.DrawLine( 5, 3, 14, 3);
+   surface.DrawLine( 5, 4, 14, 4);
+}
+
+static void Button::Maximize_OnRedraw(Surface surface)
+{
+   SmallButton_OnRedraw(this, surface);
+   surface.Rectangle( 5, 2, 14, 7);
+   surface.DrawLine( 5, 3, 14, 4);
+}
+
+static void Button::Close_OnRedraw(Surface surface)
+{
+   SmallButton_OnRedraw(this, surface);
+   surface.DrawLine( 7, 2, 12, 7);
+   surface.DrawLine( 8, 2, 13, 7);
+   surface.DrawLine( 12, 2, 7, 7);
+   surface.DrawLine( 13, 2, 8, 7);
+}
+
+class AcovelSkin_StatusBar : StatusBar
+{
+   void OnApplyGraphics()
+   {
+      background = skinBackground;
+      foreground = skinForeground;
+   }
+}
+
+class AcovelSkin_FileDialog : FileDialog
+{
+   void OnApplyGraphics()
+   {
+      Window child;
+      background = skinBackground;
+      foreground = skinForeground;
+      for(child = firstChild; child; child = child.next)
+      {
+         if(!child.opacity)
+            child.foreground = skinForeground;
+      }
+   }
+}
+
+#define PUREVTBL(c)     ((int (**)())*(void **)((byte *)class(c).data + 4))
+
+extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics;
+extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw;
+
+static void Dummy()
+{
+   Window a;
+   a.OnApplyGraphics();
+   a.OnRedraw(null);
+}
+
+class AcovelSkin_Button : Button
+{
+   void OnApplyGraphics()
+   {
+      //background = skinBackground;
+      background = Color { (int)70 * 6/10, (int)130* 6/10, (int)180* 6/10 };
+      foreground = skinForeground;
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      int isDefault = (int)this.isDefault;
+      PUREVTBL(Button)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw](this, surface);
+      if(bevel || (bevelOver && (buttonState == down || buttonState == over || checked)))
+      {
+         Color c = steelBlue;
+         if(buttonState == down || checked)
+         {
+            surface.SetForeground(selectionColor);
+            surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
+            surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
+            surface.SetForeground(Color { Min((int)c.r * 16/10, 255), Min((int)c.g * 16/10, 255), Min((int)c.b * 16/10,255) });
+            surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
+            surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
+         }
+         else
+         {
+            surface.SetForeground(Color { Min((int)c.r * 16/10, 255), Min((int)c.g * 16/10, 255), Min((int)c.b * 16/10,255) });
+            surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
+            surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
+            
+            
+            surface.SetForeground(selectionColor);
+            surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
+            surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
+            
+            if(bevel)
+            {
+               //surface.SetForeground(skinForeground);
+               surface.SetForeground(Color { c.r * 4/10, c.g * 4/10, c.b * 4/10 });
+               surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
+               surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
+            }
+         }
+      }
+      if(isDefault)
+      {
+         surface.SetForeground(skinForeground);
+         surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
+      }
+      if(!(bevelOver) && !isRemote)
+      {
+         if(active)
+         {
+            int x1,y1,x2,y2;
+            int offset = (buttonState == down && this.offset) ? 1 : 0;
+            surface.SetForeground(skinForeground);
+            surface.LineStipple(0x5555);
+
+            #define CAPTION_DISTANCE   18
+            if((isRadio || isCheckbox) && text)
+            {
+               x1 = /*clientSize.h + */CAPTION_DISTANCE;
+               y1 = 0;
+               x2 = clientSize.w-4;
+               y2 = clientSize.h-4;
+            }
+            else
+            {
+               x1 = 3+offset;
+               y1 = 3+offset;
+               x2 = clientSize.w - 5+offset;
+               y2 = clientSize.h - 5+offset;
+
+               if(isRadio || isCheckbox)
+               {
+                  x1-=3;
+                  y1-=3;
+                  x2+=1;
+                  y2+=1;
+               }
+            }
+            if((x2 - x1) & 1) x2++;
+            if((y2 - y1) & 1) y2++;
+
+            surface.Rectangle(x1, y1, x2, y2);
+            surface.LineStipple(0);
+         }
+      }
+   }
+}
+
+class AcovelSkin_ScrollBar : ScrollBar
+{
+   void OnApplyGraphics()
+   {
+      PUREVTBL(ScrollBar)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics](this);
+
+      background = { skinBackground.r * 9 / 6, skinBackground.g * 9 / 6, skinBackground.b * 9 / 6 };
+
+      upBtn.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
+      upBtn.bitmap = { "<:ecere>elements/arrow-up.png", monochrome = true };
+      downBtn.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
+      downBtn.bitmap = { "<:ecere>elements/arrow-down.png", monochrome = true };
+      thumb.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
+   }   
+}
+
+class AcovelSkin_DropBox : DropBox
+{
+   void OnApplyGraphics()
+   {
+      PUREVTBL(DropBox)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics](this);
+      button.bitmap = { "<:ecere>elements/arrow-down.png", monochrome = true };
+      background = skinBackground;
+      foreground = skinForeground;
+      this.selectionColor = ::selectionColor;
+   }   
+}
+
+class AcovelSkin_ListBox : ListBox
+{
+   void OnApplyGraphics()
+   {
+      PUREVTBL(ListBox)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics](this);
+      background = skinBackground;
+      foreground = skinForeground;
+      this.selectionColor = ::selectionColor;
+   }   
+}
+
+class AcovelSkin : Skin
+{
+   class_property(name) = "Acovel";
+   class_property(selectionColor) = (Color)selectionColor;
+   class_property(disabledBackColor) = (Color)Color{ 0,0,0 };
+   class_property(disabledFrontColor) = (Color)Color{ 128,128,128 };
+
+   FontResource ::SystemFont()
+   {
+      return { faceName = "Tahoma", size = 8.25f };
+   }
+
+   FontResource ::CaptionFont()
+   {
+      return { faceName = "Verdana", size = 6.5f };
+   }
+
+   char * ::CursorsBitmaps(uint id, int *hotSpotX, int *hotSpotY, byte ** paletteShades)
+   {
+      return null;
+   }
+
+   BitmapResource ::GetBitmap(int id)
+   {
+      return null;
+   }
+
+   int ::VerticalSBW()
+   {
+      return 16;
+   }
+
+   int ::HorizontalSBH()
+   {
+      return 16;
+   }
+}
diff --git a/explorer/src/SplitWindow.ec b/explorer/src/SplitWindow.ec
new file mode 100644 (file)
index 0000000..b625e03
--- /dev/null
@@ -0,0 +1,109 @@
+import "ecere"
+
+class SplitWindow : Window
+{
+   bool sliding;
+   int startPos, startX;
+   background = activeBorder, borderStyle = bevel;
+   anchor = { top = -2, bottom = -2 }, stayOnTop = true, inactive = true;
+   size = { w = 6 };
+   cursor = ((GuiApplication)__thisModule).GetCursor(sizeWE);
+
+   Window leftPane, rightPane;
+   double scaleSplit;
+   int split;
+   bool scale;
+
+   property double scaleSplit
+   {
+      set
+      {
+         scaleSplit = value;
+         property::split = (int)(parent.clientSize.w * (value) + 0.5);
+         scale = true;
+      }
+   }
+   property int split
+   {
+      set
+      {
+         int w = size.w;
+         int pw = parent.clientSize.w;
+         int x = value;
+
+         split = value;
+         
+         if(leftPane && !rightPane)
+            leftPane.anchor.right = 0;
+         else if(rightPane && !leftPane)
+            rightPane.anchor.left = 0;
+         else
+         {
+            if(leftPane)
+               leftPane.anchor.right = pw - x;
+            if(rightPane)
+               rightPane.anchor.left = x + w / 2;
+         }
+         anchor.left = x;
+
+         scale = false;
+      }      
+   }
+
+   void OnResize(int width, int height)
+   {
+      //if(visible)
+      {
+         if(scale)
+            property::scaleSplit = scaleSplit;
+         else
+            property::split = split;
+      }
+   }
+         
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      sliding = true;
+      startPos = x + absPosition.x;
+      startX = position.x;
+
+      Capture();
+      
+      return true;
+   }
+
+   bool OnMouseLeave(Modifiers mods)
+   {
+      parent.cursor = null;
+      return true;
+   }
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      parent.cursor = cursor;
+      if(sliding)
+      {
+         bool oldScale = scale;
+         x += absPosition.x;
+         x -= startPos;
+         x += startX;
+         x = Max(x, 20);
+         x = Min(x, parent.clientSize.w - 20);
+
+         property::split = x;
+         scale = oldScale;
+      }
+      return true;
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      if(sliding)
+      {
+         ReleaseCapture();
+         parent.cursor = null;
+         sliding = false;
+      }
+      return true;
+   }
+}
+
diff --git a/explorer/src/Structures/ArrayBasic.ec b/explorer/src/Structures/ArrayBasic.ec
new file mode 100644 (file)
index 0000000..1ea0cff
--- /dev/null
@@ -0,0 +1,134 @@
+public import "ecere"
+
+import "ArrayUtilities"
+
+private:
+
+define array = ((BasicArrayImpl)this).a;
+
+class BasicArrayImpl
+{
+   uint _size;
+   Class type;
+   byte * a;
+};
+
+public class BasicArray
+{
+   uint _size;
+
+   ~BasicArray()
+   {
+      delete array;
+   }
+
+public:   
+   Class type;
+   property uint size
+   {
+      set
+      {
+         if(value != _size)
+         {
+            if(array)
+            {
+               if(value)
+                  array = renew array byte[value * sizeoftype];
+               else
+                  delete array;
+            }
+            else if(value)
+               array = new byte[value * sizeoftype];
+            _size = value;
+         }
+      }
+      get { return _size; }
+   }
+   property void * data
+   {
+      set
+      {
+         memcpy(array, value, _size * sizeoftype);
+      }
+   }
+   void Append(int n)
+   {
+      size += n;
+   }
+   void Insert(uint position, int n)
+   {
+      Append(n);
+      if(position < _size - 1)
+         MoveBytes(array + (position + n) * sizeoftype, array + position * sizeoftype, (_size - position - n) * sizeoftype);
+   }
+   void Trim(int n)
+   {
+      size -= n;
+   }
+   void Remove(uint position, int n)
+   {
+      if(position + n - 1 < _size - 1)
+         MoveBytes(array + position * sizeoftype, array + (position + n) * sizeoftype, (_size - position - n) * sizeoftype);
+      Trim(n);
+   }
+};
+
+public class IntBasicArray : BasicArray
+{
+   type = class(int);
+public:
+   int * const _;
+   uint * Add(int item)
+   {
+      uint pos = _size;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, int item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class UintBasicArray : BasicArray
+{
+   type = class(uint);
+public:
+   uint * const _;
+   uint * Add(uint item)
+   {
+      uint pos = _size;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, uint item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class StringBasicArray : BasicArray
+{
+   type = class(String);
+public:
+   String * const _;
+   String * Add(String item)
+   {
+      uint pos = _size;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   String * AddBefore(uint position, String item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
diff --git a/explorer/src/Structures/ArrayBinaryIndexed.ec b/explorer/src/Structures/ArrayBinaryIndexed.ec
new file mode 100644 (file)
index 0000000..443b9ce
--- /dev/null
@@ -0,0 +1,122 @@
+public import "ecere"
+
+import "ArrayFactoredGrowth"
+
+private:
+
+public import "ecere"
+
+import "ArrayUtilities"
+
+private:
+
+public struct BIloc
+{
+   bool valid;
+   uint offset;
+   uint pos;
+
+   String ToString()
+   {
+      String result = new char[30 + 6 + 11 + 11];
+      sprintf(result, "BIloc { valid=%d, offset=%u, pos=%u }\n", valid, offset, pos);
+      return result;
+   }
+};
+
+public class BinaryIndex : Array
+{
+   type = class(uint);
+   BinaryIndex()
+   {
+      threshold = 8;
+   }
+public:
+   uint * const _;
+   uint threshold;
+   void * client;
+
+   int (*Compare)(void * client, uint offset, typed_object data);
+
+   BIloc Find(typed_object data)
+   {
+      BIloc result { };
+      if(_count && data._class)
+      {
+         uint lo = 0, hi = _count - 1;
+         for( ; hi - lo >= threshold; )
+         {
+            uint tri = (hi - lo) / 3;
+            uint i = lo + tri;
+            if(Compare(client, _[i], data) > 0)
+               hi = i;
+            else
+            {
+               i = hi - tri;
+               if(Compare(client, _[i], data) < 0)
+                  lo = i;
+               else
+                  lo += tri, hi -= tri;
+            }
+         }
+         if(Compare(client, _[lo], data) <= 0)
+         {
+            if(Compare(client, _[hi], data) >= 0)
+            {
+               uint i;
+               for(i = lo + 1; Compare(client, _[i], data) <= 0; i++);
+               result.pos = i;
+               if(Compare(client, _[i], data) == 0)
+                  result.valid = true, result.offset = _[i];
+            }
+            else
+            {
+               if(Compare(client, _[hi], data) <= 0)
+                  result.pos = hi + 1;
+               else
+                  result.valid = true, result.pos = hi, result.offset = _[hi];
+            }
+         }
+         else
+         {
+            result.pos = lo;
+            if(Compare(client, _[lo], data) == 0)
+               result.valid = true, result.offset = _[lo];
+         }
+      }
+      return result;
+   }
+   
+   //void OnSerialize(IOChannel channel)
+   //void OnUnserialize(IOChannel channel)
+}
+
+public class StringBIArray : Array
+{
+   // can simply implement multiple compare modes and allow nice switching using enum for method names
+   type = class(String);
+public:
+   String * const _;
+   private BinaryIndex index { client = this, Compare = (void *)Compare };
+   int Compare(uint offset, typed_object data)
+   {
+      return strcmp(_[offset], (String)data);
+   }
+   BIloc Add(String item)
+   {
+      BIloc result = index.Find(item);
+      if(!result.valid)
+      {
+         result.offset = _count;
+         Append(1);
+         _[result.offset] = item;
+         index.Insert(result.pos, 1);
+      }
+      return result;
+   }
+   BIloc Remove(String item)
+   {
+      
+   }
+}
+
diff --git a/explorer/src/Structures/ArrayBinarySorted.ec b/explorer/src/Structures/ArrayBinarySorted.ec
new file mode 100644 (file)
index 0000000..38c325e
--- /dev/null
@@ -0,0 +1,182 @@
+public import "ecere"
+
+import "ArrayUtilities"
+
+private:
+
+define array = ((BinarySortedArrayImpl)this).a;
+
+public struct BSloc
+{
+   bool valid;
+   uint pos;
+   //void * item;
+
+   String ToString()
+   {
+      String result = new char[30 + 6 + 11];
+      sprintf(result, "BSloc { valid=%d, pos=%u }\n", valid, pos);
+      return result;
+   }
+};
+
+class BinarySortedArrayImpl
+{
+   uint _size;
+   uint _count;
+   uint _factor;
+   uint _threshold;
+   Class type;
+   void ** a;
+};
+
+// do another class that uses a compare method or modify to use the compare method if it was provided
+public class ArrayBinarySorted
+{
+   uint _size;
+   uint _count;
+   uint _factor;
+   uint _threshold;
+
+   ~ArrayBinarySorted()
+   {
+      delete array;
+   }
+
+public:   
+   Class type;
+   property uint count
+   {
+      set
+      {
+         if(value != _count)
+         {
+            int newsize = (value / _factor + 1) * _factor;
+            if(newsize != _size)
+               size = value ? newsize : 0;
+            _count = value;
+         }
+      }
+      get { return _count; }
+   }
+   property uint size
+   {
+      set
+      {
+         if(value != _size)
+         {
+            if(array)
+            {
+               if(value)
+                  array = renew array void * [value * sizeof(void *)];
+               else
+                  delete array;
+            }
+            else if(value)
+               array = new void * [value * sizeof(void *)];
+            _size = value;
+         }
+      }
+      get { return _size; }
+   }
+   property uint growingFactor { set { _factor = value; } get { return _factor; } }
+   property void * data
+   {
+      set
+      {
+         memcpy(array, value, _size * sizeoftype);
+      }
+   }
+   void Append(int n)
+   {
+      count += n;
+   }
+   void Insert(uint position, int n)
+   {
+      Append(n);
+      if(position < _count - 1)
+         MoveBytes(array + (position + n) * sizeoftype, array + position * sizeoftype, (_count - position - n) * sizeoftype);
+   }
+   void Trim(int n)
+   {
+      count -= n;
+   }
+   void Remove(uint position, int n)
+   {
+      if(position + n - 1 < _count - 1)
+         MoveBytes(array + position * sizeoftype, array + (position + n) * sizeoftype, (_count - position - n) * sizeoftype);
+      Trim(n);
+   }
+   BSloc Find(void * item)
+   {
+      BSloc result { };
+      if(_count && item)
+      {
+         uint lo = 0, hi = _count - 1;
+         for( ; hi - lo >= _threshold; )
+         {
+            uint tri = (hi - lo) / 3;
+            uint i = lo + tri;
+            //if(Compare(client, _[i], item) > 0)
+            if(array[i] > item)
+               hi = i;
+            else
+            {
+               i = hi - tri;
+               if(array[i] < item)
+                  lo = i;
+               else
+                  lo += tri, hi -= tri;
+            }
+         }
+         if(array[lo] <= item)
+         {
+            if(array[hi] >= item)
+            {
+               uint i;
+               for(i = lo + 1; array[i] <= item; i++);
+               //result.oldpos = i;
+               if(array[i] == item)
+                  result.valid = true, result.pos = i;
+            }
+            else
+            {
+               if(array[hi] <= item)
+                  ;//result.oldpos = hi + 1;
+               else
+                  result.valid = true/*, result.oldpos = hi*/, result.pos = hi;
+            }
+         }
+         else
+         {
+            //result.oldpos = lo;
+            if(array[lo] == item)
+               result.valid = true, result.pos = lo;
+         }
+      }
+      return result;
+   }
+   
+};
+
+public class StringBSArray : ArrayBinarySorted
+{
+   type = class(String);
+public:
+   String * const _;
+   BSloc Add(String item)
+   {
+      BSloc result = Find(item);
+      if(!result.valid)
+      {
+         Insert(result.pos, 1);
+         _[result.pos] = item;
+      }
+      return result;
+   }
+   BSloc Remove(String item)
+   {
+      
+   }
+}
+
diff --git a/explorer/src/Structures/ArrayFactoredGrowth.ec b/explorer/src/Structures/ArrayFactoredGrowth.ec
new file mode 100644 (file)
index 0000000..2386ded
--- /dev/null
@@ -0,0 +1,161 @@
+public import "ecere"
+
+import "ArrayUtilities"
+
+private:
+
+define array = ((ArrayImpl)this).a;
+
+class ArrayImpl
+{
+   uint _size;
+   uint _count;
+   uint _factor;
+   Class type;
+   byte * a;
+};
+
+public class RedjArray
+{
+   uint _size;
+   uint _count;
+   uint _factor;
+
+   ~RedjArray()
+   {
+      delete array;
+   }
+
+   //virtual void FreeItem(void *);
+
+public:   
+   Class type;
+   property uint count
+   {
+      set
+      {
+         if(value != _count)
+         {
+            int newsize = (value / _factor + 1) * _factor;
+            if(newsize != _size)
+               size = value ? newsize : 0;
+            _count = value;
+         }
+      }
+      get { return _count; }
+   }
+   property uint size
+   {
+      set
+      {
+         if(value != _size)
+         {
+            if(array)
+            {
+               if(value)
+                  array = renew array byte[value * sizeoftype];
+               else
+                  delete array;
+            }
+            else if(value)
+               array = new byte[value * sizeoftype];
+            _size = value;
+         }
+      }
+      get { return _size; }
+   }
+   property uint growingFactor { set { _factor = value; } get { return _factor; } }
+   property void * data
+   {
+      set
+      {
+         memcpy(array, value, _size * sizeoftype);
+      }
+   }
+   void Append(int n)
+   {
+      count += n;
+   }
+   void Insert(uint position, int n)
+   {
+      Append(n);
+      if(position < _count - 1)
+         MoveBytes(array + (position + n) * sizeoftype, array + position * sizeoftype, (_count - position - n) * sizeoftype);
+   }
+   void Trim(int n)
+   {
+      count -= n;
+   }
+   void Remove(uint position, int n)
+   {
+      if(position + n - 1 < _count - 1)
+         MoveBytes(array + position * sizeoftype, array + (position + n) * sizeoftype, (_count - position - n) * sizeoftype);
+      Trim(n);
+   }
+};
+
+public class IntArray : RedjArray
+{
+   type = class(int);
+public:
+   int * const _;
+   uint * Add(int item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, int item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class UintArray : RedjArray
+{
+   type = class(uint);
+public:
+   uint * const _;
+   uint * Add(uint item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, uint item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class StringArray : RedjArray
+{
+   type = class(String);
+public:
+   String * const _;
+   String * Add(String item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   String * AddBefore(uint position, String item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+   void Clear()
+   {
+      int c;
+      for(c = 0; c < _count; c++)
+         delete _[c];
+   }
+}
diff --git a/explorer/src/Structures/ArrayNotes.ec b/explorer/src/Structures/ArrayNotes.ec
new file mode 100644 (file)
index 0000000..ad727c1
--- /dev/null
@@ -0,0 +1,343 @@
+//#define ARRAY ((IncrementArrayImpl)this).array
+//
+//public import "ecere"
+//
+//default:
+//extern int __ecereVMethodID_class_OnFree;
+//
+//private:
+//
+//class IncrementArrayImpl
+//{
+//   uint count;
+//   uint size;
+//   uint increment;
+//   uint divider;
+//   Class type;
+//   byte * array;
+//};
+//
+//default:
+//extern int __ecereVMethodID_class_OnFree;
+//
+//   ~Array()
+//   {
+//      // what's up with that?
+//      //int c;
+//      //void ** array = (void **)((ArrayImpl)this).array;
+//      //if(type.type == normalClass || type.type == noHeadClass)
+//      {
+//         //for(c = 0; c<size; c++)
+//            //type._vTbl[__ecereVMethodID_class_OnFree](type, array[c]);
+//      }
+//      // TODO: Call OnFree for structClass
+//      delete ARRAY;
+//   }
+//
+//            // the following after the renew in Array's size property
+//            if(value > size)
+//               memset(array + size * GetTypeSize(), 0, (value - size) * GetTypeSize());
+//
+//      // the following after the MoveBytes in Array's Remove function
+//      FillBytes(incarray + count, 0, GetTypeSize()); // why set to 0   and is it even correct code
+//
+
+/*
+class DerivedArray : Array
+{
+   uint dummy;
+
+}
+class IntDerivedArray : DerivedArray
+{
+   type = class(int);
+   offset = offsetof(_);
+public:
+   int * const _;
+}
+*/
+
+/*
+#define ARRAY ((IndexedIncrementArrayImpl)this).array
+
+public import "ecere"
+
+default:
+extern int __ecereVMethodID_class_OnFree;
+
+private:
+
+public class IndexedIncrementArray : public IncrementArray
+{
+   UintIncrementArray index { };
+
+public:   
+   property uint count
+   {
+      set
+      {
+         if(value != _count)
+         {
+            if((value / _increment + 1) * _increment != _size)
+               size = value ? (value / _increment + 1) * _increment : 0;
+            index.count = _count;
+            _count = value;
+         }
+      }
+      get { return _count; }
+   }
+   property uint size
+   {
+      set
+      {
+         if(value != _size)
+         {
+            if(ARRAY)
+            {
+               ARRAY = renew ARRAY byte[value * GetTypeSize()];
+               if(value > _size)
+                  // this is initializing to 0, right?, do I want that? 
+                  // why memset instead of FillBytes? 
+                  memset(ARRAY + _size * GetTypeSize(), 0, (value - _size) * GetTypeSize());
+            }
+            else if(value)
+               ARRAY = new byte[value * GetTypeSize()];
+            index.size = _size;
+            _size = value;
+         }
+      }
+      get { return _size; }
+   }
+   property uint increment { set { _increment = value; } get { return _increment; } }
+   property void * data
+   {
+      set
+      {
+         memcpy(ARRAY, value, _size * GetTypeSize());
+      }
+   }
+   void Append(int n)
+   {
+      count += n;
+   }
+   void Insert(uint position, int n)
+   {
+      Append(n);
+      if(position < _count - 1)
+         MoveBytes(ARRAY + position + n, ARRAY + position, (_count - position - n) * GetTypeSize());
+   }
+   void Trim(int n)
+   {
+      count -= n;
+   }
+   void Remove(uint position, int n)
+   {
+      if(position + n - 1 < _count - 1)
+         MoveBytes(ARRAY + position, ARRAY + position + n, (_count - position - n) * GetTypeSize());
+      //FillBytes(ARRAY + _count, 0, GetTypeSize()); // why set to 0   and is it even correct code
+      Trim(n);
+   }
+};
+
+class IntIndexedIncrementArray : IndexedIncrementArray
+{
+   type = class(int);
+public:
+   int * const _;
+}
+
+class UintIndexedIncrementArray : IndexedIncrementArray
+{
+   type = class(uint);
+public:
+   uint * const _;
+   
+   uint * Add(int item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, int item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+class StringIndexedIncrementArray : IndexedIncrementArray
+{
+   type = class(String);
+public:
+   String * const _;
+
+   String * Add(String item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   String * AddBefore(uint position, String item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+
+}
+*/
+
+/*
+class UIntBIArray : IncArray
+{
+   type = class(uint);
+public:
+   uint * const _;
+   uint * Add(uint item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, uint item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+*/
+
+/*
+class UintSortArray : IncArray
+{
+   type = class(uint);
+public:
+   uint * const _;
+   uint * Add(uint item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+      return &_[pos];
+   }
+   uint * AddBefore(uint position, uint item)
+   {
+      Insert(position, 1);
+      _[position] = item;
+      return &_[position];
+   }
+}
+
+public class SortArray : IncArray
+{
+   UintIncArray index;
+   
+   
+}
+
+define array = ((SortArrayImpl)this).a;
+
+class SortArrayImpl
+{
+   uint _size;
+   uint _count;
+   uint _increment;
+   Class type;
+   byte * a;
+};
+
+public class SortArray
+{
+   uint _size;
+   uint _count;
+   uint _increment;
+
+   ~SortArray()
+   {
+      delete array;
+   }
+
+public:   
+   Class type;
+   property uint count
+   {
+      set
+      {
+         if(value != _count)
+         {
+            int newsize = (value / _increment + 1) * _increment;
+            if(newsize != _size)
+               size = value ? newsize : 0;
+            _count = value;
+         }
+      }
+      get { return _count; }
+   }
+   property uint size
+   {
+      set
+      {
+         if(value != _size)
+         {
+            if(array)
+            {
+               if(value)
+                  array = renew array byte[value * sizeoftype];
+               else
+                  delete array;
+            }
+            else if(value)
+               array = new byte[value * sizeoftype];
+            _size = value;
+         }
+      }
+      get { return _size; }
+   }
+   property uint increment { set { _increment = value; } get { return _increment; } }
+   property void * data
+   {
+      set
+      {
+         memcpy(array, value, _size * sizeoftype);
+      }
+   }
+   void Append(int n)
+   {
+      count += n;
+   }
+   void Insert(uint position, int n)
+   {
+      Append(n);
+      if(position < _count - 1)
+         MoveBytes(array + (position + n) * sizeoftype, array + position * sizeoftype, (_count - position - n) * sizeoftype);
+   }
+   void Trim(int n)
+   {
+      count -= n;
+   }
+   void Remove(uint position, int n)
+   {
+      if(position + n - 1 < _count - 1)
+         MoveBytes(array + position * sizeoftype, array + (position + n) * sizeoftype, (_count - position - n) * sizeoftype);
+      Trim(n);
+   }
+};
+*/
+
+   /*int CompareInt(uint a, uint b)
+   {
+      return (a > b) ? 1 : ((a < b) ? - 1 : 0);
+   }*/
+
+   /*int CompareString(char * a, char * b)
+   {
+      return (a && b) ? Compare(a, b) : -1;
+   }*/
+
+   //void ::FreeString(char * string)
+
diff --git a/explorer/src/Structures/ArrayTypes.ec b/explorer/src/Structures/ArrayTypes.ec
new file mode 100644 (file)
index 0000000..12be979
--- /dev/null
@@ -0,0 +1,2 @@
+public import "ecere"
+
diff --git a/explorer/src/Structures/ArrayUtilities.ec b/explorer/src/Structures/ArrayUtilities.ec
new file mode 100644 (file)
index 0000000..bb305a4
--- /dev/null
@@ -0,0 +1,10 @@
+public import "ecere"
+
+private:
+
+// would be nice not to have to do this
+define sizeoftype = GetTypeSize(type);
+int GetTypeSize(Class type)
+{
+   return type.typeSize;
+}
diff --git a/explorer/src/Structures/Stack.ec b/explorer/src/Structures/Stack.ec
new file mode 100644 (file)
index 0000000..779ac36
--- /dev/null
@@ -0,0 +1,66 @@
+public import "ecere"
+
+import "ArrayFactoredGrowth"
+
+private:
+
+public class Stack : Array
+{
+}
+
+public class IntStack : Stack
+{
+   type = class(int);
+public:
+   int * const _;
+   void Push(int item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+   }
+   int Pop()
+   {
+      int item = _[_count - 1];
+      Trim(1);
+      return item;
+   }
+}
+
+class UintStack : Stack
+{
+   type = class(uint);
+public:
+   uint * const _;
+   void Push(uint item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+   }
+   uint Pop()
+   {
+      uint item = _[_count - 1];
+      Trim(1);
+      return item;
+   }
+}
+
+class StringStack : Stack
+{
+   type = class(String);
+public:
+   String * const _;
+   void Push(String item)
+   {
+      uint pos = _count;
+      Append(1);
+      _[pos] = item;
+   }
+   String Pop()
+   {
+      String item = _[_count - 1];
+      Trim(1);
+      return item;
+   }
+}
diff --git a/explorer/src/Structures/Temp.ec b/explorer/src/Structures/Temp.ec
new file mode 100644 (file)
index 0000000..c9b600f
--- /dev/null
@@ -0,0 +1,102 @@
+public import "ecere"
+
+public class TimerTemp
+{
+   bool active;
+   
+   Time count;
+   Time from;
+   
+   void Start()
+   {
+      if(!active)
+      {
+         active = true;
+         from = GetTime();
+      }
+   }
+
+   void Stop()
+   {
+      if(active)
+      {
+         active = false;
+         count += GetTime() - from;
+      }
+   }
+
+   void Reset()
+   {
+      count = 0;
+      if(active)
+         from = GetTime();
+   }
+}
+
+/*
+public class Delay : Seconds
+{
+
+private:
+
+   Time from;
+
+public:
+
+   property bool elapsed
+   {
+      get
+      {
+         Time now = GetTime();
+         if(!from || now - from > (Time)this)
+         {
+            from = now;
+            return true;
+         }
+         return false;
+      }
+   }
+}
+*/
+
+public class TimeGate
+{
+
+private:
+
+   Time from;
+
+public:
+
+   Time delay;
+
+   /*
+   property bool
+   {
+      get
+      {
+         if(!from || GetTime() - from > delay)
+         {
+            from = GetTime();
+            return true;
+         }
+         return false;
+      }
+   }
+   */
+   
+   property bool elapsed
+   {
+      get
+      {
+         Time now = GetTime();
+         if(!from || now - from > delay)
+         {
+            from = now;
+            return true;
+         }
+         return false;
+      }
+   }
+}
+
diff --git a/explorer/tools/FinderMenuCommand.reg b/explorer/tools/FinderMenuCommand.reg
new file mode 100644 (file)
index 0000000..0d73995
--- /dev/null
@@ -0,0 +1,7 @@
+ÿþW
+
+
+
+
+
+