2 public import static "ecere"
7 define ecpDefaultCommand = "ecp";
8 define eccDefaultCommand = "ecc";
9 define ecsDefaultCommand = "ecs";
10 define earDefaultCommand = "ear";
11 define cppDefaultCommand = "gcc"; // As per #624 we decided to default to "gcc"...
12 define ccDefaultCommand = "gcc";
13 define cxxDefaultCommand = "g++";
14 //define ldDefaultCommand = "gcc";
15 define arDefaultCommand = "ar";
16 define objectDefaultFileExt = "o";
17 define outputDefaultFileExt = "";
21 import "OldIDESettings"
23 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
28 IDEConfigHolder ideConfig { };
30 IDESettings ideSettings;
32 IDESettingsContainer settingsContainer
34 dataOwner = &ideSettings;
35 dataClass = class(IDESettings);
40 enum DirTypes { includes, libraries, executables };
42 define defaultCompilerName = "Default";
44 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)";
46 const char * settingsDirectoryNames[DirTypes] =
53 // This function cannot accept same pointer for source and output
54 // todo: rename ReplaceSpaces to EscapeSpaceAndSpecialChars or something
55 void ReplaceSpaces(char * output, const char * source)
60 for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
62 if(ch == ' ') output[dc++] = '\\';
63 if(ch == '\"') output[dc++] = '\\';
64 if(ch == '&') output[dc++] = '\\';
67 if(ch == '(' || ch == ')') output[dc++] = '\\';
78 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
80 enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull };
81 PathRelationship eString_PathRelated(const char * path, const char * to, char * pathDiff)
83 PathRelationship result;
84 if(pathDiff) *pathDiff = '\0';
85 if(path && *path && to && *to)
87 char toPart[MAX_FILENAME], toRest[MAX_LOCATION];
88 char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
90 strcpy(pathRest, path);
91 for(; toRest[0] && pathRest[0];)
93 SplitDirectory(toRest, toPart, toRest);
94 SplitDirectory(pathRest, pathPart, pathRest);
95 if(!fstrcmp(pathPart, toPart)) result = siblings;
98 if(result == siblings)
100 if(!*toRest && !*pathRest) result = identical;
101 else if(!*pathRest) result = parentPath;
102 else result = subPath;
103 if(pathDiff && result != identical) strcpy(pathDiff, *pathRest == '\0' ? toRest : pathRest);
105 else result = unrelated;
111 if(!*path && !*to) result = bothEmpty;
112 else if(!*path) result = pathEmpty;
113 else result = toEmpty;
115 else if(!path && !to) result = bothNull;
116 else if(!path) result = pathNull;
117 else result = toNull;
122 char * CopyValidateMakefilePath(const char * path)
124 const int map[] = { 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 7 };
125 const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)", "$(CONFIGURATION)", "$(TARGET_PLATFORM)",(char *)0 };
131 len = (int)strlen(path);
132 copy = CopyString(path);
138 Array<const char *> parts { };
145 for(v=0; vars[v]; v++)
147 if(SearchString(&tmp[c], 0, vars[v], false, false) == &tmp[c])
151 parts.Add(vars[map[v]]);
152 c += strlen(vars[v]);
166 for(c=0; c<parts.count; c++) len += strlen(parts[c]);
167 copy = new char[++len];
169 for(c=0; c<parts.count; c++) strcat(copy, parts[c]);
180 void ValidPathBufCopy(char *output, const char *input)
183 bool volumePath = false;
185 strcpy(output, input);
186 TrimLSpaces(output, output);
187 TrimRSpaces(output, output);
188 MakeSystemPath(output);
190 if(output[0] && output[1] == ':')
197 const char * chars = "*|:\",<>?";
198 char ch, * s = output, * o = output;
199 while((ch = *s++)) { if(!strchr(chars, ch)) *o++ = ch; }
203 if(volumePath && output[0])
208 void RemoveTrailingPathSeparator(char *path)
211 len = (int)strlen(path);
212 if(len>1 && path[len-1] == DIR_SEP)
216 void BasicValidatePathBoxPath(PathBox pathBox)
218 char path[MAX_LOCATION];
219 ValidPathBufCopy(path, pathBox.path);
220 RemoveTrailingPathSeparator(path);
224 CompilerConfig MakeDefaultCompiler(const char * name, bool readOnly)
226 CompilerConfig defaultCompiler
244 incref defaultCompiler;
245 return defaultCompiler;
248 //#define SETTINGS_TEST
250 define settingsDir = ".ecereIDE-SettingsTest";
251 define ideSettingsName = "ecereIDE-SettingsTest";
253 define settingsDir = ".ecereIDE";
254 define ideSettingsName = "ecereIDE";
257 class IDEConfigHolder
259 CompilerConfigs compilers { };
260 RecentFiles recentFiles { };
261 RecentWorkspaces recentWorkspaces { };
263 void forcePathSeparatorStyle(bool unixStyle)
267 from = '\\', to = '/';
269 from = '/', to = '\\';
270 recentFiles.changeChar(from, to);
271 recentWorkspaces.changeChar(from, to);
275 class IDESettingsContainer : GlobalSettings
277 property bool useNewConfigurationFiles
283 settingsContainer.driver = "ECON";
284 settingsName = "config";
285 settingsExtension = "econ";
286 settingsDirectory = settingsDir;
290 settingsContainer.driver = "JSON";
291 settingsName = ideSettingsName;
292 settingsExtension = null;
293 settingsDirectory = null;
298 char moduleLocation[MAX_LOCATION];
302 FileSize settingsFileSize;
304 IDESettingsContainer()
306 char path[MAX_LOCATION];
308 LocateModule(null, moduleLocation);
309 strcpy(path, moduleLocation);
310 StripLastDirectory(moduleLocation, moduleLocation);
311 ChangeCh(moduleLocation, '\\', '/');
312 // PortableApps.com directory structure
313 if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe")))
315 char configFilePath[MAX_LOCATION];
316 char defaultConfigFilePath[MAX_LOCATION];
320 strcpy(configFilePath, path);
321 PathCat(configFilePath, "Data");
322 PathCat(configFilePath, ideSettingsName);
323 ChangeExtension(configFilePath, "ini", configFilePath);
325 strcpy(defaultConfigFilePath, path);
326 PathCat(defaultConfigFilePath, "App");
327 PathCat(defaultConfigFilePath, "DefaultData");
328 PathCat(defaultConfigFilePath, ideSettingsName);
329 ChangeExtension(defaultConfigFilePath, "ini", defaultConfigFilePath);
331 if(FileExists(defaultConfigFilePath))
333 if(!FileExists(configFilePath))
335 File f = FileOpen(defaultConfigFilePath, read);
336 f.CopyTo(configFilePath);
340 PathCat(path, "Data");
341 // the forced settings location will only be
342 // used if the running ide's path matches
343 // the PortableApps.com directory structure
344 // and the default ini file is found in
345 // the DefaultData directory
346 settingsLocation = path;
352 void OnAskReloadSettings()
354 FileSize newSettingsFileSize;
356 if(OpenAndLock(&newSettingsFileSize))
358 if((double)settingsFileSize - (double)newSettingsFileSize < 2048)
362 GuiApplication app = ((GuiApplication)__thisModule.application);
364 for(w = app.desktop.firstChild; w && (!w.created || !w.visible); w = w.next);
368 MessageBox { master = w, type = ok, isModal = true,
369 creationActivation = flash,
370 text = "Global Settings Modified Externally",
371 contents = "The global settings were modified by another process and a drastic shrinking of the settings file was detected.\n"
372 "The new settings will not be loaded to prevent loss of your ide settings.\n"
373 "Please check your settings file and make sure to save this IDE's global settings if your settings file has been compromised."
379 SettingsIOResult Load()
382 SettingsIOResult result;
383 useNewConfigurationFiles = true;
384 result = GlobalSettings::Load();
385 data = (IDESettings)this.data;
387 if(result == fileNotFound)
390 useNewConfigurationFiles = false;
391 result = GlobalSettings::Load();
393 data = (IDESettings)this.data;
396 this.data = IDESettings { };
398 *dataOwner = this.data;
400 if(result == fileNotCompatibleWithDriver)
403 OldIDESettings oldSettings { };
405 loaded = oldSettings.Load() == success;
409 data = (IDESettings)this.data;
411 for(c : oldSettings.compilerConfigs)
412 data.compilerConfigs.Add(c.Copy());
414 for(s : oldSettings.recentFiles) data.recentFiles.Add(s);
415 for(s : oldSettings.recentProjects) data.recentProjects.Add(s);
417 data.docDir = oldSettings.docDir;
418 data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
419 data.ideProjectFileDialogLocation = oldSettings.ideProjectFileDialogLocation;
420 data.useFreeCaret = oldSettings.useFreeCaret;
421 data.showLineNumbers = oldSettings.showLineNumbers;
422 data.caretFollowsScrolling = oldSettings.caretFollowsScrolling;
423 delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver);
424 data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
425 data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
432 if(result == fileNotFound || !data)
434 data = (IDESettings)this.data;
435 data.useFreeCaret = false; //true;
436 data.showLineNumbers = true;
437 data.caretFollowsScrolling = false; //true;
443 if(data.compilerConfigs && data.compilerConfigs.count)
445 for(c : data.compilerConfigs)
446 ideConfig.compilers.Add(c);
447 data.compilerConfigs.RemoveAll();
449 if(data.recentFiles && data.recentFiles.count)
451 for(s : data.recentFiles)
452 ideConfig.recentFiles.Add(s);
453 data.recentFiles.RemoveAll();
455 if(data.recentProjects && data.recentProjects.count)
457 for(s : data.recentProjects)
458 ideConfig.recentWorkspaces.Add(s);
459 data.recentProjects.RemoveAll();
461 ideConfig.forcePathSeparatorStyle(true);
465 FileGetSize(settingsFilePath, &settingsFileSize);
467 CompilerConfigs::fix();
468 if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
469 data.ManagePortablePaths(moduleLocation, true);
470 data.ForcePathSeparatorStyle(true);
472 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
473 globalSettingsDialog.ideSettings = data;
476 ide.updateRecentMenus();
477 ide.UpdateCompilerConfigs(true);
483 useNewConfigurationFiles = true;
485 CompilerConfigs::write(null);
490 SettingsIOResult Save()
492 SettingsIOResult result;
494 useNewConfigurationFiles = true;
495 data = (IDESettings)this.data;
496 if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
497 data.ManagePortablePaths(moduleLocation, false);
498 data.ForcePathSeparatorStyle(true);
500 settingsFilePath = null;
501 result = GlobalSettings::Save();
502 if(result != success)
503 PrintLn("Error saving IDE settings");
506 if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
507 data.ManagePortablePaths(moduleLocation, true);
510 FileGetSize(settingsFilePath, &settingsFileSize);
516 static Map<String, String> getCompilerConfigFilePathsByName(const char * path)
518 Map<String, String> map { };
519 FileListing fl { path, extensions = "econ" };
522 if(fl.stats.attribs.isFile)
524 char name[MAX_FILENAME];
525 char * path = CopyString(fl.path);
527 GetLastDirectory(path, name);
529 char * s = strstr(name, ".");
538 static Map<String, CompilerConfig> getCompilerConfigsByName(const char * path)
540 Map<String, CompilerConfig> map { };
541 FileListing fl { path, extensions = "econ" };
544 if(fl.stats.attribs.isFile)
546 char name[MAX_FILENAME];
547 char * path = CopyString(fl.path);
549 GetLastDirectory(path, name);
551 char * s = strstr(name, ".");
555 CompilerConfig ccfg = CompilerConfig::read(path);
565 static void getConfigFilePath(char * path, Class _class, char * dir, const char * configName)
568 strcpy(path, settingsContainer.settingsFilePath);
569 StripLastDirectory(path, path);
570 if(settingsContainer.oldConfig)
571 PathCatSlash(path, settingsDir);
572 if(_class == class(CompilerConfig))
574 PathCatSlash(path, "compilerConfigs");
579 PathCatSlash(path, configName);
580 ChangeExtension(path, "econ", path);
583 else if(_class == class(RecentFilesData))
584 PathCatSlash(path, "recentFiles.econ");
585 else if(_class == class(RecentWorkspacesData))
586 PathCatSlash(path, "recentWorkspaces.econ");
589 static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data)
591 SettingsIOResult result = error;
593 sf = safeWriteFileOpen(path, write);
596 WriteECONObject(sf.file, dataType, data, 0);
601 PrintLn($"error: could not safely open file for writing configuration: ", path);
605 static SettingsIOResult readConfigFile(const char * path, Class dataType, void ** data)
607 SettingsIOResult result = error;
609 if(!FileExists(path))
610 result = fileNotFound;
611 else if((sf = safeWriteFileOpen(path, read)))
613 JSONResult jsonResult;
615 ECONParser parser { f = sf.file };
616 sf.file.Seek(0, start);
617 jsonResult = parser.GetObject(dataType, data);
618 if(jsonResult != success)
622 if(jsonResult == success)
626 result = fileNotCompatibleWithDriver;
627 PrintLn($"error: could not parse configuration file: ", path);
638 char path[MAX_LOCATION];
639 char tmp[MAX_LOCATION];
644 // how to make this atomic ?
648 for(c = 0; c < 10; c++)
650 if(FileExists(path).isFile)
655 if(FileExists(path).isFile)
663 SafeFile safeWriteFileOpen(const char * path, FileOpenMode mode)
665 SafeFile sf { mode = mode };
668 strcpy(sf.path, path);
669 strcpy(sf.tmp, path);
670 strcat(sf.tmp, ".tmp");
673 if(FileExists(sf.tmp).isFile)
675 sf.file = FileOpen(sf.tmp, write);
678 for(c = 0; c < 10 && !(locked = sf.file.Lock(exclusive, 0, 0, false)); c++);
682 PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
686 PrintLn($"warning: safeWriteFileOpen: unable to open temporary file for writing: ", sf.tmp);
688 else if(mode == read)
690 sf.file = FileOpen(path, read);
693 for(c = 0; c < 10 && !(locked = sf.file.Lock(shared, 0, 0, false)); c++) Sleep(0.01);
697 PrintLn($"warning: safeWriteFileOpen: unable to obtain shared lock on file for reading: ", path);
702 PrintLn($"warning: safeWriteFileOpen: does not yet support FileOpenMode::", mode);
706 class RecentFilesData
709 RecentFiles recentFiles;
712 class RecentWorkspacesData
715 RecentWorkspaces recentWorkspaces;
718 class IDESettings : GlobalSettingsData
721 property CompilerConfigs compilerConfigs
723 set { if(settingsContainer.oldConfig) { if(compilerConfigs) compilerConfigs.Free(); delete compilerConfigs; if(value) compilerConfigs = value; } }
724 get { return compilerConfigs; }
725 isset { return false; }
727 property RecentFiles recentFiles
729 set { if(recentFiles) recentFiles.Free(); delete recentFiles; if(value) recentFiles = value; }
730 get { return recentFiles; }
731 isset { return false; }
733 property RecentWorkspaces recentProjects
735 set { if(recentProjects) recentProjects.Free(); delete recentProjects; if(value) recentProjects = value; }
736 get { return recentProjects; }
737 isset { return false; }
739 property const char * docDir
741 set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
742 get { return docDir ? docDir : ""; }
743 isset { return docDir && docDir[0]; }
745 property const char * ideFileDialogLocation
747 set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
748 get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
749 isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
751 property const char * ideProjectFileDialogLocation
753 set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
754 get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
755 isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
758 bool showLineNumbers;
759 bool caretFollowsScrolling;
760 char * displayDriver;
762 // TODO: Classify settings
763 //EditorSettings editor { };
765 property const char * projectDefaultTargetDir
767 set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
768 get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
769 isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
771 property const char * projectDefaultIntermediateObjDir
773 set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
774 get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
775 isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
778 property const char * compilerConfigsDir
780 set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
781 get { return compilerConfigsDir ? compilerConfigsDir : ""; }
782 isset { return compilerConfigsDir && compilerConfigsDir[0]; }
785 property const char * defaultCompiler
787 set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
788 get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
789 isset { return defaultCompiler && defaultCompiler[0]; }
792 property const String language
797 language = CopyString(value);
799 get { return language; }
800 isset { return language != null; }
804 CompilerConfigs compilerConfigs { };
806 char * ideFileDialogLocation;
807 char * ideProjectFileDialogLocation;
808 char * projectDefaultTargetDir;
809 char * projectDefaultIntermediateObjDir;
810 char * compilerConfigsDir;
811 char * defaultCompiler;
813 RecentFiles recentFiles { };
814 RecentWorkspaces recentProjects { };
816 CompilerConfig GetCompilerConfig(const String compilerName)
818 const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
819 CompilerConfig compilerConfig = null;
820 for(compiler : compilerConfigs)
822 if(!strcmp(compiler.name, name))
824 compilerConfig = compiler;
828 if(!compilerConfig && compilerConfigs.count)
829 compilerConfig = compilerConfigs.firstIterator.data;
831 incref compilerConfig;
832 return compilerConfig;
837 compilerConfigs.Free();
838 delete compilerConfigs;
839 if(recentProjects) { recentFiles.Free(); delete recentFiles; }
840 if(recentProjects) { recentProjects.Free(); delete recentProjects; }
843 delete projectDefaultTargetDir;
844 delete projectDefaultIntermediateObjDir;
845 delete compilerConfigsDir;
846 delete defaultCompiler;
849 delete ideFileDialogLocation;
850 delete ideProjectFileDialogLocation;
851 delete displayDriver;
854 void ForcePathSeparatorStyle(bool unixStyle)
858 from = '\\', to = '/';
860 from = '/', to = '\\';
861 if(compilerConfigs && compilerConfigs.count)
864 for(config : compilerConfigs)
866 if(config.includeDirs && config.includeDirs.count)
868 for(i = 0; i < config.includeDirs.count; i++)
870 if(config.includeDirs[i] && config.includeDirs[i][0])
871 ChangeCh(config.includeDirs[i], from, to);
874 if(config.libraryDirs && config.libraryDirs.count)
876 for(i = 0; i < config.libraryDirs.count; i++)
878 if(config.libraryDirs[i] && config.libraryDirs[i][0])
879 ChangeCh(config.libraryDirs[i], from, to);
882 if(config.executableDirs && config.executableDirs.count)
884 for(i = 0; i < config.executableDirs.count; i++)
886 if(config.executableDirs[i] && config.executableDirs[i][0])
887 ChangeCh(config.executableDirs[i], from, to);
892 recentFiles.changeChar(from, to);
893 recentProjects.changeChar(from, to);
894 if(docDir && docDir[0])
895 ChangeCh(docDir, from, to);
896 if(ideFileDialogLocation && ideFileDialogLocation[0])
897 ChangeCh(ideFileDialogLocation, from, to);
898 if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
899 ChangeCh(ideProjectFileDialogLocation, from, to);
901 if(projectDefaultTargetDir && projectDefaultTargetDir[0])
902 ChangeCh(projectDefaultTargetDir, from, to);
903 if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
904 ChangeCh(projectDefaultIntermediateObjDir, from, to);
906 if(compilerConfigsDir && compilerConfigsDir[0])
907 ChangeCh(compilerConfigsDir, from, to);
910 void ManagePortablePaths(char * location, bool makeAbsolute)
913 if(compilerConfigs && compilerConfigs.count)
915 for(config : compilerConfigs)
918 for(t = 0; t < DirTypes::enumSize; t++)
920 Array<String> dirs = null;
921 if(t == executables) dirs = config.executableDirs;
922 else if(t == includes) dirs = config.includeDirs;
923 else if(t == libraries) dirs = config.libraryDirs;
924 if(dirs && dirs.count)
926 for(c = 0; c < dirs.count; c++)
928 if(dirs[c] && dirs[c][0])
929 dirs[c] = UpdatePortablePath(dirs[c], location, makeAbsolute);
935 if(recentFiles && recentFiles.count)
937 for(c = 0; c < recentFiles.count; c++)
939 if(recentFiles[c] && recentFiles[c][0])
940 recentFiles[c] = UpdatePortablePath(recentFiles[c], location, makeAbsolute);
943 if(recentProjects && recentProjects.count)
945 for(c = 0; c < recentProjects.count; c++)
947 if(recentProjects[c] && recentProjects[c][0])
948 recentProjects[c] = UpdatePortablePath(recentProjects[c], location, makeAbsolute);
951 if(docDir && docDir[0])
952 docDir = UpdatePortablePath(docDir, location, makeAbsolute);
953 if(ideFileDialogLocation && ideFileDialogLocation[0])
954 ideFileDialogLocation = UpdatePortablePath(ideFileDialogLocation, location, makeAbsolute);
955 if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
956 ideProjectFileDialogLocation = UpdatePortablePath(ideProjectFileDialogLocation, location, makeAbsolute);
958 if(projectDefaultTargetDir && projectDefaultTargetDir[0])
959 projectDefaultTargetDir = UpdatePortablePath(projectDefaultTargetDir, location, makeAbsolute);
960 if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
961 projectDefaultIntermediateObjDir = UpdatePortablePath(projectDefaultIntermediateObjDir, location, makeAbsolute);
963 if(compilerConfigsDir && compilerConfigsDir[0])
964 compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
967 char * UpdatePortablePath(char * path, const char * location, bool makeAbsolute)
972 char p[MAX_LOCATION];
974 PathCatSlash(p, path);
976 output = CopyString(p);
980 PathRelationship rel = eString_PathRelated(path, location, null);
981 if(rel == subPath || rel == identical)
983 char p[MAX_LOCATION];
984 MakePathRelative(path, location, p);
985 if(!*p) strcpy(p, "./");
986 else ChangeCh(p, '\\', '/');
988 output = CopyString(p);
997 class RecentFiles : RecentPaths
1006 char path[MAX_LOCATION];
1007 RecentFilesData d = null;
1008 Class _class = class(RecentFilesData);
1009 getConfigFilePath(path, _class, null, null);
1010 readConfigFile(path, _class, &d);
1011 if(d && d.recentFiles && d.recentFiles.count)
1013 IDESettings s = (IDESettings)settingsContainer.data;
1014 s.property::recentFiles = d.recentFiles;
1015 d.recentFiles = null;
1016 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
1017 ide.updateRecentFilesMenu();
1025 char path[MAX_LOCATION];
1026 IDESettings s = (IDESettings)settingsContainer.data;
1027 RecentFilesData d { };
1028 Class _class = class(RecentFilesData);
1029 getConfigFilePath(path, _class, null, null);
1030 d.recentFiles = s.recentFiles;
1031 writeConfigFile(path, _class, d);
1032 d.recentFiles = null;
1037 class RecentWorkspaces : RecentPaths
1046 char path[MAX_LOCATION];
1047 RecentWorkspacesData d = null;
1048 Class _class = class(RecentWorkspacesData);
1049 getConfigFilePath(path, _class, null, null);
1050 readConfigFile(path, _class, &d);
1051 if(d && d.recentWorkspaces && d.recentWorkspaces.count)
1053 IDESettings s = (IDESettings)settingsContainer.data;
1054 s.property::recentProjects = d.recentWorkspaces;
1055 d.recentWorkspaces = null;
1056 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
1057 ide.updateRecentProjectsMenu();
1065 char path[MAX_LOCATION];
1066 IDESettings s = (IDESettings)settingsContainer.data;
1067 RecentWorkspacesData d { };
1068 Class _class = class(RecentWorkspacesData);
1069 getConfigFilePath(path, _class, null, null);
1070 d.recentWorkspaces = s.recentProjects;
1071 writeConfigFile(path, _class, d);
1072 d.recentWorkspaces = null;
1077 class RecentPaths : Array<String>
1079 virtual void onAdd();
1081 IteratorPointer Add(T value)
1084 char * filePath = (char *)value;
1085 ChangeCh(filePath, '\\', '/');
1086 for(c = 0; c < count; c++)
1088 if(this[c] && !fstrcmp(this[c], filePath))
1090 Delete((void *)&this[c]);
1094 return Array::Add((T)filePath);
1097 IteratorPointer addRecent(T value)
1100 char * filePath = (char *)value;
1102 ChangeCh(filePath, '\\', '/');
1103 for(c = 0; c < count; c++)
1105 if(this[c] && !fstrcmp(this[c], filePath))
1107 Delete((void *)&this[c]);
1111 while(count >= MaxRecent)
1113 ip = Insert(null, filePath);
1118 void changeChar(char from, char to)
1123 for(c = 0; c < count; c++)
1125 if(this[c] && this[c][0])
1126 ChangeCh(this[c], from, to);
1132 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
1133 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
1134 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
1135 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
1136 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
1137 // TODO: i18n with Array
1138 static Array<const String> compilerTypeLongNames
1140 $"GNU Compiler Collection (GCC) / GNU Make",
1141 $"Tiny C Compiler / GNU Make",
1142 $"Portable C Compiler / GNU Make",
1143 $"Microsoft Visual Studio 2005 (8.0) Compiler",
1144 $"Microsoft Visual Studio 2008 (9.0) Compiler",
1145 $"Microsoft Visual Studio 2010 (10.0) Compiler"
1147 const CompilerType firstCompilerType = gcc;
1148 const CompilerType lastCompilerType = vs10;
1149 public enum CompilerType
1151 gcc, tcc, pcc, vs8, vs9, vs10;
1155 get { return this == vs8 || this == vs9 || this == vs10; }
1158 property const char *
1160 get { return OnGetString(null, null, null); }
1166 for(c = firstCompilerType; c <= lastCompilerType; c++)
1167 if(!strcmpi(value, compilerTypeNames[c]))
1174 property const char * longName { get { return OnGetString(null, (void*)1, null); } };
1175 property const char * versionString { get { return OnGetString(null, (void*)2, null); } };
1176 property const char * yearString { get { return OnGetString(null, (void*)3, null); } };
1177 property const char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
1178 property const char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
1180 const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1182 if(this >= firstCompilerType && this <= lastCompilerType)
1185 strcpy(tempString, compilerTypeNames[this]);
1186 if(fieldData == null)
1187 return compilerTypeNames[this];
1188 else if(fieldData == (void*)1)
1189 return compilerTypeLongNames[this];
1190 else if(fieldData == (void*)2)
1191 return compilerTypeVersionString[this];
1192 else if(fieldData == (void*)3)
1193 return compilerTypeYearString[this];
1194 else if(fieldData == (void*)4)
1195 return compilerTypeProjectFileExtension[this];
1196 else if(fieldData == (void*)5)
1197 return compilerTypeSolutionFileVersionString[this];
1203 class CompilerConfig
1209 property const char * name
1211 set { delete name; if(value) name = CopyString(value); }
1212 get { return name; }
1216 Platform targetPlatform;
1218 property const char * makeCommand
1220 set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
1221 get { return makeCommand; }
1222 isset { return makeCommand && makeCommand[0]; }
1224 property const char * ecpCommand
1226 set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
1227 get { return ecpCommand; }
1228 isset { return ecpCommand && ecpCommand[0]; }
1230 property const char * eccCommand
1232 set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
1233 get { return eccCommand; }
1234 isset { return eccCommand && eccCommand[0]; }
1236 property const char * ecsCommand
1238 set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
1239 get { return ecsCommand; }
1240 isset { return ecsCommand && ecsCommand[0]; }
1242 property const char * earCommand
1244 set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
1245 get { return earCommand; }
1246 isset { return earCommand && earCommand[0]; }
1248 property const char * cppCommand
1250 set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
1251 get { return cppCommand; }
1252 isset { return cppCommand && cppCommand[0]; }
1254 property const char * ccCommand
1256 set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
1257 get { return ccCommand; }
1258 isset { return ccCommand && ccCommand[0]; }
1260 property const char * cxxCommand
1262 set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
1263 get { return cxxCommand; }
1264 isset { return cxxCommand && cxxCommand[0]; }
1266 property const char * arCommand
1268 set { delete arCommand; if(value && value[0]) arCommand = CopyString(value); }
1269 get { return arCommand; }
1270 isset { return arCommand && arCommand[0]; }
1272 property const char * ldCommand
1274 set { delete ldCommand; if(value && value[0]) ldCommand = CopyString(value); }
1275 get { return ldCommand; }
1276 isset { return ldCommand && ldCommand[0]; }
1278 property const char * objectFileExt
1280 set { delete objectFileExt; if(value && value[0]) objectFileExt = CopyString(value); }
1281 get { return objectFileExt && objectFileExt[0] ? objectFileExt : objectDefaultFileExt ; }
1282 isset { return objectFileExt && objectFileExt[0] && strcmp(objectFileExt, objectDefaultFileExt); }
1284 property const char * staticLibFileExt
1286 set { delete staticLibFileExt; if(value && value[0]) staticLibFileExt = CopyString(value); }
1287 get { return staticLibFileExt; }
1288 isset { return staticLibFileExt && staticLibFileExt[0]; }
1290 property const char * sharedLibFileExt
1292 set { delete sharedLibFileExt; if(value && value[0]) sharedLibFileExt = CopyString(value); }
1293 get { return sharedLibFileExt; }
1294 isset { return sharedLibFileExt && sharedLibFileExt[0]; }
1296 property const char * executableFileExt
1298 set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); }
1299 get { return executableFileExt; }
1300 isset { return executableFileExt && executableFileExt[0]; }
1302 property const char * executableLauncher
1304 set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
1305 get { return executableLauncher; }
1306 isset { return executableLauncher && executableLauncher[0]; }
1308 // TODO: implement CompilerConfig::windresCommand
1312 property bool supportsBitDepth { set { } get { return true; } isset { return false; } }
1314 property const char * distccHosts
1316 set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
1317 get { return distccHosts; }
1318 isset { return distccHosts && distccHosts[0]; }
1320 property const char * gnuToolchainPrefix
1322 set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
1323 get { return gnuToolchainPrefix; }
1324 isset { return gnuToolchainPrefix && gnuToolchainPrefix[0]; }
1326 property const char * sysroot
1328 set { delete sysroot; if(value && value[0]) sysroot = CopyString(value); }
1329 get { return sysroot; }
1330 isset { return sysroot && sysroot[0]; }
1332 bool resourcesDotEar;
1334 property Array<String> includeDirs
1342 includeDirs = value;
1345 get { return includeDirs; }
1346 isset { return includeDirs.count != 0; }
1348 property Array<String> libraryDirs
1356 libraryDirs = value;
1359 get { return libraryDirs; }
1360 isset { return libraryDirs.count != 0; }
1362 property Array<String> executableDirs
1366 executableDirs.Free();
1369 delete executableDirs;
1370 executableDirs = value;
1373 get { return executableDirs; }
1374 isset { return executableDirs.count != 0; }
1376 property Array<NamedString> environmentVars
1380 environmentVars.Free();
1383 delete environmentVars;
1384 environmentVars = value;
1387 get { return environmentVars; }
1388 isset { return environmentVars.count != 0; }
1390 property Array<String> prepDirectives
1394 prepDirectives.Free();
1397 delete prepDirectives;
1398 prepDirectives = value;
1401 get { return prepDirectives; }
1402 isset { return prepDirectives.count != 0; }
1404 property Array<String> excludeLibs
1412 excludeLibs = value;
1415 get { return excludeLibs; }
1416 isset { return excludeLibs.count != 0; }
1418 property Array<String> eCcompilerFlags
1422 eCcompilerFlags.Free();
1425 delete eCcompilerFlags;
1426 eCcompilerFlags = value;
1429 get { return eCcompilerFlags; }
1430 isset { return eCcompilerFlags.count != 0; }
1432 property Array<String> compilerFlags
1436 compilerFlags.Free();
1439 delete compilerFlags;
1440 compilerFlags = value;
1443 get { return compilerFlags; }
1444 isset { return compilerFlags.count != 0; }
1446 property Array<String> cxxFlags
1457 get { return cxxFlags; }
1458 isset { return cxxFlags.count != 0; }
1460 property Array<String> linkerFlags
1468 linkerFlags = value;
1471 get { return linkerFlags; }
1472 isset { return linkerFlags.count != 0; }
1474 // json backward compatibility
1475 property const char * gccPrefix
1477 set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
1478 get { return gnuToolchainPrefix; }
1479 isset { return false; }
1481 property const char * execPrefixCommand
1483 set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
1484 get { return executableLauncher; }
1485 isset { return false; }
1487 property const char * outputFileExt
1489 set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); }
1490 get { return executableFileExt; }
1491 isset { return false; }
1494 property bool hasDocumentOutput
1498 bool result = executableFileExt && executableFileExt[0] &&
1499 (!strcmpi(executableFileExt, "htm") || !strcmpi(executableFileExt, "html"));
1502 isset { return false; }
1505 Array<String> includeDirs { };
1506 Array<String> libraryDirs { };
1507 Array<String> executableDirs { };
1508 // TODO: Can JSON parse and serialize maps?
1509 //EnvironmentVariables { };
1510 Array<NamedString> environmentVars { };
1511 Array<String> prepDirectives { };
1512 Array<String> excludeLibs { };
1513 Array<String> eCcompilerFlags { };
1514 Array<String> compilerFlags { };
1515 Array<String> cxxFlags { };
1516 Array<String> linkerFlags { };
1528 char * objectFileExt;
1529 char * staticLibFileExt;
1530 char * sharedLibFileExt;
1531 char * executableFileExt;
1532 char * executableLauncher;
1534 char * gnuToolchainPrefix;
1538 struct { Array<String> includes, libraries, executables; };
1539 Array<String> dirs[DirTypes];
1554 delete objectFileExt;
1555 delete staticLibFileExt;
1556 delete sharedLibFileExt;
1557 delete executableFileExt;
1559 delete executableLauncher;
1561 delete gnuToolchainPrefix;
1563 if(environmentVars) environmentVars.Free();
1564 if(includeDirs) { includeDirs.Free(); }
1565 if(libraryDirs) { libraryDirs.Free(); }
1566 if(executableDirs) { executableDirs.Free(); }
1567 if(prepDirectives) { prepDirectives.Free(); }
1568 if(excludeLibs) { excludeLibs.Free(); }
1569 if(compilerFlags) { compilerFlags.Free(); }
1570 if(cxxFlags) { cxxFlags.Free(); }
1571 if(eCcompilerFlags) { eCcompilerFlags.Free(); }
1572 if(linkerFlags) { linkerFlags.Free(); }
1575 int OnCompare(CompilerConfig b)
1578 if(!(result = name.OnCompare(b.name)) &&
1579 !(result = ecpCommand.OnCompare(b.ecpCommand)) &&
1580 !(result = eccCommand.OnCompare(b.eccCommand)) &&
1581 !(result = ecsCommand.OnCompare(b.ecsCommand)) &&
1582 !(result = earCommand.OnCompare(b.earCommand)) &&
1583 !(result = cppCommand.OnCompare(b.cppCommand)) &&
1584 !(result = ccCommand.OnCompare(b.ccCommand)) &&
1585 !(result = cxxCommand.OnCompare(b.cxxCommand)) &&
1586 !(result = ldCommand.OnCompare(b.ldCommand)) &&
1587 !(result = arCommand.OnCompare(b.arCommand)) &&
1588 !(result = objectFileExt.OnCompare(b.objectFileExt)) &&
1589 !(result = outputFileExt.OnCompare(b.outputFileExt)) &&
1590 !(result = makeCommand.OnCompare(b.makeCommand)) &&
1591 !(result = executableLauncher.OnCompare(b.executableLauncher)) &&
1592 !(result = distccHosts.OnCompare(b.distccHosts)) &&
1593 !(result = gnuToolchainPrefix.OnCompare(b.gnuToolchainPrefix)) &&
1594 !(result = sysroot.OnCompare(b.sysroot)))
1598 !(result = includeDirs.OnCompare(b.includeDirs)) &&
1599 !(result = libraryDirs.OnCompare(b.libraryDirs)) &&
1600 !(result = executableDirs.OnCompare(b.executableDirs)) &&
1601 !(result = environmentVars.OnCompare(b.environmentVars)) &&
1602 !(result = prepDirectives.OnCompare(b.prepDirectives)) &&
1603 !(result = excludeLibs.OnCompare(b.excludeLibs)) &&
1604 !(result = cxxFlags.OnCompare(b.cxxFlags)) &&
1605 !(result = eCcompilerFlags.OnCompare(b.eCcompilerFlags)) &&
1606 !(result = compilerFlags.OnCompare(b.compilerFlags)) &&
1607 !(result = linkerFlags.OnCompare(b.linkerFlags)))
1614 CompilerConfig Copy()
1647 for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
1648 for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
1649 for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
1650 for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
1651 for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
1652 for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
1653 for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
1654 for(s : cxxFlags) copy.cxxFlags.Add(CopyString(s));
1655 for(s : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
1656 for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
1662 CompilerConfig ::read(const char * path)
1664 CompilerConfig d = null;
1665 readConfigFile(path, class(CompilerConfig), &d);
1671 char dir[MAX_LOCATION];
1672 char path[MAX_LOCATION];
1673 const char * settingsFilePath = settingsContainer.settingsFilePath;
1674 getConfigFilePath(path, _class, dir, name);
1675 if(FileExists(settingsFilePath) && !FileExists(dir))
1678 if(!FileExists(dir))
1679 PrintLn($"Error creating compiler configs directory at ", dir, " location.");
1681 writeConfigFile(path, _class, this);
1685 class CompilerConfigs : List<CompilerConfig>
1689 IDESettings s = (IDESettings)settingsContainer.data;
1690 // Ensure we have a default compiler
1691 CompilerConfig defaultCompiler = null;
1692 defaultCompiler = s.GetCompilerConfig(defaultCompilerName);
1693 if(!defaultCompiler)
1695 defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
1696 s.compilerConfigs.Insert(null, defaultCompiler);
1697 defaultCompiler = null;
1699 delete defaultCompiler;
1701 if(s.compilerConfigs)
1703 for(ccfg : s.compilerConfigs)
1705 if(!ccfg.ecpCommand || !ccfg.ecpCommand[0])
1706 ccfg.ecpCommand = ecpDefaultCommand;
1707 if(!ccfg.eccCommand || !ccfg.eccCommand[0])
1708 ccfg.eccCommand = eccDefaultCommand;
1709 if(!ccfg.ecsCommand || !ccfg.ecsCommand[0])
1710 ccfg.ecsCommand = ecsDefaultCommand;
1711 if(!ccfg.earCommand || !ccfg.earCommand[0])
1712 ccfg.earCommand = earDefaultCommand;
1713 if(!ccfg.cppCommand || !ccfg.cppCommand[0])
1714 ccfg.cppCommand = cppDefaultCommand;
1715 if(!ccfg.ccCommand || !ccfg.ccCommand[0])
1716 ccfg.ccCommand = ccDefaultCommand;
1717 if(!ccfg.cxxCommand || !ccfg.cxxCommand[0])
1718 ccfg.cxxCommand = cxxDefaultCommand;
1719 /*if(!ccfg.ldCommand || !ccfg.ldCommand[0])
1720 ccfg.ldCommand = ldDefaultCommand;*/
1721 if(!ccfg.arCommand || !ccfg.arCommand[0])
1722 ccfg.arCommand = arDefaultCommand;
1723 if(!ccfg.objectFileExt || !ccfg.objectFileExt[0])
1724 ccfg.objectFileExt = objectDefaultFileExt;
1725 /*if(!ccfg.staticLibFileExt || !ccfg.staticLibFileExt[0])
1726 ccfg.staticLibFileExt = staticLibDefaultFileExt;*/
1727 /*if(!ccfg.sharedLibFileExt || !ccfg.sharedLibFileExt[0])
1728 ccfg.sharedLibFileExt = sharedLibDefaultFileExt;*/
1729 /*if(!ccfg.executableFileExt || !ccfg.executableFileExt[0])
1730 ccfg.executableFileExt = outputDefaultFileExt;*/
1731 if(!ccfg._refCount) incref ccfg;
1736 AVLTree<String> getWriteRequiredList(CompilerConfigs oldConfigs)
1738 AVLTree<String> list { };
1741 for(occfg : oldConfigs)
1743 if(!strcmp(ccfg.name, occfg.name))
1745 if(ccfg.OnCompare(occfg))
1746 list.Add(CopyString(ccfg.name));
1756 if(settingsContainer.settingsFilePath)
1758 char dir[MAX_LOCATION];
1759 char path[MAX_LOCATION];
1760 Class _class = class(CompilerConfig);
1761 getConfigFilePath(path, _class, dir, null);
1764 CompilerConfigs ccfgs { };
1765 AVLTree<const String> addedConfigs { };
1766 IDESettings s = (IDESettings)settingsContainer.data;
1767 Map<String, CompilerConfig> compilerConfigsByName = getCompilerConfigsByName(dir);
1768 MapIterator<const String, CompilerConfig> it { map = compilerConfigsByName };
1769 if(it.Index("Default", false))
1771 CompilerConfig ccfg = it.data;
1772 ccfgs.Add(ccfg.Copy());
1773 addedConfigs.Add(ccfg.name);
1775 for(ccfg : compilerConfigsByName)
1777 if(!addedConfigs.Find(ccfg.name))
1779 ccfgs.Add(ccfg.Copy());
1780 addedConfigs.Add(ccfg.name);
1783 for(ccfg : s.compilerConfigs)
1785 if(!addedConfigs.Find(ccfg.name))
1786 ccfgs.Add(ccfg.Copy());
1788 delete addedConfigs;
1789 s.property::compilerConfigs = ccfgs;
1791 compilerConfigsByName.Free();
1792 delete compilerConfigsByName;
1793 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
1794 ide.UpdateCompilerConfigs(true);
1800 void ::write(AVLTree<String> cfgsToWrite)
1802 char dir[MAX_LOCATION];
1803 char path[MAX_LOCATION];
1804 Map<String, String> paths;
1805 IDESettings s = (IDESettings)settingsContainer.data;
1806 getConfigFilePath(path, class(CompilerConfig), dir, null);
1807 paths = getCompilerConfigFilePathsByName(dir);
1809 MapIterator<String, String> it { map = paths };
1810 for(c : s.compilerConfigs)
1812 CompilerConfig ccfg = c;
1813 if(!cfgsToWrite || cfgsToWrite.Find(ccfg.name))
1815 if(it.Index(ccfg.name, false))
1824 const char * path = p;
1832 #if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
1833 struct LanguageOption
1836 const String bitmap;
1840 const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1845 void OnDisplay(Surface surface, int x, int y, int width, void * data, Alignment alignment, DataDisplayFlags flags)
1847 Bitmap icon = res ? res.bitmap : null;
1850 surface.Blit(icon, x + (16 - icon.width) / 2,y+2,0,0, icon.width, icon.height);
1851 class::OnDisplay(surface, x + w, y, width - w, null, alignment, flags);
1855 Array<LanguageOption> languages
1857 { "English", ":countryCode/gb.png", "" },
1858 { "汉语", ":countryCode/cn.png", "zh_CN" },
1859 { "Español", ":countryCode/es.png", "es" },
1860 { "Português (Brazil)", ":countryCode/br.png", "pt_BR" },
1861 { "Русский (43%)", ":countryCode/ru.png", "ru" },
1862 { "Nederlandse (13%)", ":countryCode/nl.png", "nl" },
1863 { "Tiếng Việt (12%)", ":countryCode/vn.png", "vi" },
1864 { "मराठी (10%)", ":countryCode/in.png", "mr" },
1865 { "Hebrew (8%)", ":countryCode/il.png", "he" },
1866 { "Magyar (8%)", ":countryCode/hu.png", "hu" }
1869 const String GetLanguageString()
1871 char * dot, * colon;
1872 static char lang[256];
1873 const String language = getenv("ECERE_LANGUAGE");
1874 if(!language) language = getenv("LANGUAGE");
1875 if(!language) language = getenv("LC_ALL");
1876 if(!language) language = getenv("LC_MESSAGES");
1877 if(!language) language = getenv("LANG");
1878 if(!language) language = "";
1879 if(language && (colon = strchr(language, ':')))
1881 if(lang != language)
1882 strncpy(lang, language, sizeof(lang));
1883 lang[sizeof(lang)-1] = 0;
1884 lang[colon - language] = 0;
1887 if(language && (dot = strchr(language, '.')))
1889 if(lang != language)
1890 strncpy(lang, language, sizeof(lang));
1891 lang[sizeof(lang)-1] = 0;
1892 lang[dot - language] = 0;
1898 bool LanguageRestart(const char * code, Application app, IDESettings settings, IDESettingsContainer settingsContainer, Window ide, Window projectView, bool wait)
1900 bool restart = true;
1901 String command = null;
1902 int arg0Len = (int)strlen(app.argv[0]);
1914 for(w = ide.firstChild; w; w = w.next)
1916 if(w.isActiveClient && w.isDocument)
1918 if(!w.CloseConfirmation(true))
1927 if(!projectView.CloseConfirmation(true))
1929 if(projectView.fileName)
1931 const char * name = projectView.fileName;
1934 for(j = 0; (ch = name[j]); j++)
1935 len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1939 command = new char[len + 1];
1941 strcpy(command, app.argv[0]);
1943 if(projectView.fileName)
1945 strcat(command, " ");
1947 ReplaceSpaces(command + len, projectView.fileName);
1952 for(w = ide.firstChild; w; w = w.next)
1953 if(w.isActiveClient && w.isDocument)
1954 w.modifiedDocument = false;
1955 projectView.modifiedDocument = false;
1960 for(w = ide.firstChild; w; w = w.next)
1962 if(w.isActiveClient && w.isDocument)
1964 if(!w.CloseConfirmation(true))
1971 const char * name = w.fileName;
1973 for(j = 0; (ch = name[j]); j++)
1974 len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1981 command = new char[len + 1];
1982 strcpy(command, app.argv[0]);
1985 for(w = ide.firstChild; w; w = w.next)
1987 if(w.isActiveClient && w.isDocument)
1989 const char * name = w.fileName;
1992 strcat(command, " ");
1994 ReplaceSpaces(command + len, name);
1995 len = (int)strlen(command);
2002 for(w = ide.firstChild; w; w = w.next)
2003 if(w.isActiveClient && w.isDocument)
2004 w.modifiedDocument = false;
2009 settings.language = code;
2010 settingsContainer.Save();
2012 setEcereLanguageInWinRegEnvironment(code);
2014 if(eClass_IsDerived(app._class, class(GuiApplication)))
2016 GuiApplication guiApp = (GuiApplication)app;
2017 guiApp.desktop.Destroy(0);
2024 for(i = 1; i < app.argc; i++)
2026 const char * arg = app.argv[i];
2028 for(j = 0; (ch = arg[j]); j++)
2029 len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
2032 command = new char[len + 1];
2033 strcpy(command, app.argv[0]);
2035 for(i = 1; i < app.argc; i++)
2037 strcat(command, " ");
2039 ReplaceSpaces(command + len, app.argv[i]);
2040 len = (int)strlen(command);
2046 SetEnvironment("ECERE_LANGUAGE", code);
2048 ExecuteWait(command);