compiler/libec: (#341, #351, #644, #771) Improved enum type matching and type handlin...
[sdk] / ide / src / IDESettings.ec
index 6653053..b13c0f5 100644 (file)
@@ -1,7 +1,14 @@
+#if defined(__WIN32__)
+#define MessageBox _MessageBox
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef MessageBox
+#endif
+
 #ifdef ECERE_STATIC
-import static "ecere"
+public import static "ecere"
 #else
-import "ecere"
+public import "ecere"
 #endif
 
 import "StringsBox"
@@ -16,13 +23,38 @@ define defaultCompilerName = "Default";
 
 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)";
 
-char * settingsDirectoryNames[DirTypes] = 
+char * settingsDirectoryNames[DirTypes] =
 {
    "Include Files",
    "Library Files",
    "Executable Files"
 };
 
+// This function cannot accept same pointer for source and output
+// todo: rename ReplaceSpaces to EscapeSpaceAndSpecialChars or something
+void ReplaceSpaces(char * output, char * source)
+{
+   int c, dc;
+   char ch, pch = 0;
+
+   for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
+   {
+      if(ch == ' ') output[dc++] = '\\';
+      if(ch == '\"') output[dc++] = '\\';
+      if(ch == '&') output[dc++] = '\\';
+      if(pch != '$')
+      {
+         if(ch == '(' || ch == ')') output[dc++] = '\\';
+         pch = ch;
+      }
+      else if(ch == ')')
+         pch = 0;
+      output[dc] = ch;
+   }
+   output[dc] = '\0';
+}
+
+
 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
 
 enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull };
@@ -76,7 +108,7 @@ char * CopyValidateMakefilePath(char * path)
    if(path)
    {
       int len;
-      len = strlen(path);
+      len = (int)strlen(path);
       copy = CopyString(path);
       if(len)
       {
@@ -84,7 +116,7 @@ char * CopyValidateMakefilePath(char * path)
          char * tmp = copy;
          char * start = tmp;
          Array<char *> parts { };
-         
+
          for(c=0; c<len; c++)
          {
             if(tmp[c] == '$')
@@ -156,7 +188,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';
 }
@@ -230,7 +262,7 @@ private:
          PathCat(defaultConfigFilePath, "App");
          PathCat(defaultConfigFilePath, "DefaultData");
          PathCat(defaultConfigFilePath, "ecereIDE.ini");
-         
+
          if(FileExists(defaultConfigFilePath))
          {
             if(!FileExists(configFilePath))
@@ -242,9 +274,9 @@ private:
             }
             PathCat(path, "Data");
             // the forced settings location will only be
-            // used if the running ide's path matches 
+            // used if the running ide's path matches
             // the PortableApps.com directory structure
-            // and the default ini file is found in 
+            // and the default ini file is found in
             // the DefaultData directory
             settingsLocation = path;
             portable = true;
@@ -254,30 +286,26 @@ 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,
                   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();
          }
       }
    }
@@ -319,7 +347,7 @@ private:
                delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver);
                data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
                data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
-                        
+
                Save();
                result = success;
             }
@@ -328,9 +356,9 @@ private:
          if(result == fileNotFound || !data)
          {
             data = (IDESettings)this.data;
-            data.useFreeCaret = true;
+            data.useFreeCaret = false; //true;
             data.showLineNumbers = true;
-            data.caretFollowsScrolling = true;
+            data.caretFollowsScrolling = false; //true;
          }
       }
       // Ensure we have a default compiler
@@ -412,7 +440,7 @@ public:
    bool showLineNumbers;
    bool caretFollowsScrolling;
    char * displayDriver;
-   
+
    // TODO: Classify settings
    //EditorSettings editor { };
 
@@ -443,6 +471,17 @@ public:
       isset { return defaultCompiler && defaultCompiler[0]; }
    }
 
+   property String language
+   {
+      set
+      {
+         delete language;
+         language = CopyString(value);
+      }
+      get { return language; }
+      isset { return language != null; }
+   }
+
 private:
    char * docDir;
    char * ideFileDialogLocation;
