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