i18n: ide, ecere, documentor: New strings, Chinese
[sdk] / ide / src / IDESettings.ec
1 #ifdef ECERE_STATIC
2 import static "ecere"
3 #else
4 import "ecere"
5 #endif
6
7 import "StringsBox"
8
9 import "OldIDESettings"
10
11 define MaxRecent = 9;
12
13 enum DirTypes { includes, libraries, executables };
14
15 define defaultCompilerName = "Default";
16
17 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)";
18
19 char * settingsDirectoryNames[DirTypes] = 
20 {
21    "Include Files",
22    "Library Files",
23    "Executable Files"
24 };
25
26 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
27
28 char * CopyValidateMakefilePath(char * path)
29 {
30    const int map[]  =    { 0,           1,           2,             3,             4,           0,            1,                  7         };
31    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(PROJECT)", "$(CONFIGURATION)", (char *)0 };
32
33    char * copy = null;
34    if(path)
35    {
36       int len;
37       len = strlen(path);
38       copy = CopyString(path);
39       if(len)
40       {
41          int c;
42          char * tmp = copy;
43          char * start = tmp;
44          Array<char *> parts { };
45          
46          for(c=0; c<len; c++)
47          {
48             if(tmp[c] == '$')
49             {
50                int v;
51                for(v=0; vars[v]; v++)
52                {
53                   if(SearchString(&tmp[c], 0, vars[v], false, false) == &tmp[c])
54                   {
55                      tmp[c] = '\0';
56                      parts.Add(start);
57                      parts.Add(vars[map[v]]);
58                      c += strlen(vars[v]);
59                      start = &tmp[c];
60                      c--;
61                      break;
62                   }
63                }
64             }
65          }
66          if(start[0])
67             parts.Add(start);
68
69          if(parts.count)
70          {
71             /*int c, */len = 0;
72             for(c=0; c<parts.count; c++) len += strlen(parts[c]);
73             copy = new char[++len];
74             copy[0] = '\0';
75             for(c=0; c<parts.count; c++) strcat(copy, parts[c]);
76          }
77          else
78             copy = null;
79          delete parts;
80          delete tmp;
81       }
82    }
83    return copy;
84 }
85
86 CompilerConfig MakeDefaultCompiler(char * name, bool readOnly)
87 {
88    CompilerConfig defaultCompiler
89    {
90       name,
91       readOnly,
92       gcc,
93       GetRuntimePlatform(),
94       1,
95       makeDefaultCommand,
96       ecpDefaultCommand,
97       eccDefaultCommand,
98       ecsDefaultCommand,
99       earDefaultCommand,
100       cppDefaultCommand,
101       ccDefaultCommand
102    };
103    incref defaultCompiler;
104    return defaultCompiler;
105 }
106
107 class IDESettingsContainer : GlobalSettings
108 {
109 #ifdef SETTINGS_TEST
110    settingsName = "ecereIDESettingsTest";
111 #else
112    settingsName = "ecereIDE";
113 #endif
114
115    virtual void OnLoad(GlobalSettingsData data);
116
117 private:
118    IDESettingsContainer()
119    {
120       char path[MAX_LOCATION];
121       char * start;
122       LocateModule(null, path);
123       // PortableApps.com directory structure
124       if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe")))
125       {
126          char configFilePath[MAX_LOCATION];
127          char defaultConfigFilePath[MAX_LOCATION];
128
129          start[0] = '\0';
130
131          strcpy(configFilePath, path);
132          PathCat(configFilePath, "Data");
133          PathCat(configFilePath, "ecereIDE.ini");
134
135          strcpy(defaultConfigFilePath, path);
136          PathCat(defaultConfigFilePath, "App");
137          PathCat(defaultConfigFilePath, "DefaultData");
138          PathCat(defaultConfigFilePath, "ecereIDE.ini");
139          
140          if(FileExists(defaultConfigFilePath))
141          {
142             if(!FileExists(configFilePath))
143             {
144                File f = FileOpen(defaultConfigFilePath, read);
145                f.CopyTo(configFilePath);
146                f.Flush();
147                delete f;
148             }
149             PathCat(path, "Data");
150             // the forced settings location will only be
151             // used if the running ide's path matches 
152             // the PortableApps.com directory structure
153             // and the default ini file is found in 
154             // the DefaultData directory
155             settingsLocation = path;
156             portable = true;
157          }
158       }
159    }
160
161    void OnAskReloadSettings()
162    {
163       /*if(MessageBox { type = YesNo, master = this, 
164             text = "Global Settings Modified Externally", 
165             contents = "The global settings were modified by another instance.\n"
166             "Would you like to reload them?" }.Modal() == Yes)*/
167       {
168          Load();
169       }
170    }
171
172    void Load()
173    {
174       SettingsIOResult result = GlobalSettings::Load();
175       IDESettings data = (IDESettings)this.data;
176       //if(result == success)
177       if(!data)
178       {
179          this.data = IDESettings { };
180          if(dataOwner)
181             *dataOwner = this.data;
182
183          if(result == fileNotCompatibleWithDriver)
184          {
185             bool loaded;
186             OldIDESettings oldSettings { };
187             Close();
188             loaded = oldSettings.Load();
189             oldSettings.Close();
190             if(loaded)
191             {
192                data = (IDESettings)this.data;
193
194                for(c : oldSettings.compilerConfigs)
195                   data.compilerConfigs.Add(c.Copy());
196
197                for(s : oldSettings.recentFiles) data.recentFiles.Add(CopyString(s));
198                for(s : oldSettings.recentProjects) data.recentProjects.Add(CopyString(s));
199
200                data.docDir = oldSettings.docDir;
201                data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
202                data.ideProjectFileDialogLocation = oldSettings.ideProjectFileDialogLocation;
203                data.useFreeCaret = oldSettings.useFreeCaret;
204                data.showLineNumbers = oldSettings.showLineNumbers;
205                data.caretFollowsScrolling = oldSettings.caretFollowsScrolling;
206                delete data.displayDriver;
207                data.displayDriver = oldSettings.displayDriver;
208                data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
209                data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
210                         
211                Save();
212                result = success;
213             }
214             delete oldSettings;
215          }
216          if(result == fileNotFound || !data)
217          {
218             CompilerConfig defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
219             // We incref the compilers below, so reset refCount to 0
220             defaultCompiler._refCount = 0;
221             data = (IDESettings)this.data;
222
223             data.compilerConfigs.Add(defaultCompiler);
224             data.useFreeCaret = true;
225             data.showLineNumbers = true;
226             data.caretFollowsScrolling = true;
227          }
228       }
229       CloseAndMonitor();
230       if(data.compilerConfigs)
231       {
232          for(c : data.compilerConfigs)
233          {
234             CompilerConfig compiler = c;
235             incref compiler;
236          }
237       }
238       if(portable)
239       {
240          char location[MAX_LOCATION];
241          LocateModule(null, location);
242          StripLastDirectory(location, location);
243          if(location[0] && FileExists(location).isDirectory)
244          {
245             if(data.portableLocation && data.portableLocation[0] && strcmp(data.portableLocation, location))
246             {
247                data.UpdatePortablePaths(data.portableLocation, location);
248                data.portableLocation = location;
249                Save();
250             }
251          }
252       }
253       data.ForcePathSeparatorStyle(true);
254       OnLoad(data);
255    }
256
257    void Save()
258    {
259       IDESettings data = (IDESettings)this.data;
260       Platform runtimePlatform = GetRuntimePlatform();
261       data.ForcePathSeparatorStyle(runtimePlatform != win32);
262       if(!GlobalSettings::Save())
263          PrintLn("Error saving IDE settings");
264       if(runtimePlatform == win32)
265          data.ForcePathSeparatorStyle(true);
266       CloseAndMonitor();
267    }
268 }
269
270 class IDESettings : GlobalSettingsData
271 {
272 public:
273    List<CompilerConfig> compilerConfigs { };
274    Array<String> recentFiles { };
275    Array<String> recentProjects { };
276    property char * docDir
277    {
278       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
279       get { return docDir ? docDir : ""; }
280       isset { return docDir && docDir[0]; }
281    }
282    property char * ideFileDialogLocation
283    {
284       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
285       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
286       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
287    }
288    property char * ideProjectFileDialogLocation
289    {
290       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
291       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
292       isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
293    }
294    bool useFreeCaret;
295    bool showLineNumbers;
296    bool caretFollowsScrolling;
297    char * displayDriver;
298    
299    // TODO: Classify settings
300    //EditorSettings editor { };
301
302    property char * projectDefaultTargetDir
303    {
304       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
305       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
306       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
307    }
308    property char * projectDefaultIntermediateObjDir
309    {
310       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
311       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
312       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
313    }
314
315    property char * portableLocation
316    {
317       set { delete portableLocation; if(value && value[0]) portableLocation = CopyString(value); }
318       get { return portableLocation ? portableLocation : ""; }
319       isset { return portableLocation && portableLocation[0]; }
320    }
321
322    property char * defaultCompiler
323    {
324       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
325       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
326       isset { return defaultCompiler && defaultCompiler[0]; }
327    }
328
329 private:
330    char * docDir;
331    char * ideFileDialogLocation;
332    char * ideProjectFileDialogLocation;
333    char * projectDefaultTargetDir;
334    char * projectDefaultIntermediateObjDir;
335    char * portableLocation;
336    char * defaultCompiler;
337
338    CompilerConfig GetCompilerConfig(String compilerName)
339    {
340       char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
341       CompilerConfig compilerConfig = null;
342       for(compiler : compilerConfigs)
343       {
344          if(!strcmp(compiler.name, name))
345          {
346             compilerConfig = compiler;
347             break;
348          }
349       }
350       if(!compilerConfig && compilerConfigs.count)
351          compilerConfig = compilerConfigs.firstIterator.data;
352       if(compilerConfig)
353          incref compilerConfig;
354       return compilerConfig;
355    }
356
357    ~IDESettings()
358    {
359       compilerConfigs.Free();
360       delete compilerConfigs;
361       recentFiles.Free();
362       delete recentFiles;
363       recentProjects.Free();
364       delete recentProjects;
365       delete docDir;
366    
367       delete projectDefaultTargetDir;
368       delete projectDefaultIntermediateObjDir;
369       delete portableLocation;
370       delete defaultCompiler;
371
372       delete ideFileDialogLocation;
373       delete ideProjectFileDialogLocation;
374       delete displayDriver;
375    }
376
377    void ForcePathSeparatorStyle(bool unixStyle)
378    {
379       char from, to;
380       if(unixStyle)
381          from = '\\', to = '/';
382       else
383          from = '/', to = '\\';
384       if(compilerConfigs && compilerConfigs.count)
385       {
386          int i;
387          for(config : compilerConfigs)
388          {
389             if(config.includeDirs && config.includeDirs.count)
390             {
391                for(i = 0; i < config.includeDirs.count; i++)
392                {
393                   if(config.includeDirs[i] && config.includeDirs[i][0])
394                      ChangeCh(config.includeDirs[i], from, to);
395                }
396             }
397             if(config.libraryDirs && config.libraryDirs.count)
398             {
399                for(i = 0; i < config.libraryDirs.count; i++)
400                {
401                   if(config.libraryDirs[i] && config.libraryDirs[i][0])
402                      ChangeCh(config.libraryDirs[i], from, to);
403                }
404             }
405             if(config.executableDirs && config.executableDirs.count)
406             {
407                for(i = 0; i < config.executableDirs.count; i++)
408                {
409                   if(config.executableDirs[i] && config.executableDirs[i][0])
410                      ChangeCh(config.executableDirs[i], from, to);
411                }
412             }
413          }
414       }
415       if(recentFiles && recentFiles.count)
416       {
417          int c;
418          for(c = 0; c < recentFiles.count; c++)
419          {
420             if(recentFiles[c] && recentFiles[c][0])
421                ChangeCh(recentFiles[c], from, to);
422          }
423       }
424       if(recentProjects && recentProjects.count)
425       {
426          int c;
427          for(c = 0; c < recentProjects.count; c++)
428          {
429             if(recentProjects[c] && recentProjects[c][0])
430                ChangeCh(recentProjects[c], from, to);
431          }
432       }
433       if(docDir && docDir[0])
434          ChangeCh(docDir, from, to);
435       if(ideFileDialogLocation && ideFileDialogLocation[0])
436          ChangeCh(ideFileDialogLocation, from, to);
437       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
438          ChangeCh(ideProjectFileDialogLocation, from, to);
439
440       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
441          ChangeCh(projectDefaultTargetDir, from, to);
442       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
443          ChangeCh(projectDefaultIntermediateObjDir, from, to);
444
445       if(portableLocation && portableLocation[0])
446          ChangeCh(portableLocation, from, to);
447    }
448
449    bool UpdatePortablePaths(char * oldPath, char * newPath)
450    {
451       int oldLen = strlen(oldPath);
452       int newLen = strlen(newPath);
453       if(compilerConfigs && compilerConfigs.count)
454       {
455          for(config : compilerConfigs)
456          {
457             DirTypes c;
458             for(c = 0; c < DirTypes::enumSize; c++)
459             {
460                Array<String> dirs;
461                if(c == executables) dirs = config.executableDirs;
462                else if(c == includes) dirs = config.includeDirs;
463                else if(c == libraries) dirs = config.libraryDirs;
464                if(dirs && dirs.count)
465                {
466                   int i;
467                   for(i = 0; i < dirs.count; i++)
468                   {
469                      if(dirs[i] && dirs[i][0])
470                         dirs[i] = ReplaceInCopyString(dirs[i], oldPath, oldLen, newPath, newLen);
471                   }
472                }
473             }
474          }
475       }
476       if(recentFiles && recentFiles.count)
477       {
478          int c;
479          for(c = 0; c < recentFiles.count; c++)
480          {
481             if(recentFiles[c] && recentFiles[c][0])
482                recentFiles[c] = ReplaceInCopyString(recentFiles[c], oldPath, oldLen, newPath, newLen);
483          }
484       }
485       if(recentProjects && recentProjects.count)
486       {
487          int c;
488          for(c = 0; c < recentProjects.count; c++)
489          {
490             if(recentProjects[c] && recentProjects[c][0])
491                recentProjects[c] = ReplaceInCopyString(recentProjects[c], oldPath, oldLen, newPath, newLen);
492          }
493       }
494       if(docDir && docDir[0])
495          docDir = ReplaceInCopyString(docDir, oldPath, oldLen, newPath, newLen);
496       if(ideFileDialogLocation && ideFileDialogLocation[0])
497          ideFileDialogLocation = ReplaceInCopyString(ideFileDialogLocation, oldPath, oldLen, newPath, newLen);
498       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
499          ideProjectFileDialogLocation = ReplaceInCopyString(ideProjectFileDialogLocation, oldPath, oldLen, newPath, newLen);
500
501       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
502          projectDefaultTargetDir = ReplaceInCopyString(projectDefaultTargetDir, oldPath, oldLen, newPath, newLen);
503       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
504          projectDefaultIntermediateObjDir = ReplaceInCopyString(projectDefaultIntermediateObjDir, oldPath, oldLen, newPath, newLen);
505    }
506
507    char * ReplaceInCopyString(char * string, char * find, int lenFind, char * replace, int lenReplace)
508    {
509       char * output = string;
510       char * start;
511       if(/*string && string[0] && */(start = strstr(string, find)))
512       {
513          if(lenFind == lenReplace)
514             strncpy(start, replace, lenReplace);
515          else
516          {
517             int newLen = strlen(string) + lenReplace - lenFind + 1;
518             char * newString = new char[newLen];
519             start[0] = '\0';
520             strcpy(newString, string);
521             start += lenFind;
522             strcat(newString, replace);
523             strcat(newString, start);
524             delete string;
525             return newString;
526          }
527       }
528       return output;
529    }
530
531    void AddRecentFile(char * fileName)
532    {
533       int c;
534       char * filePath = CopyString(fileName);
535       ChangeCh(filePath, '\\', '/');
536       for(c = 0; c<recentFiles.count; c++)
537       {
538          if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
539          {
540             recentFiles.Delete((void *)&recentFiles[c]);
541             c--;
542          }
543       }
544       while(recentFiles.count >= MaxRecent)
545          recentFiles.Delete(recentFiles.GetLast());
546       recentFiles.Insert(null, filePath);
547       //UpdateRecentMenus(owner);
548    }
549
550    void AddRecentProject(char * projectName)
551    {
552       int c;
553       char * filePath = CopyString(projectName);
554       ChangeCh(filePath, '\\', '/');
555       for(c = 0; c<recentProjects.count; c++)
556       {
557          if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
558          {
559             recentProjects.Delete((void *)&recentProjects[c]);
560             c--;
561          }
562       }
563       while(recentProjects.count >= MaxRecent)
564          recentProjects.Delete(recentProjects.GetLast());
565       recentProjects.Insert(null, filePath);
566       //UpdateRecentMenus(owner);
567    }
568 }
569
570 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
571 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
572 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
573 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
574 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
575 // TODO: i18n with Array
576 static Array<String> compilerTypeLongNames
577 { [
578    $"GNU Compiler Collection (GCC) / GNU Make",
579    $"Tiny C Compiler / GNU Make",
580    $"Portable C Compiler / GNU Make",
581    $"Microsoft Visual Studio 2005 (8.0) Compiler",
582    $"Microsoft Visual Studio 2008 (9.0) Compiler",
583    $"Microsoft Visual Studio 2010 (10.0) Compiler"
584 ] };
585 const CompilerType firstCompilerType = gcc;
586 const CompilerType lastCompilerType = vs10;
587 public enum CompilerType
588 {
589    gcc, tcc, pcc, vs8, vs9, vs10;
590
591    property bool isVC
592    {
593       get { return this == vs8 || this == vs9 || this == vs10; }
594    }
595
596    property char *
597    {
598       get { return OnGetString(null, null, null); }
599       set
600       {  
601          if(value)
602          {
603             Platform c;
604             for(c = firstCompilerType; c <= lastCompilerType; c++)
605                if(!strcmpi(value, compilerTypeNames[c]))
606                   return c;
607          }
608          return gcc;
609       }
610    };
611
612    property char * longName { get { return OnGetString(null, (void*)1, null); } };
613    property char * versionString { get { return OnGetString(null, (void*)2, null); } };
614    property char * yearString { get { return OnGetString(null, (void*)3, null); } };
615    property char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
616    property char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
617
618    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
619    {
620       if(this >= firstCompilerType && this <= lastCompilerType)
621       {
622          if(tempString)
623             strcpy(tempString, compilerTypeNames[this]);
624          if(fieldData == null)
625             return compilerTypeNames[this];
626          else if(fieldData == (void*)1)
627             return compilerTypeLongNames[this];
628          else if(fieldData == (void*)2)
629             return compilerTypeVersionString[this];
630          else if(fieldData == (void*)3)
631             return compilerTypeYearString[this];
632          else if(fieldData == (void*)4)
633             return compilerTypeProjectFileExtension[this];
634          else if(fieldData == (void*)5)
635             return compilerTypeSolutionFileVersionString[this];
636       }
637       return null;
638    }
639 };
640
641 class CompilerConfig
642 {
643    class_no_expansion;
644
645    numJobs = 1;
646 public:
647    property char * name
648    {
649       set
650       {
651          delete name;
652          if(value)
653             name = CopyString(value);
654       }
655       get { return name; }
656    }
657    bool readOnly;
658    CompilerType type;
659    Platform targetPlatform;
660    int numJobs;
661    property char * makeCommand
662    {
663       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
664       get { return makeCommand; }
665       isset { return makeCommand && makeCommand[0]; }
666    }
667    property char * ecpCommand
668    {
669       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
670       get { return ecpCommand; }
671       isset { return ecpCommand && ecpCommand[0]; }
672    }
673    property char * eccCommand
674    {
675       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
676       get { return eccCommand; }
677       isset { return eccCommand && eccCommand[0]; }
678    }
679    property char * ecsCommand
680    {
681       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
682       get { return ecsCommand; }
683       isset { return ecsCommand && ecsCommand[0]; }
684    }
685    property char * earCommand
686    {
687       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
688       get { return earCommand; }
689       isset { return earCommand && earCommand[0]; }
690    }
691    property char * cppCommand
692    {
693       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
694       get { return cppCommand; }
695       isset { return cppCommand && cppCommand[0]; }
696    }
697    property char * ccCommand
698    {
699       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
700       get { return ccCommand; }
701       isset { return ccCommand && ccCommand[0]; }
702    }
703    property char * execPrefixCommand
704    {
705       set { delete execPrefixCommand; if(value && value[0]) execPrefixCommand = CopyString(value); }
706       get { return execPrefixCommand; }
707       isset { return execPrefixCommand && execPrefixCommand[0]; }
708    }
709    bool ccacheEnabled;
710    bool distccEnabled;
711    property char * distccHosts
712    {
713       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
714       get { return distccHosts; }
715       isset { return distccHosts && distccHosts[0]; }
716    }
717    property Array<String> includeDirs
718    {
719       set
720       {
721          includeDirs.Free();
722          if(value)
723          {
724             delete includeDirs;
725             includeDirs = value;
726          }
727       }
728       get { return includeDirs; }
729       isset { return includeDirs.count != 0; }
730    }
731    property Array<String> libraryDirs
732    {
733       set
734       {
735          libraryDirs.Free();
736          if(value)
737          {
738             delete libraryDirs;
739             libraryDirs = value;
740          }
741       }
742       get { return libraryDirs; }
743       isset { return libraryDirs.count != 0; }
744    }
745    property Array<String> executableDirs
746    {
747       set
748       {
749          executableDirs.Free();
750          if(value)
751          {
752             delete executableDirs;
753             executableDirs = value;
754          }
755       }
756       get { return executableDirs; }
757       isset { return executableDirs.count != 0; }
758    }
759    property Array<NamedString> environmentVars
760    {
761       set
762       {
763          environmentVars.Free();
764          if(value)
765          {
766             delete environmentVars;
767             environmentVars = value;
768          }
769       }
770       get { return environmentVars; }
771       isset { return environmentVars.count != 0; }
772    }
773 private:
774    Array<String> includeDirs { };
775    Array<String> libraryDirs { };
776    Array<String> executableDirs { };
777    // TODO: Can JSON parse and serialize maps?
778    //EnvironmentVariables { };
779    Array<NamedString> environmentVars { };
780    char * name;
781    char * makeCommand;
782    char * ecpCommand;
783    char * eccCommand;
784    char * ecsCommand;
785    char * earCommand;
786    char * cppCommand;
787    char * ccCommand;
788    char * execPrefixCommand;
789    char * distccHosts;
790    /*union
791    {
792       struct { Array<String> includes, libraries, executables; };
793       Array<String> dirs[DirTypes];
794    }*/
795
796    ~CompilerConfig()
797    {
798       delete name;
799       delete ecpCommand;
800       delete eccCommand;
801       delete ecsCommand;
802       delete earCommand;
803       delete cppCommand;
804       delete ccCommand;
805       delete makeCommand;
806       delete execPrefixCommand;
807       delete distccHosts;
808       if(environmentVars) environmentVars.Free();
809       if(includeDirs) { includeDirs.Free(); }
810       if(libraryDirs) { libraryDirs.Free(); }
811       if(executableDirs) { executableDirs.Free(); }
812    }
813    CompilerConfig Copy()
814    {
815       CompilerConfig copy
816       {
817          name,
818          readOnly,
819          type,
820          targetPlatform,
821          numJobs,
822          makeCommand,
823          ecpCommand,
824          eccCommand,
825          ecsCommand,
826          earCommand,
827          cppCommand,
828          ccCommand,
829          execPrefixCommand,
830          ccacheEnabled,
831          distccEnabled,
832          distccHosts
833       };
834       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
835       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
836       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
837       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
838
839       incref copy;
840       return copy;
841    }
842 }