ide/CodeEditor: Customizable color scheme support
[sdk] / ide / src / ide.ec
index 562de3e..815c3b0 100644 (file)
@@ -44,12 +44,39 @@ import "about"
 
 import "FileSystemIterator"
 
+AVLTree<const String> binaryDocExt
+{ [
+   "wav", "mp3", "flac", "ogg",
+   "mid",
+   "avi", "mkv", "mpg", "mpeg",
+   "7z", "zip", "gz", "bz2", "xz", "rar", "z", "tar", "ear",
+   "pdf", "odp", "ods", "odt", "ppt", "doc", "xls", "pptx", "docx", "xlsx"
+] };
+
 #if defined(__WIN32__)
 define pathListSep = ";";
 #else
 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
@@ -142,9 +169,6 @@ FileDialog ideProjectFileDialog
 
 GlobalSettingsDialog globalSettingsDialog
 {
-   ideSettings = ideSettings;
-   settingsContainer = settingsContainer;
-
    void OnGlobalSettingChange(GlobalSettingsChange globalSettingsChange)
    {
       switch(globalSettingsChange)
@@ -195,7 +219,7 @@ void DrawLineMarginIcon(Surface surface, BitmapResource resource, int line, int
 
 class IDEToolbar : ToolBar
 {
-   /* File options */
+   // File options
    // New
    ToolButton buttonNewFile { this, toolTip = $"New file", menuItemPtr = IDEItem(fileNewItem) };
    // Open
@@ -209,16 +233,16 @@ class IDEToolbar : ToolBar
 
    ToolSeparator separator1 { this };
 
-   /* Edit options */
+   // Edit options
    // Cut
-   // Copy 
+   // Copy
    // Paste
    // Undo
    // Redo
 
    // ToolSeparator separator2 { this };
 
-   /* Project  options */
+   // Project options
    // New project
    ToolButton buttonNewProject { this, toolTip = $"New project", menuItemPtr = IDEItem(projectNewItem) };
    // Open project
@@ -230,7 +254,7 @@ class IDEToolbar : ToolBar
 
    ToolSeparator separator3 { this };
 
-   /* Build/Execution options */
+   // Build/Execution options
    // Build
    ToolButton buttonBuild { this, toolTip = $"Build project", menuItemPtr = IDEItem(projectBuildItem), disabled = true; };
    // Re-link
@@ -246,10 +270,13 @@ class IDEToolbar : ToolBar
    // Compile actual file
    // Execute
    ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem), disabled = true; };
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+   ToolButton buttonInstall { this, toolTip = $"Install", menuItemPtr = IDEItem(projectInstallItem), disabled = true; };
+#endif
 
    ToolSeparator separator4 { this };
 
-   /* Debug options */
+   // Debug options
    // Start/Resume
    ToolButton buttonDebugStartResume { this, toolTip = $"Start", menuItemPtr = IDEItem(debugStartResumeItem), disabled = true; };
    // Restart
@@ -279,21 +306,7 @@ class IDEToolbar : ToolBar
       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
       {
          if(row)
-         {
-            for(prj : ide.workspace.projects)
-            {
-               for(cfg : prj.configurations)
-               {
-                  if(cfg.name && !strcmp(cfg.name, row.string))
-                  {
-                     prj.config = cfg;
-                     break;
-                  }
-               }
-            }
-            ide.UpdateToolBarActiveConfigs(true);
-            ide.projectView.Update(null);
-         }
+            ide.workspace.SelectActiveConfig(row.string);
          return true;
       }
    };
@@ -305,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);
@@ -324,13 +337,13 @@ class IDEToolbar : ToolBar
 
    DropBox activeBitDepth
    {
-      this, toolTip = $"Active Bit Depth", size = { 60 }, disabled = true;
+      this, toolTip = $"Active Bit Length", size = { 60 }, disabled = true;
       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
       {
          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)
@@ -355,7 +368,6 @@ class IDEToolbar : ToolBar
       activeBitDepth.AddString("64 bit").tag = 64;
       activeBitDepth.currentRow = row;
    }
-
 }
 
 class IDEMainFrame : Window
@@ -415,9 +427,10 @@ class IDEWorkSpace : Window
    menu = Menu {  };
    IDEToolbar toolBar;
 
-   MenuItem * driverItems, * skinItems;
+   MenuItem * driverItems, * skinItems, * languageItems;
    StatusField pos { width = 150 };
    StatusField ovr, caps, num;
+   DualPipe documentor;
 
    BitmapResource back                 { ":ecereBack.jpg", window = this };
    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
@@ -430,32 +443,145 @@ class IDEWorkSpace : Window
    BitmapResource bmpTopFrameError     { ":codeMarks/topFrameError.png", window = this };
    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))
