ide/CodeEditor: Fixed bad font when applying new font
[sdk] / ide / src / ide.ec
index 82b58e4..766f5a6 100644 (file)
@@ -59,6 +59,24 @@ define pathListSep = ";";
 define pathListSep = ":";
 #endif
 
+IDEConfigHolder ideConfig { };
+
+FontResource panelFont { $"Courier New", 10 };
+FontResource codeFont { $"Courier New", 10 };
+
+IDESettings ideSettings;
+
+IDESettingsContainer settingsContainer
+{
+   dataOwner = &ideSettings;
+   dataClass = class(IDESettings);
+
+   void onLoadCompilerConfigs()     { ide.UpdateCompilerConfigs(true); }
+   void onLoadRecentFiles()         { ide.updateRecentFilesMenu(); }
+   void onLoadRecentProjects()      { ide.updateRecentProjectsMenu(); }
+   void onLoad()                    { ide.ApplyColorScheme(colorScheme); ide.ApplyFont(ideSettings.codeEditorFont, ideSettings.codeEditorFontSize); }
+};
+
 define maxPathLen = 65 * MAX_LOCATION;
 
 class PathBackup : struct
@@ -151,9 +169,6 @@ FileDialog ideProjectFileDialog
 
 GlobalSettingsDialog globalSettingsDialog
 {
-   ideSettings = ideSettings;
-   settingsContainer = settingsContainer;
-
    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
    {
       switch(globalSettingsChange)
@@ -303,11 +318,11 @@ class IDEToolbar : ToolBar
       this, toolTip = $"Active Compiler", size = { 160 }, disabled = true;
       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
       {
-         if(ide.workspace && ide.projectView && row && strcmp(row.string, ide.workspace.compiler))
+         if(ide.workspace && ide.projectView && row && strcmp(row.string, ide.workspace.activeCompiler))
          {
             bool silent = ide.projectView.buildInProgress == none ? false : true;
-            CompilerConfig compiler = ideSettings.GetCompilerConfig(row.string);
-            ide.workspace.compiler = row.string;
+            CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(row.string);
+            ide.workspace.activeCompiler = row.string;
             ide.projectView.ShowOutputBuildLog(!silent);
             if(!silent)
                ide.projectView.DisplayCompiler(compiler, false);
@@ -328,7 +343,7 @@ class IDEToolbar : ToolBar
          if(ide.workspace && ide.projectView && row)
          {
             bool silent = ide.projectView.buildInProgress == none ? false : true;
-            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+            CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
             ide.workspace.bitDepth = (int)row.tag;
             ide.projectView.ShowOutputBuildLog(!silent);
             if(!silent)
@@ -429,8 +444,119 @@ class IDEWorkSpace : Window
    BitmapResource bmpTopFrameHalf      { ":codeMarks/topFrameHalf.png", window = this };
    BitmapResource bmpTopFrameHalfError { ":codeMarks/topFrameHalfError.png", window = this };
 
+   BuildOutputMode rightClickMenuBuildOutputMode;
+
    Debugger debugger { };
 
+   void ApplyFont(const String faceName, float size)
+   {
+      panelFont.faceName = faceName;
+      panelFont.size = size;
+
+      codeFont.faceName = faceName;
+      codeFont.size = size;
+
+      {
+         CodeEditor ce;
+         for(ce = (CodeEditor)firstChild; ce; ce = (CodeEditor)ce.next)
+            if(ce._class == class(CodeEditor))
+            {
+               FontResource font;
+               font = { codeFont.faceName, codeFont.size, codeFont.bold, codeFont.italic };
+               incref font;
+               delete *&ce.font;
+               *&ce.font = font;
+               ce.editBox.font = font;
+               ce.OnPostCreate();
+            }
+      }
+
+      threadsView.font          = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+      callStackView.font        = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+      outputView.buildBox.font  = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+      outputView.debugBox.font  = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+      outputView.findBox.font   = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+   #ifdef GDB_DEBUG_OUTPUT
+      outputView.gdbBox.font   = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+   #endif
+#ifdef GDB_DEBUG_GUI
+      if(gdbDialog)
+      {
+         gdbDialog.tree.font   = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+         gdbDialog.output.font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
+      }
+#endif
+   }
+
+   void ApplyColorScheme(IDEColorScheme cs)
+   {
+      CodeEditor ce;
+
+      colorScheme = cs;
+
+      for(ce = (CodeEditor)firstChild; ce; ce = (CodeEditor)ce.next)
+         if(ce._class == class(CodeEditor))
+         {
+            EditBox eb = ce.editBox;
+            ce.background = cs.marginColor;
+            eb.selectionColor = cs.selectionColor;
+            eb.selectionText = cs.selectionText;
+            eb.background = cs.codeEditorBG;
+            eb.foreground = cs.codeEditorFG;
+            eb.syntaxColorScheme = cs.syntaxColors;
+         }
+
+      if(projectView)
+      {
+         projectView.fileList.background = cs.projectViewBackground;
+         projectView.fileList.foreground = cs.projectViewText;
+         projectView.fileList.selectionColor = cs.selectionColor;
+         projectView.fileList.selectionText = cs.selectionText;
+      }
+
+      sheet.properties.background = cs.viewsBackground;
+      sheet.properties.foreground = cs.viewsText;
+      sheet.properties.selectionText = cs.sheetSelectionText;
+      sheet.properties.selectionColor = cs.sheetSelectionColor;
+      sheet.methods.background = cs.viewsBackground;
+      sheet.methods.foreground = cs.viewsText;
+
+      threadsView.editBox.background = cs.viewsBackground;
+      threadsView.editBox.foreground = cs.viewsText;
+      threadsView.editBox.selectionColor = cs.selectionColor;
+      threadsView.editBox.selectionText = cs.selectionText;
+
+      callStackView.editBox.background = cs.viewsBackground;
+      callStackView.editBox.foreground = cs.viewsText;
+      callStackView.editBox.selectionColor = cs.selectionColor;
+      callStackView.editBox.selectionText = cs.selectionText;
+
+      watchesView.listBox.background = cs.viewsBackground;
+      watchesView.listBox.foreground = cs.viewsText;
+      watchesView.listBox.selectionColor = cs.selectionColor;
+      watchesView.listBox.selectionText = cs.selectionText;
+
+      breakpointsView.listBox.background = cs.viewsBackground;
+      breakpointsView.listBox.foreground = cs.viewsText;
+      breakpointsView.listBox.selectionColor = cs.selectionColor;
+      breakpointsView.listBox.selectionText = cs.selectionText;
+
+      outputView.buildBox.background = cs.outputBackground;
+      outputView.buildBox.foreground = cs.outputText;
+      outputView.buildBox.selectionColor = cs.selectionColor;
+      outputView.buildBox.selectionText = cs.selectionText;
+
+      outputView.debugBox.background = cs.outputBackground;
+      outputView.debugBox.foreground = cs.outputText;
+      outputView.debugBox.selectionColor = cs.selectionColor;
+      outputView.debugBox.selectionText = cs.selectionText;
+
+      outputView.findBox.background = cs.outputBackground;
+      outputView.findBox.foreground = cs.outputText;
+      outputView.findBox.selectionColor = cs.selectionColor;
+      outputView.findBox.selectionText = cs.selectionText;
+   }
+
    ProjectView projectView;
 
    OutputView outputView
@@ -439,12 +565,18 @@ class IDEWorkSpace : Window
 
       void OnGotoError(const char * line, bool noParsing)
       {
-         ide.GoToError(line, noParsing);
+         CompilerConfig compiler = ide.workspace ? ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler) : null;
+         const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
+         ide.GoToError(line, noParsing, objectFileExt);
+         delete compiler;
       }
 
       void OnCodeLocationParseAndGoTo(const char * line)
       {
-         ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir);
+         CompilerConfig compiler = ide.workspace ? ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler) : null;
+         const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
+         ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, objectFileExt);
+         delete compiler;
       }
 
       bool OnKeyDown(Key key, unichar ch)
@@ -475,7 +607,7 @@ class IDEWorkSpace : Window
 
    CallStackView callStackView
    {
-      parent = this, font = { panelFont.faceName, panelFont.size };
+      parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
 
       void OnSelectFrame(int frameIndex)
       {
@@ -577,7 +709,7 @@ class IDEWorkSpace : Window
    WatchesView watchesView { parent = this };
    ThreadsView threadsView
    {
-      parent = this, font = { panelFont.faceName, panelFont.size };
+      parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
 
       bool OnKeyDown(Key key, unichar ch)
       {
@@ -718,9 +850,12 @@ class IDEWorkSpace : Window
          fileMenu, $"Global Settings...", g;
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
+            // Reload configs here until we setup a configs directory monitor
+            ideConfig.compilers.read(settingsContainer);
+
             globalSettingsDialog.master = this;
-            if(ide.workspace && ide.workspace.compiler)
-               globalSettingsDialog.workspaceActiveCompiler = ide.workspace.compiler;
+            if(ide.workspace && ide.workspace.activeCompiler)
+               globalSettingsDialog.workspaceActiveCompiler = ide.workspace.activeCompiler;
             else if(ideSettings.defaultCompiler)
                globalSettingsDialog.workspaceActiveCompiler = ideSettings.defaultCompiler;
             globalSettingsDialog.Modal();
@@ -728,8 +863,8 @@ class IDEWorkSpace : Window
          }
       };
       MenuDivider { fileMenu };
-      Menu recentFiles { fileMenu, $"Recent Files", r };
-      Menu recentProjects { fileMenu, $"Recent Projects", p };
+      Menu recentFilesMenu { fileMenu, $"Recent Files", r };
+      Menu recentProjectsMenu { fileMenu, $"Recent Projects", p };
       MenuDivider { fileMenu };
       MenuItem exitItem
       {
@@ -745,7 +880,8 @@ class IDEWorkSpace : Window
       bool FileRecentFile(MenuItem selection, Modifiers mods)
       {
          int id = 0;
-         for(file : ideSettings.recentFiles)
+         RecentPaths recentFiles = workspace ? workspace.recentFiles : ideConfig.recentFiles;
+         for(file : recentFiles)
          {
             if(id == selection.id)
             {
@@ -753,9 +889,9 @@ class IDEWorkSpace : Window
                char extension[MAX_EXTENSION] = "";
                GetExtension(file, extension);
                isProjectFile = (!strcmpi(extension, "epj") || !strcmpi(extension, "ews"));
-               if(mods.ctrl)
+               if(mods.ctrl && !mods.shift)
                {
-                  char * command = PrintString("ide ", isProjectFile ? "-t " : "", file);
+                  char * command = PrintString("ecere-ide ", isProjectFile ? "-t " : "", file);
                   Execute(command);
                   delete command;
                }
@@ -774,18 +910,18 @@ class IDEWorkSpace : Window
       bool FileRecentProject(MenuItem selection, Modifiers mods)
       {
          int id = 0;
-         for(file : ideSettings.recentProjects)
+         for(file : ideConfig.recentWorkspaces)
          {
             if(id == selection.id)
             {
-               if(mods.ctrl)
+               if(mods.ctrl && !mods.shift)
                {
-                  char * command = PrintString("ide ", file);
+                  char * command = PrintString("ecere-ide ", file);
                   Execute(command);
                   delete command;
                }
                else
-                  OpenFile(file, false, true, null, no, normal, mods.ctrl && mods.shift);
+                  OpenFile(file, false, true, (mods.ctrl && mods.shift) ? "txt" : null, no, normal, mods.ctrl && mods.shift);
                break;
             }
             id++;
@@ -815,9 +951,9 @@ class IDEWorkSpace : Window
                      newProjectDialog.CreateNewProject();
                      if(projectView)
                      {
-                        ideSettings.AddRecentProject(projectView.fileName);
-                        ide.UpdateRecentMenus();
-                        settingsContainer.Save();
+                        ideConfig.recentWorkspaces.addRecent(projectView.fileName);
+                        ideConfig.recentWorkspaces.write(settingsContainer);
+                        ide.updateRecentProjectsMenu();
                      }
                   }
                }
@@ -1634,6 +1770,7 @@ class IDEWorkSpace : Window
    };
 
    bool noParsing;
+   bool debugStart;
 
 #ifdef GDB_DEBUG_GUI
    GDBDialog gdbDialog
@@ -1716,6 +1853,7 @@ class IDEWorkSpace : Window
       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
 
       AdjustMenus();
+      updateRecentMenus();
 
       ide.breakpointsView.LoadFromWorkspace();
       ide.watchesView.LoadFromWorkspace();
@@ -1747,6 +1885,7 @@ class IDEWorkSpace : Window
          outputView.visible = false;
          ideMainFrame.text = titleECEREIDE;
          ide.AdjustMenus();
+         ide.updateRecentMenus();
          return true;
       }
       return false;
@@ -1813,10 +1952,10 @@ class IDEWorkSpace : Window
 
    void DocumentSaved(Window document, const char * fileName)
    {
-      ideSettings.AddRecentFile(fileName);
-      ide.UpdateRecentMenus();
+      ideConfig.recentFiles.addRecent(fileName);
+      ideConfig.recentFiles.write(settingsContainer);
+      ide.updateRecentFilesMenu();
       ide.AdjustFileMenus();
-      settingsContainer.Save();
    }
 
    bool Window::OnFileModified(FileChange fileChange, const char * param)
@@ -1824,7 +1963,7 @@ class IDEWorkSpace : Window
       char temp[4096];
       sprintf(temp, $"The document %s was modified by another application.\n"
             "Would you like to reload it and lose your changes?", this.fileName);
-      if(MessageBox { type = yesNo, master = this/*.parent*/,
+      if(MessageBox { creationActivation = flash, type = yesNo, master = this/*.parent*/,
             text = $"Document has been modified", contents = temp }.Modal() == yes)
       {
          bool noParsing = (this._class == class(CodeEditor) && ((CodeEditor)this).noParsing) ? true : false;
@@ -1852,7 +1991,7 @@ class IDEWorkSpace : Window
    {
       if(workspace)
       {
-         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+         CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(workspace.activeCompiler);
          for(prj : workspace.projects)
             projectView.ProjectUpdateMakefileForAllConfigs(prj);
          delete compiler;
@@ -1865,7 +2004,7 @@ class IDEWorkSpace : Window
       if(workspace)
       {
          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
-         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+         CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(workspace.activeCompiler);
          if(!silent)
          {
             projectView.ShowOutputBuildLog(true);
@@ -1880,14 +2019,15 @@ class IDEWorkSpace : Window
    void UpdateToolBarActiveCompilers()
    {
       toolBar.activeCompiler.Clear();
-      for(compiler : ideSettings.compilerConfigs)
+      for(compiler : ideConfig.compilers)
       {
          DataRow row = toolBar.activeCompiler.AddString(compiler.name);
-         if(workspace && workspace.compiler && !strcmp(compiler.name, workspace.compiler))
+         if(workspace && workspace.activeCompiler && !strcmp(compiler.name, workspace.activeCompiler))
             toolBar.activeCompiler.currentRow = row;
       }
       if(!toolBar.activeCompiler.currentRow && toolBar.activeCompiler.firstRow)
          toolBar.activeCompiler.SelectRow(toolBar.activeCompiler.firstRow);
+      toolBar.activeBitDepth.SelectRow(toolBar.activeBitDepth.FindRow(workspace ? workspace.bitDepth : 0));
    }
 
    void UpdateToolBarActiveConfigs(bool selectionOnly)
@@ -2039,6 +2179,7 @@ class IDEWorkSpace : Window
    {
       bool unavailable = project && projectView.buildInProgress;
       bool naForRun = unavailable || !project || project.GetTargetType(project.config) != executable;
+      BuildOutputMode mode = ide.rightClickMenuBuildOutputMode;
 
       projectNewItem.disabled             = unavailable;
       toolBar.buttonNewProject.disabled   = unavailable;
@@ -2054,7 +2195,7 @@ class IDEWorkSpace : Window
       toolBar.buttonRun.disabled = naForRun;
 
       projectBuildItem.disabled = false;
-      projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
+      projectBuildItem.text     = unavailable ? $"Stop Build" : bldMnuStrBuild[mode];
       projectBuildItem.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
 
       projectLinkItem.disabled                  = unavailable;
@@ -2084,27 +2225,28 @@ class IDEWorkSpace : Window
       if(projectView && projectView.popupMenu && projectView.popupMenu.menu && projectView.popupMenu.created)
       {
          MenuItem menu;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, 0);
+         BuildOutputMode mode = ide.rightClickMenuBuildOutputMode;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectBuild, mode);
          if(menu)
          {
             menu.disabled = false;
-            menu.text   = unavailable ? $"Stop Build" : $"Build";
+            menu.text   = unavailable ? $"Stop Build" : bldMnuStrBuild[mode];
             menu.accelerator = unavailable ? Key { pauseBreak, ctrl = true } : f7;
          }
 
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, 0);              if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, 0);           if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectCleanTarget, 0);       if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectInstall, 0);           if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, 0);      if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, 0);         if(menu) menu.disabled = unavailable;
-         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, 0); if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectLink, mode);              if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRebuild, mode);           if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectCleanTarget, mode);       if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, mode);             if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, mode);         if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, mode);        if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectInstall, mode);           if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, mode);            if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, mode);                if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, mode);              if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugPrecompile, mode);      if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugCompile, mode);         if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::FileDebugGenerateSymbols, mode); if(menu) menu.disabled = unavailable;
          projectView.popupMenu.Update(null);
       }
    }
