07e8291ab331233fd63b8009941859d406713711
[sdk] / ide / src / dialogs / GlobalSettingsDialog.ec
1 import "IDESettings"
2
3 // import "SelectorBar"
4 import "CompilersDetectionDialog"
5 import "ide"
6
7 FileDialog settingsFileDialog { type = selectDir, text = $"Select directory" };
8
9 FileDialog toolchainFileDialog { type = open, text = $"Open"; mayNotExist = true; };
10
11 class GlobalSettingsDialog : Window
12 {
13    autoCreate = false;
14    tabCycle = true;
15    background = formColor;
16    hasClose = true;
17    borderStyle = sizable;
18    text = $"Global Settings";
19    minClientSize = { 560, 542 };
20    nativeDecorations = true;
21
22    IDESettings ideSettings;
23    IDESettingsContainer settingsContainer;
24    String workspaceActiveCompiler;
25
26    TabControl tabControl { this, background = formColor, anchor = { left = 8, top = 8, right = 8, bottom = 40 } };
27
28    EditorTab editorTab { this, tabControl = tabControl };
29    CompilersTab compilersTab { this, tabControl = tabControl };
30    ProjectOptionsTab projectOptionsTab { this, tabControl = tabControl };
31    WorkspaceOptionsTab workspaceOptionsTab { this, tabControl = tabControl };
32
33    property bool settingsModified
34    {
35       get
36       {
37          return editorTab.modifiedDocument || compilersTab.modifiedDocument ||
38                projectOptionsTab.modifiedDocument || workspaceOptionsTab.modifiedDocument;
39       }
40    }
41
42    bool OnClose(bool parentClosing)
43    {
44       if(!settingsModified || MessageBox {
45          type = okCancel, master = ide,
46          text = $"Lose Changes?",
47          contents = $"Are you sure you wish to discard changes?"
48           }.Modal() == ok)
49          return true;
50       return false;
51    }
52
53    Button cancel
54    {
55       parent = this, hotKey = escape, text = $"Cancel", id = DialogResult::cancel;
56       position = { 290, 290 }, size = { 80 };
57       anchor = { right = 8, bottom = 8 };
58       NotifyClicked = ButtonCloseDialog;
59    };
60
61    Button ok
62    {
63       parent = this, isDefault = true, text = $"OK";
64       position = { 200, 290 }, size = { 90 };
65       anchor = { right = 96, bottom = 8 };
66
67       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
68       {
69          if(settingsModified)
70          {
71             bool editorSettingsChanged = false;
72             bool compilerSettingsChanged = false;
73             bool projectOptionsChanged = false;
74             AVLTree<String> cfgsToWrite = null;
75             if(editorTab.modifiedDocument)
76             {
77                if(editorTab.useFreeCaret.checked != ideSettings.useFreeCaret ||
78                      editorTab.showLineNumbers.checked != ideSettings.showLineNumbers ||
79                      editorTab.caretFollowsScrolling.checked != ideSettings.caretFollowsScrolling)
80                {
81                   ideSettings.useFreeCaret = editorTab.useFreeCaret.checked;
82                   ideSettings.showLineNumbers = editorTab.showLineNumbers.checked;
83                   ideSettings.caretFollowsScrolling = editorTab.caretFollowsScrolling.checked;
84                   editorSettingsChanged = true;
85                }
86             }
87
88             if(compilersTab.modifiedDocument)
89             {
90                if(strcmp(compilersTab.compilerConfigsDir.path, ideSettings.compilerConfigsDir))
91                   ideSettings.compilerConfigsDir = compilersTab.compilerConfigsDir.path;
92                if(compilersTab.compilerConfigs.OnCompare(ideConfig.compilers))
93                {
94                   cfgsToWrite = compilersTab.compilerConfigs.getWriteRequiredList(ideConfig.compilers);
95                   ideConfig.compilers.Free();
96                   for(compiler : compilersTab.compilerConfigs)
97                   {
98                      ideConfig.compilers.Add(compiler.Copy());
99                   }
100                   compilerSettingsChanged = true;
101                }
102             }
103
104             if(projectOptionsTab.modifiedDocument)
105             {
106                if(strcmp(projectOptionsTab.defaultTargetDir.path, ideSettings.projectDefaultTargetDir)
107                      || strcmp(projectOptionsTab.defaultIntermediateObjDir.path, ideSettings.projectDefaultIntermediateObjDir))
108                {
109                   ideSettings.projectDefaultTargetDir = projectOptionsTab.defaultTargetDir.path;
110                   ideSettings.projectDefaultIntermediateObjDir = projectOptionsTab.defaultIntermediateObjDir.path;
111                   projectOptionsChanged = true;
112                }
113             }
114
115             if(workspaceOptionsTab.modifiedDocument)
116             {
117                DataRow row = workspaceOptionsTab.defaultCompilerDropBox.currentRow;
118                if(row && row.string)
119                {
120                   if(!ideSettings.defaultCompiler || strcmp(row.string, ideSettings.defaultCompiler))
121                   {
122                      ideSettings.defaultCompiler = row.string;
123                   }
124                }
125             }
126
127             if(editorSettingsChanged || projectOptionsChanged)
128                settingsContainer.Save();
129
130             if(compilerSettingsChanged)
131             {
132                ideConfig.compilers.write(cfgsToWrite);
133                OnGlobalSettingChange(GlobalSettingsChange::compilerSettings);
134                cfgsToWrite.Free();
135                delete cfgsToWrite;
136             }
137             if(editorSettingsChanged)
138                OnGlobalSettingChange(GlobalSettingsChange::editorSettings);
139             if(projectOptionsChanged)
140                OnGlobalSettingChange(GlobalSettingsChange::projectOptions);
141
142             editorTab.modifiedDocument = false;
143             compilersTab.modifiedDocument = false;
144             projectOptionsTab.modifiedDocument = false;
145             workspaceOptionsTab.modifiedDocument = false;
146          }
147
148          Destroy(DialogResult::ok);
149          return true;
150       }
151    };
152
153    /*
154    void Temp()
155    {
156       DirTypes c;
157       for(c = 0; c < DirTypes::enumSize; c++)
158       {
159          CompilerDir compilerDir;
160
161          for(systemDir : ideSettings.systemDirs[c])
162          {
163             compilerDir = CompilerDir { type = c; compilerConfig = null; path = CopyString(systemDir) };
164             dirs.Add(compilerDir);
165          }
166
167          row = compilersTab.dirs[c].AddRow();
168          row.SetData(null, "");
169          compilersTab.dirs[c].currentRow = compilersTab.dirs[c].firstRow;
170          compilersTab.dirs[c].modifiedDocument = false;
171       }
172    }
173    */
174
175    bool OnCreate()
176    {
177       CompilerConfig activateCompiler = null;
178       CompilerConfig readonlyCompiler = null;
179
180       // EditorTab
181       editorTab.useFreeCaret.checked = ideSettings.useFreeCaret;
182       editorTab.showLineNumbers.checked = ideSettings.showLineNumbers;
183       editorTab.caretFollowsScrolling.checked = ideSettings.caretFollowsScrolling;
184
185       // CompilersTab
186       if(workspaceActiveCompiler)
187       {
188          for(compiler : ideConfig.compilers)
189          {
190             if(!activateCompiler && !strcmp(workspaceActiveCompiler, compiler.name))
191                activateCompiler = compiler;
192             if(!readonlyCompiler && compiler.readOnly)
193                readonlyCompiler = compiler;
194             if(activateCompiler && readonlyCompiler)
195                break;
196          }
197       }
198       if(!activateCompiler && readonlyCompiler)
199          activateCompiler = readonlyCompiler;
200       if(!activateCompiler && ideConfig.compilers.count)
201          activateCompiler = ideConfig.compilers[0];
202
203       for(compiler : ideConfig.compilers)
204          compilersTab.AddCompiler(compiler.Copy(), compiler == activateCompiler);
205       compilersTab.compilerConfigsDir.path = ideSettings.compilerConfigsDir;
206
207       // ProjectOptionsTab
208       projectOptionsTab.defaultTargetDir.path = ideSettings.projectDefaultTargetDir;
209       projectOptionsTab.defaultIntermediateObjDir.path = ideSettings.projectDefaultIntermediateObjDir;
210
211       return true;
212    }
213
214    void OnDestroy()
215    {
216       editorTab.modifiedDocument = false;
217       compilersTab.modifiedDocument = false;
218       compilersTab.dirsTab.modifiedDocument = false;
219       compilersTab.toolchainTab.modifiedDocument = false;
220       compilersTab.optionsTab.modifiedDocument = false;
221       compilersTab.activeCompiler = null;
222       compilersTab.compilerConfigs.Free();
223       compilersTab.compilerSelector.Clear();
224       projectOptionsTab.modifiedDocument = false;
225       workspaceOptionsTab.modifiedDocument = false;
226    }
227
228    virtual void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange);
229 }
230
231 class EditorTab : GlobalSettingsSubTab
232 {
233    background = formColor;
234    text = $"Editor";
235
236    Button useFreeCaret
237    {
238       this, text = $"Move code editor caret freely past end of line", position = { 16, 68 }, isCheckbox = true;
239       NotifyClicked = NotifyClickedModifiedDocument;
240    };
241
242    Button caretFollowsScrolling
243    {
244       this, text = $"Keep caret visible (move along) when scrolling", position = { 16, 88 }, isCheckbox = true;
245       NotifyClicked = NotifyClickedModifiedDocument;
246    };
247
248    Button showLineNumbers
249    {
250       this, text = $"Show line numbers in code editor", position = { 16, 108 }, isCheckbox = true;
251       NotifyClicked = NotifyClickedModifiedDocument;
252    };
253
254    bool NotifyClickedModifiedDocument(Button button, int x, int y, Modifiers mods)
255    {
256       modifiedDocument = true;
257       return true;
258    }
259 }
260
261 static void DrawStipple(Surface surface, Size clientSize)
262 {
263    int x1 = 0;
264    int y1 = 0;
265    int x2 = clientSize.w - 1;
266    int y2 = clientSize.h - 1;
267    if((x2 - x1) & 1) x2--;
268    if((y2 - y1) & 1) y2--;
269
270    surface.LineStipple(0x5555);
271    surface.Rectangle(x1, y1, x2, y2);
272    surface.LineStipple(0);
273 }
274
275 class CompilersTab : GlobalSettingsSubTab
276 {
277    background = formColor;
278    text = $"Compilers";
279
280    Label compilerConfigsDirLabel { this, position = { 8, 12 }, labeledWindow = compilerConfigsDir, tabCycle = false, inactive = true };
281    PathBox compilerConfigsDir
282    {
283       this, anchor = { left = 230, top = 8, right = 8 };
284       text = $"Compiler Configurations Directory", browseDialog = settingsFileDialog, NotifyModified = NotifyModifiedDocument;
285    };
286
287    SelectorBar compilerSelector
288    {
289       this, text = $"Compiler Configurations:", anchor = { left = 148, top = 38, right = 99 }; size = { 0, 26 };
290       opacity = 0;
291       direction = horizontal;
292       scrollable = true;
293       endButtons = false;
294       hoverScroll = true;
295
296       bool OnKeyDown(Key key, unichar ch)
297       {
298          if(key == insert)
299          {
300             ((CompilersTab)parent).createCompiler.NotifyClicked(parent, ((CompilersTab)parent).createCompiler, 0, 0, 0);
301             return false;
302          }
303          else if(key == del)
304          {
305             ((CompilersTab)parent).deleteCompiler.NotifyClicked(parent, ((CompilersTab)parent).deleteCompiler, 0, 0, 0);
306             return false;
307          }
308          return SelectorBar::OnKeyDown(key, ch);
309       }
310
311       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
312       {
313          ((CompilersTab)master).labelCompilers.Update(null);
314          return true;
315       }
316
317       bool OnPostCreate()
318       {
319          CompilersTab compilers = (CompilersTab)parent;
320          SelectorBar::OnPostCreate();
321          if(compilers.selectedButton)
322          {
323             Button sb = compilers.selectedButton;
324             sb.Activate();
325             sb.checked = true;
326             // Why was this being set to null? On going back to compilers the 2nd time, the selectedButton was lost and so was not made visible...
327             // compilers.selectedButton = null;
328          }
329          return true;
330       }
331    };
332
333    TabControl tabControl { this, background = formColor, anchor = { left = 8, top = 68, right = 8, bottom = 8 } };
334
335    CompilerDirectoriesTab dirsTab { this, tabControl = tabControl };
336    CompilerToolchainTab toolchainTab { this, tabControl = tabControl };
337    CompilerEnvironmentTab environmentTab { this, tabControl = tabControl };
338    CompilerOptionsTab optionsTab { this, tabControl = tabControl };
339
340    CompilerConfigs compilerConfigs { };
341    CompilerConfig activeCompiler;
342
343    Label labelCompilers
344    {
345       this, anchor = { left = 8, top = 44 }, labeledWindow = compilerSelector;
346
347       void OnRedraw(Surface surface)
348       {
349          Label::OnRedraw(surface);
350          if(labeledWindow.active)
351             DrawStipple(surface, clientSize);
352       }
353    };
354
355    void FindUniqueCompilerName(const char * baseName, CompilerConfig compiler/*, bool startWithNumber*/, char * output)
356    {
357       int num = 0;
358       char tmp[MAX_F_STRING];
359       /*if(startWithNumber)
360          sprintf(tmp, "%s%d", baseName, num);
361       else*/
362          strcpy(tmp, baseName);
363       while(true)
364       {
365          CompilerConfig matchingCompiler = null;
366          for(c : compilerConfigs)
367          {     // TOFIX: Error when omitting these brackets, c not found
368             if((!compiler || c != compiler) && c.name && !strcmp(c.name, tmp))
369             {
370                matchingCompiler = c;
371                break;
372             }
373          }
374          if(matchingCompiler)
375          {
376             num++;
377             sprintf(tmp, "%s%d", baseName, num);
378          }
379          else
380             break;
381       }
382       strcpy(output, tmp);
383    }
384
385    Button createCompiler
386    {
387       parent = this, bevelOver = true, inactive = true;
388       size = { 22, 22 };
389       anchor = { top = 40, right = 77 };
390       hotKey = altC, bitmap = BitmapResource { fileName = ":actions/docNew.png", alphaBlend = true };
391
392       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
393       {
394          char compilerName[MAX_F_STRING];
395          CompilerConfig newCompiler;
396          FindUniqueCompilerName("New Compiler", null, compilerName);
397          newCompiler = MakeDefaultCompiler(compilerName, false);
398          AddCompiler(newCompiler, true);
399          modifiedDocument = true;
400          return true;
401       }
402    };
403    Button detectCompiler
404    {
405       parent = this, bevelOver = true, inactive = true;
406       size = { 22, 22 };
407       anchor = { top = 40, right = 54 };
408       hotKey = altC, bitmap = BitmapResource { fileName = ":actions/attach.png", alphaBlend = true };
409
410       bool NotifyClicked(Button b, int x, int y, Modifiers mods)
411       {
412          CompilersDetectionDialog compilersDetectionDialog
413          {
414             dialog.parent;
415
416          };
417          if(compilersDetectionDialog.Modal())
418          {
419             if(compilersDetectionDialog.selectedCompilerType)
420             {
421                char uniqueName[MAX_F_STRING];
422                CompilerConfig newCompiler = compilersDetectionDialog.compilerConfig;
423                FindUniqueCompilerName(newCompiler.name, null, uniqueName);
424                newCompiler.name = uniqueName;
425                AddCompiler(newCompiler, true);
426                modifiedDocument = true;
427             }
428          }
429          return true;
430       }
431    };
432    Button duplicateCompiler
433    {
434       parent = this, bevelOver = true, inactive = true;
435       size = { 22, 22 };
436       anchor = { top = 40, right = 31 };
437       hotKey = altU, bitmap = BitmapResource { fileName = ":actions/editCopy.png", alphaBlend = true };
438
439       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
440       {
441          char copyName[MAX_F_STRING];
442          CompilerConfig copyCompiler = activeCompiler.Copy();
443          FindUniqueCompilerName(copyCompiler.name, null, copyName);
444          copyCompiler.readOnly = false;
445          copyCompiler.name = copyName;
446          AddCompiler(copyCompiler, true);
447          modifiedDocument = true;
448          return true;
449       }
450    };
451    Button deleteCompiler
452    {
453       parent = this, bevelOver = true, inactive = true;
454       size = { 22, 22 };
455       anchor = { top = 40, right = 8 };
456       hotKey = altD, bitmap = BitmapResource { fileName = ":actions/delete2.png", alphaBlend = true };
457
458       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
459       {
460          if(activeCompiler)
461          {
462             CompilerConfig compilerToDelete = activeCompiler;
463             String title = PrintString($"Delete ", compilerToDelete.name, $" Compiler Configuration");
464             String msg = PrintString($"Are you sure you wish to delete the ", compilerToDelete.name, $" compiler configuration?");
465             if(MessageBox { type = okCancel, text = title, contents = msg }.Modal() == ok)
466             {
467                SelectorButton button = compilerSelector.FindButtonByID((int64)(intptr)compilerToDelete);
468                if(button)
469                   compilerSelector.RemoveButton(button);
470                //DeleteCompiler(compilerToDelete);
471                {
472                   Iterator<CompilerConfig> it { compilerConfigs };
473                   if(it.Find(compilerToDelete))
474                      compilerConfigs.Delete(it.pointer);
475                }
476                modifiedDocument = true;
477             }
478             delete title;
479             delete msg;
480          }
481          return true;
482       }
483    };
484
485    void AddCompiler(CompilerConfig compiler, bool load)
486    {
487       SelectorButton selectButton;
488       if(compiler.readOnly)
489       {
490          SelectorButton button
491          {
492             compilerSelector, master = this, text = compiler.name, id = (int64)(intptr)compiler;
493             NotifyClicked = CompilerClicked;
494          };
495          selectButton = button;
496       }
497       else
498       {
499          EditableSelectorButton button
500          {
501             compilerSelector, master = this, renameable = true, text = compiler.name, id = (int64)(intptr)compiler;
502             NotifyClicked = CompilerClicked;
503
504             bool OnRename(EditableSelectorButton button, char ** oldName, char ** newName)
505             {
506                if(*newName && (*newName)[0])
507                {
508                   char compilerName[MAX_F_STRING];
509                   FindUniqueCompilerName(*newName, activeCompiler, compilerName);
510                   if(strcmp(*newName, compilerName))
511                   {
512                      delete *newName;
513                      *newName = CopyString(compilerName);
514                   }
515                   activeCompiler.name = compilerName;
516                   modifiedDocument = true;
517                   return true;
518                }
519                return false;
520             }
521          };
522          selectButton = (SelectorButton)button;
523       }
524       compilerConfigs.Add(compiler);
525       if(load)
526       {
527          LoadCompiler(compiler);
528          selectedButton = selectButton;
529          compilerSelector.Select(selectedButton);
530       }
531    }
532    SelectorButton selectedButton;
533
534    void LoadCompiler(CompilerConfig compiler)
535    {
536       bool modified = modifiedDocument;
537       activeCompiler = compiler;
538
539       dirsTab.Load();
540       toolchainTab.Load();
541       environmentTab.Load();
542       optionsTab.Load();
543
544       // Restore original modifiedDocument
545       modifiedDocument = modified;
546
547       deleteCompiler.disabled = compiler.readOnly;
548    }
549
550    bool CompilerClicked(Button clickedButton, int x, int y, Modifiers mods)
551    {
552       if(!eClass_IsDerived(clickedButton._class, class(EditableSelectorButton)) || !((EditableSelectorButton)clickedButton).editBox)
553       {
554          LoadCompiler((CompilerConfig)(intptr)clickedButton.id);
555          selectedButton = (SelectorButton)clickedButton;
556       }
557       return true;
558    }
559
560    bool NotifyModifiedDocument(PathBox pathBox)
561    {
562       BasicValidatePathBoxPath(pathBox);
563       modifiedDocument = true;
564       return true;
565    }
566 }
567
568 Array<const String> displayDirectoryNames
569 { [
570    $"Include Files",
571    $"Library Files",
572    $"Executable Files"
573 ] };
574
575 class CompilerDirectoriesTab : CompilersSubTab
576 {
577    background = formColor;
578    text = $"Directories";
579
580    Button dirTypeTglBtn[DirTypes];
581    DirectoriesBox dirs[DirTypes], currentDirs;
582
583    ~CompilerDirectoriesTab()
584    {
585       DirTypes c;
586       for(c = 0; c < DirTypes::enumSize; c++)
587       {
588          delete dirs[c];
589          delete dirTypeTglBtn[c];
590       }
591    }
592    CompilerDirectoriesTab()
593    {
594       DirTypes c;
595       int v = 8;
596       for(c = 0; c < DirTypes::enumSize; c++)
597       {
598          dirs[c] = DirectoriesBox
599          {
600             this;//, alwaysHighLight = true
601             anchor = { left = 8, top = 8, right = 8, bottom = 8 };
602             id = c;
603
604    /*   MAKE SURE THINGS ARE DONE PROPERLY IN THE NEW DIRECTORIES BOX WHEN BROWSING FOR A DIR?
605             settingsFileDialog.filePath = directory;
606          if(settingsFileDialog.Modal())
607             row.SetData(null, (s = CopyUnixPath(settingsFileDialog.filePath)));
608    */
609
610             bool NotifyModified(DirectoriesBox dirsBox)
611             {
612                CompilerConfig compiler = loadedCompiler;
613                if(compiler)
614                {
615                   DirTypes dirType = (DirTypes)dirsBox.id;
616                   if(dirType == includes)
617                      compiler.includeDirs = dirsBox.strings;
618                   else if(dirType == libraries)
619                      compiler.libraryDirs = dirsBox.strings;
620                   else if(dirType == executables)
621                      compiler.executableDirs = dirsBox.strings;
622
623                   compilersTab.modifiedDocument = true;
624                }
625                return true;
626             }
627             bool NotifyPathBoxModified(DirectoriesBox dirsBox, PathBox pathBox)
628             {
629                BasicValidatePathBoxPath(pathBox);
630                return true;
631             }
632          };
633          incref dirs[c];
634
635          if(c)
636             dirs[c].visible = false;
637
638          // (width) Should be 324 for text...
639          //field[c] = { dataType = class(char *), editable = true };
640          //dirs[c].AddField(field[c]);
641
642          dirTypeTglBtn[c] = Button
643          {
644             this, inactive = true, text = displayDirectoryNames[c], bevelOver = true, isRadio = true, bitmap = null;
645             stayOnTop = true;
646             id = c;
647             minClientSize = { 99, 20 };
648             anchor = { left = v, top = 8 }; // ((int)c) * 100 + 8
649
650             bool NotifyClicked(Button button, int x, int y, Modifiers mods)
651             {
652                DirTypes dirType = (DirTypes)button.id;
653                currentDirs.visible = false;
654                dirs[dirType].visible = true;
655                currentDirs = dirs[dirType];
656                return true;
657             }
658          };
659          v += dirTypeTglBtn[c].size.w + 1;
660          incref dirTypeTglBtn[c];
661
662          if(c == includes)
663             dirTypeTglBtn[c].hotKey = altI;
664          else if(c == libraries)
665             dirTypeTglBtn[c].hotKey = altL;
666          else if(c == executables)
667             dirTypeTglBtn[c].hotKey = altE;
668       }
669       currentDirs = dirs[includes];
670       dirTypeTglBtn[includes].checked = true;
671       return true;
672    }
673
674    bool OnLoadGraphics()
675    {
676       DirTypes c;
677       int v = 8;
678       for(c = 0; c < DirTypes::enumSize; c++)
679       {
680          dirTypeTglBtn[c].anchor.left = v;
681          v += dirTypeTglBtn[c].size.w + 1;
682       }
683       return CompilersSubTab::OnLoadGraphics();
684    }
685
686    void Load()
687    {
688       CompilerConfig compiler = loadedCompiler;
689       if(compiler)
690       {
691          dirs[includes].strings = compiler.includeDirs;
692          dirs[libraries].strings = compiler.libraryDirs;
693          dirs[executables].strings = compiler.executableDirs;
694          dirs[includes].list.scroll = { 0, 0 };
695          dirs[libraries].list.scroll = { 0, 0 };
696          dirs[executables].list.scroll = { 0, 0 };
697       }
698    }
699 }
700
701 class CompilerToolchainTab : CompilersSubTab
702 {
703    background = formColor;
704    text = $"Toolchain";
705
706    int margin;
707    margin = 130;
708
709    Label ecpLabel { this, position = { 8, 12 }, labeledWindow = ecp, tabCycle = false, inactive = true };
710    PathBox ecp
711    {
712       this, anchor = { left = margin, top = 8, right = 8 };
713       text = $"eC Precompiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
714    };
715    Label eccLabel { this, position = { 8, 38 }, labeledWindow = ecc, tabCycle = false, inactive = true };
716    PathBox ecc
717    {
718       this, anchor = { left = margin, top = 34, right = 8 };
719       text = $"eC Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
720    };
721    Label ecsLabel { this, position = { 8, 64 }, labeledWindow = ecs, tabCycle = false, inactive = true };
722    PathBox ecs
723    {
724       this, anchor = { left = margin, top = 60, right = 8 };
725       text = $"eC Symbol Loader", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
726    };
727    Label earLabel { this, position = { 8, 90 }, labeledWindow = ear, tabCycle = false, inactive = true };
728    PathBox ear
729    {
730       this, anchor = { left = margin, top = 86, right = 8 };
731       text = $"Ecere Archiver", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
732    };
733    Label cppLabel { this, position = { 8, 116 }, labeledWindow = cpp, tabCycle = false, inactive = true };
734    EditBox cpp
735    {
736       this, anchor = { left = margin, top = 112, right = 8 };
737       //text = $"C Preprocessor", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
738       text = $"C Preprocessor";//, NotifyModified = NotifyModifiedDocument;
739       bool NotifyModified(EditBox editBox)
740       {
741          CompilerConfig compiler = loadedCompiler;
742          if(compiler)
743          {
744             compiler.cppCommand = editBox.contents;
745             modifiedDocument = true;
746             compilersTab.modifiedDocument = true;
747          }
748          return true;
749       }
750    };
751    Label ccLabel { this, position = { 8, 142 }, labeledWindow = cc, tabCycle = false, inactive = true };
752    PathBox cc
753    {
754       this, anchor = { left = margin, top = 138, right = 8 };
755       text = $"C Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
756    };
757    Label cxxLabel { this, position = { 8, 168 }, labeledWindow = cxx, tabCycle = false, inactive = true };
758    PathBox cxx
759    {
760       this, anchor = { left = margin, top = 164, right = 8 };
761       text = $"C++ Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
762    };
763    Label arLabel { this, position = { 8, 194 }, labeledWindow = ar, tabCycle = false, inactive = true };
764    PathBox ar
765    {
766       this, anchor = { left = margin, top = 190, right = 8 };
767       text = $"AR", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
768    };
769    Label ldLabel { this, position = { 8, 220 }, labeledWindow = ld, tabCycle = false, inactive = true };
770    PathBox ld
771    {
772       this, anchor = { left = margin, top = 216, right = 8 };
773       text = $"Linker", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
774    };
775    Label makeLabel { this, position = { 8, 246 }, labeledWindow = make, tabCycle = false, inactive = true };
776    PathBox make
777    {
778       this, anchor = { left = margin, top = 242, right = 8 };
779       text = $"GNU Make", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
780    };
781    Label gnuToolchainPrefixLabel { this, position = { 8, 272 }, labeledWindow = gnuToolchainPrefix, tabCycle = false, inactive = true };
782    PathBox gnuToolchainPrefix
783    {
784       this, anchor = { left = margin, top = 268, right = 8 };
785       text = $"GNU Toolchain Prefix", typeExpected = directory, browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
786    };
787    Label sysrootLabel { this, position = { 8, 298 }, labeledWindow = sysroot, tabCycle = false, inactive = true };
788    PathBox sysroot
789    {
790       this, anchor = { left = margin, top = 294, right = 8 };
791       text = $"SYSROOT", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
792    };
793    Label executableLauncherLabel { this, position = { 8, 324 }, labeledWindow = executableLauncher, tabCycle = false, inactive = true };
794    PathBox executableLauncher
795    {
796       this, anchor = { left = margin, top = 320, right = 8 };
797       text = $"Executable Launcher", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
798    };
799
800    bool NotifyModifiedDocument(PathBox pathBox)
801    {
802       CompilerConfig compiler = loadedCompiler;
803       if(compiler)
804       {
805          BasicValidatePathBoxPath(pathBox);
806          if(pathBox == ecp)
807             compiler.ecpCommand = pathBox.slashPath;
808          else if(pathBox == ecc)
809             compiler.eccCommand = pathBox.slashPath;
810          else if(pathBox == ecs)
811             compiler.ecsCommand = pathBox.slashPath;
812          else if(pathBox == ear)
813             compiler.earCommand = pathBox.slashPath;
814          /*else if(pathBox == cpp)
815             compiler.cppCommand = pathBox.slashPath;*/
816          else if(pathBox == cc)
817             compiler.ccCommand = pathBox.slashPath;
818          else if(pathBox == cxx)
819             compiler.cxxCommand = pathBox.slashPath;
820          else if(pathBox == ld)
821             compiler.ldCommand = pathBox.slashPath;
822          else if(pathBox == ar)
823             compiler.arCommand = pathBox.slashPath;
824          else if(pathBox == make)
825             compiler.makeCommand = pathBox.slashPath;
826          else if(pathBox == executableLauncher)
827             compiler.executableLauncher = pathBox.slashPath;
828          else if(pathBox == gnuToolchainPrefix)
829             compiler.gccPrefix = pathBox.slashPath;
830          else if(pathBox == sysroot)
831             compiler.sysroot = pathBox.slashPath;
832          modifiedDocument = true;
833          compilersTab.modifiedDocument = true;
834       }
835       return true;
836    }
837
838    void Load()
839    {
840       CompilerConfig compiler = loadedCompiler;
841       if(compiler)
842       {
843          bool disabled = compiler.readOnly;
844          bool isVC = compiler.type.isVC;
845          ecp.path = compiler.ecpCommand;
846          ecc.path = compiler.eccCommand;
847          ecs.path = compiler.ecsCommand;
848          ear.path = compiler.earCommand;
849          //cpp.path = compiler.cppCommand;
850          cpp.contents = compiler.cppCommand;
851          cc.path = compiler.ccCommand;
852          cxx.path = compiler.cxxCommand;
853          ld.path = compiler.ldCommand;
854          ar.path = compiler.arCommand;
855          make.path = compiler.makeCommand;
856          executableLauncher.path = compiler.executableLauncher;
857          gnuToolchainPrefix.path = compiler.gnuToolchainPrefix;
858          sysroot.path = compiler.sysroot;
859
860          ecpLabel.disabled = ecp.disabled = disabled;
861          eccLabel.disabled = ecc.disabled = disabled;
862          ecsLabel.disabled = ecs.disabled = disabled;
863          earLabel.disabled = ear.disabled = disabled;
864          cppLabel.disabled = cpp.disabled = isVC || disabled;
865          cxxLabel.disabled = cxx.disabled = isVC || disabled;
866          ccLabel.disabled = cc.disabled = isVC || disabled;
867          ldLabel.disabled = ld.disabled = isVC || disabled;
868          arLabel.disabled = ar.disabled = isVC || disabled;
869          makeLabel.disabled = make.disabled = disabled;
870          executableLauncherLabel.disabled = executableLauncher.disabled = disabled;
871          gnuToolchainPrefixLabel.disabled = gnuToolchainPrefix.disabled = disabled;
872          sysrootLabel.disabled = sysroot.disabled = disabled;
873       }
874       modifiedDocument = false;
875    }
876 }
877
878 class CompilerEnvironmentTab : CompilersSubTab
879 {
880    background = formColor;
881    text = $"Environment";
882
883    Label labelEnvVars { envVars, labeledWindow = envVars, position = { 0, 6 }; };
884    NamedStringsBox envVars
885    {
886       this, size = { 290, 22 }, anchor = { left = 8, top = 8, right = 8, bottom = 8 };
887       text = $"Environment Variables", hotKey = altE; //, option = OPTION(postbuildCommands);
888
889       bool NotifyModified(NamedStringsBox stringsBox)
890       {
891          CompilerConfig compiler = loadedCompiler;
892          if(compiler)
893          {
894             compiler.environmentVars = stringsBox.namedStrings;
895             modifiedDocument = true;
896             compilersTab.modifiedDocument = true;
897          }
898          return true;
899       }
900    };
901
902    CompilerEnvironmentTab()
903    {
904    }
905
906    void Load()
907    {
908       CompilerConfig compiler = loadedCompiler;
909       if(compiler)
910       {
911          envVars.namedStrings = compiler.environmentVars;
912
913          modifiedDocument = false;
914       }
915    }
916 }
917
918 class CompilerOptionsTab : CompilersSubTab
919 {
920    background = formColor;
921    text = $"Options";
922
923    Label labelTargetPlatform { this, position = { 8, 12 }, labeledWindow = targetPlatform };   // TOCHECK: nameless instances dissapear when selecting tabs?
924    DropBox targetPlatform
925    {
926       this, position = { 110, 8 }, size = { 160 };
927       text = $"Target Platform", hotKey = altT;
928       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
929       {
930          CompilerConfig compiler = loadedCompiler;
931          if(compiler && row)
932          {
933             compiler.targetPlatform = (Platform)row.tag;
934             modifiedDocument = true;
935             compilersTab.modifiedDocument = true;
936          }
937          return true;
938       }
939    };
940
941    int numJobs;
942    Label numJobsLabel { this, position = { 8, 40 }, labeledWindow = numJobsBox };
943    DataBox numJobsBox
944    {
945       this, text = $"Number of parallel build jobs", hotKey = altJ, borderStyle = deep;
946       position = { 244, 36 }, size = { 80, 20 }, type = class(int), data = &numJobs;
947
948       bool OnKeyDown(Key key, unichar ch)
949       {
950          if((SmartKey)key == enter)
951          {
952             DataBox::OnKeyDown(key, ch);
953             return true;
954          }
955          else
956             return DataBox::OnKeyDown(key, ch);
957       }
958
959       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
960       {
961          if(!active)
962          {
963             if(!SaveData())
964                Refresh();
965          }
966          return true;
967       }
968
969       bool NotifyChanged(DataBox dataBox, bool closingDropDown)
970       {
971          CompilerConfig compiler = loadedCompiler;
972          if(compiler)
973          {
974             compiler.numJobs = numJobs;
975             modifiedDocument = true;
976             compilersTab.modifiedDocument = true;
977          }
978          return true;
979       }
980    };
981
982    Button ccacheEnabled
983    {
984       this, text = $"Use ccache", hotKey = altC, position = { 8, 68 };
985       isCheckbox = true;
986
987       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
988       {
989          CompilerConfig compiler = loadedCompiler;
990          if(compiler)
991          {
992             compiler.ccacheEnabled = button.checked;
993             modifiedDocument = true;
994             compilersTab.modifiedDocument = true;
995          }
996          return true;
997       }
998    };
999
1000    Button distccEnabled
1001    {
1002       this, text = $"Use distcc", position = { 158, 68 };
1003       isCheckbox = true;
1004
1005       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1006       {
1007          CompilerConfig compiler = loadedCompiler;
1008          if(compiler)
1009          {
1010             distccHostsLabel.disabled = distccHosts.disabled = !button.checked;
1011             compiler.distccEnabled = button.checked;
1012             modifiedDocument = true;
1013             compilersTab.modifiedDocument = true;
1014          }
1015          return true;
1016       }
1017    };
1018
1019    Label distccHostsLabel { this, position = { 240, 68 }, labeledWindow = distccHosts };
1020    EditBox distccHosts
1021    {
1022       this, text = $"distcc hosts", hotKey = altH;
1023       position = { 320, 64 }, size = { 160, 22 };
1024
1025       bool NotifyModified(EditBox editBox)
1026       {
1027          CompilerConfig compiler = loadedCompiler;
1028          if(compiler)
1029          {
1030             compiler.distccHosts = editBox.contents;
1031             modifiedDocument = true;
1032             compilersTab.modifiedDocument = true;
1033          }
1034          return true;
1035       }
1036    };
1037
1038    Label lblPrepDefs { this, position = { 8, 96 }, labeledWindow = prepDefs };
1039    StringListBox prepDefs
1040    {
1041       this, text = $"Preprocessor directives", hotKey = altP;
1042       position = { 168, 94 }, size = { 280, 22 }, anchor = { left = 168, top = 94, right = 8 };
1043
1044       bool NotifyModified(EditBox editBox)
1045       {
1046          if(loadedCompiler)
1047          {
1048             CompilerConfig compiler = loadedCompiler;
1049             compiler.prepDirectives = ((StringListBox)editBox).strings;
1050             modifiedDocument = true;
1051             compilersTab.modifiedDocument = true;
1052          }
1053          return true;
1054       }
1055    };
1056
1057    Label leCcompilerFlags { this, position = { 8, 126 }, labeledWindow = eCcompilerFlags };
1058    StringListBox eCcompilerFlags
1059    {
1060       this, text = $"Additional eC compiler flags", hotKey = altG;
1061       position = { 168, 124 }, size = { 280, 22 }, anchor = { left = 168, top = 124, right = 8 };
1062
1063       bool NotifyModified(EditBox editBox)
1064       {
1065          if(loadedCompiler)
1066          {
1067             CompilerConfig compiler = loadedCompiler;
1068             compiler.eCcompilerFlags = ((StringListBox)editBox).strings;
1069             modifiedDocument = true;
1070             compilersTab.modifiedDocument = true;
1071          }
1072          return true;
1073       }
1074    };
1075
1076    Label lblCompilerFlags { this, position = { 8, 156 }, labeledWindow = compilerFlags };
1077    StringListBox compilerFlags
1078    {
1079       this, text = $"Additional C compiler flags", hotKey = altR;
1080       position = { 168, 154 }, size = { 280, 22 }, anchor = { left = 168, top = 154, right = 8 };
1081
1082       bool NotifyModified(EditBox editBox)
1083       {
1084          if(loadedCompiler)
1085          {
1086             CompilerConfig compiler = loadedCompiler;
1087             compiler.compilerFlags = ((StringListBox)editBox).strings;
1088             modifiedDocument = true;
1089             compilersTab.modifiedDocument = true;
1090          }
1091          return true;
1092       }
1093    };
1094
1095    Label lblcxxFlags { this, position = { 8, 186 }, labeledWindow = cxxFlags };
1096    StringListBox cxxFlags
1097    {
1098       this, text = $"Additional C++ compiler flags", hotKey = altD;
1099       position = { 168, 184 }, size = { 280, 22 }, anchor = { left = 168, top = 184, right = 8 };
1100
1101       bool NotifyModified(EditBox editBox)
1102       {
1103          if(loadedCompiler)
1104          {
1105             CompilerConfig compiler = loadedCompiler;
1106             compiler.cxxFlags = ((StringListBox)editBox).strings;
1107             modifiedDocument = true;
1108             compilersTab.modifiedDocument = true;
1109          }
1110          return true;
1111       }
1112    };
1113
1114    Label lblLinkerFlags { this, position = { 8, 216 }, labeledWindow = linkerFlags };
1115    StringListBox linkerFlags
1116    {
1117       this, text = $"Additional linker flags", hotKey = altL;
1118       position = { 168, 214 }, size = { 280, 22 }, anchor = { left = 168, top = 214, right = 8 };
1119
1120       bool NotifyModified(EditBox editBox)
1121       {
1122          if(loadedCompiler)
1123          {
1124             CompilerConfig compiler = loadedCompiler;
1125             compiler.linkerFlags = ((StringListBox)editBox).strings;
1126             modifiedDocument = true;
1127             compilersTab.modifiedDocument = true;
1128          }
1129          return true;
1130       }
1131    };
1132
1133    Label lblExcludedLibraries { this, position = { 8, 246 }, labeledWindow = excludedLibraries };
1134    StringListBox excludedLibraries
1135    {
1136       this, text = $"Libraries to exclude", hotKey = altX;
1137       position = { 168, 244 }, size = { 280, 22 }, anchor = { left = 168, top = 244, right = 8 };
1138
1139       bool NotifyModified(EditBox editBox)
1140       {
1141          if(loadedCompiler)
1142          {
1143             CompilerConfig compiler = loadedCompiler;
1144             compiler.excludeLibs = ((StringListBox)editBox).strings;
1145             modifiedDocument = true;
1146             compilersTab.modifiedDocument = true;
1147          }
1148          return true;
1149       }
1150    };
1151
1152    Label objectFileExtLabel { this, position = { 8, 276 }, labeledWindow = objectFileExt };
1153    EditBox objectFileExt
1154    {
1155       this, text = $"Object file extension";//, hotKey = altH;
1156       position = { 168, 274 }, size = { 80, 22 };
1157
1158       bool NotifyModified(EditBox editBox)
1159       {
1160          CompilerConfig compiler = loadedCompiler;
1161          if(compiler)
1162          {
1163             compiler.objectFileExt = editBox.contents;
1164             modifiedDocument = true;
1165             compilersTab.modifiedDocument = true;
1166          }
1167          return true;
1168       }
1169    };
1170
1171    Label staticLibFileExtLabel { this, position = { 8, 306 }, labeledWindow = staticLibFileExt };
1172    EditBox staticLibFileExt
1173    {
1174       this, text = $"Target extensions (a, so, exe)";//, hotKey = altH;
1175       position = { 168, 304 }, size = { 80, 22 };
1176
1177       bool NotifyModified(EditBox editBox)
1178       {
1179          CompilerConfig compiler = loadedCompiler;
1180          if(compiler)
1181          {
1182             compiler.staticLibFileExt = editBox.contents;
1183             modifiedDocument = true;
1184             compilersTab.modifiedDocument = true;
1185          }
1186          return true;
1187       }
1188    };
1189    EditBox sharedLibFileExt
1190    {
1191       this;
1192       position = { 256, 304 }, size = { 80, 22 };
1193
1194       bool NotifyModified(EditBox editBox)
1195       {
1196          CompilerConfig compiler = loadedCompiler;
1197          if(compiler)
1198          {
1199             compiler.sharedLibFileExt = editBox.contents;
1200             modifiedDocument = true;
1201             compilersTab.modifiedDocument = true;
1202          }
1203          return true;
1204       }
1205    };
1206    EditBox executableFileExt
1207    {
1208       this;
1209       position = { 344, 304 }, size = { 80, 22 };
1210
1211       bool NotifyModified(EditBox editBox)
1212       {
1213          CompilerConfig compiler = loadedCompiler;
1214          if(compiler)
1215          {
1216             compiler.executableFileExt = editBox.contents;
1217             modifiedDocument = true;
1218             compilersTab.modifiedDocument = true;
1219          }
1220          return true;
1221       }
1222    };
1223
1224    Button stripTarget
1225    {
1226       this, text = $"Strip target", hotKey = altC, position = { 168, 332 };
1227       isCheckbox = true;
1228
1229       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1230       {
1231          CompilerConfig compiler = loadedCompiler;
1232          if(compiler)
1233          {
1234             compiler.noStripTarget = !button.checked;
1235             modifiedDocument = true;
1236             compilersTab.modifiedDocument = true;
1237          }
1238          return true;
1239       }
1240    };
1241
1242    Button resourcesDotEar
1243    {
1244       this, text = $"Use resources.ear", position = { 308, 332 };
1245       isCheckbox = true;
1246
1247       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1248       {
1249          CompilerConfig compiler = loadedCompiler;
1250          if(compiler)
1251          {
1252             compiler.resourcesDotEar = button.checked;
1253             modifiedDocument = true;
1254             compilersTab.modifiedDocument = true;
1255          }
1256          return true;
1257       }
1258    };
1259
1260    CompilerOptionsTab()
1261    {
1262       Platform p;
1263       DataRow row;
1264       for(p = (Platform)1; p < Platform::enumSize; p++)
1265       {
1266          row = targetPlatform.AddRow();
1267          row.tag = p;
1268          row.string = p;
1269       }
1270    }
1271
1272    void Load()
1273    {
1274       CompilerConfig compiler = loadedCompiler;
1275       if(compiler)
1276       {
1277          bool disabled = compiler.readOnly;
1278          targetPlatform.currentRow = targetPlatform.FindRow(compiler.targetPlatform);
1279          numJobs = compiler.numJobs;
1280          numJobsBox.Refresh();
1281          ccacheEnabled.checked = compiler.ccacheEnabled;
1282          distccEnabled.checked = compiler.distccEnabled;
1283          distccHostsLabel.disabled = distccHosts.disabled = !compiler.distccEnabled;
1284          distccHosts.contents = compiler.distccHosts;
1285          prepDefs.strings = compiler.prepDirectives;
1286          excludedLibraries.strings = compiler.excludeLibs;
1287          eCcompilerFlags.strings = compiler.eCcompilerFlags;
1288          compilerFlags.strings = compiler.compilerFlags;
1289          cxxFlags.strings = compiler.cxxFlags;
1290          linkerFlags.strings = compiler.linkerFlags;
1291          objectFileExt.contents = compiler.objectFileExt;
1292          staticLibFileExt.contents = compiler.staticLibFileExt;
1293          sharedLibFileExt.contents = compiler.sharedLibFileExt;
1294          executableFileExt.contents = compiler.executableFileExt;
1295          stripTarget.checked = !compiler.noStripTarget;
1296          resourcesDotEar.checked = compiler.resourcesDotEar;
1297
1298          labelTargetPlatform.disabled = disabled;
1299          targetPlatform.disabled = disabled;
1300
1301       }
1302       modifiedDocument = false;
1303    }
1304 }
1305
1306 class CompilersSubTab : Tab
1307 {
1308    property CompilersTab compilersTab
1309    {
1310       get
1311       {
1312          CompilersTab tab = (CompilersTab)master;
1313          while(tab && tab._class != class(CompilersTab))
1314             tab = (CompilersTab)tab.master;
1315          return tab;
1316       }
1317    };
1318
1319    property CompilerConfig loadedCompiler
1320    {
1321       get
1322       {
1323          CompilersTab tab = compilersTab;
1324          return tab ? tab.activeCompiler : null;
1325       }
1326    };
1327 }
1328
1329 class ProjectOptionsTab : GlobalSettingsSubTab
1330 {
1331    background = formColor;
1332    text = $"Project";
1333
1334    Label defaultTargetDirLabel { this, position = { 8, 34 }, labeledWindow = defaultTargetDir };
1335    PathBox defaultTargetDir
1336    {
1337       this, size = { 160, 21 }, position = { 8, 52 }, anchor = { left = 8, top = 52, right = 8 };
1338       text = $"Default Target Directory", hotKey = altT;
1339
1340       bool NotifyModified(PathBox pathBox)
1341       {
1342          BasicValidatePathBoxPath(pathBox);
1343          modifiedDocument = true;
1344          return true;
1345       }
1346    };
1347
1348    Label defaultIntermediateObjDirLabel { this, position = { 8, 78 }, labeledWindow = defaultIntermediateObjDir };
1349    PathBox defaultIntermediateObjDir
1350    {
1351       this, size = { 160, 21 }, position = { 8, 96 }, anchor = { left = 8, top = 96, right = 8 };
1352       text = $"Default Intermediate Objects Directory", hotKey = altI;
1353
1354       bool NotifyModified(PathBox pathBox)
1355       {
1356          BasicValidatePathBoxPath(pathBox);
1357          modifiedDocument = true;
1358          return true;
1359       }
1360    };
1361 }
1362
1363 // COMPILER TOFIX: if class GlobalSettingsSubTab is after class WorkspaceOptionsTab the OnPostCreate
1364 //                 of WorkspaceOptionsTab will *not* be called!
1365 class GlobalSettingsSubTab : Tab
1366 {
1367    property GlobalSettingsDialog dialog
1368    {
1369       get
1370       {
1371          GlobalSettingsDialog dialog = (GlobalSettingsDialog)master;
1372          while(dialog && dialog._class != class(GlobalSettingsDialog))
1373             dialog = (GlobalSettingsDialog)dialog.master;
1374          return dialog;
1375       }
1376    };
1377 }
1378
1379 class WorkspaceOptionsTab : GlobalSettingsSubTab
1380 {
1381    background = formColor;
1382    text = $"Workspace";
1383
1384    Label defaultCompilerLabel { this, position = { 8, 14 }, labeledWindow = defaultCompilerDropBox };
1385    DropBox defaultCompilerDropBox
1386    {
1387       this, position = { 140, 8 }, size = { 220 };
1388       text = $"Default Compiler", hotKey = altA;
1389
1390       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
1391       {
1392          modifiedDocument = true;
1393          return true;
1394       }
1395    };
1396
1397    bool OnCreate()
1398    {
1399       GlobalSettingsDialog dialog = this.dialog;
1400       if(dialog && dialog.compilersTab.compilerConfigs && dialog.ideSettings)
1401       {
1402          DataRow row;
1403          for(compiler : ideConfig.compilers)
1404          {
1405             row = defaultCompilerDropBox.AddString(compiler.name);
1406             if(dialog.ideSettings.defaultCompiler && dialog.ideSettings.defaultCompiler[0] &&
1407                   !strcmp(compiler.name, dialog.ideSettings.defaultCompiler))
1408                defaultCompilerDropBox.currentRow = row;
1409          }
1410          if(!defaultCompilerDropBox.currentRow && defaultCompilerDropBox)
1411             defaultCompilerDropBox.currentRow = defaultCompilerDropBox.firstRow;
1412       }
1413       return true;
1414    }
1415
1416    void OnDestroy()
1417    {
1418       // TOFIX: The selection will be lost upon changing tab...
1419       // Should either warn, or leave it modified and put in place
1420       // checks to save/find the compiler by name
1421       defaultCompilerDropBox.Clear();
1422       modifiedDocument = false;
1423    }
1424 }
1425
1426 //static define app = ((GuiApplication)__thisModule);