+            {
+               ce.font = { codeFont.faceName, codeFont.size, codeFont.bold, codeFont.italic };
+               ce.editBox.font = ce.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
    {
       parent = this;
 
-      void OnGotoError(char * line, bool noParsing)
+      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(char * line)
+      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)
       {
          switch(key)
          {
-            case escape: 
+            case escape:
                if(activeBox != findBox || !ide.findInFilesDialog || !ide.findInFilesDialog.SearchAbort())
-                  ide.ShowCodeEditor(); 
+                  ide.ShowCodeEditor();
                break;
             default:
             {
@@ -466,13 +592,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -484,18 +603,13 @@ class IDEWorkSpace : Window
 
    CallStackView callStackView
    {
-      parent = this, font = { panelFont.faceName, panelFont.size };
-
-      void OnGotoLine(char * line)
-      {
-         int stackLvl;
-         stackLvl = atoi(line);
-         ide.debugger.GoToStackFrameLine(stackLvl, true);
-      }
+      parent = this, font = { panelFont.faceName, panelFont.size, panelFont.bold, panelFont.italic };
 
-      void OnSelectFrame(int lineNumber)
+      void OnSelectFrame(int frameIndex)
       {
-         ide.debugger.SelectFrame(lineNumber);
+         ide.debugger.GoToStackFrameLine(frameIndex, true, true);
+         if(frameIndex >= 0)
+            ide.debugger.SelectFrame(frameIndex);
       }
 
       void OnToggleBreakpoint()
@@ -504,33 +618,7 @@ class IDEWorkSpace : Window
          if(debugger.activeFrame && debugger.activeFrame.absoluteFile)
          {
             int line = debugger.activeFrame.line;
-            char name[MAX_LOCATION];
-            Project prj = null;
-            // TOFIX: Improve on this, don't use only filename, make a function
-            GetLastDirectory(debugger.activeFrame.absoluteFile, name);
-            if(ide && ide.workspace)
-            {
-               for(p : ide.workspace.projects)
-               {
-                  if(p.topNode.Find(name, false))
-                  {
-                     prj = p;
-                     break;
-                  }
-               }
-               if(!prj)
-               {
-                  for(p : ide.workspace.projects)
-                  {
-                     if(IsPathInsideOf(debugger.activeFrame.absoluteFile, p.topNode.path))
-                     {
-                        prj = p;
-                        break;
-                     }
-                  }
-               }
-            }
-            debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line, prj);
+            debugger.ToggleBreakpoint(debugger.activeFrame.absoluteFile, line);
             Update(null);
             {
                CodeEditor codeEditor = (CodeEditor)ide.FindWindow(debugger.activeFrame.absoluteFile);
@@ -548,13 +636,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -570,7 +651,7 @@ class IDEWorkSpace : Window
          if(activeFrame)
          {
             bool error;
-            int lineCursor, lineTopFrame, activeThread, hitThread;
+            int lineCursor, lineTopFrame;
             int lineH, scrollY, boxH;
             BitmapResource bmp;
             Breakpoint bp = null;
@@ -578,8 +659,8 @@ class IDEWorkSpace : Window
             boxH = clientSize.h;
             scrollY = editBox.scroll.y;
             displaySystem.FontExtent(editBox.font.font, " ", 1, null, &lineH);
-            activeThread = debugger.activeThread;
-            hitThread = debugger.hitThread;
+            //activeThread = debugger.activeThread;
+            //hitThread = debugger.hitThread;
             debugger.GetCallStackCursorLine(&error, &lineCursor, &lineTopFrame);
 
             // TODO: improve bp drawing... it should be visible even if it's not on the activeFrame
@@ -620,11 +701,11 @@ 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)
       {
@@ -635,13 +716,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -669,8 +743,8 @@ class IDEWorkSpace : Window
    };
    BreakpointsView breakpointsView { parent = this };
 
-   ToolBox toolBox { parent = this };
-   Sheet sheet { parent = this };
+   ToolBox toolBox { parent = this, visible = false };
+   Sheet sheet { parent = this, visible = false };
 
    char * tmpPrjDir;
    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
@@ -682,11 +756,14 @@ class IDEWorkSpace : Window
          bitmap = { ":actions/docNew.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            Window document = (Window)NewCodeEditor(this, normal, false);
+            Window currentDoc = activeClient;
+            bool maximizeDoc = ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
+            Window document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, false);
+            RepositionWindows(false);
             document.NotifySaved = DocumentSaved;
             return true;
          }
-      }
+      };
       MenuItem fileOpenItem
       {
          fileMenu, $"Open...", o, ctrlO;
@@ -702,19 +779,20 @@ class IDEWorkSpace : Window
                   bool gotWhatWeWant = false;
                   int c;
                   int numSelections = ideFileDialog.numSelections;
-                  char ** multiFilePaths = ideFileDialog.multiFilePaths;
+                  const char * const * multiFilePaths = ideFileDialog.multiFilePaths;
 
                   for(c = 0; c < numSelections; c++)
                   {
-                     if(OpenFile(multiFilePaths[c], normal, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
+                     if(OpenFile(multiFilePaths[c], false, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
                         gotWhatWeWant = true;
                   }
                   if(gotWhatWeWant ||
-                     MessageBox { type = yesNo, master = this, text = $"Error opening file", 
+                     MessageBox { type = yesNo, master = this, text = $"Error opening file",
                      contents = $"Open a different file?" }.Modal() == no)
                   {
                      if(!projectView && gotWhatWeWant)
                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
+                     ide.RepositionWindows(false);
                      break;
                   }
                }
@@ -723,7 +801,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
       MenuDivider { fileMenu };
       MenuItem fileSaveItem
@@ -751,7 +829,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuItem replaceInFiles
       {
          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
@@ -761,29 +839,32 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       MenuItem globalSettingsItem
       {
          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();
             return true;
          }
-      }
+      };
       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
       {
-         fileMenu, $"Exit", x, altF4, NotifySelect = MenuFileExit;
+         fileMenu, $"Exit", x, altF4;
 
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
@@ -795,7 +876,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)
             {
@@ -803,14 +885,17 @@ 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;
                }
                else
-                  OpenFile(file, normal, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
+               {
+                  OpenFile(file, false, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
+                  ide.RepositionWindows(false);
+               }
                break;
             }
             id++;
@@ -821,18 +906,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, normal, 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++;
@@ -841,7 +926,7 @@ class IDEWorkSpace : Window
       }
 
    MenuPlacement editMenu { menu, $"Edit", e };
-   
+
    Menu projectMenu { menu, $"Menu"."Project", p, hasMargin = true };
       MenuItem projectNewItem
       {
@@ -853,6 +938,7 @@ class IDEWorkSpace : Window
             {
                DialogResult result;
                NewProjectDialog newProjectDialog { master = this };
+               incref newProjectDialog;
                result = newProjectDialog.Modal();
                if(result == ok)
                {
@@ -861,16 +947,17 @@ 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();
                      }
                   }
                }
+               delete newProjectDialog;
             }
             return true;
          }
-      }
+      };
       MenuItem projectOpenItem
       {
          projectMenu, $"Open...", o, Key { o, true, true };
@@ -883,12 +970,12 @@ class IDEWorkSpace : Window
             ideProjectFileDialog.text = openProjectFileDialogTitle;
             if(ideProjectFileDialog.Modal() == ok)
             {
-               OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
+               OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
             }
             return true;
          }
-      }
+      };
       MenuItem projectQuickItem
       {
          projectMenu, $"Quick...", q, f7, disabled = true;
@@ -898,7 +985,7 @@ class IDEWorkSpace : Window
                QuickProjectDialog { this }.Modal();
             return true;
          }
-      }
+      };
       MenuItem projectAddItem
       {
          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
@@ -914,9 +1001,9 @@ class IDEWorkSpace : Window
             {
                if(ideProjectFileDialog.Modal() == ok)
                {
-                  if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
+                  if(OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
                      break;
-                  if(MessageBox { type = yesNo, master = this, text = $"Error opening project file", 
+                  if(MessageBox { type = yesNo, master = this, text = $"Error opening project file",
                         contents = $"Add a different project?" }.Modal() == no)
                   {
                      break;
@@ -927,7 +1014,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCloseItem
       {
          projectMenu, $"Close", c, disabled = true;
@@ -940,7 +1027,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectSettingsItem
       {
@@ -950,7 +1037,7 @@ class IDEWorkSpace : Window
             projectView.MenuSettings(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectBrowseFolderItem
       {
@@ -961,7 +1048,7 @@ class IDEWorkSpace : Window
                projectView.MenuBrowseFolder(null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectRunItem
       {
@@ -973,7 +1060,7 @@ class IDEWorkSpace : Window
                projectView.Run(null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectBuildItem
       {
          projectMenu, $"Build", b, f7, disabled = true;
@@ -989,7 +1076,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectLinkItem
       {
          projectMenu, $"Relink", l, disabled = true;
@@ -1000,7 +1087,7 @@ class IDEWorkSpace : Window
                projectView.ProjectLink(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectRebuildItem
       {
          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
@@ -1011,7 +1098,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCleanTargetItem
       {
          projectMenu, $"Clean Target", g, disabled = true;
@@ -1025,7 +1112,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCleanItem
       {
          projectMenu, $"Clean", e, disabled = true;
@@ -1039,7 +1126,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRealCleanItem
       {
          projectMenu, $"Real Clean", disabled = true;
@@ -1053,7 +1140,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRegenerateItem
       {
          projectMenu, $"Regenerate Makefile", m, disabled = true;
@@ -1064,7 +1151,20 @@ class IDEWorkSpace : Window
                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
+      MenuItem projectInstallItem
+      {
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+         projectMenu, $"Install", t, disabled = true;
+#endif
+         bitmap = { ":status/software-update-available.png" };
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(projectView)
+               projectView.ProjectInstall(projectView.active ? selection : null, mods);
+            return true;
+         }
+      };
       MenuItem projectCompileItem;
    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
       MenuItem debugStartResumeItem
@@ -1072,7 +1172,7 @@ class IDEWorkSpace : Window
          debugMenu, $"Start", s, f5, disabled = true;
          bitmap = { ":actions/debug.png" };
          NotifySelect = MenuDebugStart;
-      }
+      };
       bool MenuDebugStart(MenuItem selection, Modifiers mods)
       {
          if(projectView)
@@ -1099,7 +1199,7 @@ class IDEWorkSpace : Window
                projectView.DebugRestart();
             return true;
          }
-      }
+      };
       MenuItem debugBreakItem
       {
          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
@@ -1112,7 +1212,7 @@ class IDEWorkSpace : Window
                projectView.DebugBreak();
             return true;
          }
-      }
+      };
       MenuItem debugStopItem
       {
          debugMenu, $"Stop", p, shiftF5, disabled = true;
@@ -1123,7 +1223,151 @@ class IDEWorkSpace : Window
                projectView.DebugStop();
             return true;
          }
-      }
+      };
+      MenuDivider { debugMenu };
+      ModelView duck
+      {
+         this,
+         // nonClient = true,
+         autoCreate = false,
+         alphaBlend = true,
+         opacity = 0,
+         isRemote = true,
+         borderStyle = 0,
+         moveable = true,
+         anchor = { right = 0, bottom = 0 },
+         inactive = true,
+         isActiveClient = false,
+         stayOnTop = true,
+         clickThrough = true,
+         size = { 500, 500 };
+
+         bool OnLoadGraphics()
+         {
+            ModelView::OnLoadGraphics();
+            camera.position.z /= 1.3;
+            camera.orientation = Euler { yaw = 280, pitch = 20 };
+            camera.Update();
+            Update(null);
+            return true;
+         }
+
+         bool OnRightButtonDown(int x, int y, Modifiers mods)
+         {
+            if(!displaySystem.flags.flipping) return true;
+            MenuWindowMove(null, 0);
+            return false;
+         }
+
+         bool OnRightButtonUp(int x, int y, Modifiers mods)
+         {
+            position = position;
+            state = normal;
+            return true;
+         }
+      };
+      MenuItem debugRubberDuck
+      {
+         debugMenu, $"Rubber Duck", checkable = true, disabled = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(selection.checked)
+               duck.Create();
+            else
+               duck.Destroy(0);
+            return true;
+         }
+      };
+#ifndef __WIN32__
+      MenuDivider { debugMenu };
+      MenuItem debugUseValgrindItem
+      {
+         debugMenu, $"Use Valgrind", d, disabled = true, checkable = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               ide.workspace.useValgrind = selection.checked;
+               ide.workspace.Save();
+            }
+            ide.AdjustValgrindMenus();
+            return true;
+         }
+      };
+      Menu debugValgrindLeakCheckItem { debugMenu, $"Valgrind Leak Check", h };
+         MenuItem debugValgrindNoLeakCheckItem      { debugValgrindLeakCheckItem, $"No"     , f, id = ValgrindLeakCheck::no     , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
+         MenuItem debugValgrindSummaryLeakCheckItem { debugValgrindLeakCheckItem, $"Summary", f, id = ValgrindLeakCheck::summary, checkable = true, disabled = true; NotifySelect = ValgrindLCSelect, checked = true; };
+         MenuItem debugValgrindYesLeakCheckItem     { debugValgrindLeakCheckItem, $"Yes"    , f, id = ValgrindLeakCheck::yes    , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
+         MenuItem debugValgrindFullLeakCheckItem    { debugValgrindLeakCheckItem, $"Full"   , f, id = ValgrindLeakCheck::full   , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; };
+         bool ValgrindLCSelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               if(selection.checked)
+               {
+                  ValgrindLeakCheck vgLeakCheck = (ValgrindLeakCheck)selection.id;
+
+                  debugValgrindNoLeakCheckItem.checked      = debugValgrindNoLeakCheckItem.id      == vgLeakCheck;
+                  debugValgrindSummaryLeakCheckItem.checked = debugValgrindSummaryLeakCheckItem.id == vgLeakCheck;
+                  debugValgrindYesLeakCheckItem.checked     = debugValgrindYesLeakCheckItem.id     == vgLeakCheck;
+                  debugValgrindFullLeakCheckItem.checked    = debugValgrindFullLeakCheckItem.id    == vgLeakCheck;
+
+                  ide.workspace.vgLeakCheck = vgLeakCheck;
+                  ide.workspace.Save();
+               }
+               else
+                  selection.checked = true;
+            }
+            return true;
+         }
+      Menu debugValgrindRedzoneSizeItem { debugMenu, $"Valgrind Redzone Size", z };
+         MenuItem debugValgrindRSDefaultItem { debugValgrindRedzoneSizeItem, $"Default", f, id =  -1, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect, checked = true; };
+         MenuItem debugValgrindRS0Item       { debugValgrindRedzoneSizeItem, "0"      , f, id =   0, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS16Item      { debugValgrindRedzoneSizeItem, "16"     , f, id =  16, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS32Item      { debugValgrindRedzoneSizeItem, "32"     , f, id =  32, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS64Item      { debugValgrindRedzoneSizeItem, "64"     , f, id =  64, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS128Item     { debugValgrindRedzoneSizeItem, "128"    , f, id = 128, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS256Item     { debugValgrindRedzoneSizeItem, "256"    , f, id = 256, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         MenuItem debugValgrindRS512Item     { debugValgrindRedzoneSizeItem, "512"    , f, id = 512, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; };
+         bool ValgrindRSSelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               if(selection.checked)
+               {
+                  int vgRedzoneSize = (int)selection.id;
+
+                  debugValgrindRSDefaultItem.checked = debugValgrindRSDefaultItem.id == vgRedzoneSize;
+                  debugValgrindRS0Item.checked       = debugValgrindRS0Item.id       == vgRedzoneSize;
+                  debugValgrindRS16Item.checked      = debugValgrindRS16Item.id      == vgRedzoneSize;
+                  debugValgrindRS32Item.checked      = debugValgrindRS32Item.id      == vgRedzoneSize;
+                  debugValgrindRS64Item.checked      = debugValgrindRS64Item.id      == vgRedzoneSize;
+                  debugValgrindRS128Item.checked     = debugValgrindRS128Item.id     == vgRedzoneSize;
+                  debugValgrindRS256Item.checked     = debugValgrindRS256Item.id     == vgRedzoneSize;
+                  debugValgrindRS512Item.checked     = debugValgrindRS512Item.id     == vgRedzoneSize;
+
+                  ide.workspace.vgRedzoneSize = vgRedzoneSize;
+                  ide.workspace.Save();
+               }
+               else
+                  selection.checked = true;
+            }
+            return true;
+         }
+      MenuItem debugValgrindTrackOriginsItem
+      {
+         debugMenu, $"Valgrind Track Origins", k, checkable = true, disabled = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               ide.workspace.vgTrackOrigins = selection.checked;
+               ide.workspace.Save();
+            }
+            return true;
+         }
+      };
+#endif
       MenuDivider { debugMenu };
       MenuItem debugStepIntoItem
       {
@@ -1131,57 +1375,80 @@ class IDEWorkSpace : Window
          bitmap = { ":actions/stepInto.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            if(projectView)
-               projectView.DebugStepInto();
+            if(projectView) projectView.DebugStepInto();
             return true;
          }
-      }
+      };
       MenuItem debugStepOverItem
       {
          debugMenu, $"Step Over", v, f10, disabled = true;
          bitmap = { ":actions/stepOver.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            if(projectView)
-               projectView.DebugStepOver(false);
+            if(projectView) projectView.DebugStepOver(false);
             return true;
          }
-      }
+      };
+      MenuItem debugSkipStepOverItem
+      {
+         debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
+         bitmap = { ":actions/stepOverSkipBreak.png" };
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(projectView) projectView.DebugStepOver(true);
+            return true;
+         }
+      };
       MenuItem debugStepOutItem
       {
          debugMenu, $"Step Out", o, shiftF11, disabled = true;
          bitmap = { ":actions/stepOut.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            if(projectView)
-               projectView.DebugStepOut(false);
+            if(projectView) projectView.DebugStepOut(false);
             return true;
          }
-      }
-      MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
-      MenuItem debugSkipStepOverItem
+      };
+      MenuItem debugSkipStepOutItem
       {
-         debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
+         debugMenu, $"Step Out Skipping Breakpoints", n, Key { f11, ctrl = true, shift = true }, disabled = true;
+         bitmap = { ":actions/skipBreaks.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            if(projectView)
-               projectView.DebugStepOver(true);
+            if(projectView) projectView.DebugStepOut(true);
             return true;
          }
-      }
-      MenuItem debugSkipStepOutItem
+      };
+#if 0
+      MenuItem debugStepUntilItem
       {
-         debugMenu, $"Step Out Skipping Breakpoints", t, Key { f11, ctrl = true, shift = true }, disabled = true;
-         bitmap = { ":actions/skipBreaks.png" };
+         debugMenu, $"Step Over Until Next Line", x, disabled = true;
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            if(projectView)
-               projectView.DebugStepOut(true);
+            if(projectView) projectView.DebugStepUntil(false);
             return true;
          }
-      }
+      };
+      MenuItem debugSkipStepUntilItem
+      {
+         debugMenu, $"Step Over Until Next Line Skipping Breakpoints", e, Key { f10, shift = true, alt = true }, disabled = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(projectView) projectView.DebugStepUntil(true);
+            return true;
+         }
+      };
+#endif
+      MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
-      MenuPlacement debugSkipRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level Skipping Breakpoints", s };
+      MenuPlacement debugRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level", l };
+      MenuPlacement debugSkipRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level Skipping Breakpoints", g };
+#if 0
+      MenuPlacement debugBpRunToCursorItem { debugMenu, $"BP Run To Cursor" };
+      MenuPlacement debugBpSkipRunToCursorItem { debugMenu, $"BP Run To Cursor Skipping Breakpoints" };
+      MenuPlacement debugBpRunToCursorAtSameLevelItem { debugMenu, $"BP Run To Cursor At Same Level" };
+      MenuPlacement debugBpSkipRunToCursorAtSameLevelItem { debugMenu, $"BP Run To Cursor At Same Level Skipping Breakpoints" };
+#endif
       //MenuDivider { debugMenu };
       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
    MenuPlacement imageMenu { menu, $"Image", i };
@@ -1198,7 +1465,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuPlacement { viewMenu, $"View Designer" };
       MenuPlacement { viewMenu, $"View Code" };
       MenuPlacement { viewMenu, $"View Properties" };
@@ -1219,7 +1486,7 @@ class IDEWorkSpace : Window
                ((CodeEditor)client).ViewDesigner();
             return true;
          }
-      }
+      };
       MenuItem viewCodeItem
       {
          viewMenu, $"View Code", c, f8;
@@ -1235,7 +1502,7 @@ class IDEWorkSpace : Window
             client.visible = true;
             return true;
          }
-      }
+      };
       MenuItem viewPropertiesItem
       {
          viewMenu, $"View Properties", p, f4;
@@ -1246,7 +1513,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewMethodsItem
       {
          viewMenu, $"View Methods", m, f4;
@@ -1257,7 +1524,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewToolBoxItem
       {
          viewMenu, $"View Toolbox", x, f12;
@@ -1267,7 +1534,7 @@ class IDEWorkSpace : Window
             toolBox.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewOutputItem
       {
          viewMenu, $"Output", o, alt2;
@@ -1276,7 +1543,7 @@ class IDEWorkSpace : Window
             outputView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewWatchesItem
       {
          viewMenu, $"Watches", w, alt3;
@@ -1285,7 +1552,7 @@ class IDEWorkSpace : Window
             watchesView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewThreadsItem
       {
          viewMenu, $"Threads", t, alt4;
@@ -1294,7 +1561,7 @@ class IDEWorkSpace : Window
             threadsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewBreakpointsItem
       {
          viewMenu, $"Breakpoints", b, alt5;
@@ -1303,7 +1570,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewCallStackItem
       {
          viewMenu, $"Call Stack", s, alt7;
@@ -1312,7 +1579,7 @@ class IDEWorkSpace : Window
             callStackView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewAllDebugViews
       {
          viewMenu, $"All Debug Views", a, alt9;
@@ -1325,7 +1592,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
 #ifdef GDB_DEBUG_GUI
       MenuDivider { viewMenu };
       MenuItem viewGDBItem
@@ -1336,7 +1603,7 @@ class IDEWorkSpace : Window
             gdbDialog.Show();
             return true;
          }
-      }
+      };
 #endif
       MenuDivider { viewMenu };
       MenuItem viewColorPicker
@@ -1348,7 +1615,7 @@ class IDEWorkSpace : Window
             colorPicker.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { viewMenu };
       /*
       MenuItem
@@ -1365,6 +1632,11 @@ class IDEWorkSpace : Window
       };
       */
       Menu driversMenu { viewMenu, $"Graphics Driver", v };
+
+      MenuDivider { viewMenu };
+
+      Menu languageMenu { viewMenu, "Language", l };
+
       //Menu skinsMenu { viewMenu, "GUI Skins", k };
    Menu windowMenu { menu, $"Window", w };
       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
@@ -1384,21 +1656,29 @@ class IDEWorkSpace : Window
          helpMenu, $"API Reference", r, f1;
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            char * p = new char[MAX_LOCATION];
-            p[0] = '\0';
-            strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-            PathCat(p, "documentor");
-#if defined(__WIN32__)
-            ChangeExtension(p, "exe", p);
-#endif
-            if(FileExists(p).isFile)
-               Execute(p);
+            if(!documentor)
+            {
+               char * p = new char[MAX_LOCATION];
+               p[0] = '\0';
+               strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+               PathCat(p, "documentor");
+   #if defined(__WIN32__)
+               ChangeExtension(p, "exe", p);
+   #endif
+               if(!FileExists(p).isFile)
+                  strcpy(p, "documentor");
+
+               documentor = DualPipeOpen({ input = true, output = true, showWindow = true }, p);
+               delete p;
+            }
             else
-               Execute("documentor");
-            delete p;
+            {
+               Process_ShowWindows(documentor.GetProcessID());
+               // documentor.Puts("Activate\n");
+            }
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1408,7 +1688,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1418,7 +1698,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("doc");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Samples Folder", s;
@@ -1427,7 +1707,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("samples");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Extras Folder", x;
@@ -1436,7 +1716,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("extras");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1446,7 +1726,7 @@ class IDEWorkSpace : Window
             ShellOpen("http://ecere.com/forums");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1456,7 +1736,7 @@ class IDEWorkSpace : Window
             AboutIDE { master = this }.Modal();
             return true;
          }
-      }
+      };
 
    property ToolBox toolBox
    {
@@ -1486,21 +1766,22 @@ class IDEWorkSpace : Window
    };
 
    bool noParsing;
+   bool debugStart;
 
 #ifdef GDB_DEBUG_GUI
    GDBDialog gdbDialog
    {
       master = this, parent = this;
-      anchor = { left = 100, top = 100, right = 100, bottom = 100 };
+      //anchor = { left = 100, top = 100, right = 100, bottom = 100 };
 
-      void OnCommand(char * string)
+      void OnCommand(const char * string)
       {
          if(ide)
             ide.debugger.SendGDBCommand(string);
       }
    };
 #endif
-   
+
    bool NotifySelectDisplayDriver(MenuItem selection, Modifiers mods)
    {
       //app.driver = app.drivers[selection.id];
@@ -1512,6 +1793,8 @@ class IDEWorkSpace : Window
       delete ideSettings.displayDriver;
       ideSettings.displayDriver = CopyString(selection.id ? "OpenGL" : "Default");
 
+      ide.debugRubberDuck.disabled = !ide.duck.modelFile || strcmpi(app.driver, "OpenGL");
+
       settingsContainer.Save();
       //SetDriverAndSkin();
       return true;
@@ -1541,19 +1824,19 @@ class IDEWorkSpace : Window
          }
    }
 
-   ProjectView CreateProjectView(Workspace workspace, char * fileName)
+   ProjectView CreateProjectView(Workspace workspace, const char * fileName)
    {
       Project project = workspace.projects.firstIterator.data;
       projectView = ProjectView
       {
          this;
          fileName = fileName;
-         
+
          void NotifyDestroyed(Window window, DialogResult result)
          {
             projectView = null;
             text = titleECEREIDE;
-            
+
             AdjustMenus();
          }
       };
@@ -1566,6 +1849,7 @@ class IDEWorkSpace : Window
       ideMainFrame.SetText("%s - %s", project.topNode.name, titleECEREIDE);
 
       AdjustMenus();
+      updateRecentMenus();
 
       ide.breakpointsView.LoadFromWorkspace();
       ide.watchesView.LoadFromWorkspace();
@@ -1592,8 +1876,12 @@ class IDEWorkSpace : Window
             findInFilesDialog.SearchStop();
             findInFilesDialog.currentDirectory = workingDir;
          }
+         sheet.visible = false;
+         toolBox.visible = false;
+         outputView.visible = false;
          ideMainFrame.text = titleECEREIDE;
          ide.AdjustMenus();
+         ide.updateRecentMenus();
          return true;
       }
       return false;
@@ -1604,7 +1892,6 @@ class IDEWorkSpace : Window
       if(this)
       {
          Window child;
-         bool inDebugMode = debugger.isActive;
          bool callStackVisible = expand ? false : callStackView.visible;
          bool threadsVisible = expand ? false : threadsView.visible;
          bool watchesVisible = expand ? false : watchesView.visible;
@@ -1613,10 +1900,10 @@ class IDEWorkSpace : Window
          bool outputVisible = expand ? false : outputView.visible;
          int topDistance = (callStackVisible || threadsVisible) ? 200 : 0;
          int bottomDistance = (outputVisible || watchesVisible || breakpointsVisible) ? 240 : 0;
-         
+
          for(child = firstChild; child; child = child.next)
          {
-            if(child._class == class(CodeEditor) || child._class == class(Designer) || 
+            if(child._class == class(CodeEditor) || child._class == class(Designer) ||
                child._class == class(Sheet) || child._class == class(ProjectView))
             {
                Anchor anchor = child.anchor;
@@ -1624,19 +1911,22 @@ class IDEWorkSpace : Window
                anchor.bottom = bottomDistance;
                if(child._class == class(CodeEditor) || child._class == class(Designer))
                {
+                  anchor.left = (sheet.visible || (projectView && projectView.visible)) ? 300 : 0;
                   anchor.right = toolBoxVisible ? 150 : 0;
                }
-               child.anchor = anchor;
+               if(ide.projectView)
+                  child.anchor = anchor;
             }
             else if(expand)
             {
-               if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) || 
+               if(child._class == class(OutputView) || child._class == class(CallStackView) || child._class == class(ThreadsView) || child._class == class(WatchesView) ||
                   child._class == class(BreakpointsView))
                   child.visible = false;
             }
          }
          // If this is not here, the IDE is not updated when doing Debug/Break then Alt-4 to show call stack (IDE not updated)
          Update(null);
+         if(duck.visible) duck.Update(null);   // TOFIX: If this is not here, the duck disappears -- Why?
       }
    }
 