@@ -2319,7 +2461,7 @@ class IDEWorkSpace : Window
 
                         ide.projectView.ShowOutputBuildLog(true);
                         {
-                           CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+                           CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(ide.workspace.activeCompiler);
                            ide.projectView.DisplayCompiler(compiler, false);
                            delete compiler;
                         }
@@ -2338,23 +2480,8 @@ class IDEWorkSpace : Window
                         // this crashes on starting ide with epj file, solution please?
                         // app.UpdateDisplay();
 
+                        workspace.OpenPreviouslyOpenedFiles(noParsing);
                         workspace.holdTracking = true;
-                        for(ofi : workspace.openedFiles)
-                        {
-                           if(ofi.state != closed)
-                           {
-                              Window file = OpenFile(ofi.path, false, true, null, no, normal, noParsing);
-                              if(file)
-                              {
-                                 char fileName[MAX_LOCATION];
-                                 ProjectNode node;
-                                 GetLastDirectory(ofi.path, fileName);
-                                 node = projectView.project.topNode.Find(fileName, true);
-                                 if(node)
-                                    node.EnsureVisible();
-                              }
-                           }
-                        }
                         ide.RepositionWindows(false);
                         workspace.holdTracking = false;
 
@@ -2437,9 +2564,9 @@ class IDEWorkSpace : Window
                   if(prj)
                   {
                      const char * activeConfigName = null;
-                     CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+                     CompilerConfig compiler = ideConfig.compilers.GetCompilerConfig(workspace.activeCompiler);
                      prj.StartMonitoring();
-                     workspace.projects.Add(prj);
+                     workspace.AddProject(prj, null);
                      if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
                            toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
                         activeConfigName = toolBar.activeConfig.currentRow.string;
@@ -2542,7 +2669,7 @@ class IDEWorkSpace : Window
          {
             document.fileName = filePath;
             if(workspace && !workspace.holdTracking)
-               workspace.UpdateOpenedFileInfo(filePath, opened);
+               workspace.UpdateOpenedFileInfo(filePath, opened, true);
          }
       }
 
