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