epj2make, ide: makefile generation, global settings dialog: add CXXFLAGS to compiler...
[sdk] / ide / src / IDESettings.ec
index e9aea97..af82e81 100644 (file)
@@ -4,10 +4,35 @@ public import static "ecere"
 public import "ecere"
 #endif
 
+define ecpDefaultCommand = "ecp";
+define eccDefaultCommand = "ecc";
+define ecsDefaultCommand = "ecs";
+define earDefaultCommand = "ear";
+define cppDefaultCommand = "gcc"; // As per #624 we decided to default to "gcc"...
+define ccDefaultCommand = "gcc";
+define cxxDefaultCommand = "g++";
+//define ldDefaultCommand = "gcc";
+define arDefaultCommand = "ar";
+define objectDefaultFileExt = "o";
+define outputDefaultFileExt = "";
+
 import "StringsBox"
 
 import "OldIDESettings"
 
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+import "ide"
+import "process"
+#endif
+
+IDESettings ideSettings;
+
+IDESettingsContainer settingsContainer
+{
+   dataOwner = &ideSettings;
+   dataClass = class(IDESettings);
+};
+
 define MaxRecent = 9;
 
 enum DirTypes { includes, libraries, executables };
@@ -16,7 +41,7 @@ define defaultCompilerName = "Default";
 
 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)";
 
