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