@@ -1645,32 +1935,31 @@ class IDEWorkSpace : Window
       if(activeClient)
          activeClient.Activate();
       else if(projectView)
-      { 
+      {
          projectView.visible = true;
          projectView.Activate();
       }
-      else
-      {
-         sheet.visible = true;
+      else if(sheet.visible)
          sheet.Activate();
-      }
+      else
+         outputView.visible = false;
       return false;
    }
 
-   void DocumentSaved(Window document, char * fileName)
+   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, char * param)
+   bool Window::OnFileModified(FileChange fileChange, const char * param)
    {
       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;
@@ -1681,7 +1970,7 @@ class IDEWorkSpace : Window
 
          this.modifiedDocument = false;
          this.Destroy(0);
-         this = ide.OpenFile(fileName, normal, true, null, no, normal, noParsing);
+         this = ide.OpenFile(fileName, false, true, null, no, normal, noParsing);
          if(this)
          {
             this.anchor = anchor;
@@ -1698,7 +1987,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;
@@ -1711,7 +2000,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);
@@ -1726,14 +2015,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)
@@ -1828,11 +2118,38 @@ class IDEWorkSpace : Window
       toolBar.activeCompiler.disabled     = unavailable;
       toolBar.activeBitDepth.disabled     = unavailable;
 