@@ -451,6 +490,7 @@ private:
    char * projectDefaultIntermediateObjDir;
    char * compilerConfigsDir;
    char * defaultCompiler;
+   String language;
 
    CompilerConfig GetCompilerConfig(String compilerName)
    {
@@ -480,11 +520,12 @@ private:
       recentProjects.Free();
       delete recentProjects;
       delete docDir;
-   
+
       delete projectDefaultTargetDir;
       delete projectDefaultIntermediateObjDir;
       delete compilerConfigsDir;
       delete defaultCompiler;
+      delete language;
 
       delete ideFileDialogLocation;
       delete ideProjectFileDialogLocation;
@@ -718,10 +759,10 @@ public enum CompilerType
    {
       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;
@@ -941,6 +982,20 @@ public:
       get { return excludeLibs; }
       isset { return excludeLibs.count != 0; }
    }
+   property Array<String> eCcompilerFlags
+   {
+      set
+      {
+         eCcompilerFlags.Free();
+         if(value)
+         {
+            delete eCcompilerFlags;
+            eCcompilerFlags = value;
+         }
+      }
+      get { return eCcompilerFlags; }
+      isset { return eCcompilerFlags.count != 0; }
+   }
    property Array<String> compilerFlags
    {
       set
@@ -978,6 +1033,7 @@ private:
    Array<NamedString> environmentVars { };
    Array<String> prepDirectives { };
    Array<String> excludeLibs { };
+   Array<String> eCcompilerFlags { };
    Array<String> compilerFlags { };
    Array<String> linkerFlags { };
    char * name;
@@ -1021,6 +1077,7 @@ private:
       if(prepDirectives) { prepDirectives.Free(); }
       if(excludeLibs) { excludeLibs.Free(); }
       if(compilerFlags) { compilerFlags.Free(); }
+      if(eCcompilerFlags) { eCcompilerFlags.Free(); }
       if(linkerFlags) { linkerFlags.Free(); }
    }
    CompilerConfig Copy()
@@ -1055,9 +1112,249 @@ 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 : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
 
       incref copy;
       return copy;
    }
 }
