Makefile: Installing man pages, making tao.pdf optional; i18n: generating same .pot...
[sdk] / ecere / src / gui / dialogs / FileDialog.ec
1 namespace gui::dialogs;
2
3 //#ifdef __WIN32__  // We want the strings in the .pot when building on Unix
4 static define rootName = $"Entire Computer";
5 static define msNetwork = $"Microsoft Windows Network";
6 //#endif
7
8 import "Window"
9
10 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
11
12 default:
13 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
14 private:
15
16 static char * iconNames[] = 
17 {
18    "<:ecere>places/folder.png",
19    "<:ecere>status/folderOpen.png",
20    "<:ecere>devices/computer.png",
21    "<:ecere>devices/driveHardDisk.png",
22    "<:ecere>places/driveRemote.png",
23    "<:ecere>devices/mediaOptical.png",
24    "<:ecere>devices/driveRemovableMedia.png",
25    "<:ecere>devices/mediaFloppy.png",
26    "<:ecere>places/networkWorkgroup.png",
27    "<:ecere>places/networkServer.png",
28    "<:ecere>places/folderRemote.png",
29
30    "<:ecere>mimeTypes/file.png",                   /*normalFile*/
31    "<:ecere>mimeTypes/textEcereWorkspace.png",     /*ewsFile*/
32    "<:ecere>mimeTypes/textEcereProject.png",       /*epjFile*/
33    "<:ecere>mimeTypes/textEcereSource.png",        /*ecFile*/
34    "<:ecere>mimeTypes/textEcereHeader.png",        /*ehFile*/
35    "<:ecere>mimeTypes/textCSource.png",            /*cFile*/
36    "<:ecere>mimeTypes/textCHeader.png",            /*hFile*/
37    "<:ecere>mimeTypes/textC++Source.png",          /*cppFile*/
38    "<:ecere>mimeTypes/textC++Header.png",          /*hppFile*/
39    "<:ecere>mimeTypes/text.png",                   /*textFile*/
40    "<:ecere>mimeTypes/textHyperTextMarkup.png",    /*webFile*/
41    "<:ecere>mimeTypes/image.png",                  /*pictureFile*/
42    "<:ecere>status/audioVolumeHigh.png",           /*soundFile*/
43    "<:ecere>mimeTypes/package.png",                /*archiveFile*/
44    "<:ecere>mimeTypes/packageSoftware.png",        /*packageFile*/
45    "<:ecere>mimeTypes/packageOpticalDisc.png"      /*opticalMediaImageFile*/
46 };
47
48 public enum FileNameType     // Had to be private, since icons member of FileDialog is also private... (Static members? Not for a while...)
49 {
50    folder, folderOpen, computer,
51    drive, netDrive, cdrom, removable, floppy, network, server, share, // these are sort equal
52
53    normalFile, ewsFile, epjFile, ecFile, ehFile, cFile, hFile, cppFile, hppFile,
54    textFile, webFile, pictureFile, soundFile,
55    archiveFile, packageFile, opticalMediaImageFile; /* these (all previous) are sort equal */
56
57    /*property char * 
58    {
59       set
60       {
61          this = SelectByExtension(value);
62       }
63    }*/
64    public property bool isFolderType
65    {
66       get { return this >= folder && this <= share; }
67    }
68
69    public property bool isFileType
70    {
71       get { return this >= normalFile && this <= opticalMediaImageFile; }
72    }
73
74    FileNameType ::SelectByExtension(char * extension)
75    {
76       if(!strcmpi(extension, "ews"))
77          return ewsFile;
78       else if(!strcmpi(extension, "epj"))
79          return epjFile;
80       else if(!strcmpi(extension, "ec"))
81          return ecFile;
82       else if(!strcmpi(extension, "eh"))
83          return ehFile;
84       else if(!strcmpi(extension, "cpp") ||
85             !strcmpi(extension, "cc") || !strcmpi(extension, "cxx"))
86          return cppFile;
87       else if(!strcmpi(extension, "hpp") ||
88             !strcmpi(extension, "hh") || !strcmpi(extension, "hxx"))
89          return hppFile;
90       else if(!strcmpi(extension, "c"))
91          return cFile;
92       else if(!strcmpi(extension, "h"))
93          return hFile;
94       else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
95             !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
96          return textFile;
97       else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
98             !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
99             !strcmpi(extension, "js"))
100          return webFile;
101       else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
102             !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
103             !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
104             !strcmpi(extension, "ico"))
105          return pictureFile;
106       else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
107             !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
108          return soundFile;
109       else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
110             !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
111             !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
112             !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
113             !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
114             !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
115          return archiveFile;
116       else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
117             !strcmpi(extension, "rpm"))
118          return packageFile;
119       else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
120             !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
121             !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
122             !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
123          return opticalMediaImageFile;
124       return normalFile;
125    }
126 };
127
128 public enum FileDialogType { open, save, selectDir, multiOpen };
129 public struct FileFilter
130 {
131    char * name, * extensions;
132
133    bool ValidateFileName(char * name)
134    {
135       if(strcmp(name, "..") && strcmp(name, ".") && strcmp(name, ""))
136       {
137          if(extensions) // && !stats.attribs.isDirectory)
138          {
139             char extension[MAX_EXTENSION], compared[MAX_EXTENSION];
140             int c;
141
142             GetExtension(name, extension);
143             for(c = 0; extensions[c];)
144             {
145                int len = 0;
146                char ch;
147                for(;(ch = extensions[c]) && !IS_ALUNDER(ch); c++);
148                for(;(ch = extensions[c]) &&  IS_ALUNDER(ch); c++)
149                   compared[len++] = ch;
150                compared[len] = '\0';
151
152                if(!strcmpi(extension, compared))
153                   return true;
154             }
155          }
156          else
157             return true;
158       }
159       return false;
160    }
161 };
162 public enum FileForceExtension { never, always, whenNoneGiven };
163 public struct FileType
164 {
165    char * name, * typeExtension; 
166    FileForceExtension forceExtension;
167 };
168
169 static enum FileDialogSelectFrom { fromEditBox, fromListBox, fromDropBox };
170
171 public struct FileName
172 {
173    char * name;
174    FileNameType type;
175    int indent;
176
177    void OnDisplay(Surface surface, int x, int y, int width, FileDialog fileDialog, Alignment alignment, DataDisplayFlags displayFlags)
178    {
179       int indentSize = (displayFlags.dropBox) ? 0 : 10;
180       int textOffset;
181
182       Bitmap icon = fileDialog.icons[type].bitmap;
183       if(!icon && type > normalFile)
184          icon = fileDialog.icons[normalFile].bitmap;
185       if(!icon)
186       {
187          if(type == folder || type == folderOpen)
188             surface.SetForeground(red); //Color { 170, 170, 0 } // REDJ What is that color?
189          indentSize = 8;
190       }
191       textOffset = indent * indentSize + (icon ? (icon.width + 4) : 0);
192       
193       surface.WriteTextDots
194          (alignment, x + textOffset, y + 2, width - textOffset, name, strlen(name));
195       if(icon)
196          surface.Blit(icon, x + indent * indentSize, y,0,0, icon.width, icon.height);
197    }
198
199    int OnCompare(FileName b)
200    {
201       int result;
202       if(type == b.type || (type >= normalFile && b.type >= normalFile) || (type >= drive && type <= share))
203          result = strcmpi(name, b.name);
204       else
205       {
206          if(type == folder && b.type >= normalFile) result = -1;
207          else if(type >= normalFile && b.type == folder) result = 1;
208       }
209       return result;
210    }
211
212    void OnCopy(FileName newData)
213    {
214       type = newData.type;
215       indent = newData.indent;
216       if(newData.name)
217       {
218          int len = strlen(newData.name) + 1;
219          name = new char[len];
220          CopyBytes(name, newData.name, len);
221       }
222    }
223
224    bool OnGetDataFromString(char * string)
225    {
226       int len = strlen(string) + 1;
227       name = new char[len];
228       CopyBytes(name, string, len);
229       return true;
230    }
231
232    void OnFree()
233    {
234       delete name;
235    }
236
237    char * OnGetString(char * string, void * fieldData, bool * needClass)
238    {
239       return name;
240    }
241 };
242
243 public class FileDialog : Window
244 {
245    text = $"Select a file...";
246    background = formColor;
247    hasClose = true;
248    borderStyle = sizable;
249    tabCycle = true;
250    autoCreate = false;
251    nativeDecorations = true;
252    minClientSize = { 500, 300 };
253
254 public:
255    property FileDialogType type
256    {
257       get { return style; }
258       set
259       {
260          int numTypes = sizeTypes / sizeof(FileType);
261          int rightOffset = (value != selectDir && numTypes > 0) ? 48 : 16;
262
263          style = value;
264
265          if(value == selectDir)
266          {
267             // Filters
268             filter.visible = false;
269             filterLabel.visible = false;
270             // Types
271             type.visible = false;
272             typeLabel.visible = false;
273
274             open.visible = true;
275             open.isDefault = true;
276
277             ok.text = $"Select";
278             ok.id = DialogResult::ok;
279             ok.hotKey = altS;
280             ok.isDefault = false;
281          }
282          else
283          {
284             // Filters
285             filter.visible = true;
286             filterLabel.visible = true;
287
288             // Types
289             type.visible = numTypes != 0;
290             typeLabel.visible = numTypes != 0;
291
292             open.visible = false;
293             open.isDefault = false;
294
295             ok.text = $"OK";
296             ok.id = 0;
297             ok.hotKey = 0;
298             ok.isDefault = true;
299          }
300          ok.anchor = { right = 10, bottom = 32 + rightOffset - 1 };
301
302          // Cancel Button
303          cancel.anchor = { right = 10, bottom = rightOffset - 1 };
304
305          listBox.anchor = { left = 8, right = 8, top = 40, bottom = 64 + rightOffset };
306          fileName.anchor = { left = 96, bottom = 32 + rightOffset, right = 104 };
307          fileNameLabel.anchor = { left = 8, bottom = 35 + rightOffset };
308
309          listBox.multiSelect = value == multiOpen;
310          fileName.text = (value == selectDir) ? $"Directory:" : $"File Name:";
311       }
312    };
313
314    // Stuff currently in config moving to FileDialog:
315    property char * filePath { set { strcpy(filePath, value); } get { return (char *)filePath; } };
316    property char * currentDirectory
317    { 
318       set
319       {
320          GetWorkingDir(currentDirectory, MAX_DIRECTORY);
321          PathCat(currentDirectory, value);
322          FileFixCase(currentDirectory);
323       }
324       get { return (char *)currentDirectory; }
325    };
326    property FileFilter * filters { set { filters = value; } get { return filters; } };
327    property FileType * types { set { types = value; } get { return types; } };
328
329    // Replace with Array system
330    property int sizeFilters
331    {
332       set
333       {
334          int numFilters = value / sizeof(FileFilter);
335          int c;
336
337          sizeFilters = value;
338
339          // File Extension Filter
340          filter.Clear();
341          // filter.AddField(null);
342          if(filters)
343          {
344             for(c = 0; c<numFilters; c++)
345             {
346                DataRow row = filter.AddString(filters[c].name);
347                row.tag = c;
348             }
349          }
350          if(!numFilters)
351             filter.AddString($"All files");
352
353          if(fileFilter >= numFilters) fileFilter = 0;
354          filter.currentRow = filter.FindRow(fileFilter);
355       }
356       get { return sizeFilters; }
357    };
358    property int sizeTypes
359    {
360       set
361       {
362          int numTypes = value / sizeof(FileType);
363          int rightOffset = (numTypes > 0) ? 48 : 16;
364
365          sizeTypes = value;
366
367          // Filters
368          filter.anchor = { left = 96, right = 104, bottom = rightOffset };
369          filterLabel.anchor = { left = 8, bottom = 3 + rightOffset };
370
371          if(style != selectDir)
372          {
373             // Types
374             type.visible = numTypes ? true : false;
375             typeLabel.visible = numTypes ? true : false;
376
377             // Ok Button
378             ok.anchor = { right = 10, bottom = 32 + rightOffset - 1 };
379
380             // Cancel Button
381             cancel.anchor = { right = 10, bottom = rightOffset - 1 };
382          }
383
384          listBox.anchor = { left = 8, right = 8, top = 40, bottom = 64 + rightOffset };
385          fileName.anchor = { left = 96, bottom = 32 + rightOffset, right = 104 };
386          fileNameLabel.anchor = { left = 8, bottom = 35 + rightOffset };
387
388          // File Types
389          if(numTypes)
390          {
391             int c;
392
393             type.Clear();
394             // type.AddField(null);
395             if(types)
396             {
397                for(c = 0; c<numTypes; c++)
398                {
399                   DataRow row = type.AddString(types[c].name);
400                   row.tag = c;
401                }
402             }
403             if(fileType >= numTypes) fileType = 0;
404
405             type.currentRow = type.FindRow(fileType);
406          }
407       }
408       get { return sizeTypes; }
409    };
410
411    property int filter { set { fileFilter = value; } get { return fileFilter; } };
412    property int fileType { set { fileType = value; } get { return fileType; } };
413    property bool mayNotExist { set { mayNotExist = value; } get { return mayNotExist; } };
414
415    // Get only
416    property int numSelections { get { return numSelections; } };
417    property char ** multiFilePaths { get { return multiFilePaths; } };
418
419 private:
420    FileDialog()
421    {
422       FileNameType c;
423
424       lookIn.AddField(lookInField);
425       listBox.AddField(nameField);
426       listBox.AddField(typeField);
427       listBox.AddField(sizeField);
428
429       filter.currentRow = filter.AddString($"All files");
430
431       GetWorkingDir(currentDirectory, MAX_DIRECTORY);
432       FileFixCase(currentDirectory);
433
434       // Resources
435       for(c = 0; c<FileNameType::enumSize; c++)
436       {
437          icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
438          AddResource(icons[c]);
439       }
440    }
441
442    ~FileDialog()
443    {
444       delete customFilter.extensions;
445       if(multiFilePaths)
446       {
447          int c;
448          for(c = 0; c<numSelections; c++)
449             delete multiFilePaths[c];
450          delete multiFilePaths;
451       }
452    }
453
454    void ListDrives()
455    {
456       int start = 0;
457       char tmpDir[MAX_FILENAME];
458       DataRow row;
459       FileName fileName;
460       FileListing listing { "/" };
461       int c;
462
463       fileName.indent = 0;
464
465       // Fill the path dropbox
466       lookIn.Clear();
467
468       row = lookIn.AddRow();
469
470    #ifdef __WIN32__
471       fileName.name = rootName;
472    #else
473       fileName.name = "/";
474    #endif
475       fileName.type = computer;
476       row.SetData(null, fileName);
477       lookIn.currentRow = row;
478
479       start = 0;
480    #ifdef __WIN32__
481       while(listing.Find())
482       {
483          row = lookIn.AddRow();
484
485          fileName.name = listing.name;
486          fileName.type = drive;
487          if(listing.stats.attribs.isCDROM) fileName.type = cdrom;
488          if(listing.stats.attribs.isRemote) fileName.type = netDrive;
489          if(listing.stats.attribs.isRemovable)
490          {
491             if(listing.name[0] == 'A' || listing.name[0] == 'B')
492                fileName.type = floppy;
493             else
494                fileName.type = removable;
495          }
496
497          fileName.indent = 1;
498          row.SetData(null, fileName);
499
500          start = 2;
501          if(listing.name[0] == currentDirectory[0] &&
502             listing.name[1] == currentDirectory[1])
503          {
504    #endif
505             for(c = start; currentDirectory[c]; ) 
506             {
507                int len = 0;
508                char ch;
509                for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
510                for(;(ch = currentDirectory[c]) && (ch != '/' && ch != '\\'); c++)
511                {
512                   if(len < MAX_FILENAME)
513                      tmpDir[len++] = ch;  
514                }
515                for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
516                tmpDir[len] = '\0';
517
518                if(len > 0)
519                {
520                   row = lookIn.AddRow();
521                   fileName.name = tmpDir;
522                   fileName.type = ch ? folder : folderOpen;
523                   fileName.indent++;
524                   row.SetData(null, fileName);
525                }
526                if(!ch)
527                   lookIn.currentRow = row;
528             }
529    #ifdef __WIN32__
530             if(c == start)
531                lookIn.currentRow = row;
532          }
533          c++;
534       }
535
536       row = lookIn.AddRow();
537       fileName.name = msNetwork;
538       fileName.type = network;
539       fileName.indent = 0;
540       row.SetData(null, fileName);
541       if(!strcmp(currentDirectory, "\\\\"))
542          lookIn.currentRow = row;
543       else
544       {
545          if(currentDirectory[0] == '\\' && currentDirectory[1] == '\\')
546          {
547             for(c = 2; currentDirectory[c]; ) 
548             {
549                int len = 0;
550                char ch;
551                for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
552                for(;(ch = currentDirectory[c]) && (ch != '/' && ch != '\\'); c++)
553                {
554                   if(len < MAX_FILENAME)
555                      tmpDir[len++] = ch;  
556                }
557                for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
558                tmpDir[len] = '\0';
559
560                if(len > 0)
561                {
562                   row = lookIn.AddRow();
563                   fileName.name = tmpDir;
564                   fileName.indent++;
565                   if(fileName.indent == 1)
566                      fileName.type = server;
567                   else if(fileName.indent == 2)
568                      fileName.type = share;
569                   else
570                      fileName.type = ch ? folder : folderOpen;
571                   row.SetData(null, fileName);
572                }
573                if(!ch)
574                   lookIn.currentRow = row;
575             }
576             if(c == 2)
577                lookIn.currentRow = row;
578          }
579       }
580    #endif
581    }
582
583    // flag = false : Get Name From Edit Box, flag = true : Get Name From List Box
584    void GetNameFromListBox(bool flag)
585    {
586       bool okDisabled;
587       if(flag)
588       {
589          if(style == multiOpen)
590             okDisabled = listBox.numSelections == 0;
591          else
592             okDisabled = listBox.currentRow == null;
593          if(okDisabled) flag = false;
594       }
595       else
596          okDisabled = !fileName || !fileName.line.text[0];
597
598       getNameFromListBox = flag;
599       ok.disabled = okDisabled;
600       open.disabled = okDisabled;
601    }
602
603    void ListFiles()
604    {
605       FileListing listing { currentDirectory };
606       int c = 0;
607       FileName fileName;
608       DataRow row;
609
610       if(sizeFilters < sizeof(FileFilter) && !fileFilter)
611          listing.extensions = null;
612       else if(filters && fileFilter < sizeFilters / (int)sizeof(FileFilter))
613          listing.extensions = filters[fileFilter].extensions;    
614       else
615          listing.extensions = customFilter.extensions;
616      
617       listBox.Clear();
618
619       fileName.indent = 0;
620       while(listing.Find())
621       {
622          if(style != selectDir || (listing.stats.attribs.isDirectory))
623          {
624             row = listBox.AddRow();
625
626             fileName.name = listing.name;
627             if(listing.stats.attribs.isDirectory)
628             {
629                fileName.type = (listing.stats.attribs.isDrive) ? drive : folder;
630                if(listing.stats.attribs.isServer) fileName.type = server;
631                if(listing.stats.attribs.isShare) fileName.type = share;
632                if(listing.stats.attribs.isCDROM) fileName.type = cdrom;
633                if(listing.stats.attribs.isRemote) fileName.type = netDrive;
634                if(listing.stats.attribs.isRemovable) 
635                {
636                   if(listing.name[0] == 'A' || listing.name[0] == 'B')
637                      fileName.type = floppy;
638                   else
639                      fileName.type = removable;
640                }
641                row.SetData(nameField, fileName);
642                row.SetData(typeField, null);
643             }
644             else
645             {
646                char extension[MAX_EXTENSION];
647
648                GetExtension(fileName.name, extension);
649                fileName.type = FileNameType::SelectByExtension(extension);
650                strupr(extension);
651                row.SetData(nameField, fileName);
652                row.SetData(typeField, extension);
653                row.SetData(sizeField, listing.stats.size);
654             }
655          }
656          c++;
657       }
658
659       if(sortOrder && sortField != nameField)
660          listBox.Sort(nameField, 1);
661       listBox.Sort(sortField, sortOrder);
662
663       GetNameFromListBox(activeChild == listBox);
664
665       // Go up button
666       if(!strcmp(currentDirectory, "/"))
667       {
668    #ifdef __WIN32__
669          row = listBox.AddRow();
670          fileName.name = msNetwork;
671          fileName.type = network;
672          fileName.indent = 0;
673          row.SetData(nameField, fileName);
674          row.SetData(typeField, null);
675    #endif
676          goUp.disabled = true;
677       }
678       else 
679          goUp.disabled = false;
680
681       ListDrives();
682
683       listBox.typingTimeout = 0.5;
684    }
685
686
687    bool GetNamesFromRow(DataRow row, char ** selectedFileName)
688    {
689       FileName * fileName;
690
691       if(style == multiOpen)
692       {
693          OldList selection;
694          listBox.GetMultiSelection(selection);
695          if(selection.count)
696          {
697             int totalLen = 0;
698             OldLink item;
699
700             // First allocate enough memory
701             for(item = selection.first; item; item = item.next)
702             {
703                row = item.data;
704                fileName = row.GetData(nameField);
705    #ifdef __WIN32__
706                if(!strcmp(currentDirectory, "/"))
707                {
708                   if(!strcmp(fileName->name, msNetwork))
709                      totalLen += 2;
710                   else
711                      totalLen += 3;
712                }
713                else
714    #endif
715                totalLen += strlen(fileName->name);
716                if(item != selection.first)
717                   totalLen += 3;
718             }
719             if(selection.count > 1) totalLen += 2;
720             *selectedFileName = new char[totalLen + 1];
721             (*selectedFileName)[0] = '\0';
722
723             // Then build the string
724             for(item = selection.first; item; item = item.next)
725             {
726                row = item.data;
727                fileName = row.GetData(nameField);
728                if(item != selection.first) strcat(*selectedFileName, " ");
729                if(selection.count > 1) strcat(*selectedFileName, "\"");
730    #ifdef __WIN32__
731                if(!strcmp(currentDirectory, "/"))
732                {
733                   char name[3];
734                   if(!strcmp(fileName->name, msNetwork))
735                      strcpy(name, "\\\\");
736                   else
737                   {
738                      name[0] = fileName->name[0];
739                      name[1] = fileName->name[1];
740                      name[2] = '\0';
741                   }
742                   strcat(*selectedFileName, name);
743                }
744                else
745    #endif            
746                strcat(*selectedFileName, fileName->name);
747                if(selection.count > 1) strcat(*selectedFileName, "\"");
748             }
749             selection.Free(null);
750             return true;
751          }
752       }
753       else if(row)
754       {
755          fileName = row.GetData(nameField);
756          if(fileName != null)
757          {
758    #ifdef __WIN32__
759             if(!strcmp(currentDirectory, "/"))
760             {
761                *selectedFileName = new char[3];
762                if(!strcmp(fileName->name, msNetwork))
763                   strcpy(*selectedFileName, "\\\\");
764                else
765                {
766                   (*selectedFileName)[0] = fileName->name[0];
767                   (*selectedFileName)[1] = fileName->name[1];
768                   (*selectedFileName)[2] = '\0';
769                }
770             }
771             else
772    #endif
773             {
774                *selectedFileName = new char[strlen(fileName->name) + 1];
775                strcpy(*selectedFileName, fileName->name);
776             }
777             return true;
778          }
779       }
780       return false;
781    }
782
783    bool SelectFile(char * fileName, FileDialogSelectFrom from, bool isOK)
784    {
785       bool result = true;
786       FileAttribs exists;
787       char * wildcardPointer = strstr(fileName, "*");
788       
789       if(wildcardPointer)
790       {
791          if(style != selectDir)
792          {
793             int numFilters = sizeFilters / sizeof(FileFilter);
794             int numExtensions = 0;
795             char * pointer = wildcardPointer;
796
797             fileFilter = -1;
798
799             // Count the number of extensions in requested filter
800             while(pointer)
801             {
802                if(pointer[1] == '.' && pointer[2])
803                   pointer +=3; 
804                else 
805                   pointer ++;
806                pointer = strstr(pointer, "*");
807                numExtensions++;
808             }
809
810             // Try to match the extension(s) to an existing filter
811             if(numFilters)
812             {
813                int filter;
814                char extension[MAX_EXTENSION], compared[MAX_EXTENSION];
815                for(filter = 0; filter<numFilters; filter++)
816                {
817                   if(filters[filter].extensions)
818                   {
819                      int numMatched = 0;
820                      int c, d;
821
822                      for(c = 0; filters[filter].extensions[c];)
823                      {
824                         bool matched = false;
825                         int len = 0;
826                         char ch;
827                         for(;(ch = filters[filter].extensions[c]) && !IS_ALUNDER(ch); c++);
828                         for(;(ch = filters[filter].extensions[c]) &&  IS_ALUNDER(ch); c++)
829                            compared[len++] = ch;
830                         compared[len] = '\0';
831
832                         for(d = 0; wildcardPointer[d]; )
833                         {
834                            len = 0;
835                            for(;(ch = wildcardPointer[d]) && !IS_ALUNDER(ch); d++);
836                            for(;(ch = wildcardPointer[d]) && IS_ALUNDER(ch); d++)
837                            {
838                               if(len < MAX_EXTENSION)
839                                  extension[len++] = ch;
840                            }
841                            extension[len] = '\0';
842                      
843                            if(!strcmpi(extension, compared))
844                            {
845                               matched = true;
846                               break;
847                            }
848                         }
849
850                         if(matched)
851                            numMatched++;
852                         else
853                         {
854                            numMatched = 0;
855                            break;
856                         }
857                      }
858                      if(numMatched == numExtensions)
859                      {
860                         fileFilter = filter;
861                         break;
862                      }
863                   }
864                   else if(!strcmp(wildcardPointer, "*") || strstr(wildcardPointer, "*.*"))
865                   {
866                      fileFilter = filter;
867                      break;
868                   }
869                }
870             }
871             // Only have *.* to check for
872             else if(!strcmp(wildcardPointer, "*") || strstr(wildcardPointer, "*.*"))
873                this.fileFilter = 0;
874
875             // If we can't match it, use the custom extension
876             if(this.fileFilter == -1)
877             {
878                char extension[MAX_EXTENSION];
879                int c;
880                char * name = new char[numExtensions * (4 + MAX_EXTENSION)];
881
882                delete customFilter.extensions;
883             
884                if(!strcmp(wildcardPointer, "*") || strstr(wildcardPointer, "*.*"))
885                {
886                   strcpy(name, $"All Files");
887                   customFilter.extensions = null;
888                }
889                else
890                {
891                   customFilter.extensions = new char[numExtensions * (2 + MAX_EXTENSION)];
892                   customFilter.extensions[0] = '\0';
893                   name[0] = '\0';
894             
895                   numExtensions = 0;
896                   for(c = 0; wildcardPointer[c]; )
897                   {
898                      int len = 0;
899                      char ch;
900                      for(;(ch = wildcardPointer[c]) && !IS_ALUNDER(ch); c++);
901                      for(;(ch = wildcardPointer[c]) && IS_ALUNDER(ch); c++)
902                      {
903                         if(len < MAX_EXTENSION)
904                            extension[len++] = ch;  
905                      }
906                      extension[len] = '\0';
907
908                      if(numExtensions)
909                      {
910                         strcat(name, ", ");
911                         strcat(customFilter.extensions, ", ");
912                      }
913                      strcat(name, "*.");
914                      strcat(name, extension);
915                      if(!extension[0]) 
916                         strcat(customFilter.extensions, ".");
917                      else
918                         strcat(customFilter.extensions, extension);
919
920                      numExtensions++;
921                   }
922                }
923
924                if(!customFilterRow)
925                {
926                   customFilterRow = filter.AddRow();
927                   customFilterRow.tag = numFilters + 1;
928                }
929
930                customFilterRow.SetData(null, name);
931
932                fileFilter = numFilters + 1;
933
934                delete name;
935             }
936          
937             filter.currentRow = filter.FindRow(fileFilter);
938             ListFiles();
939             result = true;
940          }
941          *wildcardPointer = '\0';
942       }
943
944       if(style == multiOpen)
945       {
946          int c;
947          bool quoted = false;
948          bool needQuotes = strchr(fileName, '\"') != null;
949          char currentFileName[MAX_LOCATION], * curFileName = currentFileName;
950          char ch;
951          OldList selections { };
952          OldLink selection = null;
953
954          for(c = 0;; c++)
955          {
956             ch = fileName[c];
957             switch(ch)
958             {
959                case '\0':
960                case '\"':
961                   if(!ch || quoted)
962                   {
963                      // Add filename
964                      (*curFileName) = '\0';
965
966                      selection = OldLink { data = new char[strlen(currentFileName)+1] };
967                      strcpy(selection.data, currentFileName);
968                      selections.Add(selection);
969
970                      curFileName = currentFileName;
971                   }
972                   quoted ^= true;
973                   break;
974                default:
975                   if(needQuotes && !quoted)
976                      break;
977                   // Add to filename
978                   *(curFileName++) = ch;
979             }
980             if(!ch) break;
981          }
982
983          numSelections = 0;
984          multiFilePaths = new char *[selections.count];
985
986          for(selection = selections.first; selection; selection = selection.next)
987          {
988             char * fileName = selection.data;
989             
990             // For every file
991             strcpy(currentFileName, currentDirectory);
992             if(PathCat(currentFileName, fileName))
993             {
994                FileFixCase(currentFileName);
995                exists = FileExists(currentFileName);
996                if(exists.isDirectory)
997                {
998                   strcpy(currentDirectory, currentFileName);
999                   ListFiles();
1000                   break;
1001                }
1002                else if(exists || mayNotExist)
1003                {
1004                   char ** path = &multiFilePaths[numSelections++];
1005                   *path = new char[strlen(currentFileName)+1];
1006                   strcpy(*path, currentFileName);
1007
1008                   result = false;
1009                }
1010                if(exists && result && from == fromListBox) // From List Box
1011                {
1012                   char pathName[MAX_LOCATION];
1013                   GetLastDirectory(this.fileName.line.text, pathName);
1014                   this.fileName.Clear();
1015                   if(!exists.isDirectory)
1016                      this.fileName.PutS(pathName);
1017                   break;
1018                }
1019             }
1020          }
1021          selections.Free(OldLink::Free);
1022          if(result)
1023          {
1024             if(multiFilePaths)
1025             {
1026                for(c = 0; c<numSelections; c++)
1027                   if(multiFilePaths[c])
1028                      delete multiFilePaths[c];
1029                delete multiFilePaths;
1030             }
1031          }
1032       }
1033       else
1034       {
1035          strcpy(filePath, currentDirectory);
1036          if(PathCat(filePath, fileName))
1037          {
1038             FileFixCase(filePath);
1039             exists = FileExists(filePath);
1040             if(exists.isDirectory && (style != selectDir || !isOK))
1041             {
1042                strcpy(currentDirectory, filePath);
1043
1044                ListFiles();
1045             }
1046             else 
1047             {
1048                // *** SAVING ONLY ****
1049                if(style == save)
1050                {
1051                   if(fileType >= 0 && fileType < sizeTypes)
1052                   {
1053                      FileType type = types[fileType];
1054                      if(type.forceExtension && type.typeExtension)
1055                      {
1056                         char extension[MAX_EXTENSION];
1057                         GetExtension(filePath, extension);
1058                         if(type.forceExtension == always || !extension[0])
1059                         {
1060                            ChangeExtension(filePath, type.typeExtension, filePath);
1061                            exists = FileExists(filePath);
1062                         }
1063                      }
1064                   }
1065                   if(!exists || MessageBox { master = this, type = yesNo, text = $"File Already Exists", contents = $"Replace existing file?" }.Modal() == yes)
1066                      result = false;
1067                }
1068                else if(exists || mayNotExist)
1069                {
1070                   if(style != selectDir ||
1071                      (strcmp(filePath, "\\\\") &&
1072                      strcmp(filePath, "\\\\") &&
1073                      !(exists.isServer)))
1074                         result = false;
1075                }
1076                // *** DIRECTORY SELECTION ONLY ****
1077                else if(isOK && style == selectDir &&
1078                   MessageBox { this, type = yesNo, text = $"Directory doesn't exist", contents = $"Create directory?" }.Modal() == yes)
1079                {
1080                   if(MakeDir(filePath))
1081                      result = false;
1082                }
1083             }
1084
1085             if(exists && result && from == fromListBox) // From List Box
1086             {
1087                char pathName[MAX_LOCATION];
1088                GetLastDirectory(this.fileName.line.text, pathName);
1089                this.fileName.Clear();
1090                if(!exists.isDirectory)
1091                   this.fileName.PutS(pathName);
1092             }
1093          }
1094       }
1095
1096       if(!exists && result && from == fromDropBox) // drive Drop Box
1097       {
1098          ListDrives();
1099       }
1100
1101       if(!result)
1102          Destroy(DialogResult::ok);
1103       return result;
1104    }
1105
1106    bool OnPostCreate()
1107    {
1108       if(multiFilePaths)
1109       {
1110          int c;
1111          for(c = 0; c<numSelections; c++)
1112             delete multiFilePaths[c];
1113          delete multiFilePaths;
1114       }
1115       numSelections = 0;
1116
1117       ListFiles();
1118       {
1119          // Fix up config input directory
1120          char fileName[MAX_FILENAME];
1121          #if defined(__WIN32__)
1122          char * dirOccur = SearchString(filePath, 0, currentDirectory, false, false);
1123          #else
1124          char * dirOccur = strstr(filePath, currentDirectory);
1125          #endif
1126          if(dirOccur)
1127          {
1128             dirOccur += strlen(currentDirectory);
1129             for(;*dirOccur && (*dirOccur == '/' || *dirOccur == '\\'); dirOccur++);
1130             strcpy(fileName, dirOccur);
1131          }
1132          else
1133             strcpy(fileName, filePath);
1134
1135          this.fileName.Clear();
1136          this.fileName.PutS(fileName);
1137       }
1138
1139       GetNameFromListBox(false);
1140
1141       fileName.MakeActive();
1142       return true;
1143    }
1144
1145    FileDialogType style;
1146    FileFilter customFilter;
1147    DataRow customFilterRow;
1148    bool getNameFromListBox;
1149    BitmapResource icons[FileNameType];
1150
1151    char currentDirectory[MAX_DIRECTORY];
1152    char filePath[MAX_LOCATION];
1153    FileFilter * filters;
1154    int sizeFilters;
1155    FileType * types;
1156    int sizeTypes;
1157    int fileFilter, fileType;
1158
1159    int numSelections;
1160    char ** multiFilePaths;
1161    bool mayNotExist;
1162
1163    // ListBox Configuration
1164    DataField sortField;
1165    int sortOrder;
1166
1167    // File Extension Filter
1168    DropBox filter
1169    {
1170       this, text = $"Filter:", anchor = { left = 96, right = 104, bottom = 16 }, hotKey = altR;
1171       
1172       bool NotifySelect(DropBox control, DataRow row, Modifiers mods)
1173       {
1174          fileFilter = row ? row.tag : 0;
1175          ListFiles();
1176          return true;
1177       }
1178    };
1179
1180    Label filterLabel
1181    {
1182       this, inactive = true, anchor = { left = 8, bottom = 3 + 16 }, labeledWindow = filter
1183    };
1184
1185    // File Types
1186    DropBox type
1187    {
1188       this, text = $"As Type:", visible = false, anchor = { left = 96, right = 104, bottom = 16 }, hotKey = altT;
1189
1190       bool NotifySelect(DropBox control, DataRow row, Modifiers mods)
1191       {
1192          char * selectedFileName = null;
1193          fileType = row ? row.tag : 0;
1194          if(style == save && types && types[fileType].typeExtension)
1195          {
1196             if(getNameFromListBox)
1197             {
1198                if(GetNamesFromRow(listBox.currentRow, &selectedFileName))
1199                   fileName.Clear();
1200             }
1201             else
1202             {
1203                char * fileName = this.fileName.line.text;
1204                //selectedFileName = new char[strlen(fileName)+1];      // Room to change extension???
1205                selectedFileName = new char[MAX_FILENAME];
1206                strcpy(selectedFileName, fileName);
1207             }
1208             ChangeExtension(selectedFileName, types[fileType].typeExtension, selectedFileName);
1209             fileName.Select(null,0,0, null,0,0);
1210             fileName.PutS(selectedFileName);
1211             GetNameFromListBox(false);
1212          }
1213          delete selectedFileName;
1214          return true;
1215       }
1216    };
1217       
1218    Label typeLabel
1219    {
1220       this, inactive = true, visible = false, anchor = { left = 8, bottom = 19 }, labeledWindow = type;
1221    };
1222
1223    // Ok Button
1224    Button ok
1225    {
1226       this, isDefault = true, text = $"OK", anchor = { right = 10, bottom = 32 + 16 - 1 }, size = { 80 };
1227
1228       bool NotifyClicked(Button control, int x, int y, Modifiers mods)
1229       {
1230          bool result;
1231          char * selectedFileName = null;
1232          if(getNameFromListBox)
1233          {
1234             GetNamesFromRow(listBox.currentRow, &selectedFileName);
1235             result = SelectFile(selectedFileName, fromEditBox, control.id == DialogResult::ok);
1236             if(result && style == selectDir)
1237                listBox.MakeActive();
1238          }
1239          else
1240          {
1241             char * fileName = this.fileName.line.text;
1242             selectedFileName = new char[strlen(fileName)+1];
1243             strcpy(selectedFileName, fileName);
1244             result = SelectFile(selectedFileName, fromListBox, control.id == DialogResult::ok);
1245             if(result && style == selectDir)
1246                this.fileName.MakeActive();
1247          }
1248          delete selectedFileName;
1249          return result;
1250       }
1251    };
1252
1253    // Open Button (SelectDir only)
1254    Button open
1255    {
1256       this, visible = false, text = $"Open", hotKey = altO, anchor = { right = 100, bottom = 16 - 1 }, size = { 80 };
1257       NotifyClicked = ok.NotifyClicked;
1258    };
1259
1260    // Cancel Button
1261    Button cancel
1262    {
1263       this, text = $"Cancel", anchor = { right = 10, bottom = 16 - 1 }, size = { 80 }, hotKey = escape;
1264       bool NotifyClicked(Button control, int x, int y, Modifiers mods)
1265       {
1266          Destroy(DialogResult::cancel);
1267          return true;
1268       }
1269    };
1270
1271    // Look In Dropbox
1272    DropBox lookIn
1273    {
1274       this, text = $"Look in:", anchor = { left = 81, top = 8, right = 109 }, hotKey = altL, maxShown = 12;
1275
1276       bool OnKeyHit(Key key, unichar ch)
1277       {
1278          if(key == wheelDown || key == wheelUp)
1279          {
1280             ((FileDialog)master).listBox.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, key, ch);
1281             return false;
1282          }
1283          return true;
1284       }
1285
1286       bool NotifySelect(DropBox control, DataRow row, Modifiers mods)
1287       {
1288          if(row)
1289          {
1290             FileName * fileName = row.GetData(null);
1291             char name[MAX_LOCATION] = "/";
1292             int indent = 0;
1293             int c = 0;
1294          #ifdef __WIN32__
1295             if(!strcmp(fileName->name, msNetwork) ||
1296                (currentDirectory[0] == '\\' &&
1297                 currentDirectory[1] == '\\' &&
1298                 strcmp(fileName->name, rootName) &&
1299                 (!fileName->name[0] || fileName->name[1] != ':')))
1300             {
1301                strcpy(name, "\\\\");
1302                c = 2;
1303             }
1304             else
1305             {
1306                if(fileName->indent > 1)
1307                {
1308                   name[0] = currentDirectory[0];
1309                   name[1] = ':';
1310                   name[2] = '\0';
1311                   indent++;
1312                   c = 2;
1313                }
1314                else if(fileName->indent == 1)
1315                {
1316                   name[0] = fileName->name[0];
1317                   name[1] = ':';
1318                   name[2] = '\0';
1319                   c = 2;
1320                   indent++;
1321                }
1322             }
1323          #endif
1324             if(indent < fileName->indent)
1325             {
1326                for(; currentDirectory[c]; ) 
1327                {
1328                   int len = 0;
1329                   char ch;
1330                   char directory[MAX_FILENAME];
1331                   for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
1332                   for(;(ch = currentDirectory[c]) && (ch != '/' && ch != '\\'); c++)
1333                   {
1334                      if(len < MAX_FILENAME)
1335                         directory[len++] = ch;  
1336                   }
1337                   for(;(ch = currentDirectory[c]) && (ch == '/' || ch == '\\'); c++);
1338                   directory[len] = '\0';
1339
1340                   if(indent >= fileName->indent) break;
1341                   PathCat(name, directory);
1342                   indent++;
1343                }
1344             }
1345
1346             SelectFile(name, fromDropBox, false);
1347          }
1348          return true;
1349       }
1350    };
1351
1352    DataField lookInField { dataType = "FileName", userData = this };
1353    
1354    Label lookInLabel
1355    {
1356       this, position = { 10, 11 }, labeledWindow = lookIn;
1357    };
1358
1359    // Main Listbox
1360    ListBox listBox
1361    {
1362       this, borderStyle = deep, hasVertScroll = true, hasHorzScroll = true,
1363       anchor = { left = 8, right = 8, top = 40, bottom = 64 + 16 },
1364       hasHeader = true, moveFields = true, resizable = true, sortable = true;
1365
1366       bool NotifySelect(ListBox control, DataRow row, Modifiers mods)
1367       {
1368          GetNameFromListBox(true);
1369          return true;
1370       }
1371
1372       bool NotifyDoubleClick(ListBox control, int x, int y, Modifiers mods)
1373       {
1374          bool result = true;
1375          char * selectedFileName = null;
1376          if(GetNamesFromRow(control.currentRow, &selectedFileName))
1377          {
1378             result = SelectFile(selectedFileName, fromEditBox, false);
1379          }
1380          delete selectedFileName;
1381          return result;
1382       }
1383
1384       bool NotifyKeyDown(ListBox control, DataRow row, Key key, unichar ch)
1385       {
1386          if(key == backSpace)
1387          {
1388             if(strcmp(currentDirectory, "/"))
1389                goUp.NotifyClicked(this, goUp, 0,0, key.modifiers);
1390          }
1391          return true;
1392       }
1393
1394       bool NotifySort(ListBox control, DataField field, Modifiers mods)
1395       {
1396          sortField = field;
1397          sortOrder = field.sortOrder;
1398          return true;
1399       }
1400
1401       bool NotifyActivate(Window control, bool active, Window previous)
1402       {
1403          if(active)
1404             GetNameFromListBox(true);
1405          return true;
1406       }
1407    };
1408
1409    DataField nameField { header = $"Name", dataType = "FileName", width = 304, userData = this }; // editable = true
1410    DataField typeField { header = $"Type", dataType = /*"String"*/ "char *", width = 40 };
1411    DataField sizeField { header = $"Size", dataType = "FileSize", width = 96, alignment = right };
1412
1413    // Go up button
1414    Button goUp
1415    {
1416       this, inactive = true, anchor = { right = 79, top = 8 }, size = { 24, 24 },
1417       bitmap = { "<:ecere>actions/goUp.png", alphaBlend = true };
1418       symbol = 30;
1419
1420       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1421       {
1422          char * notRoot;
1423          notRoot = StripLastDirectory(currentDirectory, currentDirectory);
1424          if(!notRoot)
1425             strcpy(currentDirectory, "/");
1426          ListFiles();
1427          return true;
1428       }
1429    };
1430
1431    Button createDirectory
1432    {
1433       this, inactive = true, anchor = { right = 51, top = 8 }, size = { 24, 24 },
1434       bitmap = { "<:ecere>actions/folderNew.png", alphaBlend = true };
1435       symbol = 30;    // what the heck is that?
1436
1437       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1438       {
1439          if(CreateDirectoryDialog { master = this, parent = parent, currentDirectory = currentDirectory }.Modal() == ok )
1440             ListFiles();
1441          return true;
1442       }
1443    };
1444
1445    // File name editbox
1446    EditBox fileName
1447    {
1448       this, text = $"File Name:", anchor = { left = 96, bottom = 32 + 16, right = 104 }, size.h = 20, hotKey = altF;
1449
1450       bool NotifyActivate(Window control, bool active, Window previous)
1451       {
1452          if(active)
1453          {
1454             char * selectedFileName = null;
1455             char * editText;
1456             if(getNameFromListBox)
1457             {
1458                DataRow row = listBox.currentRow;
1459                if(row)
1460                {
1461                   if(GetNamesFromRow(row, &selectedFileName))
1462                   {
1463                      fileName.Clear();
1464                      fileName.PutS(selectedFileName);
1465                      fileName.Select(null,0,0, null,0,0);
1466                   }
1467                   GetNameFromListBox(false);
1468                }
1469             }
1470             delete selectedFileName;
1471             editText = fileName.contents;
1472             ok.disabled = !editText || !editText[0];
1473          }
1474          return true;
1475       }
1476
1477       void NotifyUpdate(EditBox control)
1478       {
1479          GetNameFromListBox(false);
1480       }
1481    };
1482
1483    Label fileNameLabel
1484    {
1485       this, inactive = true, anchor = { left = 8, bottom = 35 + 16 };
1486       labeledWindow = fileName;
1487    };
1488 };
1489
1490 public class CreateDirectoryDialog : Window
1491 {
1492    background = formColor;
1493    minClientSize = Size { 240, 100 };
1494    tabCycle = true;
1495    hasClose = true;
1496    text = $"Create Directory";
1497
1498 public:
1499
1500    property char * currentDirectory
1501    {
1502       set
1503       {
1504          GetWorkingDir(currentDirectory, MAX_DIRECTORY);  // is this necessary?
1505          PathCat(currentDirectory, value);
1506          FileFixCase(currentDirectory);
1507       }
1508       get { return (char *)currentDirectory; }
1509    };
1510
1511 private:
1512
1513    char currentDirectory[MAX_DIRECTORY];
1514
1515    CreateDirectoryDialog()
1516    {
1517       FileNameType c;
1518
1519       GetWorkingDir(currentDirectory, MAX_DIRECTORY);
1520       FileFixCase(currentDirectory);
1521    }
1522
1523    ~CreateDirectoryDialog()
1524    {
1525    }
1526
1527    bool OnPostCreate()
1528    {
1529       newDirectoryName.SelectAll();
1530       return true;
1531    }
1532
1533    Button ok
1534    {
1535       parent = this, isDefault = true, position = { 70, 60 }, size = { 60 }, text = $"OK";
1536       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1537       {
1538          if(newDirectoryName.contents && newDirectoryName.contents[0])
1539          {
1540             char newDirPath[MAX_DIRECTORY];
1541             strcpy(newDirPath, currentDirectory);
1542             PathCat(newDirPath, newDirectoryName.contents);
1543             if(!FileExists(newDirPath).isDirectory)
1544             {
1545                MakeDir(newDirPath);
1546                Destroy(DialogResult::ok);
1547             }
1548             else
1549                MessageBox { master = this, parent = parent, type = ok, text = $"Create Directory Error", contents = $"Directory already exists." }.Modal();
1550          }
1551          else
1552             MessageBox { master = this, parent = parent, type = ok, text = $"Create Directory Error", contents = $"Please enter a name." }.Modal();
1553          return true;
1554       }
1555    };
1556    
1557    Button cancel
1558    {
1559       parent = this, position = { 140, 60 }, size = { 60 }, hotKey = escape, text = $"Cancel";
1560       NotifyClicked = ButtonCloseDialog;
1561    };
1562
1563    EditBox newDirectoryName
1564    {
1565       this, textHorzScroll = true, anchor = { left = 10, right = 10, top = 30 }, size = { 250 };
1566       hotKey = altN, text = $"Name";
1567       contents = $"New Directory";
1568    };
1569    Label { this, position = { 10, 10 }, labeledWindow = newDirectoryName };
1570
1571 }