+#ifndef __WIN32__
+      debugUseValgrindItem.disabled       = unavailable;
+      AdjustValgrindMenus();
+#endif
+
       AdjustFileMenus();
       AdjustBuildMenus();
       AdjustDebugMenus();
    }
 
+#ifndef __WIN32__
+   void AdjustValgrindMenus()
+   {
+      bool unavailable = !project || !debugUseValgrindItem.checked;
+      debugValgrindNoLeakCheckItem.disabled        = unavailable;
+      debugValgrindSummaryLeakCheckItem.disabled   = unavailable;
+      debugValgrindYesLeakCheckItem.disabled       = unavailable;
+      debugValgrindFullLeakCheckItem.disabled      = unavailable;
+
+      debugValgrindTrackOriginsItem.disabled       = unavailable;
+
+      debugValgrindRSDefaultItem.disabled          = unavailable;
+      debugValgrindRS0Item.disabled                = unavailable;
+      debugValgrindRS16Item.disabled               = unavailable;
+      debugValgrindRS32Item.disabled               = unavailable;
+      debugValgrindRS64Item.disabled               = unavailable;
+      debugValgrindRS128Item.disabled              = unavailable;
+      debugValgrindRS256Item.disabled              = unavailable;
+      debugValgrindRS512Item.disabled              = unavailable;
+   }
+#endif
+
    property bool hasOpenedCodeEditors
    {
       get
@@ -1857,6 +2174,8 @@ class IDEWorkSpace : Window
    void AdjustBuildMenus()
    {
       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;
@@ -1868,11 +2187,11 @@ class IDEWorkSpace : Window
       projectCloseItem.disabled           = unavailable;
       // toolBar.buttonCloseProject.disabled = unavailable;
 
-      projectRunItem.disabled    = unavailable || project.GetTargetType(project.config) != executable;
-      toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
+      projectRunItem.disabled    = naForRun;
+      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;
@@ -1886,6 +2205,10 @@ class IDEWorkSpace : Window
       // toolBar.buttonRealClean.disabled          = unavailable;
       projectRegenerateItem.disabled            = unavailable;
       toolBar.buttonRegenerateMakefile.disabled = unavailable;
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+      projectInstallItem.disabled               = unavailable;
+      toolBar.buttonInstall.disabled            = unavailable;
+#endif
       projectCompileItem.disabled               = unavailable;
 
       AdjustPopupBuildMenus();
@@ -1898,26 +2221,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::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);
       }
    }
@@ -1928,66 +2253,67 @@ class IDEWorkSpace : Window
             projectView.buildInProgress == buildingMainProject;
    } }
 
