libede:FileSystemBox : added navigateFolders feature and NotifyNodeOpen
[ede] / explorer / src / Explorer.ec
1 public import "ecere"
2 import "SplitWindow"
3
4 import "ExplorerTree" // meant to be called ExplorerTools
5 import "Finder"
6 import "Search"       // meant to be called ExplorerSearch
7 import "Panels"
8
9 /*
10 #ifdef __WIN32__
11 #include <direct.h>
12 #else
13 #include <unistd.h>
14 #endif
15 */
16
17 enum ExplorerToolId
18 {
19    none,
20    newWindow, goBack, goForward, goUp, goHome,
21    browse,
22    panelTree, panelSearch,
23    addressBar,
24    refreshView,
25    viewList, viewDetails, viewIcons, viewCards, viewShowcase, viewCustom,
26    previewPictures
27 };
28
29 static char * toolIconNames[] = 
30 {
31    "<:ecere>emblems/unreadable.png",     /* none */
32
33    "<:ecere>actions/windowNew.png",            /* newWindow */
34    "<:ecere>actions/goPrevious.png",           /* goBack */
35    "<:ecere>actions/goNext.png",               /* goForward */
36    "<:ecere>actions/goUp.png",                 /* goUp */
37    "<:ecere>actions/goHome.png",               /* goHome */
38
39    ":browse.png",                                           /* browse */
40
41    ":panel-tree.png",                                       /* panelTree */
42    "<:ecere>actions/editFind.png",             /* panelSearch */
43
44    "<:ecere>emblems/unreadable.png",     /* addressBar */
45
46    "<:ecere>actions/viewRefresh.png",          /* refreshView */
47
48    ":view-list.png",                                        /* viewList */
49    ":view-details.png",                                     /* viewDetails */
50    ":view-icons.png",                                       /* viewIcons */
51    ":view-cards.png",                                       /* viewCards */
52    ":view-showcase-right.png",                              /* viewShowcase */
53    ":view-custom.png",                                      /* viewCustom */
54    
55    "<:ecere>mimetypes/image.png",     /* previewPictures */
56    
57    ""
58 };
59
60 class ToolButton : Button
61 {
62    size = Size { 24, 24 };
63
64    property int toolId
65    {
66       set
67       {
68          bitmap = BitmapResource { fileName = toolIconNames[value], alphaBlend = true };
69          id = value;
70       }
71    }
72
73    bool TestToolBar::NotifyClicked(ToolButton button, int x, int y, Modifiers mods)
74    {
75       NotifyToolClick(this.parent, this, button.id);
76       return true;
77    }
78 }
79
80 class ToggleToolButton : ToolButton
81 {
82    toggle = true;
83    size = Size { 24, 24 };
84    
85    bool TestToolBar::NotifyClicked(ToggleToolButton button, int x, int y, Modifiers mods)
86    {
87       NotifyToolClick(this.parent, this, button.id);
88       return true;
89    }
90 }
91
92 class GroupToggleToolButton : ToolButton
93 {
94    toggle = true;
95    size = Size { 24, 24 };
96    GroupToggleToolButton * selected;
97    bool TestToolBar::NotifyClicked(GroupToggleToolButton button, int x, int y, Modifiers mods)
98    {
99       bool configured = (bool)button.selected;
100       bool preselection = (configured && (*button.selected));
101       bool reclick = preselection ? (*button.selected == button) : false;
102       if(configured && preselection && !reclick)
103       {
104          (*button.selected).checked = false;
105          *button.selected = button;
106       }
107       NotifyToolClick(this.parent, this, button.id);
108       return true;
109    }
110 }
111
112 class OptionToolButton : ToolButton
113 {
114    toggle = true;
115    size = Size { 24, 24 };
116    OptionToolButton * selected;
117    bool TestToolBar::NotifyClicked(OptionToolButton button, int x, int y, Modifiers mods)
118    {
119       bool configured = (bool)button.selected;
120       bool preselection = (configured && (*button.selected));
121       bool reclick = preselection ? (*button.selected == button) : false;
122       if(configured && !preselection)
123          *button.selected = button;
124       else if(configured && !reclick)
125       {
126          (*button.selected).checked = false;
127          *button.selected = button;
128       }
129       button.checked = true;
130       if(!reclick)
131          NotifyToolClick(this.parent, this, button.id);
132       return true;
133    }
134 }
135
136 class TestToolBar : Window
137 {
138    int xL, xR;
139
140    borderStyle = bevel;
141    inactive = true;
142    background = activeBorder;
143    size = Size { h = 32 };
144    anchor = Anchor { left = 0, top = 0, right = 0 };
145
146    virtual void Window::NotifyToolClick(TestToolBar toolBar, int id);
147
148    xL = 0;
149
150    ToolButton newWindow { this, anchor = { left = (xL += 20) }, toolId = ExplorerToolId::newWindow };
151
152    ToolButton goBack    { this, anchor = { left = (xL += 46) }, toolId = ExplorerToolId::goBack };
153    ToolButton goForward { this, anchor = { left = (xL += 26) }, toolId = ExplorerToolId::goForward };
154    ToolButton goUp      { this, anchor = { left = (xL += 30) }, toolId = ExplorerToolId::goUp };
155    ToolButton goHome    { this, anchor = { left = (xL += 30) }, toolId = ExplorerToolId::goHome };
156
157    GroupToggleToolButton panelTree   { this, anchor = { left = (xL += 46) }, toolId = ExplorerToolId::panelTree, selected = &selectedPanel, checked = true };
158    GroupToggleToolButton panelSearch { this, anchor = { left = (xL += 26) }, toolId = ExplorerToolId::panelSearch, selected = &selectedPanel };
159    GroupToggleToolButton selectedPanel;
160    selectedPanel = panelTree;
161
162    xR = 0;
163
164    OptionToolButton viewList     { this, anchor = { right = (xR += 20) }, toolId = ExplorerToolId::viewList, selected = &selectedView, checked = true };
165    OptionToolButton viewDetails  { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewDetails, selected = &selectedView };
166    OptionToolButton viewIcons    { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewIcons, selected = &selectedView };
167    OptionToolButton viewTiles    { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewCards, selected = &selectedView };
168    OptionToolButton viewShowcase { this, anchor = { right = (xR += 26) }, toolId = ExplorerToolId::viewShowcase, selected = &selectedView };
169    OptionToolButton selectedView;
170    selectedView = viewList;
171
172    ToggleToolButton previewPictures { this, anchor = { right = (xR += 46) }, toolId = ExplorerToolId::previewPictures };
173
174    ToolButton refreshView { this, anchor = { right = (xR += 46) }, toolId = ExplorerToolId::refreshView };
175
176    EditBox addressBar
177    {
178       this, master;
179       size = { w = 250 }, anchor = { left = (xL += 30), right = (xR += 30) }, id = ExplorerToolId::addressBar;
180    };
181
182    /*DropBox tileSet
183    {
184       this,
185       anchor = { left = (xPos += 88) };
186       inactive = true;
187
188       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
189       {
190          return true;
191       }
192    };*/
193
194    TestToolBar()
195    {
196       /*tileSet.AddString("Forest").tag = 0;
197       tileSet.AddString("Winter").tag = 0;
198       tileSet.AddString("Wasteland").tag = 0;*/
199    }
200 };
201
202 class ExplorerWindow : Window
203 {
204    text = "Ecere Explorer";
205    background = activeBorder;
206    borderStyle = sizable;
207    hasMaximize = true;
208    hasMinimize = true;
209    hasClose = true;
210    hasMenuBar = true;
211    tabCycle = true;
212    size = Size { 888, 840 };
213
214    bool userMode;
215    bool clipboard;
216
217    int treeSplit;
218    int searchSplit;
219    
220    ExplorerToolId lastViewId;
221
222    menu = Menu { };
223    
224    Menu fileMenu { menu, "File", f };
225    Menu windowMenu { menu, "Window", w };
226       MenuItem newWindow
227       {
228          windowMenu, "New Window", n;
229          
230          bool NotifySelect(MenuItem selection, Modifiers mods)
231          {
232             ExplorerWindow { }.Create();
233             return true;
234          }
235       };
236
237    TestToolBar toolBar
238    {
239       this;
240
241       void NotifyToolClick(TestToolBar toolBar, int id)
242       {
243          ExplorerToolId toolId = (ExplorerToolId)id;
244          switch(toolId)
245          {
246             case none:
247                break;
248             case newWindow:
249                ExplorerWindow { }.Create();
250                break;
251             case goBack:
252             case goForward:
253                break;
254             case goUp:
255             {
256                ExplorerFileBranch branch = tree.branch;
257                if(branch && branch.parent)
258                   tree.Select(branch.parent);
259                break;
260             }
261             case panelTree:
262                if(tree.visible)
263                {
264                   split.leftPane = null;
265                   split.visible = false;
266                   tree.visible = false;
267                   treeSplit = split.split;
268                   split.OnResize(split.size.w, split.size.h);
269                }
270                else
271                {
272                   if(search.visible)
273                   {
274                      search.visible = false;
275                      searchSplit = split.split;
276                   }
277                   tree.visible = true;
278                   split.leftPane = tree;
279                   split.visible = true;
280                   split.split = treeSplit;
281                   split.OnResize(split.size.w, split.size.h);
282                   SwitchViews(lastViewId);
283                }
284                break;
285             case panelSearch:
286                if(search.visible)
287                {
288                   split.leftPane = null;
289                   split.visible = false;
290                   search.visible = false;
291                   searchSplit = split.split;
292                   split.OnResize(split.size.w, split.size.h);
293                   SwitchViews(lastViewId);
294                }
295                else
296                {
297                   if(tree.visible)
298                   {
299                      tree.visible = false;
300                      treeSplit = split.split;
301                   }
302                   search.visible = true;
303                   split.leftPane = search;
304                   split.visible = true;
305                   split.split = searchSplit;
306
307                   view = ExplorerSearchViewTree { viewHolder, this };
308                   view.previewPictures = toolBar.previewPictures.checked;
309                   view.anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
310                   view.NotifyItemOpen = ViewNotifyItemOpen;
311                   view.Create();
312                   search.view = (ExplorerSearchViewTree)view;
313
314                   split.OnResize(split.size.w, split.size.h);
315                }
316                break;
317             case refreshView:
318                if(view)
319                   view.Refresh();
320                break;
321             case previewPictures:
322                if(view)
323                   view.previewPictures = toolBar.previewPictures.checked;
324                break;
325             case viewList:
326             case viewDetails:
327             case viewIcons:
328             case viewCards:
329             case viewShowcase:
330                SwitchViews(toolId);
331                break;
332          }
333       }
334    };
335
336    void SwitchViews(ExplorerToolId viewId)
337    {
338       ExplorerFileBranch branch = tree.branch;
339       view.Destroy(0);
340       switch(viewId)
341       {
342          case viewList:     view = ExplorerViewList     { parent = viewHolder, master = this }; break;
343          case viewDetails:  view = ExplorerViewDetails  { parent = viewHolder, master = this }; break;
344          case viewIcons:    view = ExplorerViewIcons    { parent = viewHolder, master = this }; break;
345          case viewCards:    view = ExplorerViewCards    { parent = viewHolder, master = this }; break;
346          case viewShowcase: view = ExplorerViewShowcase { parent = viewHolder, master = this }; break;
347       }
348       view.tabCycle = true;
349       view.previewPictures = toolBar.previewPictures.checked;
350       view.anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
351       view.NotifyItemOpen = ViewNotifyItemOpen;
352       view.Create();
353       view.Load(branch);
354       lastViewId = viewId;
355    }
356
357    Window deep
358    {
359       this;
360       borderStyle = deep;
361       tabCycle = true;
362       anchor = Anchor { left = 0, top = 34, right = 0, bottom = 0 };
363    };
364
365    ExplorerTree tree
366    {
367       deep, this;
368       //visible = false;
369       tabCycle = true;
370       size = Size { 624, 268 };
371       anchor = Anchor { left = 0, top = 0, bottom = 0 };
372       text = "Browser", hotKey = Key { e, alt = true };
373       NotifyBranchSelect = TreeNotifyBranchSelect;
374    };
375    
376    ExplorerView view;
377
378    ExplorerSearch search
379    {
380       deep, this;
381       visible = false;
382       tabCycle = true;
383       size = Size { 624, 268 };
384       anchor = Anchor { left = 0, top = 0, bottom = 0 };
385    };
386
387    ExplorerViewSearch results;
388
389    bool TreeNotifyBranchSelect(ExplorerTree tree, ExplorerFileBranch branch)
390    {
391       if(branch)
392       {
393          char path[MAX_LOCATION];
394          branch.GetPath(path);
395          toolBar.addressBar.contents = path;
396          view.Load(branch);
397       }
398       return true;
399    }
400    
401    bool ViewNotifyItemOpen(ExplorerView view, ExplorerFileItem item)
402    {
403       ExplorerFileBranch branch = tree.branch;
404       if(item && branch)
405       {
406          if(item.type.isFolderType)
407          {
408             ExplorerFileBranch child;
409             
410             if(!branch.loaded || !branch.childrenLoaded)
411                BranchLoad(branch, tree.tree);
412
413             for(child = branch.children.first; child; child = child.next)
414                if(!strcmp(child.name, item.name))
415                   break;
416             
417             if(child)
418             {
419                if(branch.row.collapsed)
420                   child.row.collapsed = true;
421                child.EnsureVisible(false);
422                tree.Select(child);
423             }
424          }
425          else
426          {
427             char path[MAX_LOCATION];
428             branch.GetPath(path);
429             PathCat(path, item.name);
430             ShellOpen(path);
431          }
432       }
433    }
434
435    Window viewHolder
436    {
437       parent = deep, master = this;
438       tabCycle = true;
439       anchor = Anchor { top = 0, bottom = 0, right = 0 };
440    };
441
442    SplitWindow split
443    {
444       deep, this;
445       leftPane = tree, rightPane = viewHolder;
446       split = 300;
447    };
448
449    void GoToLocation(char * location)
450    {
451       int c;
452       char * temp;
453       char step[MAX_LOCATION];
454       
455       StringArray steps { growingFactor = 4 };
456       ExplorerFileBranch last = null;
457       
458       temp = CopyString(location);
459       while(strlen(temp))
460       {
461          GetLastDirectory(temp, step);
462          StripLastDirectory(temp, temp);
463          steps.Add(CopyString(step));
464       }
465       
466       for(c = steps._count - 1; c >= 0; c--)
467       {
468          last = tree.Find(steps._[c], last);
469          if(!last)
470             break;
471          tree.Select(last);
472       }
473       
474       delete temp;
475       delete steps;
476    }
477
478    void SearchLocation(char * location)
479    {
480       GoToLocation(location);
481       search.location.editBox.contents = location;
482    }
483
484    bool OnPostCreate()
485    {
486       userMode = true;
487       return true;
488    }
489
490    ExplorerWindow()
491    {
492       userMode = false;
493
494       treeSplit = 300;
495       searchSplit = 200;
496
497       view = ExplorerViewList
498       {
499          parent = viewHolder, master = this;
500          tabCycle = true;
501          previewPictures = toolBar.previewPictures.checked;
502          anchor = Anchor { left = 0, top = 0, bottom = 0, right = 0 };
503          NotifyItemOpen = ViewNotifyItemOpen;
504       };
505       lastViewId = viewList;
506       
507       tree.Load();
508       view.Load(tree.root);
509    }
510
511    void InitTree()
512    {
513    }
514
515    void InitSearch()
516    {
517    }
518 }
519
520 class ExplorerApp : GuiApplication
521 {
522    //skin = "Acovel";
523
524    bool Init()
525    {
526       QuickPathTool goPath { };
527       QuickPathTool searchPath { };
528       char * findWhat = null;
529       SetLoggingMode(debug, null);
530       if(argc > 1)
531       {
532          if(!strcmpi(argv[1], "go") && argc > 2)
533             goPath = argv[2];
534          else if(!strcmpi(argv[1], "find") && argc > 2)
535          {
536             char * unquoted;
537             if(argv[2][0] == '\"')
538                StripQuotes(argv[2], unquoted);
539             else
540                unquoted = argv[2];
541             findWhat = CopyString(unquoted);
542             if(argc > 3)
543             {
544                if(!strcmpi(argv[3], "in") && argc > 4)
545                   searchPath = argv[4];
546                else
547                   searchPath = ""; // this should make it current dir
548             }
549             else
550                searchPath = ""; // same
551          } 
552          else if(!strcmpi(argv[1], "search") && argc > 2)
553             searchPath = argv[2];
554          else if(!strcmpi(argv[1], "image") && argc > 2)
555             ;
556          else if(!strcmpi(argv[1], "slides") && argc > 2)
557             ;
558          else
559             goPath = argv[1];
560       }
561       else
562          goPath = "";
563       if(goPath)
564       {
565          ExplorerWindow explorerWnd { };
566          explorerWnd.Create();
567          explorerWnd.GoToLocation(goPath);
568       }
569       else if(searchPath)
570       {
571          ExplorerWindow explorerWnd { };
572          explorerWnd.Create();
573          explorerWnd.SearchLocation(searchPath);
574       }
575       return true;
576    }
577 }
578
579 struct QuickPathTool
580 {
581    char path[MAX_LOCATION];
582
583    property char * 
584    {
585       set
586       {
587          char * unquoted;
588          GetWorkingDir(path, MAX_LOCATION);
589          if(value[0] == '\"')
590             StripQuotes(value, unquoted);
591          else
592             unquoted = value;
593          PathCat(path, unquoted);
594          if(!FileExists(path))
595          {
596             // this incomplete functionality is not quite at it's place in this class
597             int len;
598             char * original = CopyString(path);
599             while((len = strlen(path)))
600             {
601                StripLastDirectory(path, path);
602                if(FileExists(path))
603                {
604                   // TODO: message location does not exist, 
605                   //       this higher location exists though
606                   //       go there?
607                   break;
608                }
609             }
610             if(!len)
611             {
612                // TODO: message location does not exist, 
613                //       unable to select alternate location
614             }
615             path[0] = '\0';
616             delete original;
617          }
618       }
619       get { return path[0] ? path : null; }
620    }
621    property bool { get { return (bool)path[0]; } }
622 };
623
624 define app = ((ExplorerApp)__thisModule);