@@ -2562,21 +2689,7 @@ class IDEWorkSpace : Window
       if(document)
       {
          if(projectView && document._class == class(CodeEditor) && workspace)
-         {
-            int lineNumber, position;
-            Point scroll;
-            CodeEditor editor = (CodeEditor)document;
-            editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
-            editor.openedFileInfo.holdTracking = true;
-            lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
-            position = Max(editor.openedFileInfo.position - 1, 0);
-            if(editor.editBox.GoToLineNum(lineNumber))
-               editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
-            scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
-            scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
-            editor.editBox.scroll = scroll;
-            editor.openedFileInfo.holdTracking = false;
-         }
+            workspace.RestorePreviouslyOpenedFileState((CodeEditor)document);
 
          if(needFileModified)
             document.OnFileModified = OnFileModified;
@@ -2585,13 +2698,20 @@ class IDEWorkSpace : Window
             document.state = maximized;
 
          if(isProject)
-            ideSettings.AddRecentProject(document.fileName);
+         {
+            ideConfig.recentWorkspaces.addRecent(document.fileName);
+            ideConfig.recentWorkspaces.write(settingsContainer);
+            ide.updateRecentProjectsMenu();
+         }
+         else if(workspace)
+            workspace.recentFiles.addRecent(document.fileName);
          else
-            ideSettings.AddRecentFile(document.fileName);
-         ide.UpdateRecentMenus();
+         {
+            ideConfig.recentFiles.addRecent(document.fileName);
+            ideConfig.recentFiles.write(settingsContainer);
+         }
+         ide.updateRecentFilesMenu();
          ide.AdjustFileMenus();
-         settingsContainer.Save();
-
          return document;
       }
       else