-   property bool isBreakpointTogglingUnavailable { get {
-      return !project;
-   } }
-
-   property bool isDebuggerExecuting { get {
-      if(!ide.debugger)
-         return false;
-      else
-         return ide.debugger.state == running;
-   } }
+   property bool isBreakpointTogglingUnavailable { get { return !project; } }
+   property bool isDebuggerRunning { get { if(ide.debugger) return ide.debugger.state == running; return false; } }
+   property bool isDebuggerStopped { get { if(ide.debugger) return ide.debugger.state == stopped; return false; } }
 
    void AdjustDebugMenus()
    {
       bool unavailable = areDebugMenusUnavailable;
+      bool running = isDebuggerRunning;
+      bool stopped = isDebuggerStopped;
       bool active = debugger.isActive;
-      bool bpNoToggle = isBreakpointTogglingUnavailable;
-      bool executing = isDebuggerExecuting;
-      //bool holding = debugger.state == stopped;
 
-      debugStartResumeItem.disabled       = unavailable || executing;
+      bool isNotRunning    = unavailable || !running;
+      bool isNotNotRunning = unavailable || running;
+      bool isNotStopped    = unavailable || !stopped;
+      bool isNotActive     = unavailable || !active;
+
+      debugStartResumeItem.disabled       = isNotNotRunning;
       debugStartResumeItem.text           = active ? $"Resume" : $"Start";
       debugStartResumeItem.NotifySelect   = active ? MenuDebugResume : MenuDebugStart;
       if(toolBar)
       {
-         toolBar.buttonDebugStartResume.disabled      = unavailable || executing;
+         toolBar.buttonDebugStartResume.disabled      = isNotNotRunning;
          toolBar.buttonDebugStartResume.toolTip       = active ? $"Resume" : $"Start";
       }
 
-      debugBreakItem.disabled             = unavailable || !executing;
-      debugStopItem.disabled              = unavailable || !active;
-      debugRestartItem.disabled           = unavailable || !active;
+      debugBreakItem.disabled             = isNotRunning;
+      debugStopItem.disabled              = isNotActive;
+      debugRestartItem.disabled           = isNotActive;
       if(toolBar)
       {
-         toolBar.buttonDebugPause.disabled            = unavailable || !executing;
-         toolBar.buttonDebugStop.disabled             = unavailable || !active;
-         toolBar.buttonDebugRestart.disabled          = unavailable || !active;
+         toolBar.buttonDebugPause.disabled            = isNotRunning;
+         toolBar.buttonDebugStop.disabled             = isNotActive;
+         toolBar.buttonDebugRestart.disabled          = isNotActive;
       }
 
-      debugStepIntoItem.disabled          = unavailable || executing;
-      debugStepOverItem.disabled          = unavailable || executing;
-      debugStepOutItem.disabled           = unavailable || executing || !active;
-      debugSkipStepOverItem.disabled      = unavailable || executing;
-      debugSkipStepOutItem.disabled       = unavailable || executing || !active;
+      debugStepIntoItem.disabled          = isNotNotRunning;
+      debugStepOverItem.disabled          = isNotNotRunning;
+      debugSkipStepOverItem.disabled      = isNotNotRunning;
+      debugStepOutItem.disabled           = isNotStopped;
+      debugSkipStepOutItem.disabled       = isNotStopped;
+#if 0
+      debugStepUntilItem.disabled         = isNotStopped;
+      debugSkipStepUntilItem.disabled     = isNotStopped;
+#endif
       if(toolBar)
       {
-         toolBar.buttonDebugStepInto.disabled         = unavailable || executing;
-         toolBar.buttonDebugStepOver.disabled         = unavailable || executing;
-         toolBar.buttonDebugStepOut.disabled          = unavailable || executing || !active;
-         toolBar.buttonDebugSkipStepOver.disabled     = unavailable || executing;
-         // toolBar.buttonDebugSkipStepOutItem.disabled  = unavailable || executing;
+         toolBar.buttonDebugStepInto.disabled         = isNotNotRunning;
+         toolBar.buttonDebugStepOver.disabled         = isNotNotRunning;
+         toolBar.buttonDebugSkipStepOver.disabled     = isNotNotRunning;
+         toolBar.buttonDebugStepOut.disabled          = isNotStopped;
+         //toolBar.buttonDebugSkipStepOutItem.disabled  = isNotNotRunning;
       }
       if((Designer)GetActiveDesigner())
       {
          CodeEditor codeEditor = ((Designer)GetActiveDesigner()).codeEditor;
          if(codeEditor)
-            codeEditor.AdjustDebugMenus(unavailable, bpNoToggle, executing);
+            codeEditor.AdjustDebugMenus();
       }
    }
 
-   void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
+   void ChangeFileDialogsDirectory(const char * directory, bool saveSettings)
    {
       char tempString[MAX_LOCATION];
       strcpy(tempString, directory);
@@ -2008,14 +2334,14 @@ class IDEWorkSpace : Window
       settingsContainer.Save();
    }
 
-   Window FindWindow(char * filePath)
+   Window FindWindow(const char * filePath)
    {
       Window document = null;
 
       // TOCHECK: Do we need to change slashes here?
       for(document = firstChild; document; document = document.next)
       {
-         char * fileName = document.fileName;
+         const char * fileName = document.fileName;
          if(document.isDocument && fileName && !fstrcmp(fileName, filePath))
          {
             document.visible = true;
@@ -2026,17 +2352,17 @@ class IDEWorkSpace : Window
       return null;
    }
 
-   bool DontTerminateDebugSession(char * title)
+   bool DontTerminateDebugSession(const char * title)
    {
       if(debugger.isActive)
       {
-         if(MessageBox { type = yesNo, master = ide, 
-                           contents = $"Do you want to terminate the debugging session in progress?", 
+         if(MessageBox { type = yesNo, master = ide,
+                           contents = $"Do you want to terminate the debugging session in progress?",
                            text = title }.Modal() == no)
             return true;
          /*
-         MessageBox msg { type = yesNo, master = ide, 
-                           contents = "Do you want to terminate the debugging session in progress?", 
+         MessageBox msg { type = yesNo, master = ide,
+                           contents = "Do you want to terminate the debugging session in progress?",
                            text = title };
          if(msg.Modal() == no)
          {
@@ -2050,15 +2376,16 @@ class IDEWorkSpace : Window
       return false;
    }
 
-   Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
+   Window OpenFile(const char * origFilePath, bool dontMaximize, bool visible, const char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
    {
       char extension[MAX_EXTENSION] = "";
       Window document = null;
       bool isProject = false;
       bool needFileModified = true;
       char winFilePath[MAX_LOCATION];
-      char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
-
+      const char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
+      Window currentDoc = activeClient;
+      bool maximizeDoc = !dontMaximize && ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
       if(!type)
       {
          GetExtension(filePath, extension);
@@ -2071,7 +2398,7 @@ class IDEWorkSpace : Window
       {
          for(document = firstChild; document; document = document.next)
          {
-            char * fileName = document.fileName;
+            const char * fileName = document.fileName;
             if(document.isDocument && fileName && !fstrcmp(fileName, filePath) && document.created)
             {
                document.visible = true;
@@ -2098,9 +2425,8 @@ class IDEWorkSpace : Window
                {
                   for(;;)
                   {
-                     Project project;
                      Workspace workspace = null;
-                     
+
                      if(FileExists(filePath))
                      {
                         if(!strcmp(extension, ProjectExtension))
@@ -2114,21 +2440,24 @@ class IDEWorkSpace : Window
                            workspace = LoadWorkspace(filePath, null);
                         else
                            return null;
-                        //project = LoadProject(filePath, null);
                      }
-                     
+
                      if(workspace)
                      {
-                        char absolutePath[MAX_LOCATION];
                         CreateProjectView(workspace, filePath);
                         document = projectView;
 
-                        workspace.DropInvalidBreakpoints();
+                        toolBox.visible = true;
+                        sheet.visible = true;
+                        projectView.MakeActive();
+
+                        workspace.ParseLoadedBreakpoints();
+                        workspace.DropInvalidBreakpoints(null);
                         workspace.Save();
 
                         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;
                         }
@@ -2147,44 +2476,46 @@ 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, normal, 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;
 
                         workspace.timer.Start();
 
+#if !defined(__WIN32__)
+                        // Valgrind Debug menu updates
+                        debugUseValgrindItem.checked = workspace.useValgrind;
+
+                        debugValgrindNoLeakCheckItem.checked      = workspace.vgLeakCheck == no;
+                        debugValgrindSummaryLeakCheckItem.checked = workspace.vgLeakCheck == summary;
+                        debugValgrindYesLeakCheckItem.checked     = workspace.vgLeakCheck == yes;
+                        debugValgrindFullLeakCheckItem.checked    = workspace.vgLeakCheck == full;
+
+                        debugValgrindRSDefaultItem.checked = workspace.vgRedzoneSize == -1;
+                        debugValgrindRS0Item.checked       = workspace.vgRedzoneSize == 0;
+                        debugValgrindRS16Item.checked      = workspace.vgRedzoneSize == 16;
+                        debugValgrindRS32Item.checked      = workspace.vgRedzoneSize == 32;
+                        debugValgrindRS64Item.checked      = workspace.vgRedzoneSize == 64;
+                        debugValgrindRS128Item.checked     = workspace.vgRedzoneSize == 128;
+                        debugValgrindRS256Item.checked     = workspace.vgRedzoneSize == 256;
+                        debugValgrindRS512Item.checked     = workspace.vgRedzoneSize == 512;
+
+                        debugValgrindTrackOriginsItem.checked = workspace.vgTrackOrigins;
+#endif
+
                         findInFilesDialog.mode = FindInFilesMode::project;
                         findInFilesDialog.currentDirectory = ide.project.topNode.path;
-                        
+
                         {
                            char location[MAX_LOCATION];
                            StripLastDirectory(ide.project.topNode.path, location);
                            ChangeProjectFileDialogDirectory(location);
                         }
-                        
-                        /*
-                        if(projectView.debugger)
-                           projectView.debugger.EvaluateWatches();
-                        */
-                        
+
                         break;
                      }
-                     else 
+                     else
                      {
                         if(MessageBox { type = yesNo, master = this, text = $"Error opening project", contents = $"Open a different project?" }.Modal() == yes)
                         {
@@ -2220,7 +2551,7 @@ class IDEWorkSpace : Window
                }
                if(prj)
                {
-                  MessageBox { type = ok, parent = parent, master = this, text = $"Same Project", 
+                  MessageBox { type = ok, parent = parent, master = this, text = $"Same Project",
                         contents = $"This project is already present in workspace." }.Modal();
                }
                else
@@ -2228,10 +2559,10 @@ class IDEWorkSpace : Window
                   prj = LoadProject(filePath, null);
                   if(prj)
                   {
-                     char * activeConfigName = null;
-                     CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+                     const char * activeConfigName = null;
+                     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;
@@ -2276,8 +2607,8 @@ class IDEWorkSpace : Window
             !strcmp(extension, "jpeg") || !strcmp(extension, "png"))
       {
          if(FileExists(filePath))
-            document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
-                                       hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
+            document = PictureEdit { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
+                                       hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
                                        visible = visible, bitmapFile = filePath, OnClose = PictureEditOnClose/*why?--GenericDocumentOnClose*/;
                                     };
          if(!document)
@@ -2287,8 +2618,8 @@ class IDEWorkSpace : Window
       else if(!strcmp(extension, "3ds"))
       {
          if(FileExists(filePath))
-            document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable, 
-                                    hasVertScroll = true, hasHorzScroll = true, parent = this, state = state, 
+            document = ModelView { hasMaximize = true, hasMinimize = true, hasClose = true, borderStyle = sizable,
+                                    hasVertScroll = true, hasHorzScroll = true, parent = this, state = state,
                                     visible = visible, modelFile = filePath, OnClose = ModelViewOnClose/*why?--GenericDocumentOnClose*/
                                     };
 
@@ -2334,18 +2665,18 @@ class IDEWorkSpace : Window
          {
             document.fileName = filePath;
             if(workspace && !workspace.holdTracking)
-               workspace.UpdateOpenedFileInfo(filePath, opened);
+               workspace.UpdateOpenedFileInfo(filePath, opened, true);
          }
       }
-      
+
       if(!document && createIfFails != no)
       {
-         if(createIfFails != yes && !needFileModified && 
+         if(createIfFails != yes && !needFileModified &&
                MessageBox { type = yesNo, master = this, text = filePath, contents = $"File doesn't exist. Create?" }.Modal() == yes)
             createIfFails = yes;
          if(createIfFails == yes || createIfFails == whatever)
          {
-            document = (Window)NewCodeEditor(this, state, true);
+            document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, true);
             if(document)
                document.fileName = filePath;
          }
@@ -2354,34 +2685,29 @@ 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);
-            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;
          document.NotifySaved = DocumentSaved;
-         
+         if(maximizeDoc && document.hasMaximize)
+            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
@@ -2392,19 +2718,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;
    }
 
@@ -2417,10 +2743,17 @@ class IDEWorkSpace : Window
    }
    */
 
+   void UpdateStateLight(StatusField fld, bool on)
+   {
+      fld.color = on ? lime : Color { 128,128,128 };
+      fld.backColor = on ? dimGray : 0;
+      fld.bold = on;
+   }
+
    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
    {
-      caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
-      num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
+      UpdateStateLight(caps, app.GetKeyState(capsState));
+      UpdateStateLight(num, app.GetKeyState(numState));
       return true;
    }
 
@@ -2428,41 +2761,139 @@ class IDEWorkSpace : Window
    {
       switch(key)
       {
-         case b:
-            projectView.Update(null);
-            break;
-         case capsLock:
-            caps.color = app.GetKeyState(capsState) ? black : Color { 128,128,128 };
-            break;
-         case numLock:
-            num.color = app.GetKeyState(numState) ? black : Color { 128,128,128 };
-            break;
+         case b: projectView.Update(null); break;
+         case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
+         case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
       }
       return true;
    }
 
-   void GoToError(const char * line, bool noParsing)
+   bool OnKeyUp(Key key, unichar ch)
+   {
+      switch(key)
+      {
+         case capsLock: UpdateStateLight(caps, app.GetKeyState(capsState)); break;
+         case numLock:  UpdateStateLight(num, app.GetKeyState(numState)); break;
+      }
+      return true;
+   }
+
+   void GoToError(const char * line, bool noParsing, const char * objectFileExt)
    {
       if(projectView)
-         projectView.GoToError(line, noParsing);
+         projectView.GoToError(line, noParsing, objectFileExt);
+   }
+
+   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)
+   void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir, const char * objectFileExt)
    {
-      char *path = text;
+      char *s = null;
+      const char *path = text;
       char *colon = strchr(text, ':');
       char filePath[MAX_LOCATION] = "";
       char completePath[MAX_LOCATION];
       int line = 0, col = 0;
+      int len = strlen(text);
       Project prj = null;
       FileAttribs fileAttribs;
 
-      if(colon && (colon[1] == '/' || colon[1] == '\\'))
+      // support for valgrind output
+      if((s = strstr(text, "==")) && s == text && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
       {
-         path = (colon - 1 > path) ? colon - 1 : path;
-         colon = strstr(colon + 1, ":");
+         colon = s;
+         for(; s>text; s--)
+         {
+            if(*s == '(')
+            {
+               path = s+1;
+               break;
+            }
+         }
+         /*for(s=colon; *s; s++)
+         {
+            if(*s == ')')
+            {
+               *s = '\0';;
+               break;
+            }
+         }*/
+         //*colon = '\0';
+         //line = atoi(colon+1);
+      }
+      // support for "Found n match(es) in "file/path";
+      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;
+      }
+      else
+      {
+         if(colon && (colon[1] == '/' || colon[1] == '\\'))
+         {
+            path = (colon - 1 > path) ? colon - 1 : path;
+            colon = strstr(colon + 1, ":");
+         }
+         if(*path == '*' && (s = strchr(path+1, '*')))
+            path = s+1;
+         while(isspace(*path)) path++;
       }
-      while(isspace(*path)) path++;
       if(*path == '(')
       {
          char * close = strchr(path, ')');
@@ -2507,44 +2938,39 @@ class IDEWorkSpace : Window
          strcpy(filePath, path);
       }
 
-      if(filePath[0])
+      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)
+   {
+      if(fileAttribs.isFile)
       {
-         if(prj)
-            strcpy(completePath, prj.topNode.path);
-         else if(dir && dir[0])
-            strcpy(completePath, dir);
+         char ext[MAX_EXTENSION];
+         GetExtension(path, ext);
+         strlwr(ext);
+         if(binaryDocExt.Find(ext))
+            ShellOpen(path);
+         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);
+            ShellOpen(dirPath);
+         }
          else
-            completePath[0] = '\0';
-         PathCat(completePath, filePath);
-
-
-         fileAttribs = FileExists(completePath);
-         if(fileAttribs.isFile)
          {
-            char ext[MAX_EXTENSION];
-            GetExtension(completePath, ext);
-            if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
-               ShellOpen(completePath);
-            else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
-            {
-               char dirPath[MAX_LOCATION];
-               StripLastDirectory(completePath, dirPath);
-               ShellOpen(dirPath);
-            }
-            else
+            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false);
+            if(codeEditor && codeEditor._class == class(CodeEditor) && line)
             {
-               CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal, false);
-               if(codeEditor && line)
-               {
-                  EditBox editBox = codeEditor.editBox;
-                  editBox.GoToLineNum(line - 1);
-                  editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
-               }
+               EditBox editBox = codeEditor.editBox;
+               editBox.GoToLineNum(line - 1);
+               editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
             }
          }
-         else if(fileAttribs.isDirectory)
-            ShellOpen(completePath);
       }
+      else if(fileAttribs.isDirectory)
+         ShellOpen(path);
    }
 
    void OnRedraw(Surface surface)
