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