@@ -2602,19 +2722,19 @@ class IDEWorkSpace : Window
    /*bool Window::GenericDocumentOnClose(bool parentClosing)
    {
       if(!parentClosing && ide.workspace)
-         ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
+         ide.workspace.UpdateOpenedFileInfo(fileName, unknown, false);
       return true;
    }*/
    bool ModelView::ModelViewOnClose(bool parentClosing)
    {
       if(!parentClosing && ide.workspace)
-         ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
+         ide.workspace.UpdateOpenedFileInfo(fileName, unknown, false);
       return true;
    }
    bool PictureEdit::PictureEditOnClose(bool parentClosing)
    {
       if(!parentClosing && ide.workspace)
-         ide.workspace.UpdateOpenedFileInfo(fileName, unknown);
+         ide.workspace.UpdateOpenedFileInfo(fileName, unknown, false);
       return true;
    }
 
@@ -2662,13 +2782,72 @@ class IDEWorkSpace : Window
       return true;
    }
 
-   void GoToError(const char * line, bool noParsing)
+   void GoToError(const char * line, bool noParsing, const char * objectFileExt)
    {
       if(projectView)
-         projectView.GoToError(line, noParsing);
+         projectView.GoToError(line, noParsing, objectFileExt);
    }
 
-   void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
+   FileAttribs GoToCodeSelectFile(const char * filePath, const char * dir, Project prj, ProjectNode * node, char * selectedPath, const char * objectFileExt)
+   {
+      FileAttribs result { };
+      FileAttribs fileAttribs;
+      if(filePath[0])
+      {
+         if(prj)
+            strcpy(selectedPath, prj.topNode.path);
+         else if(dir && dir[0])
+            strcpy(selectedPath, dir);
+         else
+            selectedPath[0] = '\0';
+         PathCat(selectedPath, filePath);
+
+         if((fileAttribs = FileExists(selectedPath)).isFile)
+            result = fileAttribs;
+         else if(workspace)
+         {
+            bool done = false;
+            for(p : workspace.projects)
+            {
+               strcpy(selectedPath, p.topNode.path);
+               PathCat(selectedPath, filePath);
+               if((fileAttribs = FileExists(selectedPath)).isFile)
+               {
+                  done = true;
+                  result = fileAttribs;
+                  break;
+               }
+            }
+            if(!done)
+            {
+               Project project;
+               ProjectNode n = null;
+               for(p : workspace.projects)
+               {
+                  if((n = p.topNode.Find(filePath, false)))
+                  {
+                     n.GetFullFilePath(selectedPath, true);
+                     if((fileAttribs = FileExists(selectedPath)).isFile)
+                     {
+                        if(node) *node = n;
+                        result = fileAttribs;
+                        break;
+                     }
+                  }
+               }
+               if(!n && (n = workspace.GetObjectFileNode(filePath, &project, selectedPath, objectFileExt)) && project &&
+                     (fileAttribs = FileExists(selectedPath)).isFile)
+               {
+                  if(node) *node = n;
+                  result = fileAttribs;
+               }
+            }
+         }
+      }
+      return result;
+   }
+
+   void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, const char * objectFileExt)
    {
       char *s = null;
       const char *path = text;
@@ -2681,7 +2860,7 @@ class IDEWorkSpace : Window
       FileAttribs fileAttribs;
 
       // support for valgrind output
-      if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
+      if((s = strstr(text, "==")) && s == text && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
       {
          colon = s;
          for(; s>text; s--)
@@ -2704,7 +2883,7 @@ class IDEWorkSpace : Window
          //line = atoi(colon+1);
       }
       // support for "Found n match(es) in "file/path";
-      else if(path[len-1] == '\"' && strstr(path, $"Found %d match%s in \"%s\"%s\n\n"."Found") && strstr(path, $"match") && strstr(path, $"in") && (s = strstr(path, "\"")) && s != path+len-1)
+      else if(len > 0 && path[len-1] == '\"' && strstr(path, $"Found %d match%s in \"%s\"%s\n\n"."Found") && strstr(path, $"match") && strstr(path, $"in") && (s = strstr(path, "\"")) && s != path+len-1)
       {
          path = s+1;
       }
@@ -2763,50 +2942,8 @@ class IDEWorkSpace : Window
          strcpy(filePath, path);
       }
 
-      if(filePath[0])
-      {
-         if(prj)
-            strcpy(completePath, prj.topNode.path);
-         else if(dir && dir[0])
-            strcpy(completePath, dir);
-         else
-            completePath[0] = '\0';
-         PathCat(completePath, filePath);
-
-         if((fileAttribs = FileExists(completePath)))
-            CodeLocationGoTo(completePath, fileAttribs, line, col);
-         else if(ide.workspace)
-         {
-            bool done = false;
-            for(p : ide.workspace.projects)
-            {
-               strcpy(completePath, p.topNode.path);
-               PathCat(completePath, filePath);
-               if((fileAttribs = FileExists(completePath)).isFile)
-               {
-                  CodeLocationGoTo(completePath, fileAttribs, line, col);
-                  done = true;
-                  break;
-               }
-            }
-            if(!done)
-            {
-               for(p : ide.workspace.projects)
-               {
-                  ProjectNode node = p.topNode.Find(filePath, false);
-                  if(node)
-                  {
-                     node.GetFullFilePath(completePath);
-                     if((fileAttribs = FileExists(completePath)).isFile)
-                     {
-                        CodeLocationGoTo(completePath, fileAttribs, line, col);
-                        break;
-                     }
-                  }
-               }
-            }
-         }
-      }
+      if((fileAttribs = GoToCodeSelectFile(filePath, dir, prj, null, completePath, objectFileExt)))
+         CodeLocationGoTo(completePath, fileAttribs, line, col);
    }
 
    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
