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