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