b0354a59fe401075aaa3fe6552e8e2ff3a83c4a5
[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       modifiedDocument = true;
554       return true;
555    }
556 }
557
558 Array<String> displayDirectoryNames
559 { [
560    $"Include Files",
561    $"Library Files",
562    $"Executable Files"
563 ] };
564
565 class CompilerDirectoriesTab : CompilersSubTab
566 {
567    background = formColor;
568    text = $"Directories";
569
570    Button dirTypeTglBtn[DirTypes];
571    DirectoriesBox dirs[DirTypes], currentDirs;
572
573    ~CompilerDirectoriesTab()
574    {
575       DirTypes c;
576       for(c = 0; c < DirTypes::enumSize; c++)
577       {
578          delete dirs[c];
579          delete dirTypeTglBtn[c];
580       }
581    }
582    CompilerDirectoriesTab()
583    {
584       DirTypes c;
585       for(c = 0; c < DirTypes::enumSize; c++)
586       {
587          dirs[c] = DirectoriesBox
588          {
589             this;//, alwaysHighLight = true
590             anchor = { left = 8, top = 8, right = 8, bottom = 8 };
591             id = c;
592
593    /*   MAKE SURE THINGS ARE DONE PROPERLY IN THE NEW DIRECTORIES BOX WHEN BROWSING FOR A DIR?
594             settingsFileDialog.filePath = directory;
595          if(settingsFileDialog.Modal())
596             row.SetData(null, (s = CopyUnixPath(settingsFileDialog.filePath)));
597    */
598
599             bool NotifyModified(DirectoriesBox dirsBox)
600             {
601                CompilerConfig compiler = loadedCompiler;
602                if(compiler)
603                {
604                   DirTypes dirType = (DirTypes)dirsBox.id;
605                   if(dirType == includes)
606                      compiler.includeDirs = dirsBox.strings;
607                   else if(dirType == libraries)
608                      compiler.libraryDirs = dirsBox.strings;
609                   else if(dirType == executables)
610                      compiler.executableDirs = dirsBox.strings;
611
612                   compilersTab.modifiedDocument = true;
613                }
614                return true;
615             }
616          };
617          incref dirs[c];
618          
619          if(c)
620             dirs[c].visible = false;
621          
622          // (width) Should be 324 for text...
623          //field[c] = { dataType = class(char *), editable = true };
624          //dirs[c].AddField(field[c]);
625
626          {
627          int v = (int)c * 100 + 8;
628          dirTypeTglBtn[c] = Button
629          {
630             this, inactive = true, text = displayDirectoryNames[c], bevelOver = true, isRadio = true, bitmap = null;
631             stayOnTop = true;
632             id = c;
633             size = { 99, 20 };
634             anchor = { left = v, top = 8 }; // ((int)c) * 100 + 8
635
636             bool NotifyClicked(Button button, int x, int y, Modifiers mods)
637             {
638                DirTypes dirType = (DirTypes)button.id;
639                currentDirs.visible = false;
640                dirs[dirType].visible = true;
641                currentDirs = dirs[dirType];
642                return true;
643             }
644          };
645          incref dirTypeTglBtn[c];
646
647          if(c == includes)
648             dirTypeTglBtn[c].hotKey = altI;
649          else if(c == libraries)
650             dirTypeTglBtn[c].hotKey = altL;
651          else if(c == executables)
652             dirTypeTglBtn[c].hotKey = altE;
653          }
654       }   
655       currentDirs = dirs[includes];
656       dirTypeTglBtn[includes].checked = true;
657       return true;
658    }
659
660    void Load()
661    {
662       CompilerConfig compiler = loadedCompiler;
663       if(compiler)
664       {
665          dirs[includes].strings = compiler.includeDirs;
666          dirs[libraries].strings = compiler.libraryDirs;
667          dirs[executables].strings = compiler.executableDirs;
668          dirs[includes].list.scroll = { 0, 0 };
669          dirs[libraries].list.scroll = { 0, 0 };
670          dirs[executables].list.scroll = { 0, 0 };
671       }
672    }
673 }
674
675 class CompilerToolchainTab : CompilersSubTab
676 {
677    background = formColor;
678    text = $"Toolchain";
679
680    int margin;
681    margin = 130;
682
683    Label ecpLabel { this, position = { 8, 12 }, labeledWindow = ecp, tabCycle = false, inactive = true };
684    PathBox ecp
685    {
686       this, anchor = { left = margin, top = 8, right = 8 };
687       text = $"eC Precompiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
688    };
689    Label eccLabel { this, position = { 8, 38 }, labeledWindow = ecc, tabCycle = false, inactive = true };
690    PathBox ecc
691    {
692       this, anchor = { left = margin, top = 34, right = 8 };
693       text = $"eC Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
694    };
695    Label ecsLabel { this, position = { 8, 64 }, labeledWindow = ecs, tabCycle = false, inactive = true };
696    PathBox ecs
697    {
698       this, anchor = { left = margin, top = 60, right = 8 };
699       text = $"eC Symbol Loader", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
700    };
701    Label earLabel { this, position = { 8, 90 }, labeledWindow = ear, tabCycle = false, inactive = true };
702    PathBox ear
703    {
704       this, anchor = { left = margin, top = 86, right = 8 };
705       text = $"Ecere Archiver", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
706    };
707    Label cppLabel { this, position = { 8, 116 }, labeledWindow = cpp, tabCycle = false, inactive = true };
708    PathBox cpp
709    {
710       this, anchor = { left = margin, top = 112, right = 8 };
711       text = $"C Preprocessor", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
712    };
713    Label ccLabel { this, position = { 8, 142 }, labeledWindow = cc, tabCycle = false, inactive = true };
714    PathBox cc
715    {
716       this, anchor = { left = margin, top = 138, right = 8 };
717       text = $"C Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
718    };
719    Label cxxLabel { this, position = { 8, 168 }, labeledWindow = cxx, tabCycle = false, inactive = true };
720    PathBox cxx
721    {
722       this, anchor = { left = margin, top = 164, right = 8 };
723       text = $"C++ Compiler", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
724    };
725    Label makeLabel { this, position = { 8, 194 }, labeledWindow = make, tabCycle = false, inactive = true };
726    PathBox make
727    {
728       this, anchor = { left = margin, top = 190, right = 8 };
729       text = $"GNU Make", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
730    };
731    Label gnuToolchainPrefixLabel { this, position = { 8, 220 }, labeledWindow = gnuToolchainPrefix, tabCycle = false, inactive = true };
732    PathBox gnuToolchainPrefix
733    {
734       this, anchor = { left = margin, top = 216, right = 8 };
735       text = $"GNU Toolchain Prefix", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
736    };
737    Label sysrootLabel { this, position = { 8, 246 }, labeledWindow = sysroot, tabCycle = false, inactive = true };
738    PathBox sysroot
739    {
740       this, anchor = { left = margin, top = 242, right = 8 };
741       text = $"SYSROOT", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
742    };
743    Label executableLauncherLabel { this, position = { 8, 272 }, labeledWindow = executableLauncher, tabCycle = false, inactive = true };
744    PathBox executableLauncher
745    {
746       this, anchor = { left = margin, top = 268, right = 8 };
747       text = $"Executable Launcher", browseDialog = toolchainFileDialog, NotifyModified = NotifyModifiedDocument;
748    };
749
750    bool NotifyModifiedDocument(PathBox pathBox)
751    {
752       CompilerConfig compiler = loadedCompiler;
753       if(compiler)
754       {
755          if(pathBox == ecp)
756             compiler.ecpCommand = pathBox.slashPath;
757          else if(pathBox == ecc)
758             compiler.eccCommand = pathBox.slashPath;
759          else if(pathBox == ecs)
760             compiler.ecsCommand = pathBox.slashPath;
761          else if(pathBox == ear)
762             compiler.earCommand = pathBox.slashPath;
763          else if(pathBox == cpp)
764             compiler.cppCommand = pathBox.slashPath;
765          else if(pathBox == cc)
766             compiler.ccCommand = pathBox.slashPath;
767          else if(pathBox == cxx)
768             compiler.cxxCommand = pathBox.slashPath;
769          else if(pathBox == make)
770             compiler.makeCommand = pathBox.slashPath;
771          else if(pathBox == executableLauncher)
772             compiler.execPrefixCommand = pathBox.slashPath;
773          else if(pathBox == gnuToolchainPrefix)
774             compiler.gccPrefix = pathBox.slashPath;
775          else if(pathBox == sysroot)
776             compiler.sysroot = pathBox.slashPath;
777          modifiedDocument = true;
778          compilersTab.modifiedDocument = true;
779       }
780       return true;
781    }
782
783    void Load()
784    {
785       CompilerConfig compiler = loadedCompiler;
786       if(compiler)
787       {
788          bool disabled = compiler.readOnly;
789          bool isVC = compiler.type.isVC;
790          ecp.path = compiler.ecpCommand;
791          ecc.path = compiler.eccCommand;
792          ecs.path = compiler.ecsCommand;
793          ear.path = compiler.earCommand;
794          cpp.path = compiler.cppCommand;
795          cc.path = compiler.ccCommand;
796          cxx.path = compiler.cxxCommand;
797          make.path = compiler.makeCommand;
798          executableLauncher.path = compiler.executableLauncher;
799          gnuToolchainPrefix.path = compiler.gnuToolchainPrefix;
800          sysroot.path = compiler.sysroot;
801
802          ecpLabel.disabled = ecp.disabled = disabled;
803          eccLabel.disabled = ecc.disabled = disabled;
804          ecsLabel.disabled = ecs.disabled = disabled;
805          earLabel.disabled = ear.disabled = disabled;
806          cppLabel.disabled = cpp.disabled = isVC || disabled;
807          cxxLabel.disabled = cxx.disabled = isVC || disabled;
808          ccLabel.disabled = cc.disabled = isVC || disabled;
809          makeLabel.disabled = make.disabled = disabled;
810          executableLauncherLabel.disabled = executableLauncher.disabled = disabled;
811          gnuToolchainPrefixLabel.disabled = gnuToolchainPrefix.disabled = disabled;
812          sysrootLabel.disabled = sysroot.disabled = disabled;
813       }
814       modifiedDocument = false;
815    }
816 }
817
818 class CompilerEnvironmentTab : CompilersSubTab
819 {
820    background = formColor;
821    text = $"Environment";
822
823    Label labelEnvVars { envVars, labeledWindow = envVars, position = { 0, 6 }; };
824    NamedStringsBox envVars
825    {
826       this, size = { 290, 22 }, anchor = { left = 8, top = 8, right = 8, bottom = 8 };
827       text = $"Environment Variables", hotKey = altE; //, option = OPTION(postbuildCommands);
828
829       bool NotifyModified(NamedStringsBox stringsBox)
830       {
831          CompilerConfig compiler = loadedCompiler;
832          if(compiler)
833          {
834             compiler.environmentVars = stringsBox.namedStrings;
835             modifiedDocument = true;
836             compilersTab.modifiedDocument = true;
837          }
838          return true;
839       }
840    };
841
842    CompilerEnvironmentTab()
843    {
844    }
845
846    void Load()
847    {
848       CompilerConfig compiler = loadedCompiler;
849       if(compiler)
850       {
851          envVars.namedStrings = compiler.environmentVars;
852
853          modifiedDocument = false;
854       }
855    }
856 }
857
858 class CompilerOptionsTab : CompilersSubTab
859 {
860    background = formColor;
861    text = $"Options";
862
863    Label labelTargetPlatform { this, position = { 8, 12 }, labeledWindow = targetPlatform };   // TOCHECK: nameless instances dissapear when selecting tabs?
864    DropBox targetPlatform
865    {
866       this, position = { 110, 8 }, size = { 160 };
867       text = $"Target Platform", hotKey = altT;
868       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
869       {
870          CompilerConfig compiler = loadedCompiler;
871          if(compiler && row)
872          {
873             compiler.targetPlatform = (Platform)row.tag;
874             modifiedDocument = true;
875             compilersTab.modifiedDocument = true;
876          }
877          return true;
878       }
879    };
880
881    int numJobs;
882    Label numJobsLabel { this, position = { 8, 40 }, labeledWindow = numJobsBox };
883    DataBox numJobsBox
884    {
885       this, text = $"Number of parallel build jobs", hotKey = altJ, borderStyle = deep;
886       position = { 184, 36 }, size = { 80, 20 }, type = class(int), data = &numJobs;
887
888       bool OnKeyDown(Key key, unichar ch)
889       {
890          if((SmartKey)key == enter)
891          {  
892             DataBox::OnKeyDown(key, ch);
893             return true;
894          }
895          else
896             return DataBox::OnKeyDown(key, ch);
897       }
898       
899       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
900       {
901          if(!active)
902          {
903             if(!SaveData())
904                Refresh();
905          }
906          return true;
907       }
908
909       bool NotifyChanged(bool closingDropDown)
910       {
911          CompilerConfig compiler = loadedCompiler;
912          if(compiler)
913          {
914             compiler.numJobs = numJobs;
915             modifiedDocument = true;
916             compilersTab.modifiedDocument = true;
917          }
918          return true;
919       }
920    };
921
922    Button ccacheEnabled
923    {
924       this, text = $"Use ccache", hotKey = altC, position = { 8, 68 };
925       isCheckbox = true;
926
927       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
928       {
929          CompilerConfig compiler = loadedCompiler;
930          if(compiler)
931          {
932             compiler.ccacheEnabled = button.checked;
933             modifiedDocument = true;
934             compilersTab.modifiedDocument = true;
935          }
936          return true;
937       }
938    };
939
940    Button distccEnabled
941    {
942       this, text = $"Use distcc", hotKey = altD, position = { 158, 68 };
943       isCheckbox = true;
944
945       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
946       {
947          CompilerConfig compiler = loadedCompiler;
948          if(compiler)
949          {
950             distccHosts.disabled = !button.checked;
951             compiler.distccEnabled = button.checked;
952             modifiedDocument = true;
953             compilersTab.modifiedDocument = true;
954          }
955          return true;
956       }
957    };
958
959    Label distccHostsLabel { this, position = { 8, 96 }, labeledWindow = distccHosts };
960    EditBox distccHosts
961    {
962       this, text = $"distcc hosts", hotKey = altH;
963       position = { 88, 92 }, size = { 300 };
964
965       bool NotifyModified(EditBox editBox)
966       {
967          CompilerConfig compiler = loadedCompiler;
968          if(compiler)
969          {
970             compiler.distccHosts = editBox.contents;
971             modifiedDocument = true;
972             compilersTab.modifiedDocument = true;
973          }
974          return true;
975       }
976    }
977
978    Label lblPrepDefs { this, position = { 8, 124 }, labeledWindow = prepDefs };
979    StringListBox prepDefs
980    {
981       this, text = $"Preprocessor directives:", hotKey = altP;
982       position = { 148, 124 }, size = { 300 };
983
984       bool NotifyModified(EditBox editBox)
985       {
986          if(loadedCompiler)
987          {
988             CompilerConfig compiler = loadedCompiler;
989             compiler.prepDirectives = ((StringListBox)editBox).strings;
990             modifiedDocument = true;
991             compilersTab.modifiedDocument = true;
992          }
993          return true;
994       }
995    }
996
997    Label lblLinkerFlags { this, position = { 8, 152 }, labeledWindow = linkerFlags };
998    StringListBox linkerFlags
999    {
1000       this, text = $"Additional Linker flags:", hotKey = altL;
1001       position = { 148, 152 }, size = { 300 };
1002
1003       bool NotifyModified(EditBox editBox)
1004       {
1005          if(loadedCompiler)
1006          {
1007             CompilerConfig compiler = loadedCompiler;
1008             compiler.linkerFlags = ((StringListBox)editBox).strings;
1009             modifiedDocument = true;
1010             compilersTab.modifiedDocument = true;
1011          }
1012          return true;
1013       }
1014    }
1015
1016    Label lblExcludedLibraries { this, position = { 8, 180 }, labeledWindow = excludedLibraries };
1017    StringListBox excludedLibraries
1018    {
1019       this, text = $"Libraries to exclude:", hotKey = altX;
1020       position = { 148, 180 }, size = { 300 };
1021
1022       bool NotifyModified(EditBox editBox)
1023       {
1024          if(loadedCompiler)
1025          {
1026             CompilerConfig compiler = loadedCompiler;
1027             compiler.excludeLibs = ((StringListBox)editBox).strings;
1028             modifiedDocument = true;
1029             compilersTab.modifiedDocument = true;
1030          }
1031          return true;
1032       }
1033    }
1034
1035    CompilerOptionsTab()
1036    {
1037       Platform p;
1038       DataRow row;
1039       for(p = (Platform)1; p < Platform::enumSize; p++)
1040       {
1041          row = targetPlatform.AddRow();
1042          row.tag = p;
1043          row.string = p;
1044       }
1045    }
1046
1047    void Load()
1048    {
1049       CompilerConfig compiler = loadedCompiler;
1050       if(compiler)
1051       {
1052          bool disabled = compiler.readOnly;
1053          targetPlatform.currentRow = targetPlatform.FindRow(compiler.targetPlatform);
1054          numJobs = compiler.numJobs;
1055          numJobsBox.Refresh();
1056          ccacheEnabled.checked = compiler.ccacheEnabled;
1057          distccEnabled.checked = compiler.distccEnabled;
1058          distccHosts.disabled = !compiler.distccEnabled;
1059          distccHosts.contents = compiler.distccHosts;
1060          prepDefs.strings = compiler.prepDirectives;
1061          excludedLibraries.strings = compiler.excludeLibs;
1062          linkerFlags.strings = compiler.linkerFlags;
1063          
1064          labelTargetPlatform.disabled = disabled;
1065          targetPlatform.disabled = disabled;
1066
1067       }
1068       modifiedDocument = false;
1069    }
1070 }
1071
1072 class CompilersSubTab : Tab
1073 {
1074    property CompilersTab compilersTab
1075    {
1076       get
1077       {
1078          CompilersTab tab = (CompilersTab)master;
1079          while(tab && tab._class != class(CompilersTab))
1080             tab = (CompilersTab)tab.master;
1081          return tab;
1082       }
1083    };
1084
1085    property CompilerConfig loadedCompiler
1086    {
1087       get
1088       {
1089          CompilersTab tab = compilersTab;
1090          return tab ? tab.activeCompiler : null;
1091       }
1092    };
1093 }
1094
1095 class ProjectOptionsTab : GlobalSettingsSubTab
1096 {
1097    background = formColor;
1098    text = $"Project";
1099
1100    Label defaultTargetDirLabel { this, position = { 8, 34 }, labeledWindow = defaultTargetDir };
1101    PathBox defaultTargetDir
1102    {
1103       this, size = { 160, 21 }, position = { 8, 52 }, anchor = { left = 8, top = 52, right = 8 };
1104       text = $"Default Target Directory", hotKey = altT;
1105
1106       bool NotifyModified(PathBox editBox)
1107       {
1108          modifiedDocument = true;
1109          return true;
1110       }
1111    };
1112
1113    Label defaultIntermediateObjDirLabel { this, position = { 8, 78 }, labeledWindow = defaultIntermediateObjDir };
1114    PathBox defaultIntermediateObjDir
1115    {
1116       this, size = { 160, 21 }, position = { 8, 96 }, anchor = { left = 8, top = 96, right = 8 };
1117       text = $"Default Intermediate Objects Directory", hotKey = altI;
1118
1119       bool NotifyModified(PathBox editBox)
1120       {
1121          modifiedDocument = true;
1122          return true;
1123       }
1124    };
1125 }
1126
1127 // COMPILER TOFIX: if class GlobalSettingsSubTab is after class WorkspaceOptionsTab the OnPostCreate 
1128 //                 of WorkspaceOptionsTab will *not* be called!
1129 class GlobalSettingsSubTab : Tab
1130 {
1131    property GlobalSettingsDialog dialog
1132    {
1133       get
1134       {
1135          GlobalSettingsDialog dialog = (GlobalSettingsDialog)master;
1136          while(dialog && dialog._class != class(GlobalSettingsDialog))
1137             dialog = (GlobalSettingsDialog)dialog.master;
1138          return dialog;
1139       }
1140    };
1141 }
1142
1143 class WorkspaceOptionsTab : GlobalSettingsSubTab
1144 {
1145    background = formColor;
1146    text = $"Workspace";
1147
1148    Label defaultCompilerLabel { this, position = { 8, 14 }, labeledWindow = defaultCompilerDropBox };
1149    DropBox defaultCompilerDropBox
1150    {
1151       this, position = { 140, 8 }, size = { 220 };
1152       text = $"Default Compiler", hotKey = altA;
1153
1154       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
1155       {
1156          modifiedDocument = true;
1157          return true;
1158       }
1159    };
1160
1161    bool OnCreate()
1162    {
1163       GlobalSettingsDialog dialog = this.dialog;
1164       if(dialog && dialog.compilersTab.compilerConfigs && dialog.ideSettings)
1165       {
1166          DataRow row;
1167          for(compiler : dialog.ideSettings.compilerConfigs)
1168          {
1169             row = defaultCompilerDropBox.AddString(compiler.name);
1170             if(dialog.ideSettings.defaultCompiler && dialog.ideSettings.defaultCompiler[0] && 
1171                   !strcmp(compiler.name, dialog.ideSettings.defaultCompiler))
1172                defaultCompilerDropBox.currentRow = row;
1173          }
1174          if(!defaultCompilerDropBox.currentRow && defaultCompilerDropBox)
1175             defaultCompilerDropBox.currentRow = defaultCompilerDropBox.firstRow;
1176       }
1177       return true;
1178    }
1179
1180    void OnDestroy()
1181    {
1182       // TOFIX: The selection will be lost upon changing tab...
1183       // Should either warn, or leave it modified and put in place
1184       // checks to save/find the compiler by name
1185       defaultCompilerDropBox.Clear();
1186       modifiedDocument = false;
1187    }
1188 }
1189
1190 //static define app = ((GuiApplication)__thisModule);