@@ -2818,7 +2955,8 @@ class IDEWorkSpace : Window
          strlwr(ext);
          if(binaryDocExt.Find(ext))
             ShellOpen(path);
-         else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
+         else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "bc") ||
+               !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
          {
             char dirPath[MAX_LOCATION];
             StripLastDirectory(path, dirPath);
@@ -2826,8 +2964,8 @@ class IDEWorkSpace : Window
          }
          else
          {
-            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, ext, no, normal, false);
-            if(codeEditor && line)
+            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false);
+            if(codeEditor && codeEditor._class == class(CodeEditor) && line)
             {
                EditBox editBox = codeEditor.editBox;
                editBox.GoToLineNum(line - 1);
@@ -3048,7 +3186,7 @@ class IDEWorkSpace : Window
                               {
                                  List<ProjectNode> nodes { };
                                  nodes.Add(node);
-                                 projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
+                                 projectView.Compile(node.project, nodes, normal, isCObject ? cObject : normal);
                                  delete nodes;
                               }
                            }
@@ -3089,7 +3227,6 @@ class IDEWorkSpace : Window
    {
       int c;
       bool passThrough = false;
-      bool debugStart = false;
       bool debugWorkDir = false;
       char * passDebugWorkDir = null;
       bool openAsText = false;
@@ -3119,7 +3256,7 @@ class IDEWorkSpace : Window
          else if(!strcmp(app.argv[c], "-no-parsing"))
             ide.noParsing = true;
          else if(!strcmp(app.argv[c], "-debug-start"))
-            debugStart = true;
+            ide.debugStart = true;
          else if(!strcmp(app.argv[c], "-debug-work-dir"))
             debugWorkDir = true;
          else if(!strcmp(app.argv[c], "-@"))
@@ -3137,7 +3274,7 @@ class IDEWorkSpace : Window
             GetExtension(app.argv[c], ext);
             isProject = !openAsText && !strcmpi(ext, "epj");
 
-            if(isProject && c > (debugStart ? 2 : 1)) continue;
+            if(isProject && c > 1 + (ide.debugStart ? 1 : 0)) continue;
 
             // Create directory for projects (only)
             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
@@ -3168,9 +3305,9 @@ class IDEWorkSpace : Window
                   newProjectDialog.Modal();
                   if(projectView)
                   {
-                     ideSettings.AddRecentProject(projectView.fileName);
-                     ide.UpdateRecentMenus();
-                     settingsContainer.Save();
+                     ideConfig.recentWorkspaces.addRecent(projectView.fileName);
+                     ideConfig.recentWorkspaces.write(settingsContainer);
+                     ide.updateRecentMenus();
                   }
                   delete newProjectDialog;
                   // Open only one project
@@ -3190,8 +3327,6 @@ class IDEWorkSpace : Window
          workspace.debugDir = passDebugWorkDir;
          delete passDebugWorkDir;
       }