@@ -2616,12 +3042,12 @@ class IDEWorkSpace : Window
             SetCurrentContext(codeEditor.globalContext);
             SetTopContext(codeEditor.globalContext);
             SetGlobalContext(codeEditor.globalContext);
-            
+
             SetDefines(&codeEditor.defines);
             SetImports(&codeEditor.imports);
 
             SetActiveDesigner(codeEditor.designer);
-            
+
             sheet.codeEditor = codeEditor;
             toolBox.codeEditor = codeEditor;
 
@@ -2680,6 +3106,12 @@ class IDEWorkSpace : Window
          }
          else
          {
+            if(!client && !projectView && sheet.visible)
+            {
+               if(sheet)
+                  sheet.visible = false;
+               toolBox.visible = false;
+            }
             if(sheet)
                sheet.codeEditor = null;
             toolBox.codeEditor = null;
@@ -2704,22 +3136,25 @@ class IDEWorkSpace : Window
 
             statusBar.AddField(pos);
 
-            caps = { width = 40, text = $"CAPS", color = app.GetKeyState(capsState) ? black : Color { 128, 128, 128 } };
+            caps = { width = 40, text = $"CAPS" };
             statusBar.AddField(caps);
+            UpdateStateLight(caps, app.GetKeyState(capsState));
 
-            ovr = { width = 30, text = $"OVR", color = (editBox && editBox.overwrite) ? black : Color { 128, 128, 128 } };
+            ovr = { width = 36, text = $"OVR" };
             statusBar.AddField(ovr);
+            UpdateStateLight(ovr, (editBox && editBox.overwrite));
 
-            num = { width = 30, text = $"NUM", color = app.GetKeyState(numState) ? black : Color { 128, 128, 128 } };
+            num = { width = 36, text = $"NUM" };
             statusBar.AddField(num);
+            UpdateStateLight(num, app.GetKeyState(numState));
 
             //statusBar.text = "Ready";
 
             if(projectView && projectView.project)
             {
                bool isCObject = false;
-               ProjectNode node = projectView.GetNodeFromWindow(client, null, false);
-               if(!node && (node = projectView.GetNodeFromWindow(client, null, true)))
+               ProjectNode node = projectView.GetNodeFromWindow(client, null, true, false, null);
+               if(!node && (node = projectView.GetNodeFromWindow(client, null, true, true, null)))
                   isCObject = true;
                if(node)
                {
@@ -2728,7 +3163,7 @@ class IDEWorkSpace : Window
                   if(isCObject)
                      ChangeExtension(node.name, "c", nodeName);
                   sprintf(name, $"Compile %s", isCObject ? nodeName : node.name);
-                  projectCompileItem = 
+                  projectCompileItem =
                   {
                      copyText = true, text = name, c, ctrlF7, disabled = projectView.buildInProgress;
 
@@ -2736,26 +3171,21 @@ class IDEWorkSpace : Window
                      {
                         if(projectView)
                         {
-                           bool result = false;
                            bool isCObject = false;
-                           ProjectNode node = null;
-                           for(p : ide.workspace.projects)
-                           {
-                              node = projectView.GetNodeFromWindow(activeClient, p, false);
-                              if(node) break;
-                           }
-                           if(!node && (node = projectView.GetNodeFromWindow(activeClient, null, true)))
-                              isCObject = true;
+                           bool isExcluded = false;
+                           ProjectNode node = projectView.GetNodeForCompilationFromWindow(activeClient, true, &isExcluded, &isCObject);
                            if(node)
                            {
-                              List<ProjectNode> nodes { };
-                              nodes.Add(node);
-                              projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
-                              delete nodes;
-                              result = true;
+                              if(isExcluded)
+                                 ide.outputView.buildBox.Logf($"%s %s is excluded from current build configuration.\n", isCObject ? "Object file" : "File", node.name);
+                              else
+                              {
+                                 List<ProjectNode> nodes { };
+                                 nodes.Add(node);
+                                 projectView.Compile(node.project, nodes, normal, isCObject ? cObject : normal);
+                                 delete nodes;
+                              }
                            }
-                           if(!result && node)
-                              ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
                         }
                         return true;
                      }
@@ -2793,27 +3223,23 @@ class IDEWorkSpace : Window
    {
       int c;
       bool passThrough = false;
-      bool debugStart = false;
       bool debugWorkDir = false;
       char * passDebugWorkDir = null;
       bool openAsText = false;
       DynamicString passArgs { };
+      int ptArg = 0;
+
       for(c = 1; c<app.argc; c++)
       {
-         if(!strcmp(app.argv[c], "-t"))
-            openAsText = true;
-         else if(!strcmp(app.argv[c], "-no-parsing"))
-            ide.noParsing = true;
-         else if(!strcmp(app.argv[c], "-debug-start"))
-            debugStart = true;
-         else if(!strcmp(app.argv[c], "-debug-work-dir"))
-            debugWorkDir = true;
-         else if(!passThrough && !strcmp(app.argv[c], "-@"))
-            passThrough = true;
-         else if(passThrough)
+         if(passThrough)
          {
-            passArgs.concat(" ");
-            passArgs.concat(app.argv[c]);
+            const char * arg = app.argv[c];
+            char * buf = new char[strlen(arg)*2+1];
+            if(ptArg++ > 0)
+               passArgs.concat(" ");
+            PassArg(buf, arg);
+            passArgs.concat(buf);
+            delete buf;
          }
          else if(debugWorkDir)
          {
@@ -2821,6 +3247,16 @@ class IDEWorkSpace : Window
             StripQuotes(passDebugWorkDir, passDebugWorkDir);
             debugWorkDir = false;
          }
+         else if(!strcmp(app.argv[c], "-t"))
+            openAsText = true;
+         else if(!strcmp(app.argv[c], "-no-parsing"))
+            ide.noParsing = true;
+         else if(!strcmp(app.argv[c], "-debug-start"))
+            ide.debugStart = true;
+         else if(!strcmp(app.argv[c], "-debug-work-dir"))
+            debugWorkDir = true;
+         else if(!strcmp(app.argv[c], "-@"))
+            passThrough = true;
          else
          {
             char fullPath[MAX_LOCATION];
@@ -2834,7 +3270,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)
@@ -2865,71 +3301,28 @@ 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
                   break;
                }
                else
-                  ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
+                  ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
             }
             else if(strstr(fullPath, "http://") == fullPath)
-               ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
+               ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
          }
       }
       if(passThrough && projectView && projectView.project && workspace)
-      {
-         char * fixSpacing = new char[Max(passArgs.size * 1.5, 32)];
-         {
-            int c, d = 0;
-            char j = 0;
-            char k = ' ';
-            char l = 0;
-            bool inQuote = false;
-            for(c=0; c<passArgs.size; c++)
-            {
-               l = passArgs[c];
-               if(inQuote && k != '\\' && l == ' ')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = ' ';
-                  inQuote = false;
-               }
-               else if(inQuote && l == '\0')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = '\0';
-                  inQuote = false;
-               }
-               else if(!inQuote && j != '\\' && k == ' ' && l != '-' && l != ' ')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = l;
-                  inQuote = true;
-               }
-               else if(k == '\\' && l == ' ')
-                  fixSpacing[d++] = ' ';
-               else if(k == '\\' && l == '\\')
-                  fixSpacing[d++] = '\\';
-               else if(l != '\\')
-                  fixSpacing[d++] = l;
-               j = k;
-               k = l;
-            }
-            fixSpacing[d] = '\0';
-         }
-         workspace.commandLineArgs = fixSpacing;
-      }
-      if(passDebugWorkDir)
+         workspace.commandLineArgs = passArgs;
+      if(passDebugWorkDir && projectView && projectView.project && workspace)
       {
          workspace.debugDir = passDebugWorkDir;
          delete passDebugWorkDir;
       }