+
+struct LanguageOption
+{
+   String name;
+   String bitmap;
+   String code;
+   BitmapResource res;
+
+   char * OnGetString(char * tempString, void * fieldData, bool * needClass)
+   {
+      return name;
+   }
+
+   void OnDisplay(Surface surface, int x, int y, int width, void * data, Alignment alignment, DataDisplayFlags flags)
+   {
+      Bitmap icon = res ? res.bitmap : null;
+      int w = 8 + 16;
+      if(icon)
+         surface.Blit(icon, x + (16 - icon.width) / 2,y+2,0,0, icon.width, icon.height);
+      class::OnDisplay(surface, x + w, y, width - w, null, alignment, flags);
+   }
+};
+
+Array<LanguageOption> languages
+{ [
+   { "English",            ":countryCode/gb.png", "" },
+   { "汉语",                ":countryCode/cn.png", "zh_CN" },
+   { "Español",            ":countryCode/es.png", "es" },
+   { "Português (Brazil)", ":countryCode/br.png", "pt_BR" },
+   { "Русский (43%)",      ":countryCode/ru.png", "ru" },
+   { "Nederlandse (13%)",  ":countryCode/nl.png", "nl" },
+   { "Tiếng Việt (12%)",   ":countryCode/vn.png", "vi" },
+   { "मराठी (10%)",          ":countryCode/in.png", "mr" },
+   { "Hebrew (8%)",        ":countryCode/il.png", "he" },
+   { "Magyar (8%)",        ":countryCode/hu.png", "hu" }
+] };
+
+String GetLanguageString()
+{
+   char * dot, * colon;
+   static char lang[256];
+   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 restart = true;
+   String command = null;
+   int arg0Len = (int)strlen(app.argv[0]);
+   int len = arg0Len;
+   int j;
+   char ch;
+
+   if(ide)
+   {
+      Window w;
+
+      if(projectView)
+      {
+         Window w;
+         for(w = ide.firstChild; w; w = w.next)
+         {
+            if(w.isActiveClient && w.isDocument)
+            {
+               if(!w.CloseConfirmation(true))
+               {
+                  restart = false;
+                  break;
+               }
+            }
+         }
+         if(restart)
+         {
+            if(!projectView.CloseConfirmation(true))
+               restart = false;
+            if(projectView.fileName)
+            {
+               char * name = projectView.fileName;
+               if(name)
+               {
+                  for(j = 0; (ch = name[j]); j++)
+                     len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
+               }
+            }
+
+            command = new char[len + 1];
+
+            strcpy(command, app.argv[0]);
+            len = arg0Len;
+            if(projectView.fileName)
+            {
+               strcat(command, " ");
+               len++;
+               ReplaceSpaces(command + len, projectView.fileName);
+            }
+         }
+         if(restart)
+         {
+            for(w = ide.firstChild; w; w = w.next)
+               if(w.isActiveClient && w.isDocument)
+                  w.modifiedDocument = false;
+            projectView.modifiedDocument = false;
+         }
+      }
+      else
+      {
+         for(w = ide.firstChild; w; w = w.next)
+         {
+            if(w.isActiveClient && w.isDocument)
+            {
+               if(!w.CloseConfirmation(true))
+               {
+                  restart = false;
+                  break;
+               }
+               if(w.fileName)
+               {
+                  char * name = w.fileName;
+                  len++;
+                  for(j = 0; (ch = name[j]); j++)
+                     len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
+               }
+            }
+         }
+
+         if(restart)
+         {
+            command = new char[len + 1];
+            strcpy(command, app.argv[0]);
+            len = arg0Len;
+
+            for(w = ide.firstChild; w; w = w.next)
+            {
+               if(w.isActiveClient && w.isDocument)
+               {
+                  char * name = w.fileName;
+                  if(name)
+                  {
+                     strcat(command, " ");
+                     len++;
+                     ReplaceSpaces(command + len, name);
+                     len = (int)strlen(command);
+                  }
+               }
+            }
+         }
+         if(restart)
+         {
+            for(w = ide.firstChild; w; w = w.next)
+               if(w.isActiveClient && w.isDocument)
+                  w.modifiedDocument = false;
+         }
+      }
+      if(restart)
+      {
+         settings.language = code;
+         settingsContainer.Save();
+
+#if defined(__WIN32__)
+         // Set LANGUAGE environment variable
+         {
+            HKEY key = null;
+            uint16 wLanguage[256];
+            HRESULT status;
+            wLanguage[0] = 0;
+
+            RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
+            if(key)
+            {
+               UTF8toUTF16Buffer(code, wLanguage, sizeof(wLanguage) / sizeof(uint16));
+               RegSetValueExW(key, L"ECERE_LANGUAGE", 0, REG_EXPAND_SZ, (byte *)wLanguage, (uint)(wcslen(wLanguage)+1) * 2);
+               RegCloseKey(key);
+            }
+         }
+#endif
+
+         if(eClass_IsDerived(app._class, class(GuiApplication)))
+         {
+            GuiApplication guiApp = (GuiApplication)app;
+            guiApp.desktop.Destroy(0);
+         }
+      }
+   }
+   else
+   {
+      int i;
+      for(i = 1; i < app.argc; i++)
+      {
+         char * arg = app.argv[i];
+         len++;
+         for(j = 0; (ch = arg[j]); j++)
+            len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
+      }
+
+      command = new char[len + 1];
+      strcpy(command, app.argv[0]);
+      len = arg0Len;
+      for(i = 1; i < app.argc; i++)
+      {
+         strcat(command, " ");
+         len++;
+         ReplaceSpaces(command + len, app.argv[i]);
+         len = (int)strlen(command);
+      }
+   }
+
+   if(restart)
+   {
+      SetEnvironment("ECERE_LANGUAGE", code);
+      if(wait)
+         ExecuteWait(command);
+      else
+         Execute(command);
+   }
+   delete command;
+   return restart;
+}