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