-      if(debugStart)
-         ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
 
       UpdateToolBarActiveConfigs(false);
       UpdateToolBarActiveCompilers();
@@ -3024,17 +3417,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);
@@ -3083,8 +3480,9 @@ class IDEWorkSpace : Window
       {
          if(!libPathExists[item])  // fstrcmp should be used
          {
-            newLibPaths.Add(item);
-            libPathExists[item] = true;
+            String s = CopyString(item);
+            newLibPaths.Add(s);
+            libPathExists[s] = true;
          }
       }
 
@@ -3101,8 +3499,9 @@ class IDEWorkSpace : Window
       {
          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
          {
-            newLibPaths.Add(oldPaths[c]);
-            libPathExists[oldPaths[c]] = true;
+            String s = CopyString(oldPaths[c]);
+            newLibPaths.Add(s);
+            libPathExists[s] = true;
          }
       }
 
@@ -3127,6 +3526,7 @@ class IDEWorkSpace : Window
 #endif*/
       delete newList;
 
+      newLibPaths.Free();
       delete newLibPaths;
       delete libPathExists;
 #endif
@@ -3150,7 +3550,6 @@ class IDEWorkSpace : Window
    IDEWorkSpace()
    {
       // Graphics Driver Menu
-      int c;
 
       /*
       app.currentSkin.selectionColor = selectionColor;
@@ -3163,29 +3562,29 @@ class IDEWorkSpace : Window
       {
          driverItems[c] = MenuItem { driversMenu, app.drivers[c], NotifySelect = NotifySelectDisplayDriver };
          driverItems[c].id = c;
-         driverItems[c].isRadio = true;         
+         driverItems[c].isRadio = true;
       }
 */
       driverItems = new MenuItem[2];
 #if defined(__unix__)
          driverItems[0] = MenuItem { driversMenu, "X", NotifySelect = NotifySelectDisplayDriver };
          driverItems[0].id = 0;
-         driverItems[0].isRadio = true;         
+         driverItems[0].isRadio = true;
 #else
          driverItems[0] = MenuItem { driversMenu, "GDI", NotifySelect = NotifySelectDisplayDriver };
          driverItems[0].id = 0;
-         driverItems[0].isRadio = true;         
+         driverItems[0].isRadio = true;
 #endif
          driverItems[1] = MenuItem { driversMenu, "OpenGL", NotifySelect = NotifySelectDisplayDriver };
          driverItems[1].id = 1;
-         driverItems[1].isRadio = true;         
+         driverItems[1].isRadio = true;
 
 /*      skinItems = new MenuItem[app.numSkins];
       for(c = 0; c < app.numSkins; c++)
       {
          skinItems[c] = MenuItem {skinsMenu, app.skins[c], NotifySelect = NotifySelectDisplaySkin };
          skinItems[c].id = c;
-         skinItems[c].isRadio = true;         
+         skinItems[c].isRadio = true;
       }
 */
       ideFileDialog.master = this;
@@ -3195,39 +3594,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];
-      MenuItem item;
-
-      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;
    }
@@ -3236,7 +3642,14 @@ class IDEWorkSpace : Window
    {
       delete driverItems;
       delete skinItems;
+      delete languageItems;
       delete ideSettings;
+      if(documentor)
+      {
+         documentor.Puts("Quit\n");
+         documentor.Wait();
+         delete documentor;
+      }
    }
 }
 
@@ -3253,153 +3666,104 @@ define sdkDirName = "Ecere SDK";
 define sdkDirName = "ecere";
 #endif
 