-char * settingsDirectoryNames[DirTypes] =
+const char * settingsDirectoryNames[DirTypes] =
 {
    "Include Files",
    "Library Files",
@@ -25,7 +50,7 @@ char * settingsDirectoryNames[DirTypes] =
 
 // This function cannot accept same pointer for source and output
 // todo: rename ReplaceSpaces to EscapeSpaceAndSpecialChars or something
-void ReplaceSpaces(char * output, char * source)
+void ReplaceSpaces(char * output, const char * source)
 {
    int c, dc;
    char ch, pch = 0;
@@ -51,7 +76,7 @@ void ReplaceSpaces(char * output, char * source)
 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
 
 enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull };
-PathRelationship eString_PathRelated(char * path, char * to, char * pathDiff)
+PathRelationship eString_PathRelated(const char * path, const char * to, char * pathDiff)
 {
    PathRelationship result;
    if(pathDiff) *pathDiff = '\0';
@@ -92,7 +117,7 @@ PathRelationship eString_PathRelated(char * path, char * to, char * pathDiff)
    return result;
 }
 
-char * CopyValidateMakefilePath(char * path)
+char * CopyValidateMakefilePath(const char * path)
 {
    const int map[]  =    {           0,           1,             2,             3,           4,                    5,                 6,            0,                   1,                    2,        7 };
    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)",  "$(CONFIGURATION)", "$(TARGET_PLATFORM)",(char *)0 };
@@ -101,14 +126,14 @@ char * CopyValidateMakefilePath(char * path)
    if(path)
    {
       int len;
-      len = strlen(path);
+      len = (int)strlen(path);
       copy = CopyString(path);
       if(len)
       {
          int c;
          char * tmp = copy;
          char * start = tmp;
-         Array<char *> parts { };
+         Array<const char *> parts { };
 
          for(c=0; c<len; c++)
          {
@@ -150,7 +175,7 @@ char * CopyValidateMakefilePath(char * path)
    return copy;
 }
 
-void ValidPathBufCopy(char *output, char *input)
+void ValidPathBufCopy(char *output, const char *input)
 {
 #ifdef __WIN32__
    bool volumePath = false;
@@ -167,7 +192,7 @@ void ValidPathBufCopy(char *output, char *input)
    }
 #endif
    {
-      char * chars = "*|:\",<>?";
+      const char * chars = "*|:\",<>?";
       char ch, * s = output, * o = output;
       while((ch = *s++)) { if(!strchr(chars, ch)) *o++ = ch; }
       *o = '\0';
@@ -181,7 +206,7 @@ void ValidPathBufCopy(char *output, char *input)
 void RemoveTrailingPathSeparator(char *path)
 {
    int len;
-   len = strlen(path);
+   len = (int)strlen(path);
    if(len>1 && path[len-1] == DIR_SEP)
       path[--len] = '\0';
 }
@@ -194,14 +219,14 @@ void BasicValidatePathBoxPath(PathBox pathBox)
    pathBox.path = path;
 }
 
-CompilerConfig MakeDefaultCompiler(char * name, bool readOnly)
+CompilerConfig MakeDefaultCompiler(const char * name, bool readOnly)
 {
    CompilerConfig defaultCompiler
    {
       name,
       readOnly,
       gcc,
-      GetRuntimePlatform(),
+      __runtimePlatform,
       1,
       makeDefaultCommand,
       ecpDefaultCommand,
@@ -210,25 +235,49 @@ CompilerConfig MakeDefaultCompiler(char * name, bool readOnly)
       earDefaultCommand,
       cppDefaultCommand,
       ccDefaultCommand,
-      cxxDefaultCommand
+      cxxDefaultCommand,
+      arDefaultCommand
+      //ldDefaultCommand
    };
    incref defaultCompiler;
    return defaultCompiler;
 }
 
-class IDESettingsContainer : GlobalSettings
-{
 #ifdef SETTINGS_TEST
-   settingsName = "ecereIDESettingsTest";
+define settingsDir = ".ecereIDE-SettingsTest";
+define ideSettingsName = "ecereIDE-SettingsTest";
 #else
-   settingsName = "ecereIDE";
+define settingsDir = ".ecereIDE";
+define ideSettingsName = "ecereIDE";
 #endif
 
-   virtual void OnLoad(GlobalSettingsData data);
+class IDESettingsContainer : GlobalSettings
+{
+   property bool useNewConfigurationFiles
+   {
+      set
+      {
+         if(value)
+         {
+            settingsContainer.driver = "ECON";
+            settingsName = "config";
+            settingsExtension = "econ";
+            settingsDirectory = settingsDir;
+         }
+         else
+         {
+            settingsContainer.driver = "JSON";
+            settingsName = ideSettingsName;
+            settingsExtension = null;
+            settingsDirectory = null;
+         }
+      }
+   }
 
    char moduleLocation[MAX_LOCATION];
 
 private:
+   bool oldConfig;
    FileSize settingsFileSize;
 
    IDESettingsContainer()
@@ -249,12 +298,14 @@ private:
 
          strcpy(configFilePath, path);
          PathCat(configFilePath, "Data");
-         PathCat(configFilePath, "ecereIDE.ini");
+         PathCat(configFilePath, ideSettingsName);
+         ChangeExtension(configFilePath, "ini", configFilePath);
 
          strcpy(defaultConfigFilePath, path);
          PathCat(defaultConfigFilePath, "App");
          PathCat(defaultConfigFilePath, "DefaultData");
-         PathCat(defaultConfigFilePath, "ecereIDE.ini");
+         PathCat(defaultConfigFilePath, ideSettingsName);
+         ChangeExtension(defaultConfigFilePath, "ini", defaultConfigFilePath);
 
          if(FileExists(defaultConfigFilePath))
          {
@@ -279,39 +330,46 @@ private:
 
    void OnAskReloadSettings()
    {
-      /*if(MessageBox { type = YesNo, master = this,
-            text = "Global Settings Modified Externally",
-            contents = "The global settings were modified by another instance.\n"
-            "Would you like to reload them?" }.Modal() == Yes)*/
+      FileSize newSettingsFileSize;
+
+      if(OpenAndLock(&newSettingsFileSize))
       {
-         double o, n;
-         FileSize newSettingsFileSize;
-         FileGetSize(settingsFilePath, &newSettingsFileSize);
-         o = settingsFileSize;
-         n = newSettingsFileSize;
-         if(o - n < 2048)
+         if((double)settingsFileSize - (double)newSettingsFileSize < 2048)
             Load();
          else
          {
             GuiApplication app = ((GuiApplication)__thisModule.application);
             Window w;
             for(w = app.desktop.firstChild; w && (!w.created || !w.visible); w = w.next);
-            MessageBox { master = w, type = ok,
+
+            CloseAndMonitor();
+
+            MessageBox { master = w, type = ok, isModal = true,
+                  creationActivation = flash,
                   text = "Global Settings Modified Externally",
                   contents = "The global settings were modified by another process and a drastic shrinking of the settings file was detected.\n"
                   "The new settings will not be loaded to prevent loss of your ide settings.\n"
                   "Please check your settings file and make sure to save this IDE's global settings if your settings file has been compromised."
                   }.Create();
-            //Save();
          }
       }
    }
 
    SettingsIOResult Load()
    {
-      SettingsIOResult result = GlobalSettings::Load();
-      IDESettings data = (IDESettings)this.data;
-      CompilerConfig defaultCompiler = null;
+      IDESettings data;
+      SettingsIOResult result;
+      useNewConfigurationFiles = true;
+      result = GlobalSettings::Load();
+      data = (IDESettings)this.data;
+      oldConfig = false;
+      if(result == fileNotFound)
+      {
+         oldConfig = true;
+         useNewConfigurationFiles = false;
+         result = GlobalSettings::Load();
+      }
+      data = (IDESettings)this.data;
       if(!data)
       {
          this.data = IDESettings { };
@@ -332,8 +390,8 @@ private:
                for(c : oldSettings.compilerConfigs)
                   data.compilerConfigs.Add(c.Copy());
 
-               for(s : oldSettings.recentFiles) data.recentFiles.Add(CopyString(s));
-               for(s : oldSettings.recentProjects) data.recentProjects.Add(CopyString(s));
+               for(s : oldSettings.recentFiles) data.recentFiles.Add(s);
+               for(s : oldSettings.recentProjects) data.recentProjects.Add(s);
 
                data.docDir = oldSettings.docDir;
                data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
@@ -358,76 +416,302 @@ private:
             data.caretFollowsScrolling = false; //true;
          }
       }
-      // Ensure we have a default compiler
-      defaultCompiler = data.GetCompilerConfig(defaultCompilerName);
-      if(!defaultCompiler)
-      {
-         defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
-         data.compilerConfigs.Add(defaultCompiler);
-      }
-
-      // We incref the compilers below, so reset refCount to 0
-      defaultCompiler._refCount = 0;
 
       CloseAndMonitor();
       FileGetSize(settingsFilePath, &settingsFileSize);
-      if(data.compilerConfigs)
-      {
-         for(c : data.compilerConfigs)
-         {
-            CompilerConfig compiler = c;
-            char * cxxCommand = compiler.cxxCommand;
-            if(!cxxCommand || !cxxCommand[0])
-               compiler.cxxCommand = cxxDefaultCommand;
-            incref compiler;
-         }
-      }
+      CompilerConfigs::fix();
       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
          data.ManagePortablePaths(moduleLocation, true);
       data.ForcePathSeparatorStyle(true);
-      OnLoad(data);
+
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+      globalSettingsDialog.ideSettings = data;
+      if(oldConfig)
+      {
+         ide.updateRecentMenus();
+         ide.UpdateCompilerConfigs(true);
+      }
+#endif
+
+      if(oldConfig)
+      {
+         useNewConfigurationFiles = true;
+         Save();
+         CompilerConfigs::write(null);
+      }
       return result;
    }
 
    SettingsIOResult Save()
    {
       SettingsIOResult result;
-
-      IDESettings data = (IDESettings)this.data;
-      Platform runtimePlatform = GetRuntimePlatform();
+      IDESettings data;
+      useNewConfigurationFiles = true;
+      data = (IDESettings)this.data;
       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
          data.ManagePortablePaths(moduleLocation, false);
       data.ForcePathSeparatorStyle(true);
+      if(oldConfig)
+         settingsFilePath = null;
       result = GlobalSettings::Save();
       if(result != success)
          PrintLn("Error saving IDE settings");
+      else
+         oldConfig = false;
       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
          data.ManagePortablePaths(moduleLocation, true);
+
       CloseAndMonitor();
       FileGetSize(settingsFilePath, &settingsFileSize);
+
       return result;
    }
 }
 
+static Map<String, String> getCompilerConfigFilePathsByName(const char * path)
+{
+   Map<String, String> map { };
+   FileListing fl { path, extensions = "econ" };
+   while(fl.Find())
+   {
+      if(fl.stats.attribs.isFile)
+      {
+         char name[MAX_FILENAME];
+         char * path = CopyString(fl.path);
+         MakeSlashPath(path);
+         GetLastDirectory(path, name);
+         {
+            char * s = strstr(name, ".");
+            if(s) *s = 0;
+         }
+         map[name] = path;
+      }
+   }
+   return map;
+}
+
+static Map<String, CompilerConfig> getCompilerConfigsByName(const char * path)
+{
+   Map<String, CompilerConfig> map { };
+   FileListing fl { path, extensions = "econ" };
+   while(fl.Find())
+   {
+      if(fl.stats.attribs.isFile)
+      {
+         char name[MAX_FILENAME];
+         char * path = CopyString(fl.path);
+         MakeSlashPath(path);
+         GetLastDirectory(path, name);
+         {
+            char * s = strstr(name, ".");
+            if(s) *s = 0;
+         }
+         {
+            CompilerConfig ccfg = CompilerConfig::read(path);
+            if(ccfg)
+               map[name] = ccfg;
+         }
+         delete path;
+      }
+   }
+   return map;
+}
+
+static void getConfigFilePath(char * path, Class _class, char * dir, const char * configName)
+{
+   if(dir) *dir = 0;
+   strcpy(path, settingsContainer.settingsFilePath);
+   StripLastDirectory(path, path);
+   if(settingsContainer.oldConfig)
+      PathCatSlash(path, settingsDir);
+   if(_class == class(CompilerConfig))
+   {
+      PathCatSlash(path, "compilerConfigs");
+      if(dir)
+         strcpy(dir, path);
+      if(configName)
+      {
+         PathCatSlash(path, configName);
+         ChangeExtension(path, "econ", path);
+      }
+   }
+   else if(_class == class(RecentFilesData))
+      PathCatSlash(path, "recentFiles.econ");
+   else if(_class == class(RecentWorkspacesData))
+      PathCatSlash(path, "recentWorkspaces.econ");
+}
+
+static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data)
+{
+   SettingsIOResult result = error;
+   SafeFile sf;
+   sf = safeWriteFileOpen(path, write);
+   if(sf.file)
+   {
+      WriteECONObject(sf.file, dataType, data, 0);
+      delete sf;
+      result = success;
+   }
+   else
+      PrintLn($"error: could not safely open file for writing configuration: ", path);
+   return result;
+}
+
+static SettingsIOResult readConfigFile(const char * path, Class dataType, void ** data)
+{
+   SettingsIOResult result = error;
+   SafeFile sf;
+   if(!FileExists(path))
+      result = fileNotFound;
+   else if((sf = safeWriteFileOpen(path, read)))
+   {
+      JSONResult jsonResult;
+      {
+         ECONParser parser { f = sf.file };
+         sf.file.Seek(0, start);
+         jsonResult = parser.GetObject(dataType, data);
+         if(jsonResult != success)
+            delete *data;
+         delete parser;
+      }
+      if(jsonResult == success)
+         result = success;
+      else
+      {
+         result = fileNotCompatibleWithDriver;
+         PrintLn($"error: could not parse configuration file: ", path);
+      }
+      delete sf;
+   }
+   return result;
+}
+
+class SafeFile
+{
+   File file;
+   File lock;
+   FileOpenMode mode;
+   char tmp[MAX_LOCATION];
+   char lck[MAX_LOCATION];
+
+   ~SafeFile()
+   {
+      delete file;
+      if(mode == write)
+         DeleteFile(tmp);
+      if(lock)
+      {
+         delete lock;
+         DeleteFile(lck);
+      }
+   }
+}
+
+SafeFile safeWriteFileOpen(const char * path, FileOpenMode mode)
+{
+   SafeFile sf { mode = mode };
+   strcpy(sf.lck, path);
+   strcat(sf.lck, ".lck");
+   strcpy(sf.tmp, path);
+   strcat(sf.tmp, ".tmp");
+   if(mode == write)
+   {
+      sf.lock = FileOpen(sf.lck, write);
+      if(sf.lock && sf.lock.Lock(exclusive, 0, 0, false))
+      {
+         if(sf.tmp && FileExists(path).isFile)
+            MoveFile(path, sf.tmp);
+         sf.file = FileOpen(path, write);
+      }
+      else
+         PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock for writing: ", sf.lck);
+   }
+   else if(mode == read)
+   {
+      int c;
+      bool locked = false;
+      bool failed = false;
+      for(c = 0; c < 10 && !(failed = sf.tmp && FileExists(sf.tmp).isFile); c++) Sleep(0.01);
+      if(failed)
+      {
+         sf.lock = FileOpen(sf.lck, write);
+         if(sf.lock && sf.lock.Lock(exclusive, 0, 0, false))
+         {
+            if(FileExists(sf.tmp).isFile)
+            {
+               if(FileExists(path).isFile)
+                  DeleteFile(path);
+               MoveFile(sf.tmp, path);
+            }
+            else
+               PrintLn($"warning: safeWriteFileOpen: file is gone: ", sf.tmp);
+            delete sf.lock;
+            DeleteFile(sf.lck);
+         }
+         else
+            PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock for failed write repair: ", sf.lck);
+      }
+      sf.lock = FileOpen(sf.lck, write);
+      if(sf.lock) delete sf.lock;
+      sf.lock = FileOpen(sf.lck, read);
+      if(sf.lock)
+      {
+         for(c = 0; c < 10 && !(locked = sf.lock.Lock(shared, 0, 0, false)); c++) Sleep(0.01);
+         if(locked)
+            sf.file = FileOpen(path, read);
+      }
+   }
+   else
+      PrintLn($"warning: safeWriteFileOpen: does not yet support FileOpenMode::", mode);
+   return sf;
+}
+
+class RecentFilesData
+{
+public:
+   RecentFiles recentFiles;
+}
+
+class RecentWorkspacesData
+{
+public:
+   RecentWorkspaces recentWorkspaces;
+}
+
 class IDESettings : GlobalSettingsData
 {
 public:
-   List<CompilerConfig> compilerConfigs { };
-   Array<String> recentFiles { };
-   Array<String> recentProjects { };
-   property char * docDir
+   property CompilerConfigs compilerConfigs
+   {
+      set { if(compilerConfigs) compilerConfigs.Free(); delete compilerConfigs; if(value) compilerConfigs = value; }
+      get { return compilerConfigs; }
+      isset { return false; }
+   }
+   property RecentFiles recentFiles
+   {
+      set { if(recentFiles) recentFiles.Free(); delete recentFiles; if(value) recentFiles = value; }
+      get { return recentFiles; }
+      isset { return false; }
+   }
+   property RecentWorkspaces recentProjects
+   {
+      set { if(recentProjects) recentProjects.Free(); delete recentProjects; if(value) recentProjects = value; }
+      get { return recentProjects; }
+      isset { return false; }
+   }
+   property const char * docDir
    {
       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
       get { return docDir ? docDir : ""; }
       isset { return docDir && docDir[0]; }
    }
-   property char * ideFileDialogLocation
+   property const char * ideFileDialogLocation
    {
       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
    }
-   property char * ideProjectFileDialogLocation
+   property const char * ideProjectFileDialogLocation
    {
       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
@@ -441,34 +725,34 @@ public:
    // TODO: Classify settings
    //EditorSettings editor { };
 
-   property char * projectDefaultTargetDir
+   property const char * projectDefaultTargetDir
    {
       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
    }
-   property char * projectDefaultIntermediateObjDir
+   property const char * projectDefaultIntermediateObjDir
    {
       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
    }
 
-   property char * compilerConfigsDir
+   property const char * compilerConfigsDir
    {
       set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
       get { return compilerConfigsDir ? compilerConfigsDir : ""; }
       isset { return compilerConfigsDir && compilerConfigsDir[0]; }
    }
 
-   property char * defaultCompiler
+   property const char * defaultCompiler
    {
       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
       isset { return defaultCompiler && defaultCompiler[0]; }
    }
 
-   property String language
+   property const String language
    {
       set
       {
@@ -480,6 +764,7 @@ public:
    }
 
 private:
+   CompilerConfigs compilerConfigs { };
    char * docDir;
    char * ideFileDialogLocation;
    char * ideProjectFileDialogLocation;
@@ -488,10 +773,12 @@ private:
    char * compilerConfigsDir;
    char * defaultCompiler;
    String language;
+   RecentFiles recentFiles { };
+   RecentWorkspaces recentProjects { };
 
-   CompilerConfig GetCompilerConfig(String compilerName)
+   CompilerConfig GetCompilerConfig(const String compilerName)
    {
-      char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
+      const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
       CompilerConfig compilerConfig = null;
       for(compiler : compilerConfigs)
       {
@@ -512,16 +799,15 @@ private:
    {
       compilerConfigs.Free();
       delete compilerConfigs;
-      recentFiles.Free();
-      delete recentFiles;
-      recentProjects.Free();
-      delete recentProjects;
+      if(recentProjects) { recentFiles.Free(); delete recentFiles; }
+      if(recentProjects) { recentProjects.Free(); delete recentProjects; }
       delete docDir;
 
       delete projectDefaultTargetDir;
       delete projectDefaultIntermediateObjDir;
       delete compilerConfigsDir;
       delete defaultCompiler;
+      delete language;
 
       delete ideFileDialogLocation;
       delete ideProjectFileDialogLocation;
@@ -566,24 +852,8 @@ private:
             }
          }
       }
-      if(recentFiles && recentFiles.count)
-      {
-         int c;
-         for(c = 0; c < recentFiles.count; c++)
-         {
-            if(recentFiles[c] && recentFiles[c][0])
-               ChangeCh(recentFiles[c], from, to);
-         }
-      }
-      if(recentProjects && recentProjects.count)
-      {
-         int c;
-         for(c = 0; c < recentProjects.count; c++)
-         {
-            if(recentProjects[c] && recentProjects[c][0])
-               ChangeCh(recentProjects[c], from, to);
-         }
-      }
+      recentFiles.changeChar(from, to);
+      recentProjects.changeChar(from, to);
       if(docDir && docDir[0])
          ChangeCh(docDir, from, to);
       if(ideFileDialogLocation && ideFileDialogLocation[0])
@@ -657,7 +927,7 @@ private:
          compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
    }
 
-   char * UpdatePortablePath(char * path, char * location, bool makeAbsolute)
+   char * UpdatePortablePath(char * path, const char * location, bool makeAbsolute)
    {
       char * output;
       if(makeAbsolute)
@@ -685,43 +955,140 @@ private:
       }
       return output;
    }
+}
+
+class RecentFiles : RecentPaths
+{
+   void onAdd()
+   {
+      write();
+   }
+
+   void ::read()
+   {
+      char path[MAX_LOCATION];
+      RecentFilesData d = null;
+      Class _class = class(RecentFilesData);
+      getConfigFilePath(path, _class, null, null);
+      readConfigFile(path, _class, &d);
+      if(d && d.recentFiles && d.recentFiles.count)
+      {
+         IDESettings s = (IDESettings)settingsContainer.data;
+         s.property::recentFiles = d.recentFiles;
+         d.recentFiles = null;
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+         ide.updateRecentFilesMenu();
+#endif
+      }
+      delete d;
+   }
+
+   void ::write()
+   {
+      char path[MAX_LOCATION];
+      IDESettings s = (IDESettings)settingsContainer.data;
+      RecentFilesData d { };
+      Class _class = class(RecentFilesData);
+      getConfigFilePath(path, _class, null, null);
+      d.recentFiles = s.recentFiles;
+      writeConfigFile(path, _class, d);
+      d.recentFiles = null;
+      delete d;
+   }
+}
+
+class RecentWorkspaces : RecentPaths
+{
+   void onAdd()
+   {
+      write();
+   }
+
+   void ::read()
+   {
+      char path[MAX_LOCATION];
+      RecentWorkspacesData d = null;
+      Class _class = class(RecentWorkspacesData);
+      getConfigFilePath(path, _class, null, null);
+      readConfigFile(path, _class, &d);
+      if(d && d.recentWorkspaces && d.recentWorkspaces.count)
+      {
+         IDESettings s = (IDESettings)settingsContainer.data;
+         s.property::recentProjects = d.recentWorkspaces;
+         d.recentWorkspaces = null;
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+         ide.updateRecentProjectsMenu();
+#endif
+      }
+      delete d;
+   }
+
+   void ::write()
+   {
+      char path[MAX_LOCATION];
+      IDESettings s = (IDESettings)settingsContainer.data;
+      RecentWorkspacesData d { };
+      Class _class = class(RecentWorkspacesData);
+      getConfigFilePath(path, _class, null, null);
+      d.recentWorkspaces = s.recentProjects;
+      writeConfigFile(path, _class, d);
+      d.recentWorkspaces = null;
+      delete d;
+   }
+}
+
+class RecentPaths : Array<String>
+{
+   virtual void onAdd();
 
-   void AddRecentFile(char * fileName)
+   IteratorPointer Add(T value)
    {
       int c;
-      char * filePath = CopyString(fileName);
+      char * filePath = (char *)value;
       ChangeCh(filePath, '\\', '/');
-      for(c = 0; c<recentFiles.count; c++)
+      for(c = 0; c < count; c++)
       {
-         if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
+         if(this[c] && !fstrcmp(this[c], filePath))
          {
-            recentFiles.Delete((void *)&recentFiles[c]);
+            Delete((void *)&this[c]);
             c--;
          }
       }
-      while(recentFiles.count >= MaxRecent)
-         recentFiles.Delete(recentFiles.GetLast());
-      recentFiles.Insert(null, filePath);
-      //UpdateRecentMenus(owner);
+      return Array::Add((T)filePath);
    }
 
-   void AddRecentProject(char * projectName)
+   IteratorPointer addRecent(T value)
    {
       int c;
-      char * filePath = CopyString(projectName);
+      char * filePath = (char *)value;
+      IteratorPointer ip;
       ChangeCh(filePath, '\\', '/');
-      for(c = 0; c<recentProjects.count; c++)
+      for(c = 0; c < count; c++)
       {
-         if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
+         if(this[c] && !fstrcmp(this[c], filePath))
          {
-            recentProjects.Delete((void *)&recentProjects[c]);
+            Delete((void *)&this[c]);
             c--;
          }
       }
-      while(recentProjects.count >= MaxRecent)
-         recentProjects.Delete(recentProjects.GetLast());
-      recentProjects.Insert(null, filePath);
-      //UpdateRecentMenus(owner);
+      while(count >= MaxRecent)
+         Delete(GetLast());
+      ip = Insert(null, filePath);
+      onAdd();
+      return ip;
+   }
+
+   void changeChar(char from, char to)
+   {
+      if(this && count)
+      {
+         int c;
+         for(c = 0; c < count; c++)
+         {
+            if(this[c] && this[c][0])
+               ChangeCh(this[c], from, to);
+         }
+      }
    }
 }
 
@@ -731,7 +1098,7 @@ static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "",
 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
 // TODO: i18n with Array
-static Array<String> compilerTypeLongNames
+static Array<const String> compilerTypeLongNames
 { [
    $"GNU Compiler Collection (GCC) / GNU Make",
    $"Tiny C Compiler / GNU Make",
@@ -751,14 +1118,14 @@ public enum CompilerType
       get { return this == vs8 || this == vs9 || this == vs10; }
    }
 
-   property char *
+   property const char *
    {
       get { return OnGetString(null, null, null); }
       set
       {
          if(value)
          {
-            Platform c;
+            CompilerType c;
             for(c = firstCompilerType; c <= lastCompilerType; c++)
                if(!strcmpi(value, compilerTypeNames[c]))
                   return c;
@@ -767,13 +1134,13 @@ public enum CompilerType
       }
    };
 
-   property char * longName { get { return OnGetString(null, (void*)1, null); } };
-   property char * versionString { get { return OnGetString(null, (void*)2, null); } };
-   property char * yearString { get { return OnGetString(null, (void*)3, null); } };
-   property char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
-   property char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
+   property const char * longName { get { return OnGetString(null, (void*)1, null); } };
+   property const char * versionString { get { return OnGetString(null, (void*)2, null); } };
+   property const char * yearString { get { return OnGetString(null, (void*)3, null); } };
+   property const char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
+   property const char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
 
-   char * OnGetString(char * tempString, void * fieldData, bool * needClass)
+   const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
    {
       if(this >= firstCompilerType && this <= lastCompilerType)
       {
@@ -802,69 +1169,88 @@ class CompilerConfig
 
    numJobs = 1;
 public:
-   property char * name
+   property const char * name
    {
-      set
-      {
-         delete name;
-         if(value)
-            name = CopyString(value);
-      }
+      set { delete name; if(value) name = CopyString(value); }
       get { return name; }
    }
    bool readOnly;
    CompilerType type;
    Platform targetPlatform;
    int numJobs;
-   property char * makeCommand
+   property const char * makeCommand
    {
       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
       get { return makeCommand; }
       isset { return makeCommand && makeCommand[0]; }
    }
-   property char * ecpCommand
+   property const char * ecpCommand
    {
       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
       get { return ecpCommand; }
       isset { return ecpCommand && ecpCommand[0]; }
    }
-   property char * eccCommand
+   property const char * eccCommand
    {
       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
       get { return eccCommand; }
       isset { return eccCommand && eccCommand[0]; }
    }
-   property char * ecsCommand
+   property const char * ecsCommand
    {
       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
       get { return ecsCommand; }
       isset { return ecsCommand && ecsCommand[0]; }
    }
-   property char * earCommand
+   property const char * earCommand
    {
       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
       get { return earCommand; }
       isset { return earCommand && earCommand[0]; }
    }
-   property char * cppCommand
+   property const char * cppCommand
    {
       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
       get { return cppCommand; }
       isset { return cppCommand && cppCommand[0]; }
    }
-   property char * ccCommand
+   property const char * ccCommand
    {
       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
       get { return ccCommand; }
       isset { return ccCommand && ccCommand[0]; }
    }
-   property char * cxxCommand
+   property const char * cxxCommand
    {
       set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
       get { return cxxCommand; }
       isset { return cxxCommand && cxxCommand[0]; }
    }
-   property char * execPrefixCommand // <-- old name for json ide settings file compatibility
+   property const char * arCommand
+   {
+      set { delete arCommand; if(value && value[0]) arCommand = CopyString(value); }
+      get { return arCommand; }
+      isset { return arCommand && arCommand[0]; }
+   }
+   property const char * ldCommand
+   {
+      set { delete ldCommand; if(value && value[0]) ldCommand = CopyString(value); }
+      get { return ldCommand; }
+      isset { return ldCommand && ldCommand[0]; }
+   }
+   property const char * objectFileExt
+   {
+      set { delete objectFileExt; if(value && value[0]) objectFileExt = CopyString(value); }
+      get { return objectFileExt && objectFileExt[0] ? objectFileExt : objectDefaultFileExt ; }
+      isset { return objectFileExt && objectFileExt[0] && strcmp(objectFileExt, objectDefaultFileExt); }
+   }
+   property const char * outputFileExt
+   {
+      set { delete outputFileExt; if(value && value[0]) outputFileExt = CopyString(value); }
+      get { return outputFileExt; }
+      isset { return outputFileExt && outputFileExt[0]; }
+   }
+   property const char * executableLauncher
    {
       set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
       get { return executableLauncher; }
@@ -876,24 +1262,25 @@ public:
    // deprecated
    property bool supportsBitDepth { set { } get { return true; } isset { return false; } }
 
-   property char * distccHosts
+   property const char * distccHosts
    {
       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
       get { return distccHosts; }
       isset { return distccHosts && distccHosts[0]; }
    }
-   property char * gccPrefix // <-- old name for json ide settings file compatibility
+   property const char * gnuToolchainPrefix
    {
       set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
       get { return gnuToolchainPrefix; }
       isset { return gnuToolchainPrefix && gnuToolchainPrefix[0]; }
    }
-   property char * sysroot
+   property const char * sysroot
    {
       set { delete sysroot; if(value && value[0]) sysroot = CopyString(value); }
       get { return sysroot; }
       isset { return sysroot && sysroot[0]; }
    }
+   bool resourcesDotEar;
    property Array<String> includeDirs
    {
       set
@@ -1006,6 +1393,20 @@ public:
       get { return compilerFlags; }
       isset { return compilerFlags.count != 0; }
    }
+   property Array<String> cxxFlags
+   {
+      set
+      {
+         cxxFlags.Free();
+         if(value)
+         {
+            delete cxxFlags;
+            cxxFlags = value;
+         }
+      }
+      get { return cxxFlags; }
+      isset { return cxxFlags.count != 0; }
+   }
    property Array<String> linkerFlags
    {
       set
@@ -1020,6 +1421,19 @@ public:
       get { return linkerFlags; }
       isset { return linkerFlags.count != 0; }
    }
+   // json backward compatibility
+   property const char * gccPrefix
+   {
+      set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
+      get { return gnuToolchainPrefix; }
+      isset { return false; }
+   }
+   property const char * execPrefixCommand
+   {
+      set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
+      get { return executableLauncher; }
+      isset { return false; }
+   }
 private:
    Array<String> includeDirs { };
    Array<String> libraryDirs { };
@@ -1031,6 +1445,7 @@ private:
    Array<String> excludeLibs { };
    Array<String> eCcompilerFlags { };
    Array<String> compilerFlags { };
+   Array<String> cxxFlags { };
    Array<String> linkerFlags { };
    char * name;
    char * makeCommand;
@@ -1041,6 +1456,10 @@ private:
    char * cppCommand;
    char * ccCommand;
    char * cxxCommand;
+   char * ldCommand;
+   char * arCommand;
+   char * objectFileExt;
+   char * outputFileExt;
    char * executableLauncher;
    char * distccHosts;
    char * gnuToolchainPrefix;
@@ -1061,6 +1480,10 @@ private:
       delete cppCommand;
       delete ccCommand;
       delete cxxCommand;
+      delete ldCommand;
+      delete arCommand;
+      delete objectFileExt;
+      delete outputFileExt;
       delete makeCommand;
       delete executableLauncher;
       delete distccHosts;
@@ -1073,9 +1496,50 @@ private:
       if(prepDirectives) { prepDirectives.Free(); }
       if(excludeLibs) { excludeLibs.Free(); }
       if(compilerFlags) { compilerFlags.Free(); }
+      if(cxxFlags) { cxxFlags.Free(); }
       if(eCcompilerFlags) { eCcompilerFlags.Free(); }
       if(linkerFlags) { linkerFlags.Free(); }
    }
+
+   int OnCompare(CompilerConfig b)
+   {
+      int result;
+      if(!(result = name.OnCompare(b.name)) &&
+         !(result = ecpCommand.OnCompare(b.ecpCommand)) &&
+         !(result = eccCommand.OnCompare(b.eccCommand)) &&
+         !(result = ecsCommand.OnCompare(b.ecsCommand)) &&
+         !(result = earCommand.OnCompare(b.earCommand)) &&
+         !(result = cppCommand.OnCompare(b.cppCommand)) &&
+         !(result = ccCommand.OnCompare(b.ccCommand)) &&
+         !(result = cxxCommand.OnCompare(b.cxxCommand)) &&
+         !(result = ldCommand.OnCompare(b.ldCommand)) &&
+         !(result = arCommand.OnCompare(b.arCommand)) &&
+         !(result = objectFileExt.OnCompare(b.objectFileExt)) &&
+         !(result = outputFileExt.OnCompare(b.outputFileExt)) &&
+         !(result = makeCommand.OnCompare(b.makeCommand)) &&
+         !(result = executableLauncher.OnCompare(b.executableLauncher)) &&
+         !(result = distccHosts.OnCompare(b.distccHosts)) &&
+         !(result = gnuToolchainPrefix.OnCompare(b.gnuToolchainPrefix)) &&
+         !(result = sysroot.OnCompare(b.sysroot)))
+         ;
+
+      if(!result &&
+         !(result = includeDirs.OnCompare(b.includeDirs)) &&
+         !(result = libraryDirs.OnCompare(b.libraryDirs)) &&
+         !(result = executableDirs.OnCompare(b.executableDirs)) &&
+         !(result = environmentVars.OnCompare(b.environmentVars)) &&
+         !(result = prepDirectives.OnCompare(b.prepDirectives)) &&
+         !(result = excludeLibs.OnCompare(b.excludeLibs)) &&
+         !(result = cxxFlags.OnCompare(b.cxxFlags)) &&
+         !(result = eCcompilerFlags.OnCompare(b.eCcompilerFlags)) &&
+         !(result = compilerFlags.OnCompare(b.compilerFlags)) &&
+         !(result = linkerFlags.OnCompare(b.linkerFlags)))
+         ;
+
+      return result;
+   }
+
+public:
    CompilerConfig Copy()
    {
       CompilerConfig copy
@@ -1093,13 +1557,18 @@ private:
          cppCommand,
          ccCommand,
          cxxCommand,
+         arCommand,
+         ldCommand,
+         objectFileExt,
+         outputFileExt,
          executableLauncher,
          ccacheEnabled,
          distccEnabled,
          false,
          distccHosts,
          gnuToolchainPrefix,
-         sysroot
+         sysroot,
+         resourcesDotEar
       };
       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
@@ -1108,22 +1577,189 @@ private:
       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
       for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
+      for(s : cxxFlags) copy.cxxFlags.Add(CopyString(s));
       for(s : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
 
       incref copy;
       return copy;
    }
+
+   CompilerConfig ::read(const char * path)
+   {
+      CompilerConfig d = null;
+      readConfigFile(path, class(CompilerConfig), &d);
+      return d;
+   }
+
+   void write()
+   {
+      char dir[MAX_LOCATION];
+      char path[MAX_LOCATION];
+      const char * settingsFilePath = settingsContainer.settingsFilePath;
+      getConfigFilePath(path, _class, dir, name);
+      if(FileExists(settingsFilePath) && !FileExists(dir))
+      {
+         MakeDir(dir);
+         if(!FileExists(dir))
+            PrintLn($"Error creating compiler configs directory at ", dir, " location.");
+      }
+      writeConfigFile(path, _class, this);
+   }
 }
 
+class CompilerConfigs : List<CompilerConfig>
+{
+   void ::fix()
+   {
+      IDESettings s = (IDESettings)settingsContainer.data;
+      // Ensure we have a default compiler
+      CompilerConfig defaultCompiler = null;
+      defaultCompiler = s.GetCompilerConfig(defaultCompilerName);
+      if(!defaultCompiler)
+      {
+         defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
+         s.compilerConfigs.Insert(null, defaultCompiler);
+         defaultCompiler = null;
+      }
+      delete defaultCompiler;
+
+      if(s.compilerConfigs)
+      {
+         for(ccfg : s.compilerConfigs)
+         {
+            if(!ccfg.ecpCommand || !ccfg.ecpCommand[0])
+               ccfg.ecpCommand = ecpDefaultCommand;
+            if(!ccfg.eccCommand || !ccfg.eccCommand[0])
+               ccfg.eccCommand = eccDefaultCommand;
+            if(!ccfg.ecsCommand || !ccfg.ecsCommand[0])
+               ccfg.ecsCommand = ecsDefaultCommand;
+            if(!ccfg.earCommand || !ccfg.earCommand[0])
+               ccfg.earCommand = earDefaultCommand;
+            if(!ccfg.cppCommand || !ccfg.cppCommand[0])
+               ccfg.cppCommand = cppDefaultCommand;
+            if(!ccfg.ccCommand || !ccfg.ccCommand[0])
+               ccfg.ccCommand = ccDefaultCommand;
+            if(!ccfg.cxxCommand || !ccfg.cxxCommand[0])
+               ccfg.cxxCommand = cxxDefaultCommand;
+            /*if(!ccfg.ldCommand || !ccfg.ldCommand[0])
+               ccfg.ldCommand = ldDefaultCommand;*/
+            if(!ccfg.arCommand || !ccfg.arCommand[0])
+               ccfg.arCommand = arDefaultCommand;
+            if(!ccfg.objectFileExt || !ccfg.objectFileExt[0])
+               ccfg.objectFileExt = objectDefaultFileExt;
+            /*if(!ccfg.outputFileExt || !ccfg.outputFileExt[0])
+               ccfg.outputFileExt = outputDefaultFileExt;*/
+            if(!ccfg._refCount) incref ccfg;
+         }
+      }
+   }
+
+   AVLTree<String> getWriteRequiredList(CompilerConfigs oldConfigs)
+   {
+      AVLTree<String> list { };
+      for(ccfg : this)
+      {
+         for(occfg : oldConfigs)
+         {
+            if(!strcmp(ccfg.name, occfg.name))
+            {
+               if(ccfg.OnCompare(occfg))
+                  list.Add(CopyString(ccfg.name));
+               break;
+            }
+         }
+      }
+      return list;
+   }
+
+   void ::read()
+   {
+      if(settingsContainer.settingsFilePath)
+      {
+         char dir[MAX_LOCATION];
+         char path[MAX_LOCATION];
+         Class _class = class(CompilerConfig);
+         getConfigFilePath(path, _class, dir, null);
+         if(dir[0])
+         {
+            CompilerConfigs ccfgs { };
+            AVLTree<const String> addedConfigs { };
+            IDESettings s = (IDESettings)settingsContainer.data;
+            Map<String, CompilerConfig> compilerConfigsByName = getCompilerConfigsByName(dir);
+            MapIterator<const String, CompilerConfig> it { map = compilerConfigsByName };
+            if(it.Index("Default", false))
+            {
+               CompilerConfig ccfg = it.data;
+               ccfgs.Add(ccfg.Copy());
+               addedConfigs.Add(ccfg.name);
+            }
+            for(ccfg : compilerConfigsByName)
+            {
+               if(!addedConfigs.Find(ccfg.name))
+               {
+                  ccfgs.Add(ccfg.Copy());
+                  addedConfigs.Add(ccfg.name);
+               }
+            }
+            for(ccfg : s.compilerConfigs)
+            {
+               if(!addedConfigs.Find(ccfg.name))
+                  ccfgs.Add(ccfg.Copy());
+            }
+            delete addedConfigs;
+            s.property::compilerConfigs = ccfgs;
+            fix();
+            compilerConfigsByName.Free();
+            delete compilerConfigsByName;
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
+            ide.UpdateCompilerConfigs(true);
+#endif
+         }
+      }
+   }
+
+   void ::write(AVLTree<String> cfgsToWrite)
+   {
+      char dir[MAX_LOCATION];
+      char path[MAX_LOCATION];
+      Map<String, String> paths;
+      IDESettings s = (IDESettings)settingsContainer.data;
+      getConfigFilePath(path, class(CompilerConfig), dir, null);
+      paths = getCompilerConfigFilePathsByName(dir);
+      {
+         MapIterator<String, String> it { map = paths };
+         for(c : s.compilerConfigs)
+         {
+            CompilerConfig ccfg = c;
+            if(!cfgsToWrite || cfgsToWrite.Find(ccfg.name))
+               ccfg.write();
+            if(it.Index(ccfg.name, false))
+            {
+               delete it.data;
+               it.Remove();
+            }
+         }
+      }
+      for(p : paths)
+      {
+         const char * path = p;
+         DeleteFile(path);
+      }
+      paths.Free();
+      delete paths;
+   }
+}
+
+#if !defined(ECERE_DOCUMENTOR) && !defined(ECERE_EPJ2MAKE)
 struct LanguageOption
 {
-   String name;
-   String bitmap;
-   String code;
+   const String name;
+   const String bitmap;
+   const String code;
    BitmapResource res;
 
-   char * OnGetString(char * tempString, void * fieldData, bool * needClass)
+   const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
    {
       return name;
    }
@@ -1152,21 +1788,40 @@ Array<LanguageOption> languages
    { "Magyar (8%)",        ":countryCode/hu.png", "hu" }
 ] };
 
-String GetLanguageString()
+const String GetLanguageString()
 {
-   String language = getenv("LANGUAGE");
+   char * dot, * colon;
+   static char lang[256];
+   const String language = getenv("ECERE_LANGUAGE");
+   if(!language) language = getenv("LANGUAGE");
    if(!language) language = getenv("LC_ALL");
    if(!language) language = getenv("LC_MESSAGES");
    if(!language) language = getenv("LANG");
    if(!language) language = "";
+   if(language && (colon = strchr(language, ':')))
+   {
+      if(lang != language)
+         strncpy(lang, language, sizeof(lang));
+      lang[sizeof(lang)-1] = 0;
+      lang[colon - language] = 0;
+      language = lang;
+   }
+   if(language && (dot = strchr(language, '.')))
+   {
+      if(lang != language)
+         strncpy(lang, language, sizeof(lang));
+      lang[sizeof(lang)-1] = 0;
+      lang[dot - language] = 0;
+      language = lang;
+   }
    return language;
 }
 
-bool LanguageRestart(char * code, Application app, IDESettings settings, IDESettingsContainer settingsContainer, Window ide, Window projectView, bool wait)
+bool LanguageRestart(const char * code, Application app, IDESettings settings, IDESettingsContainer settingsContainer, Window ide, Window projectView, bool wait)
 {
    bool restart = true;
    String command = null;
-   int arg0Len = strlen(app.argv[0]);
+   int arg0Len = (int)strlen(app.argv[0]);
    int len = arg0Len;
    int j;
    char ch;
@@ -1195,7 +1850,7 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
                restart = false;
             if(projectView.fileName)
             {
-               char * name = projectView.fileName;
+               const char * name = projectView.fileName;
                if(name)
                {
                   for(j = 0; (ch = name[j]); j++)
@@ -1235,7 +1890,7 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
                }
                if(w.fileName)
                {
-                  char * name = w.fileName;
+                  const char * name = w.fileName;
                   len++;
                   for(j = 0; (ch = name[j]); j++)
                      len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
@@ -1253,13 +1908,13 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
             {
                if(w.isActiveClient && w.isDocument)
                {
-                  char * name = w.fileName;
+                  const char * name = w.fileName;
                   if(name)
                   {
                      strcat(command, " ");
                      len++;
                      ReplaceSpaces(command + len, name);
-                     len = strlen(command);
+                     len = (int)strlen(command);
                   }
                }
             }
@@ -1275,6 +1930,9 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
       {
          settings.language = code;
          settingsContainer.Save();
+
+         setEcereLanguageInWinRegEnvironment(code);
+
          if(eClass_IsDerived(app._class, class(GuiApplication)))
          {
             GuiApplication guiApp = (GuiApplication)app;
@@ -1287,7 +1945,7 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
       int i;
       for(i = 1; i < app.argc; i++)
       {
-         char * arg = app.argv[i];
+         const char * arg = app.argv[i];
          len++;
          for(j = 0; (ch = arg[j]); j++)
             len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
@@ -1301,13 +1959,13 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
          strcat(command, " ");
          len++;
          ReplaceSpaces(command + len, app.argv[i]);
-         len = strlen(command);
+         len = (int)strlen(command);
       }
    }
 
    if(restart)
    {
-      SetEnvironment("LANGUAGE", code);
+      SetEnvironment("ECERE_LANGUAGE", code);
       if(wait)
          ExecuteWait(command);
       else
@@ -1316,3 +1974,4 @@ bool LanguageRestart(char * code, Application app, IDESettings settings, IDESett
    delete command;
    return restart;
 }
+#endif