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