-void FindAndShellOpenInstalledFolder(char * name)
+bool GetInstalledFileOrFolder(const char * subDir, const char * name, char * path, FileAttribs attribs)
 {
-   char * p = new char[MAX_LOCATION];
+   bool found = false;
    char * v = new char[maxPathLen];
-   byte * tokens[256];
-   int c, numTokens;
-   Array<String> paths { };
-   p[0] = v[0] = '\0';
-   strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-   StripLastDirectory(p, p);
-   PathCat(p, name);
-   paths.Add(CopyString(p));
-#if defined(__WIN32__)
-   GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("AppData", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("ProgramFiles", v, maxPathLen);
-   if(v[0])
+   v[0] = '\0';
+   if(!found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
+      strncpy(path, settingsContainer.moduleLocation, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+      StripLastDirectory(path, path);
+      PathCat(path, subDir);
+      if(name) PathCat(path, name);
+      if(FileExists(path) & attribs) found = true;
    }
-   GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("SystemDrive", v, maxPathLen);
-   if(v[0])
+#if defined(__WIN32__)
+   if(!found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
+      for(s : [ "ECERE_SDK_SRC", "AppData", "ALLUSERSPROFILE", "USERPROFILE", "HOMEPATH", "ProgramData", "ProgramFiles", "ProgramFiles(x86)", "SystemDrive" ])
+      {
+         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
-   GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
-   numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
-   for(c=0; c<numTokens; c++)
+   if(!found)
    {
-      strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
-   }
-#endif
-   for(path : paths)
-   {
-      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      if(FileExists(p).isDirectory)
+      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)
       {
-         ShellOpen(p);
-         break;
+         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;
+            break;
+         }
+      }
+      delete p;
    }
-   delete p;
+#endif
    delete v;
-   paths.Free();
-   delete paths;
+   return found;
 }
 
-void FindAndShellOpenInstalledFile(char * subdir, char * name)
+void FindAndShellOpenInstalledFolder(const char * name)
 {
-   char * p = new char[MAX_LOCATION];
-   char * v = new char[maxPathLen];
-   byte * tokens[256];
-   int c, numTokens;
-   Array<String> paths { };
-   p[0] = v[0] = '\0';
-   strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-   paths.Add(CopyString(p));
-   StripLastDirectory(p, p);
-   PathCat(p, subdir);
-   paths.Add(CopyString(p));
-#if defined(__WIN32__)
-   GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-   GetEnvironment("AppData", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-   GetEnvironment("ProgramFiles", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-   GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-   GetEnvironment("SystemDrive", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-#else
-   GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
-   numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
-   for(c=0; c<numTokens; c++)
-   {
-      strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
-   }
-#endif
-   for(path : paths)
-   {
-      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, name);
-      if(FileExists(p).isFile)
-      {
-         ShellOpen(p);
-         break;
-      }
-   }
-   delete p;
-   delete v;
-   paths.Free();
-   delete paths;
+   char path[MAX_LOCATION];
+   if(GetInstalledFileOrFolder(name, null, path, { isDirectory = true }))
+      ShellOpen(path);
+}
+
+void FindAndShellOpenInstalledFile(const char * subdir, const char * name)
+{
+   char path[MAX_LOCATION];
+   if(GetInstalledFileOrFolder(subdir, name, path, { isFile = true }))
+      ShellOpen(path);
 }
 
 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
 {
    bool preserveRootFolder;
 
-   void OutFolder(char * folderPath, bool isRoot)
+   void OutFolder(const char * folderPath, bool isRoot)
    {
       if(!(preserveRootFolder && isRoot))
          RemoveDir(folderPath);
    }
 
-   bool OnFile(char * filePath)
+   bool OnFile(const char * filePath)
    {
       DeleteFile(filePath);
       return true;
@@ -3414,23 +3778,84 @@ class IDEApp : GuiApplication
    //skin = "TVision";
 
    TempFile includeFile { };
+   int argFilesCount;
 
    bool Init()
    {
+      char ext[MAX_EXTENSION];
       SetLoggingMode(stdOut, null);
       //SetLoggingMode(debug, null);
 
       settingsContainer.Load();
+
+      if(ideSettings.language)
+      {
+         const String language = GetLanguageString();
+         if(ideSettings.language.OnCompare(language))
+         {
+            LanguageRestart(ideSettings.language, app, null, null, null, null, true);
+            return false;
+         }
+      }
+
+      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;
+         int c;
+         argFilesCount = 0;
+         for(c = 1; c<app.argc; c++)
+         {
+            if(passThrough);
+            else if(debugWorkDir)
+               debugWorkDir = false;
+            else if(!strcmp(app.argv[c], "-t"));
+            else if(!strcmp(app.argv[c], "-no-parsing"));
+            else if(!strcmp(app.argv[c], "-debug-start"));
+            else if(!strcmp(app.argv[c], "-debug-work-dir"))
+               debugWorkDir = true;
+            else if(!strcmp(app.argv[c], "-@"))
+               passThrough = true;
+            else
+               argFilesCount++;
+         }
+      }
+
+      if(app.argFilesCount > 0 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
+      {
+         app.driver = "OpenGL";
+         ide.driverItems[1].checked = true;
+      }
+      else
+      {
 #if defined(__unix__) || defined(__APPLE__)
-      app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
+         app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
 #else
-      app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
+         app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
 #endif
-      ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
+         ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
+      }
+
+      {
+         char model[MAX_LOCATION];
+         if(GetInstalledFileOrFolder("samples", "3D/ModelViewer/models/duck/duck.3DS", model, { isFile = true }))
+         {
+            ide.duck.modelFile = model;
+            ide.duck.parent = ideMainFrame;
+         }
+      }
+      if(ide.duck.modelFile && !strcmpi(app.driver, "OpenGL"))
+         ide.debugRubberDuck.disabled = false;
 
       SetInIDE(true);
 
-      desktop.text = titleECEREIDE;
+      desktop.caption = titleECEREIDE;
       /*
       int c;
       for(c = 1; c<app.argc; c++)
@@ -3438,13 +3863,191 @@ class IDEApp : GuiApplication
          char fullPath[MAX_LOCATION];
          GetWorkingDir(fullPath, MAX_LOCATION);
          PathCat(fullPath, app.argv[c]);
-         ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
+         ide.OpenFile(fullPath, app.argFilesCount > 1, true, null, yes, normal, false);
       }
       */
 
+      // Default to language specified by environment if no language selected
+      if(!ideSettings.language)
+      {
+         ideSettings.language = GetLanguageString();
+         settingsContainer.Save();
+      }
+
+      // Default to home directory if no directory yet set up
+      if(!ideSettings.ideProjectFileDialogLocation[0])
+      {
+         bool found = false;
+         char location[MAX_LOCATION];
+         char * home = getenv("HOME");
+         char * homeDrive = getenv("HOMEDRIVE");
+         char * homePath = getenv("HOMEPATH");
+         char * userProfile = getenv("USERPROFILE");
+         char * systemDrive = getenv("SystemDrive");
+         if(home && FileExists(home).isDirectory)
+         {
+            strcpy(location, home);
+            found = true;
+         }
+         if(!found && homeDrive && homePath)
+         {
+            strcpy(location, homeDrive);
+            PathCat(location, homePath);
+            if(FileExists(location).isDirectory)
+               found = true;
+         }
+         if(!found && FileExists(userProfile).isDirectory)
+         {
+            strcpy(location, userProfile);
+            found = true;
+         }
+         if(!found && FileExists(systemDrive).isDirectory)
+         {
+            strcpy(location, systemDrive);
+            found = true;
+         }
+         if(found)
+         {
+            ideSettings.ideProjectFileDialogLocation = location;
+            if(!ideSettings.ideFileDialogLocation[0])
+               ideSettings.ideFileDialogLocation = location;
+         }
+      }
+
       if(!LoadIncludeFile())
-         PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
+         PrintLn($"error: unable to load :crossplatform.mk file inside ide binary.");
+
+      // Create language menu
+      {
+         String language = ideSettings.language;
+         int i = 0;
+         bool found = false;
+
+         ide.languageItems = new MenuItem[languages.count];
+         for(l : languages)
+         {
+            ide.languageItems[i] =
+            {
+               ide.languageMenu, l.name;
+               bitmap = { l.bitmap };
+               id = i;
+               isRadio = true;
+
+               bool Window::NotifySelect(MenuItem selection, Modifiers mods)
+               {
+                  if(!LanguageRestart(languages[(int)selection.id].code, app, ideSettings, settingsContainer, ide, ide.projectView, false))
+                  {
+                     // Re-select previous selected language if aborted
+                     String language = ideSettings.language;
+                     int i = 0;
+                     for(l : languages)
+                     {
+                        if(((!language || !language[0]) && i == 0) ||
+                           (language && !strcmpi(l.code, language)))
+                        {
+                           ide.languageItems[i].checked = true;
+                           break;
+                        }
+                        i++;
+                     }
+                  }
+                  return true;
+               }
+            };
+            i++;
+         }
+
+         // Try to find country-specific language first
+         if(language)
+         {
+            i = 0;
+            for(l : languages)
+            {
+               if(!strcmpi(l.code, language) || (i == 0 && !strcmpi("en", language)))
+               {
+                  ide.languageItems[i].checked = true;
+                  found = true;
+                  break;
+               }
+               i++;
+            }
+         }
+
+         // Try generalizing locale
+         if(!found && language)
+         {
+            char * under;
+            char genericLocale[256];
+            i = 0;
+            strncpy(genericLocale, language, sizeof(genericLocale));
+            genericLocale[sizeof(genericLocale)-1] = 0;
+
+            under = strchr(genericLocale, '_');
+            if(under)
+               *under = 0;
+            if(!strcmpi(genericLocale, "zh"))
+               strcpy(genericLocale, "zh_CN");
+            if(strcmp(genericLocale, language))
+            {
+               for(l : languages)
+               {
+                  if(!strcmpi(l.code, genericLocale) || (i == 0 && !strcmpi("en", genericLocale)))
+                  {
+                     ide.languageItems[i].checked = true;
+                     found = true;
+                     break;
+                  }
+                  i++;
+               }
+            }
+         }
+
+         if(!found)
+            ide.languageItems[0].checked = true;
+
+         MenuDivider { ide.languageMenu };
+         MenuItem
+         {
+            ide.languageMenu, "Help Translate";
 
+            bool Window::NotifySelect(MenuItem selection, Modifiers mods)
+            {
+               ShellOpen("http://translations.launchpad.net/ecere");
+               return true;
+            }
+         };
+      }
+
+      ideMainFrame.Create();
+      if(app.argFilesCount > 1)
+         ide.MenuWindowTileVert(null, 0);
+      return true;
+   }
+
+   bool Cycle(bool idle)
+   {
+      if(ide.documentor)
+      {
+         if(ide.documentor.Peek())
+         {
+            char line[1024];
+            ide.documentor.GetLine(line, sizeof(line));
+            if(!strcmpi(line, "Exited"))
+            {
+               ide.documentor.CloseInput();
+               ide.documentor.CloseOutput();
+               ide.documentor.Wait();
+               delete ide.documentor;
+            }
+         }
+         if(ide.documentor && ide.documentor.eof)
+         {
+            ide.documentor.CloseInput();
+            ide.documentor.CloseOutput();
+            ide.documentor.Wait();
+            delete ide.documentor;
+         }
+      }
       return true;
    }
 
@@ -3475,7 +4078,7 @@ IDEMainFrame ideMainFrame { };
 
 define app = ((IDEApp)__thisModule);
 #ifdef _DEBUG
-define titleECEREIDE = $"ECERE IDE (Debug)";
+define titleECEREIDE = $"Ecere IDE (Debug)";
 #else
-define titleECEREIDE = $"ECERE IDE";
+define titleECEREIDE = $"Ecere IDE";
 #endif