-      if(debugStart)
-         ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
 
       UpdateToolBarActiveConfigs(false);
       UpdateToolBarActiveCompilers();
@@ -3286,17 +3421,21 @@ class IDEWorkSpace : Window
 
       for(item : compiler.executableDirs)
       {
+         DirExpression dirExpr { };
+         dirExpr.Evaluate(item, null, compiler, null, 0);
          found = false;
+
          for(p : newExePaths)
          {
-            if(!fstrcmp(p, item))
+            if(!fstrcmp(p, dirExpr.dir))
             {
                found = true;
                break;
             }
          }
          if(!found)
-            newExePaths.Add(CopySystemPath(item));
+            newExePaths.Add(CopySystemPath(dirExpr.dir));
+         delete dirExpr;
       }
 
       GetEnvironment("PATH", oldList, maxPathLen);
@@ -3459,38 +3598,46 @@ class IDEWorkSpace : Window
       return true;
    }
 
-   void UpdateRecentMenus()
+   void updateRecentMenus()
    {
-      int c;
-      Menu fileMenu = menu.FindMenu($"File");
-      Menu recentFiles = fileMenu.FindMenu($"Recent Files");
-      Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
+      updateRecentFilesMenu();
+      updateRecentProjectsMenu();
+   }
+
+   void updateRecentFilesMenu()
+   {
+      int c = 0;
       char * itemPath = new char[MAX_LOCATION];
       char * itemName = new char[MAX_LOCATION+4];
-
-      recentFiles.Clear();
-      c = 0;
-
-      for(recent : ideSettings.recentFiles)
+      Workspace ws = workspace;
+      RecentPaths recentFiles = ws ? ws.recentFiles : ideConfig.recentFiles;
+      recentFilesMenu.Clear();
+      for(recent : recentFiles)
       {
          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
          MakeSystemPath(itemPath);
          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
-         recentFiles.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
+         recentFilesMenu.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentFile }, ide, true);
          c++;
       }
