X-Git-Url: http://ecere.com/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ide%2Fsrc%2FIDESettings.ec;h=b0feab9318e892889748d8c780f7a0c95e89ed13;hb=994fcf9778f7dcaab018a33afb641b1727e838a2;hp=2bbc100be72458f168f0d06161ee2f403837cba4;hpb=77df72bd393764769805dc6a54ea4610c504ab0c;p=sdk diff --git a/ide/src/IDESettings.ec b/ide/src/IDESettings.ec index 2bbc100..b0feab9 100644 --- a/ide/src/IDESettings.ec +++ b/ide/src/IDESettings.ec @@ -1,13 +1,200 @@ #ifdef ECERE_STATIC -import static "ecere" +public import static "ecere" #else -import "ecere" +public import "ecere" #endif +// *** Color Schemes *** +class IDEColorScheme +{ + String name; + SyntaxColorScheme syntaxColors; +public: + + property const String name + { + set { delete name; name = CopyString(value); } + get { return name; } + } + + Color selectionColor; + Color selectionText; + Color viewsBackground; + Color viewsText; + Color outputBackground; + Color outputText; + Color projectViewBackground; + Color projectViewText; + Color codeEditorBG; + Color codeEditorFG; + Color marginColor; + Color selectedMarginColor; + Color lineNumbersColor; + Color sheetSelectionColor; + Color sheetSelectionText; + + property SyntaxColorScheme syntaxColors + { + set { delete syntaxColors; syntaxColors = value; if(value) incref value; } + get { return syntaxColors; } + } + + ~IDEColorScheme() + { + delete syntaxColors; + delete name; + } +} + +IDEColorScheme colorScheme; + +// Default Color Schemes +IDEColorScheme darkColorScheme +{ + name = "Classic Dark"; + selectionColor = lightYellow; + selectionText = Color { 30, 40, 50 }; + viewsBackground = Color { 30, 40, 50 }; + viewsText = lightGray; + outputBackground = black; + outputText = lime; + sheetSelectionColor = Color { 170, 220, 255 }; + sheetSelectionText = black; + projectViewBackground = Color { 30, 40, 50 }; + projectViewText = lightGray; + codeEditorBG = black; + codeEditorFG = ivory; + marginColor = Color {24, 24, 24}; + selectedMarginColor = Color {64, 64, 64}; + lineNumbersColor = Color {160, 160, 160}; + syntaxColors = + { + keywordColors = [ skyBlue, skyBlue ]; + commentColor = Color { 125, 125, 125 }; + charLiteralColor = Color { 245, 50, 245 }; + stringLiteralColor = Color { 245, 50, 245 }; + preprocessorColor = { 120, 220, 140 }; + numberColor = Color { 0, 192, 192 }; + }; +}; + +IDEColorScheme lightColorScheme +{ + name = "Classic Light"; + selectionColor = Color { 10, 36, 106 }; + selectionText = white; + viewsBackground = white; + viewsText = black; + outputBackground = white; + outputText = black; + sheetSelectionColor = Color { 170, 220, 255 }; + sheetSelectionText = black; + projectViewBackground = white; + projectViewText = black; + codeEditorBG = white; + codeEditorFG = black; + marginColor = Color {230, 230, 230}; + selectedMarginColor = Color {200, 200, 200}; + lineNumbersColor = Color {60, 60, 60}; + syntaxColors = + { + keywordColors = [ blue, blue ]; + commentColor = dimGray; + charLiteralColor = crimson; + stringLiteralColor = crimson; + preprocessorColor = green; + numberColor = teal; + }; +}; + +IDEColorScheme greenColorScheme +{ + name = "Green", + selectionColor = 0x00FFFFE0, + selectionText = 0x001E2832, + viewsBackground = 0x001E2832, + viewsText = 0x00D3D3D3, + outputBackground = 0x00000000, + outputText = 0x0000FF00, + projectViewBackground = 0x001E2832, + projectViewText = 0x00D3D3D3, + codeEditorBG = 0x00000000, + codeEditorFG = 0x00FFFFF0, + marginColor = 0x001100, + selectedMarginColor = 0x002200, + lineNumbersColor = 0x00FF00, + sheetSelectionColor = 0x00AADCFF, + sheetSelectionText = 0x00000000, + syntaxColors = { + commentColor = 0x008c00, + charLiteralColor = 0x89de00, + stringLiteralColor = 0x89de00, + preprocessorColor = 0x0078DC8C, + numberColor = { 8, 237, 141 }, + keywordColors = [ + 0x00e09d, + 0x00e09d + ] + } +}; + +IDEColorScheme grayColorScheme +{ + name = "Gray & Orange", + selectionColor = 0x00FFFFE0, + selectionText = 0x001E2832, + viewsBackground = 0x001E2832, + viewsText = 0x00D3D3D3, + outputBackground = 0x00000000, + outputText = 0x0000FF00, + projectViewBackground = 0x001E2832, + projectViewText = 0x00D3D3D3, + codeEditorBG = 0x00202020, + codeEditorFG = 0x00E2E2E5, + marginColor = 0x00303030, + selectedMarginColor = 0x00404040, + lineNumbersColor = 0x00939395, + sheetSelectionColor = 0x00AADCFF, + sheetSelectionText = 0x00000000, + syntaxColors = { + commentColor = 0x005F5F5F, + charLiteralColor = 0x0089DE00, + stringLiteralColor = 0x00707070, + preprocessorColor = 0x00FAF4C6, + numberColor = 0x00FF9800, + keywordColors = [ + 0x00FF9800, + 0x00FF9800 + ] + } +}; + +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" +#ifdef __WIN32__ +#define WIN32_LEAN_AND_MEAN +#define Sleep _Sleep +#include +#undef MoveFileEx +#undef Sleep +#undef MessageBox +#undef GetObject +#endif + define MaxRecent = 9; enum DirTypes { includes, libraries, executables }; @@ -16,17 +203,42 @@ define defaultCompilerName = "Default"; define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)"; -char * settingsDirectoryNames[DirTypes] = +const 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, const 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 }; -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'; @@ -67,24 +279,24 @@ 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, 7 }; - const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)", "$(CONFIGURATION)",(char *)0 }; + 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 }; char * copy = null; 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 parts { }; - + Array parts { }; + for(c=0; c?"; + char ch, * s = output, * o = output; + while((ch = *s++)) { if(!strchr(chars, ch)) *o++ = ch; } + *o = '\0'; + } +#ifdef __WIN32__ + if(volumePath && output[0]) + output[1] = ':'; +#endif +} + +void RemoveTrailingPathSeparator(char *path) +{ + int len; + len = (int)strlen(path); + if(len>1 && path[len-1] == DIR_SEP) + path[--len] = '\0'; +} + +void BasicValidatePathBoxPath(PathBox pathBox) +{ + char path[MAX_LOCATION]; + ValidPathBufCopy(path, pathBox.path); + RemoveTrailingPathSeparator(path); + pathBox.path = path; +} + +CompilerConfig MakeDefaultCompiler(const char * name, bool readOnly) { CompilerConfig defaultCompiler { name, readOnly, gcc, - GetRuntimePlatform(), + __runtimePlatform, 1, makeDefaultCommand, ecpDefaultCommand, @@ -141,25 +397,170 @@ CompilerConfig MakeDefaultCompiler(char * name, bool readOnly) earDefaultCommand, cppDefaultCommand, ccDefaultCommand, - cxxDefaultCommand + cxxDefaultCommand, + arDefaultCommand + //ldDefaultCommand }; incref defaultCompiler; return defaultCompiler; } -class IDESettingsContainer : GlobalSettings -{ +//#define SETTINGS_TEST #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 IDEConfigHolder +{ + CompilerConfigs compilers { }; + RecentFiles recentFiles { }; + RecentWorkspaces recentWorkspaces { }; + + property CompilerConfigs compilers + { + set { compilers.Free(); delete compilers; compilers = value; } + get { return compilers; } + } + property RecentFiles recentFiles + { + set { recentFiles.Free(); delete recentFiles; recentFiles = value; } + get { return recentFiles; } + } + property RecentWorkspaces recentProjects + { + set { recentWorkspaces.Free(); delete recentWorkspaces; recentWorkspaces = value; } + get { return recentWorkspaces; } + } + + ~IDEConfigHolder() + { + compilers.Free(); + recentFiles.Free(); + recentWorkspaces.Free(); + } + + void forcePathSeparatorStyle(bool unixStyle) + { + char from, to; + if(unixStyle) + from = '\\', to = '/'; + else + from = '/', to = '\\'; + recentFiles.changeChar(from, to); + recentWorkspaces.changeChar(from, to); + } +} + +class IDESettingsContainer : GlobalSettings +{ + virtual void onLoadCompilerConfigs(); + virtual void onLoadRecentFiles(); + virtual void onLoadRecentProjects(); + virtual void onLoad(); + + CompilerConfigs compilerConfigs; + RecentFiles recentFiles; + RecentWorkspaces recentProjects; + + property bool useNewConfigurationFiles + { + set + { + if(value) + { + driver = "ECON"; + settingsName = "config"; + settingsExtension = "econ"; + settingsDirectory = settingsDir; + } + else + { + driver = "JSON"; + settingsName = ideSettingsName; + settingsExtension = null; + settingsDirectory = null; + } + } + } char moduleLocation[MAX_LOCATION]; + FileMonitor recentFilesMonitor + { + this, fileChange = { modified = true }; + + bool OnFileNotify(FileChange action, const char * param) + { + File f; + recentFilesMonitor.StopMonitoring(); + f = FileOpen(recentFilesMonitor.fileName, read); + if(f) + { + int c; + bool locked; + for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++) ecere::sys::Sleep(0.01); + recentFiles.read(this); + f.Unlock(0,0,true); + delete f; + } + return true; + } + }; + + FileMonitor recentProjectsMonitor + { + this, fileChange = { modified = true }; + + bool OnFileNotify(FileChange action, const char * param) + { + File f; + recentProjectsMonitor.StopMonitoring(); + f = FileOpen(recentProjectsMonitor.fileName, read); + if(f) + { + int c; + bool locked; + for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++) ecere::sys::Sleep(0.01); + recentProjects.read(this); + f.Unlock(0,0,true); + delete f; + } + return true; + } + }; + + static void getConfigFilePath(char * path, Class _class, char * dir, const char * configName) + { + if(dir) *dir = 0; + strcpy(path, settingsFilePath); + StripLastDirectory(path, path); + if(oldConfig) + PathCatSlash(path, settingsDir); + if(_class == class(CompilerConfig)) + { + PathCatSlash(path, "compilerConfigs"); + if(dir) + strcpy(dir, path); + if(configName) + { + PathCatSlash(path, configName); + strcat(path, ".econ"); + } + } + else if(_class == class(RecentFilesData)) + PathCatSlash(path, "recentFiles.econ"); + else if(_class == class(RecentWorkspacesData)) + PathCatSlash(path, "recentWorkspaces.econ"); + } + private: + bool oldConfig; + FileSize settingsFileSize; + IDESettingsContainer() { char path[MAX_LOCATION]; @@ -169,7 +570,7 @@ private: StripLastDirectory(moduleLocation, moduleLocation); ChangeCh(moduleLocation, '\\', '/'); // PortableApps.com directory structure - if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe"))) + if((start = strstr(path, "\\App\\EcereSDK\\bin\\ecere-ide.exe"))) { char configFilePath[MAX_LOCATION]; char defaultConfigFilePath[MAX_LOCATION]; @@ -178,13 +579,15 @@ 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)) { if(!FileExists(configFilePath)) @@ -196,9 +599,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; @@ -208,20 +611,47 @@ 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)) { - Load(); + //if((double)settingsFileSize - (double)newSettingsFileSize < 2048) + Load(); + onLoad(); + /*else + { + GuiApplication app = ((GuiApplication)__thisModule.application); + Window w; + for(w = app.desktop.firstChild; w && (!w.created || !w.visible); w = w.next); + + 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(); + }*/ } } - void Load() + 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 { }; @@ -233,7 +663,7 @@ private: bool loaded; OldIDESettings oldSettings { }; Close(); - loaded = oldSettings.Load(); + loaded = oldSettings.Load() == success; oldSettings.Close(); if(loaded) { @@ -242,8 +672,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; @@ -254,7 +684,7 @@ private: delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver); data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir; data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir; - + Save(); result = success; } @@ -263,71 +693,341 @@ 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 - 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(); - if(data.compilerConfigs) + FileGetSize(settingsFilePath, &settingsFileSize); + if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory) + data.ManagePortablePaths(moduleLocation, true); + data.ForcePathSeparatorStyle(true); + + if(!data.colorSchemes || !data.colorSchemes.count) + { + if(!data.colorSchemes) data.colorSchemes = { }; + + data.colorSchemes.Add(darkColorScheme); incref darkColorScheme; + data.colorSchemes.Add(lightColorScheme); incref lightColorScheme; + data.colorSchemes.Add(greenColorScheme); incref greenColorScheme; + data.colorSchemes.Add(grayColorScheme); incref grayColorScheme; + } + colorScheme = null; + if(data.activeColorScheme) { - for(c : data.compilerConfigs) + for(cs : data.colorSchemes; cs.name && !strcmp(cs.name, data.activeColorScheme)) { - CompilerConfig compiler = c; - incref compiler; + colorScheme = cs; + break; } } - if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory) - data.ManagePortablePaths(moduleLocation, true); - data.ForcePathSeparatorStyle(true); - OnLoad(data); + if(!colorScheme) + { + colorScheme = data.colorSchemes[0]; + data.activeColorScheme = colorScheme.name; + } + + // Import from previous ecereIDE settings + if(oldConfig) + { + // Save first so that settingsFilePath get set up + Save(); + + data.compilerConfigs.ensureDefaults(); + data.compilerConfigs.write(this, null); + data.compilerConfigs.Free(); + + data.recentFiles.write(this); + data.recentFiles.Free(); + + data.recentProjects.write(this); + data.recentProjects.Free(); + } + return result; } - void Save() + SettingsIOResult Save() { - IDESettings data = (IDESettings)this.data; - Platform runtimePlatform = GetRuntimePlatform(); + SettingsIOResult result; + IDESettings data; + useNewConfigurationFiles = true; + data = (IDESettings)this.data; if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory) data.ManagePortablePaths(moduleLocation, false); data.ForcePathSeparatorStyle(true); - if(!GlobalSettings::Save()) + 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 getCompilerConfigFilePathsByName(const char * path) +{ + Map 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); + StripExtension(name); + map[name] = path; + } + } + return map; +} + +static Map getCompilerConfigsByName(const char * path) +{ + Map 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); + StripExtension(name); + { + CompilerConfig ccfg = CompilerConfig::read(path); + if(ccfg) + map[name] = ccfg; + } + delete path; + } + } + return map; +} + +static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data) +{ + SettingsIOResult result = error; + SafeFile sf = SafeFile::open(path, write); + if(sf) + { + WriteECONObject(sf.file, dataType, data, 0); + sf.sync(); + 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 = SafeFile::open(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; + FileOpenMode mode; + char path[MAX_LOCATION]; + char tmp[MAX_LOCATION]; + + SafeFile ::open(const char * path, FileOpenMode mode) + { + SafeFile result = null; + if(mode == write || mode == read) + { + SafeFile sf { mode = mode }; + int c; + bool locked = false; + FileLock lockType = mode == write ? exclusive : shared; + + strcpy(sf.path, path); + strcpy(sf.tmp, path); + strcat(sf.tmp, ".tmp"); + if(mode == write && FileExists(sf.tmp).isFile) + DeleteFile(sf.tmp); + + if(mode == write) + { + sf.file = FileOpen(sf.tmp, readWrite); + if(!sf.file) + { + sf.file = FileOpen(sf.tmp, writeRead); + if(sf.file) + { + delete sf.file; + sf.file = FileOpen(sf.tmp, readWrite); + } + } + } + else + sf.file = FileOpen(path, mode); + if(sf.file) + { + for(c = 0; c < 10 && !(locked = sf.file.Lock(lockType, 0, 0, false)); c++) Sleep(0.01); + if(locked) + { + sf.file.Truncate(0); + sf.file.Seek(0, start); + result = sf; + } + else if(mode == write) + PrintLn($"warning: SafeFile::open: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp); + else + PrintLn($"warning: SafeFile::open: unable to obtain shared lock on file for reading: ", path); + } + else if(mode == write) + PrintLn($"warning: SafeFile::open: unable to open temporary file for writing: ", sf.tmp); + else + PrintLn($"warning: SafeFile::open: unable to open file for reading: ", path); + + if(!result) + delete sf; + } + else + PrintLn($"warning: SafeFile::open: does not yet support FileOpenMode::", mode); + return result; + } + + void sync() + { + if(file && mode == write) + { + int c; + File f = FileOpen(path, readWrite); + if(!f) + { + f = FileOpen(path, writeRead); + if(f) + { + delete f; + f = FileOpen(path, readWrite); + } + } + if(f) + { + bool locked = true; + for(c = 0; c < 10 && !(locked = f.Lock(exclusive, 0,0, false)); c++) Sleep(0.01); + + if(locked) + { + f.Unlock(0,0, false); + delete f; + file.Unlock(0,0, false); + delete file; + + for(c = 0; c < 10; c++) + { + if(MoveFileEx(tmp, path, { true, true })) + break; + else + Sleep(0.01); + } + } + else + { + delete f; + PrintLn($"warning: SafeFile::sync: failed to lock file for ", mode); + } + } + } + } + + + ~SafeFile() + { + if(file) + { + file.Unlock(0,0, false); + delete file; + } + } +} + +class RecentFilesData +{ +public: + RecentFiles recentFiles; +} + +class RecentWorkspacesData +{ +public: + RecentWorkspaces recentWorkspaces; } class IDESettings : GlobalSettingsData { public: - List compilerConfigs { }; - Array recentFiles { }; - Array recentProjects { }; - property char * docDir + property CompilerConfigs compilerConfigs + { + set { /*if(settingsContainer.oldConfig)*/ { 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 : ""; } @@ -337,38 +1037,87 @@ public: bool showLineNumbers; bool caretFollowsScrolling; char * displayDriver; - + // 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 const String language + { + set + { + delete language; + language = CopyString(value); + } + get { return language; } + isset { return language != null; } + } + + property const String codeEditorFont + { + set + { + delete codeEditorFont; + codeEditorFont = CopyString(value); + } + get { return codeEditorFont; } + } + + float codeEditorFontSize; + bool showFixedPitchFontsOnly; + + property Array colorSchemes + { + set + { + if(colorSchemes && colorSchemes._refCount < 2) + colorSchemes.Free(); + delete colorSchemes; + colorSchemes = value; + if(value) + incref colorSchemes; + } + get { return colorSchemes; } + } + + property const String activeColorScheme + { + set + { + delete activeColorScheme; + activeColorScheme = CopyString(value); + } + get { return activeColorScheme; } + } + private: + CompilerConfigs compilerConfigs { }; char * docDir; char * ideFileDialogLocation; char * ideProjectFileDialogLocation; @@ -376,44 +1125,42 @@ private: char * projectDefaultIntermediateObjDir; char * compilerConfigsDir; char * defaultCompiler; + String language; + RecentFiles recentFiles { }; + RecentWorkspaces recentProjects { }; - CompilerConfig GetCompilerConfig(String compilerName) - { - char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName; - CompilerConfig compilerConfig = null; - for(compiler : compilerConfigs) - { - if(!strcmp(compiler.name, name)) - { - compilerConfig = compiler; - break; - } - } - if(!compilerConfig && compilerConfigs.count) - compilerConfig = compilerConfigs.firstIterator.data; - if(compilerConfig) - incref compilerConfig; - return compilerConfig; - } + Array colorSchemes; + + String codeEditorFont; + + String activeColorScheme; + + showFixedPitchFontsOnly = true; + codeEditorFontSize = 12; + codeEditorFont = CopyString("Courier New"); ~IDESettings() { 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; delete displayDriver; + + delete codeEditorFont; + + colorSchemes.Free(); + delete activeColorScheme; } void ForcePathSeparatorStyle(bool unixStyle) @@ -454,24 +1201,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]) @@ -545,7 +1276,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) @@ -571,45 +1302,127 @@ private: else output = path; } - return output; + return output; + } +} + +class RecentFiles : RecentPaths +{ + void read(IDESettingsContainer settingsContainer) + { + char path[MAX_LOCATION]; + RecentFilesData d = null; + Class _class = class(RecentFilesData); + settingsContainer.getConfigFilePath(path, _class, null, null); + readConfigFile(path, _class, &d); + if(d && d.recentFiles && d.recentFiles.count) + { + Free(); + Copy((void *)d.recentFiles); + settingsContainer.recentFiles = this; // Merge IDEConfigHolder / IDESettingsContainer? + } + delete d; + settingsContainer.recentFilesMonitor.fileName = path; + settingsContainer.recentFilesMonitor.StartMonitoring(); + settingsContainer.onLoadRecentFiles(); + } + + void write(IDESettingsContainer settingsContainer) + { + char path[MAX_LOCATION]; + RecentFilesData d { }; + Class _class = class(RecentFilesData); + settingsContainer.getConfigFilePath(path, _class, null, null); + d.recentFiles = this; + writeConfigFile(path, _class, d); + d.recentFiles = null; + delete d; + } +} + +class RecentWorkspaces : RecentPaths +{ + void read(IDESettingsContainer settingsContainer) + { + char path[MAX_LOCATION]; + RecentWorkspacesData d = null; + Class _class = class(RecentWorkspacesData); + settingsContainer.getConfigFilePath(path, _class, null, null); + readConfigFile(path, _class, &d); + if(d && d.recentWorkspaces && d.recentWorkspaces.count) + { + Free(); + Copy((void *)d.recentWorkspaces); + settingsContainer.recentProjects = this; // Merge IDEConfigHolder / IDESettingsContainer? + } + delete d; + settingsContainer.recentProjectsMonitor.fileName = path; + settingsContainer.recentProjectsMonitor.StartMonitoring(); + settingsContainer.onLoadRecentProjects(); + } + + void write(IDESettingsContainer settingsContainer) + { + char path[MAX_LOCATION]; + RecentWorkspacesData d { }; + Class _class = class(RecentWorkspacesData); + settingsContainer.getConfigFilePath(path, _class, null, null); + d.recentWorkspaces = this; + writeConfigFile(path, _class, d); + d.recentWorkspaces = null; + delete d; } +} - void AddRecentFile(char * fileName) +class RecentPaths : Array +{ + IteratorPointer Add(T value) { int c; - char * filePath = CopyString(fileName); + char * filePath = (char *)value; ChangeCh(filePath, '\\', '/'); - for(c = 0; c= MaxRecent) - recentFiles.Delete(recentFiles.GetLast()); - recentFiles.Insert(null, filePath); - //UpdateRecentMenus(owner); + return Array::Add((T)filePath); } - void AddRecentProject(char * projectName) + IteratorPointer addRecent(const String value) { int c; - char * filePath = CopyString(projectName); + char * filePath = CopyString((char *)value); + IteratorPointer ip; ChangeCh(filePath, '\\', '/'); - for(c = 0; c= MaxRecent) - recentProjects.Delete(recentProjects.GetLast()); - recentProjects.Insert(null, filePath); - //UpdateRecentMenus(owner); + while(count >= MaxRecent) + Delete(GetLast()); + ip = Insert(null, filePath); + 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); + } + } } } @@ -619,7 +1432,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 compilerTypeLongNames +static Array compilerTypeLongNames { [ $"GNU Compiler Collection (GCC) / GNU Make", $"Tiny C Compiler / GNU Make", @@ -639,14 +1452,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; @@ -655,13 +1468,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) { @@ -690,82 +1503,131 @@ 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 + 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 * staticLibFileExt + { + set { delete staticLibFileExt; if(value && value[0]) staticLibFileExt = CopyString(value); } + get { return staticLibFileExt; } + isset { return staticLibFileExt && staticLibFileExt[0]; } + } + property const char * sharedLibFileExt + { + set { delete sharedLibFileExt; if(value && value[0]) sharedLibFileExt = CopyString(value); } + get { return sharedLibFileExt; } + isset { return sharedLibFileExt && sharedLibFileExt[0]; } + } + property const char * executableFileExt + { + set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); } + get { return executableFileExt; } + isset { return executableFileExt && executableFileExt[0]; } + } + property const char * executableLauncher { - set { delete execPrefixCommand; if(value && value[0]) execPrefixCommand = CopyString(value); } - get { return execPrefixCommand; } - isset { return execPrefixCommand && execPrefixCommand[0]; } + set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); } + get { return executableLauncher; } + isset { return executableLauncher && executableLauncher[0]; } } + // TODO: implement CompilerConfig::windresCommand bool ccacheEnabled; bool distccEnabled; - property char * distccHosts + // deprecated + property bool supportsBitDepth { set { } get { return true; } isset { return false; } } + + property const char * distccHosts { set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); } get { return distccHosts; } isset { return distccHosts && distccHosts[0]; } } + property const char * gnuToolchainPrefix + { + set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); } + get { return gnuToolchainPrefix; } + isset { return gnuToolchainPrefix && gnuToolchainPrefix[0]; } + } + property const char * sysroot + { + set { delete sysroot; if(value && value[0]) sysroot = CopyString(value); } + get { return sysroot; } + isset { return sysroot && sysroot[0]; } + } + bool resourcesDotEar; + bool noStripTarget; property Array includeDirs { set @@ -850,6 +1712,92 @@ public: get { return excludeLibs; } isset { return excludeLibs.count != 0; } } + property Array eCcompilerFlags + { + set + { + eCcompilerFlags.Free(); + if(value) + { + delete eCcompilerFlags; + eCcompilerFlags = value; + } + } + get { return eCcompilerFlags; } + isset { return eCcompilerFlags.count != 0; } + } + property Array compilerFlags + { + set + { + compilerFlags.Free(); + if(value) + { + delete compilerFlags; + compilerFlags = value; + } + } + get { return compilerFlags; } + isset { return compilerFlags.count != 0; } + } + property Array cxxFlags + { + set + { + cxxFlags.Free(); + if(value) + { + delete cxxFlags; + cxxFlags = value; + } + } + get { return cxxFlags; } + isset { return cxxFlags.count != 0; } + } + property Array linkerFlags + { + set + { + linkerFlags.Free(); + if(value) + { + delete linkerFlags; + linkerFlags = value; + } + } + 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; } + } + property const char * outputFileExt + { + set { delete executableFileExt; if(value && value[0]) executableFileExt = CopyString(value); } + get { return executableFileExt; } + isset { return false; } + } + // utility + property bool hasDocumentOutput + { + get + { + bool result = executableFileExt && executableFileExt[0] && + (!strcmpi(executableFileExt, "htm") || !strcmpi(executableFileExt, "html")); + return result; + } + isset { return false; } + } private: Array includeDirs { }; Array libraryDirs { }; @@ -859,6 +1807,10 @@ private: Array environmentVars { }; Array prepDirectives { }; Array excludeLibs { }; + Array eCcompilerFlags { }; + Array compilerFlags { }; + Array cxxFlags { }; + Array linkerFlags { }; char * name; char * makeCommand; char * ecpCommand; @@ -868,8 +1820,16 @@ private: char * cppCommand; char * ccCommand; char * cxxCommand; - char * execPrefixCommand; + char * ldCommand; + char * arCommand; + char * objectFileExt; + char * staticLibFileExt; + char * sharedLibFileExt; + char * executableFileExt; + char * executableLauncher; char * distccHosts; + char * gnuToolchainPrefix; + char * sysroot; /*union { struct { Array includes, libraries, executables; }; @@ -886,16 +1846,76 @@ private: delete cppCommand; delete ccCommand; delete cxxCommand; + delete ldCommand; + delete arCommand; + delete objectFileExt; + delete staticLibFileExt; + delete sharedLibFileExt; + delete executableFileExt; delete makeCommand; - delete execPrefixCommand; + delete executableLauncher; delete distccHosts; + delete gnuToolchainPrefix; + delete sysroot; if(environmentVars) environmentVars.Free(); if(includeDirs) { includeDirs.Free(); } if(libraryDirs) { libraryDirs.Free(); } if(executableDirs) { executableDirs.Free(); } 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 = type.OnCompare(b.type)) && + !(result = targetPlatform.OnCompare(b.targetPlatform)) && + !(result = numJobs.OnCompare(b.numJobs)) && + !(result = ccacheEnabled.OnCompare(b.ccacheEnabled)) && + !(result = distccEnabled.OnCompare(b.distccEnabled)) && + !(result = resourcesDotEar.OnCompare(b.resourcesDotEar)) && + !(result = noStripTarget.OnCompare(b.noStripTarget)) + ); + + if(!result && + !(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 @@ -913,10 +1933,21 @@ private: cppCommand, ccCommand, cxxCommand, - execPrefixCommand, + arCommand, + ldCommand, + objectFileExt, + staticLibFileExt, + sharedLibFileExt, + executableFileExt, + executableLauncher, ccacheEnabled, distccEnabled, - distccHosts + false, + distccHosts, + gnuToolchainPrefix, + sysroot, + resourcesDotEar, + noStripTarget }; for(s : includeDirs) copy.includeDirs.Add(CopyString(s)); for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s)); @@ -924,8 +1955,436 @@ private: for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string }); 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(IDESettingsContainer settingsContainer) + { + char dir[MAX_LOCATION]; + char path[MAX_LOCATION]; + const char * settingsFilePath = settingsContainer.settingsFilePath; + settingsContainer.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 GetCompilerConfig(const String compilerName) + { + const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName; + CompilerConfig compilerConfig = null; + for(compiler : this) + { + if(!strcmp(compiler.name, name)) + { + compilerConfig = compiler; + break; + } + } + if(!compilerConfig && count) + compilerConfig = this[0]; + if(compilerConfig) + { + incref compilerConfig; + if(compilerConfig._refCount == 1) + incref compilerConfig; + } + return compilerConfig; + } + + void ensureDefaults() + { + // Ensure we have a default compiler + CompilerConfig defaultCompiler = GetCompilerConfig(defaultCompilerName); + if(!defaultCompiler) + { + defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true); + Insert(null, defaultCompiler); + defaultCompiler = null; + } + delete defaultCompiler; + + for(ccfg : this) + { + 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.staticLibFileExt || !ccfg.staticLibFileExt[0]) + ccfg.staticLibFileExt = staticLibDefaultFileExt;*/ + /*if(!ccfg.sharedLibFileExt || !ccfg.sharedLibFileExt[0]) + ccfg.sharedLibFileExt = sharedLibDefaultFileExt;*/ + /*if(!ccfg.executableFileExt || !ccfg.executableFileExt[0]) + ccfg.executableFileExt = outputDefaultFileExt;*/ + if(!ccfg._refCount) incref ccfg; + } + } + + AVLTree getWriteRequiredList(CompilerConfigs oldConfigs) + { + AVLTree list { }; + for(ccfg : this) + { + bool found = false; + for(occfg : oldConfigs; !strcmp(ccfg.name, occfg.name)) + { + found = true; + if(ccfg.OnCompare(occfg)) + list.Add(CopyString(ccfg.name)); + break; + } + if(!found) + list.Add(CopyString(ccfg.name)); + } + return list; + } + + bool read(IDESettingsContainer settingsContainer) + { + if(settingsContainer.settingsFilePath) + { + char dir[MAX_LOCATION]; + char path[MAX_LOCATION]; + Class _class = class(CompilerConfig); + settingsContainer.getConfigFilePath(path, _class, dir, null); + if(dir[0]) + { + AVLTree addedConfigs { }; + Map compilerConfigsByName = getCompilerConfigsByName(dir); + MapIterator it { map = compilerConfigsByName }; + Free(); + settingsContainer.compilerConfigs = this; // Merge IDEConfigHolder / IDESettingsContainer? + if(it.Index("Default", false)) + { + CompilerConfig ccfg = it.data; + Add(ccfg.Copy()); + addedConfigs.Add(ccfg.name); + } + for(ccfg : compilerConfigsByName) + { + if(!addedConfigs.Find(ccfg.name)) + { + Add(ccfg.Copy()); + addedConfigs.Add(ccfg.name); + } + } + delete addedConfigs; + ensureDefaults(); + compilerConfigsByName.Free(); + delete compilerConfigsByName; + settingsContainer.onLoadCompilerConfigs(); + return true; + } + } + return false; + } + + void write(IDESettingsContainer settingsContainer, AVLTree cfgsToWrite) + { + char dir[MAX_LOCATION]; + char path[MAX_LOCATION]; + Map paths; + settingsContainer.getConfigFilePath(path, class(CompilerConfig), dir, null); + paths = getCompilerConfigFilePathsByName(dir); + { + MapIterator it { map = paths }; + for(c : this) + { + CompilerConfig ccfg = c; + if(!cfgsToWrite || cfgsToWrite.Find(ccfg.name)) + ccfg.write(settingsContainer); + 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 +{ + const String name; + const String bitmap; + const String code; + BitmapResource res; + + const 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 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" } +] }; + +const String GetLanguageString() +{ + 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; +} + +void setEcereLanguageInWinRegEnvironment(const char * languageCode) +{ +#ifdef __WIN32__ + HKEY key = null; + uint16 wLanguage[256]; + DWORD status; + wLanguage[0] = 0; + + RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status); + if(key) + { + UTF8toUTF16Buffer(languageCode, wLanguage, sizeof(wLanguage) / sizeof(uint16)); + RegSetValueExW(key, L"ECERE_LANGUAGE", 0, REG_EXPAND_SZ, (byte *)wLanguage, (uint)(wcslen(wLanguage)+1) * 2); + RegCloseKey(key); + } +#endif +} + +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 = (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) + { + const 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) + { + const 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) + { + const 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(); + + setEcereLanguageInWinRegEnvironment(code); + + 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++) + { + const 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; } +#endif