ecere/ide: Added DirPath/FilePath support in DataBox (tweaked PathBox), Merged PathBo...
[sdk] / ecere / src / gui / controls / PathBox.ec
1 namespace gui::controls;
2 import "Window"
3 import "Array"
4
5 default extern int __ecereVMethodID_class_OnGetDataFromString;
6 default static void _workAround()
7 {
8    int a;
9    a.OnGetDataFromString(0);
10 }
11
12 public class FilePath : String
13 {
14    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
15    {
16       PathBox pathBox
17       {
18          dataBox, borderStyle = 0, anchor = { 0, 0, 0, 0 },
19          typeExpected = file;
20          browseDialog = userData ? (FileDialog)userData : { type = open, text = "Select a file..." };
21          path = this;
22       };
23       pathBox.Create();
24       return pathBox;
25    }
26
27    bool OnSaveEdit(PathBox pathBox, void * object)
28    {
29       bool changed = false;
30       if(pathBox.modifiedDocument)
31       {
32          String::OnFree();
33          changed = _class._vTbl[__ecereVMethodID_class_OnGetDataFromString](_class, &this, pathBox.systemPath);
34       }
35       return changed;
36    }
37
38    int OnCompare(DirPath b)
39    {
40       return fstrcmp(this, b);
41    }
42 }
43
44 public class DirPath : FilePath
45 {
46    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
47    {
48       PathBox pathBox
49       {
50          dataBox, borderStyle = 0, anchor = { 0, 0, 0, 0 },
51          typeExpected = directory;
52          browseDialog = userData ? (FileDialog)userData : { type = selectDir, text = "Select a folder..." };
53          path = this;
54       };
55       pathBox.Create();
56       return pathBox;
57    }
58 }
59
60 public enum PathTypeExpected { none, any, directory, file };
61
62 public class PathBox : CommonControl
63 {
64    borderStyle = deep;
65    clientSize = { 64, 18 };
66
67 #if defined(__WIN32__)
68    PathBox()
69    {
70       path[0] = '\0';
71    }
72 #endif
73
74    PathTypeExpected typeExpected;
75    FileDialog browseDialog;
76 #if defined(__WIN32__)
77    char path[MAX_LOCATION];
78 #endif
79
80    BitmapResource file { "<:ecere>mimeTypes/file.png", transparent = true, alphaBlend = true };
81    BitmapResource brokenFile { "<:ecere>mimeTypes/brokenFile.png", transparent = true, alphaBlend = true };
82    BitmapResource folder { "<:ecere>places/folder.png", transparent = true, alphaBlend = true };
83    BitmapResource brokenFolder { "<:ecere>places/brokenFolder.png", transparent = true, alphaBlend = true };
84
85    Picture picture
86    {
87       this/*, size = { 318, 94 }*/, anchor = { left = 1, vert = 0 };
88       image = brokenFolder;
89       visible = false;
90       opacity = 0;
91       alphaBlend = true;
92    };
93
94    public property EditBox editBox { get { return editBox; } }
95    EditBox editBox
96    {
97       this/*, size = { 290, 22 }*/, position = { 1, 1 }, anchor = { left = 1, top = 1, right = 1 };
98       borderStyle = none;
99
100       void NotifyUpdate(EditBox editBox)
101       {
102          CheckFileExists();
103          modifiedDocument = true;
104       }
105       bool NotifyModified(EditBox editBox)
106       {
107          return NotifyModified(master, this);
108       }
109
110       bool OnKeyDown(Key key, unichar ch)
111       {
112          if(key == f4)
113             ((PathBox)parent).browse.NotifyClicked(parent, ((PathBox)parent).browse, 0, 0, (Modifiers)null);
114          return EditBox::OnKeyDown(key, ch);
115       }
116    };
117
118    // For chaining popup-key event
119    bool OnKeyHit(Key key, unichar ch)
120    {
121       return editBox.OnKeyHit(key, ch);
122    }
123
124    Button browse
125    {
126       this, size = { w = 26 }, anchor = { top = 0, right = 0, bottom = 0 };
127       text = "...";
128       hotKey = f2;
129       visible = false;
130       inactive = true;
131
132       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
133       {
134          if(browseDialog)
135          {
136             /* We need a reinterpret cast :) Easy eC compiler introduction contribution! :)
137             DataBox dataBox = reinterpret(master);
138             ListBox lb = dataBox ? reinterpret(dataBox.parent) : null;
139             DirectoriesBox dirBox = lb ? reinterpret(lb.parent) : null;
140             */
141             DataBox dataBox = eClass_IsDerived(master._class, class(DataBox)) ? (DataBox)master : null;
142             ListBox lb = ((dataBox && eClass_IsDerived(dataBox.parent._class, class(ListBox))) ? (ListBox)dataBox.parent : null;
143             DirectoriesBox dirBox = ((lb && eClass_IsDerived(lb.parent._class, class(DirectoriesBox))) ? (DirectoriesBox)lb.parent : null;
144             char * browsePath = CopyString(editBox.contents);
145             char fileName[MAX_LOCATION];
146
147             incref this;
148
149             GetLastDirectory(browsePath, fileName);
150             StripLastDirectory(browsePath, browsePath);
151
152             if(!browsePath[0])
153             {
154                char filePath[MAX_LOCATION];
155                delete browsePath;
156                LocateModule(null, filePath);
157                StripLastDirectory(filePath, filePath);
158                browsePath = CopyString(filePath);
159             }
160
161             while(browsePath[0] && !FileExists(browsePath).isDirectory)
162             {
163                char temp[MAX_LOCATION];
164                GetLastDirectory(browsePath, temp);
165                PathCat(temp, fileName);
166                strcpy(fileName, temp);
167                StripLastDirectory(browsePath, browsePath);
168             }
169             browseDialog.filePath = fileName;
170             browseDialog.currentDirectory = browsePath;
171             delete browsePath;
172             browseDialog.master = rootWindow;
173
174             // THIS PART WAS MISSING IN THE PathBox/DirectoriesBox INTEGRATION AND WAS CRUCIAL
175             if(dirBox) dirBox.browsing = true;
176             if(browseDialog.Modal())
177             {
178                PathBox pathBox = dataBox ? (PathBox)dataBox.editor : this;
179                pathBox.modifiedDocument = true;
180                pathBox.property::path = browseDialog.filePath;
181                if(dataBox)
182                   dataBox.SaveData();
183                else
184                   pathBox.editBox.SelectAll();
185                if(lb) lb.StopEditing(true);
186                pathBox.NotifyModified(pathBox.master, this);
187             }
188             if(dirBox) dirBox.browsing = false;
189
190             delete this;
191          }
192          return true;
193       }
194    };
195
196    void CheckFileExists()
197    {
198       if(typeExpected != none)
199       {
200          BitmapResource icon = null;
201          FileAttribs exists = FileExists(editBox.contents);
202          
203          switch(typeExpected)
204          {
205             case any:
206                // TODO: improvements, add drive, etc, also find a better solution/icon for expect any and file doesn't exist
207                icon = exists ? exists.isFile ? file : exists.isDirectory ? folder : null : null;
208                break;
209             case directory:
210                icon = exists && exists.isDirectory ? folder : brokenFolder;
211                break;
212             case file:
213                icon = exists && exists.isFile ? file : brokenFile;
214                break;
215          }
216
217          picture.image = icon;
218       }
219    }
220
221    void OnRedraw(Surface surface)
222    {
223       if(!isEnabled)
224       {
225          surface.SetBackground(activeBorder);
226          surface.Area(0, 0, clientSize.w, clientSize.h);
227       }
228       else
229          Window::OnRedraw(surface);
230    }
231
232    ~PathBox()
233    {
234       delete browseDialog;
235    }
236
237 public:
238    property PathTypeExpected typeExpected
239    {
240       set
241       {
242          if(value != typeExpected)
243          {
244             if(value == none || typeExpected == none)
245             {
246                bool withIcon = value != none;
247                picture.visible = withIcon;
248                editBox.anchor.left = withIcon ? 18 : 1;
249                CheckFileExists();
250             }
251             typeExpected = value;
252             if(browseDialog && browseDialog.type == open && typeExpected == directory)
253                browseDialog.type = selectDir;
254          }         
255       }
256    }
257
258    property FileDialog browseDialog
259    {
260       set
261       {
262          delete browseDialog;
263          browseDialog = value;
264          if(browseDialog)
265          {
266             incref browseDialog;
267             if(browseDialog.type == open && typeExpected == directory)
268                browseDialog.type = selectDir;
269             if(!strcmp(browseDialog.text, "Select a file...") && text)
270             {
271                char temp[1024] = "Select ";
272                strcat(temp, text);
273                strcat(temp, "...");
274                browseDialog.text = temp;
275             }
276          }
277          browse.visible = browseDialog ? true : false;
278          editBox.anchor.right = browseDialog ? 26 : 1;
279       }
280    }
281
282    void Home() { editBox.Home(); }
283    void End() { editBox.End(); }
284
285    void SelectAll() { editBox.SelectAll(); }
286    void Deselect() { editBox.Deselect(); }
287
288    property String path
289    {
290       set
291       {
292          char path[MAX_LOCATION];
293          GetSystemPathBuffer(path, value);
294          editBox.contents = path;
295          if(active)
296             editBox.SelectAll();
297          CheckFileExists();
298       }
299       get { return editBox.contents; }
300    }
301    property String slashPath  { get { return GetSlashPathBuffer (path, editBox.contents); } };
302    property String systemPath { get { return GetSystemPathBuffer(path, editBox.contents); } };
303
304    virtual bool Window::NotifyModified(PathBox pathBox);
305 }
306
307 // DirectoriesBox
308 FileDialog browseFileDialog { type = selectDir, text = "Select directory" };
309
310 public class DirectoriesBox : CommonControl
311 {
312 public:
313
314    bool browsing;
315
316    opacity = 0;
317
318    virtual bool OnChangedDir(char ** directory)
319    {
320       return true;
321    }
322    virtual bool OnPrepareBrowseDir(char ** directory)
323    {
324       return true;
325    }
326    virtual bool OnBrowsedDir(char ** directory)
327    {
328       return true;
329    }
330
331    watch(foreground) { list.foreground = foreground; };
332    watch(background) { list.background = background; };
333
334    property Array<String> strings
335    {
336       set
337       {
338          list.Clear();
339          if(value)
340          {
341             for(s : value)
342             {
343                char temp[MAX_LOCATION];
344                list.AddString(GetSystemPathBuffer(temp, s));
345             }
346          }
347          list.AddString("");
348          list.currentRow = list.firstRow;
349          list.modifiedDocument = false;
350       }
351       get
352       {
353          Array<String> array { };
354          DataRow row;
355          for(row = list.firstRow; row; row = row.next)
356          {
357             String string = row.string;
358             if(string && string[0])
359                array.Add(CopyUnixPath(string));
360          }
361          return array;
362       }
363    }
364
365    virtual bool Window::NotifyModified(DirectoriesBox dirsBox);
366
367    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
368    {
369       // Browsing was not being set, fixed by introducing dependency to this class to PathBox
370       if(!active && !browsing)
371       {
372          list.StopEditing(true);
373          if(list.modifiedDocument)
374          {
375             NotifyModified(master, this);
376             list.modifiedDocument = false;
377             modifiedDocument = true;
378          }
379       }
380       return true;
381    }
382
383    Button add
384    {
385       parent = this, bevelOver = true, inactive = true;
386       position = { 265, 0 }, size = { 22, 22 };
387       anchor = { top = 0, right = 77 };
388       hotKey = plus, bitmap = BitmapResource { fileName = "<:ecere>actions/listAdd.png", alphaBlend = true };
389       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
390       {
391          list.StopEditing(true);
392          list.lastRow.Edit(null);
393          list.modifiedDocument = true;
394          return true;
395       }
396    };
397    Button remove
398    {
399       parent = this, bevelOver = true, inactive = true;
400       position = { 290, 0 }, size = { 22, 22 };
401       anchor = { top = 0, right = 54 };
402       hotKey = del, bitmap = BitmapResource { fileName = "<:ecere>actions/listRemove.png", alphaBlend = true };
403       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
404       {
405          list.StopEditing(true);
406          if(list.currentRow != list.lastRow)
407          {
408             list.DeleteRow(null);
409             list.modifiedDocument = true;
410          }
411          return true;
412       }
413    };
414    Button up
415    {
416       parent = this, bevelOver = true, inactive = true;
417       position = { 315, 0 }, size = { 22, 22 };
418       anchor = { top = 0, right = 31 };
419       hotKey = ctrlUp, bitmap = BitmapResource { fileName = "<:ecere>actions/goUp.png", alphaBlend = true };
420       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
421       {
422          if(list.currentRow != list.lastRow)
423          {
424             DataRow current = list.currentRow, row;
425             if(current)
426             {
427                row = current.previous;
428                if(row)
429                {
430                   row = row.previous;
431                   current.Move(row);
432                   list.modifiedDocument = true;
433                }
434             }
435          }
436          return true;
437       }
438    };
439    Button down
440    {
441       parent = this, bevelOver = true, inactive = true;
442       position = { 340, 0 }, size = { 22, 22 };
443       anchor = { top = 0, right = 8 };
444       hotKey = ctrlDown, bitmap = BitmapResource { fileName = "<:ecere>actions/goDown.png", alphaBlend = true };
445       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
446       {
447          DataRow current = list.currentRow, row;
448          if(current)
449          {
450             row = current.next;
451             if(row && row != list.lastRow)
452             {
453                current.Move(row);
454                list.modifiedDocument = true;
455             }
456          }
457          return true;
458       }
459    };
460    ListBox list
461    {
462       this, moveRows = true, hasVertScroll = true, dontHideScroll = true;
463       borderStyle = deep, position = { 0, 22 }, size = { 300, 60 };
464       anchor = { left = 0, top = 22, right = 0, bottom = 0 };
465
466       bool OnRightButtonDown(int x, int y, Modifiers mods)
467       {
468          return parent.OnRightButtonDown(x + position.x + parent.clientStart.x, y + position.y + parent.clientStart.y, mods);
469       }
470
471       bool NotifyChanged(ListBox listBox, DataRow row)
472       {
473          char * directory = listBox.GetData(null);
474          if(directory && directory[0])
475          {
476             char * dir = CopyString(directory);
477             if(OnChangedDir(&dir))
478             {
479                listBox.SetData(null, dir);
480                listBox.modifiedDocument = true;
481                if(listBox.currentRow == listBox.lastRow && listBox.lastRow.string)
482                {
483                   DataRow r = listBox.lastRow;
484                   char * s = r.string;
485                   listBox.currentRow = listBox.AddString("");
486                }
487             }
488             delete dir;
489          }
490          else if(listBox.currentRow != listBox.lastRow)
491          {
492             listBox.DeleteRow(null);
493             listBox.modifiedDocument = true;
494          }
495          return true;
496       }
497
498       bool NotifyEditDone(ListBox listBox, DataRow row)
499       {
500          return true;
501       }
502
503       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
504       {
505          if(key == del)
506          {
507             listBox.StopEditing(true);
508             if(listBox.currentRow != listBox.lastRow)
509                listBox.DeleteRow(null);
510             return false;
511          }
512          return true;
513       }
514
515       bool NotifyMove(ListBox listBox, DataRow row, Modifiers mods)
516       {
517          if(listBox.currentRow == listBox.lastRow)
518             return false;
519          else if(row == listBox.lastRow)
520          {
521             if(listBox.currentRow == row.previous)
522                return false;
523             listBox.currentRow.Move(row.previous);
524             return false;
525          }
526          return true;
527       }
528
529       bool NotifyReclick(ListBox listBox, DataRow row, Modifiers mods)
530       {
531          row.Edit(null);
532          return true;
533       }
534    };
535    DataField dirField { dataType = class(DirPath), editable = true, userData = browseFileDialog };
536
537    DirectoriesBox()
538    {
539       list.AddField(dirField);
540       list.AddString("");
541       list.modifiedDocument = false;
542    }
543 }