+      delete itemPath;
+      delete itemName;
+   }
 
-      recentProjects.Clear();
-      c = 0;
-      for(recent : ideSettings.recentProjects)
+   void updateRecentProjectsMenu()
+   {
+      int c = 0;
+      char * itemPath = new char[MAX_LOCATION];
+      char * itemName = new char[MAX_LOCATION+4];
+      recentProjectsMenu.Clear();
+      for(recent : ideConfig.recentWorkspaces)
       {
          strncpy(itemPath, recent, MAX_LOCATION); itemPath[MAX_LOCATION-1] = '\0';
          MakeSystemPath(itemPath);
          snprintf(itemName, MAX_LOCATION+4, "%d %s", 1 + c, itemPath); itemName[MAX_LOCATION+4-1] = '\0';
-         recentProjects.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
+         recentProjectsMenu.AddDynamic(MenuItem { copyText = true, text = itemName, (Key)k1 + c, id = c, NotifySelect = ide.FileRecentProject }, ide, true);
          c++;
       }
-
       delete itemPath;
       delete itemName;
    }
@@ -3528,7 +3675,7 @@ bool GetInstalledFileOrFolder(const char * subDir, const char * name, char * pat
    bool found = false;
    char * v = new char[maxPathLen];
    v[0] = '\0';
-   if(found)
+   if(!found)
    {
       strncpy(path, settingsContainer.moduleLocation, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
       StripLastDirectory(path, path);
@@ -3539,92 +3686,57 @@ bool GetInstalledFileOrFolder(const char * subDir, const char * name, char * pat
 #if defined(__WIN32__)
    if(!found)
    {
-      GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
-      if(v[0])
+      for(s : [ "ECERE_SDK_SRC", "AppData", "ALLUSERSPROFILE", "USERPROFILE", "HOMEPATH", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "SystemDrive" ])
       {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
-      }
-   }
-   if(!found)
-   {
-      GetEnvironment("AppData", v, maxPathLen);
-      if(v[0])
-      {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, sdkDirName);
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
-      }
-   }
-   if(!found)
-   {
-      GetEnvironment("ProgramData", v, maxPathLen);
-      if(v[0])
-      {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, sdkDirName);
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
-      }
-   }
-   if(!found)
-   {
-      GetEnvironment("ProgramFiles", v, maxPathLen);
-      if(v[0])
-      {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, sdkDirName);
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
-      }
-   }
-   if(!found)
-   {
-      GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
-      if(v[0])
-      {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, sdkDirName);
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
-      }
-   }
-   if(!found)
-   {
-      GetEnvironment("SystemDrive", v, maxPathLen);
-      if(v[0])
-      {
-         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
-         PathCat(path, "Program Files");
-         PathCat(path, sdkDirName);
-         PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
+         GetEnvironment(s, v, maxPathLen);
+         if(v[0])
+         {
+            strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+            if(!strcmp(s, "SystemDrive"))
+               PathCat(path, "Program Files");
+            if(strcmp(s, "ECERE_SDK_SRC"))
+               PathCat(path, sdkDirName);
+            PathCat(path, subDir);
+            if(name) PathCat(path, name);
+            if(FileExists(path) & attribs)
+            {
+               found = true;
+               break;
+            }
+         }
       }
    }
 #else
    if(!found)
    {
+      char * p = null;
       char * tokens[256];
       int c, numTokens;
 
       GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
       numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
+      if(!numTokens)
+      {
+         p = new char[MAX_LOCATION];
+         p[0] = '\0';
+         strcat(p, "/usr/share");
+         tokens[0] = p;
+         numTokens = 1;
+      }
       for(c=0; c<numTokens; c++)
       {
          strncpy(path, tokens[c], MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
          PathCat(path, sdkDirName);
          PathCat(path, subDir);
-         if(name) PathCat(path, name);
-         if(FileExists(path) & attribs) found = true;
+         if(name)
+            PathCat(path, name);
+         if(FileExists(path) & attribs)
+         {
+            found = true;
+            break;
+         }
       }
+      delete p;
    }
 #endif
    delete v;
@@ -3690,6 +3802,13 @@ class IDEApp : GuiApplication
          }
       }
 
+      ide.ApplyFont(ideSettings.codeEditorFont, ideSettings.codeEditorFontSize);
+      ide.ApplyColorScheme(colorScheme);
+
+      ideConfig.compilers.read(settingsContainer);
+      ideConfig.recentFiles.read(settingsContainer);
+      ideConfig.recentWorkspaces.read(settingsContainer);
+
       // First count files arg to decide whether to maximize
       {
          bool passThrough = false, debugWorkDir = false;