ide: add 'force single job compiling' checkbox to the toolbar.
[sdk] / ide / src / ide.ec
old mode 100644 (file)
new mode 100755 (executable)
index e7df724..e02a253
@@ -44,7 +44,7 @@ import "about"
 
 import "FileSystemIterator"
 
-static AVLTree<String> binaryDocExt
+AVLTree<const String> binaryDocExt
 { [
    "wav", "mp3", "flac", "ogg",
    "mid",
@@ -303,11 +303,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;
+            ide.workspace.activeCompiler = row.string;
             ide.projectView.ShowOutputBuildLog(!silent);
             if(!silent)
                ide.projectView.DisplayCompiler(compiler, false);
@@ -328,7 +328,7 @@ class IDEToolbar : ToolBar
          if(ide.workspace && ide.projectView && row)
          {
             bool silent = ide.projectView.buildInProgress == none ? false : true;
-            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.activeCompiler);
             ide.workspace.bitDepth = (int)row.tag;
             ide.projectView.ShowOutputBuildLog(!silent);
             if(!silent)
@@ -342,6 +342,10 @@ class IDEToolbar : ToolBar
       }
    };
 
+   Window spacer65 { this, size = { 4 } };
+
+   Button forceSingleJob { this, text = $"Force Single Job Compiling", isCheckbox = true; };
+
    Window spacer7 { this, size = { 4 } };
 
    void IDEToolbar()
@@ -437,14 +441,18 @@ class IDEWorkSpace : Window
    {
       parent = this;
 
-      void OnGotoError(char * line, bool noParsing)
+      void OnGotoError(const char * line, bool noParsing)
       {
-         ide.GoToError(line, noParsing);
+         CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.activeCompiler) : null;
+         const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
+         ide.GoToError(line, noParsing, objectFileExt);
       }
 
-      void OnCodeLocationParseAndGoTo(char * line)
+      void OnCodeLocationParseAndGoTo(const char * line)
       {
-         ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir);
+         CompilerConfig compiler = ide.workspace ? ideSettings.GetCompilerConfig(ide.workspace.activeCompiler) : null;
+         const char * objectFileExt = compiler ? compiler.objectFileExt : objectDefaultFileExt;
+         ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir, objectFileExt);
       }
 
       bool OnKeyDown(Key key, unichar ch)
@@ -523,7 +531,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;
@@ -531,8 +539,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
@@ -635,7 +643,7 @@ class IDEWorkSpace : Window
             document.NotifySaved = DocumentSaved;
             return true;
          }
-      }
+      };
       MenuItem fileOpenItem
       {
          fileMenu, $"Open...", o, ctrlO;
@@ -651,7 +659,7 @@ 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++)
                   {
@@ -673,7 +681,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
       MenuDivider { fileMenu };
       MenuItem fileSaveItem
@@ -701,7 +709,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuItem replaceInFiles
       {
          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
@@ -711,7 +719,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       MenuItem globalSettingsItem
       {
@@ -719,14 +727,14 @@ class IDEWorkSpace : Window
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
             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 };
@@ -825,7 +833,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectOpenItem
       {
          projectMenu, $"Open...", o, Key { o, true, true };
@@ -843,7 +851,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectQuickItem
       {
          projectMenu, $"Quick...", q, f7, disabled = true;
@@ -853,7 +861,7 @@ class IDEWorkSpace : Window
                QuickProjectDialog { this }.Modal();
             return true;
          }
-      }
+      };
       MenuItem projectAddItem
       {
          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
@@ -882,7 +890,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCloseItem
       {
          projectMenu, $"Close", c, disabled = true;
@@ -895,7 +903,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectSettingsItem
       {
@@ -905,7 +913,7 @@ class IDEWorkSpace : Window
             projectView.MenuSettings(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectBrowseFolderItem
       {
@@ -916,7 +924,7 @@ class IDEWorkSpace : Window
                projectView.MenuBrowseFolder(null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectRunItem
       {
@@ -928,7 +936,7 @@ class IDEWorkSpace : Window
                projectView.Run(null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectBuildItem
       {
          projectMenu, $"Build", b, f7, disabled = true;
@@ -944,7 +952,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectLinkItem
       {
          projectMenu, $"Relink", l, disabled = true;
@@ -955,7 +963,7 @@ class IDEWorkSpace : Window
                projectView.ProjectLink(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectRebuildItem
       {
          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
@@ -966,7 +974,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCleanTargetItem
       {
          projectMenu, $"Clean Target", g, disabled = true;
@@ -980,7 +988,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCleanItem
       {
          projectMenu, $"Clean", e, disabled = true;
@@ -994,7 +1002,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRealCleanItem
       {
          projectMenu, $"Real Clean", disabled = true;
@@ -1008,7 +1016,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRegenerateItem
       {
          projectMenu, $"Regenerate Makefile", m, disabled = true;
@@ -1019,7 +1027,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectInstallItem
       {
 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
@@ -1032,7 +1040,7 @@ class IDEWorkSpace : Window
                projectView.ProjectInstall(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCompileItem;
    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
       MenuItem debugStartResumeItem
@@ -1040,7 +1048,7 @@ class IDEWorkSpace : Window
          debugMenu, $"Start", s, f5, disabled = true;
          bitmap = { ":actions/debug.png" };
          NotifySelect = MenuDebugStart;
-      }
+      };
       bool MenuDebugStart(MenuItem selection, Modifiers mods)
       {
          if(projectView)
@@ -1067,7 +1075,7 @@ class IDEWorkSpace : Window
                projectView.DebugRestart();
             return true;
          }
-      }
+      };
       MenuItem debugBreakItem
       {
          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
@@ -1080,7 +1088,7 @@ class IDEWorkSpace : Window
                projectView.DebugBreak();
             return true;
          }
-      }
+      };
       MenuItem debugStopItem
       {
          debugMenu, $"Stop", p, shiftF5, disabled = true;
@@ -1091,7 +1099,61 @@ 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
@@ -1107,12 +1169,12 @@ class IDEWorkSpace : Window
             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; }
+         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)
@@ -1135,14 +1197,14 @@ class IDEWorkSpace : Window
             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; }
+         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)
@@ -1192,7 +1254,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepInto();
             return true;
          }
-      }
+      };
       MenuItem debugStepOverItem
       {
          debugMenu, $"Step Over", v, f10, disabled = true;
@@ -1202,7 +1264,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(false);
             return true;
          }
-      }
+      };
       MenuItem debugSkipStepOverItem
       {
          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
@@ -1212,7 +1274,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(true);
             return true;
          }
-      }
+      };
       MenuItem debugStepOutItem
       {
          debugMenu, $"Step Out", o, shiftF11, disabled = true;
@@ -1222,7 +1284,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOut(false);
             return true;
          }
-      }
+      };
       MenuItem debugSkipStepOutItem
       {
          debugMenu, $"Step Out Skipping Breakpoints", n, Key { f11, ctrl = true, shift = true }, disabled = true;
@@ -1232,7 +1294,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOut(true);
             return true;
          }
-      }
+      };
 #if 0
       MenuItem debugStepUntilItem
       {
@@ -1242,7 +1304,7 @@ class IDEWorkSpace : Window
             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;
@@ -1251,7 +1313,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepUntil(true);
             return true;
          }
-      }
+      };
 #endif
       MenuPlacement debugRunToCursorItem { debugMenu, $"Run To Cursor", c };
       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
@@ -1279,7 +1341,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuPlacement { viewMenu, $"View Designer" };
       MenuPlacement { viewMenu, $"View Code" };
       MenuPlacement { viewMenu, $"View Properties" };
@@ -1300,7 +1362,7 @@ class IDEWorkSpace : Window
                ((CodeEditor)client).ViewDesigner();
             return true;
          }
-      }
+      };
       MenuItem viewCodeItem
       {
          viewMenu, $"View Code", c, f8;
@@ -1316,7 +1378,7 @@ class IDEWorkSpace : Window
             client.visible = true;
             return true;
          }
-      }
+      };
       MenuItem viewPropertiesItem
       {
          viewMenu, $"View Properties", p, f4;
@@ -1327,7 +1389,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewMethodsItem
       {
          viewMenu, $"View Methods", m, f4;
@@ -1338,7 +1400,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewToolBoxItem
       {
          viewMenu, $"View Toolbox", x, f12;
@@ -1348,7 +1410,7 @@ class IDEWorkSpace : Window
             toolBox.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewOutputItem
       {
          viewMenu, $"Output", o, alt2;
@@ -1357,7 +1419,7 @@ class IDEWorkSpace : Window
             outputView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewWatchesItem
       {
          viewMenu, $"Watches", w, alt3;
@@ -1366,7 +1428,7 @@ class IDEWorkSpace : Window
             watchesView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewThreadsItem
       {
          viewMenu, $"Threads", t, alt4;
@@ -1375,7 +1437,7 @@ class IDEWorkSpace : Window
             threadsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewBreakpointsItem
       {
          viewMenu, $"Breakpoints", b, alt5;
@@ -1384,7 +1446,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewCallStackItem
       {
          viewMenu, $"Call Stack", s, alt7;
@@ -1393,7 +1455,7 @@ class IDEWorkSpace : Window
             callStackView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewAllDebugViews
       {
          viewMenu, $"All Debug Views", a, alt9;
@@ -1406,7 +1468,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
 #ifdef GDB_DEBUG_GUI
       MenuDivider { viewMenu };
       MenuItem viewGDBItem
@@ -1417,7 +1479,7 @@ class IDEWorkSpace : Window
             gdbDialog.Show();
             return true;
          }
-      }
+      };
 #endif
       MenuDivider { viewMenu };
       MenuItem viewColorPicker
@@ -1429,7 +1491,7 @@ class IDEWorkSpace : Window
             colorPicker.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { viewMenu };
       /*
       MenuItem
@@ -1492,7 +1554,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1502,7 +1564,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1512,7 +1574,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("doc");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Samples Folder", s;
@@ -1521,7 +1583,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("samples");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Extras Folder", x;
@@ -1530,7 +1592,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("extras");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1540,7 +1602,7 @@ class IDEWorkSpace : Window
             ShellOpen("http://ecere.com/forums");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1550,7 +1612,7 @@ class IDEWorkSpace : Window
             AboutIDE { master = this }.Modal();
             return true;
          }
-      }
+      };
 
    property ToolBox toolBox
    {
@@ -1580,6 +1642,8 @@ class IDEWorkSpace : Window
    };
 
    bool noParsing;
+   bool debugStart;
+   bool debugHideIDE;
 
 #ifdef GDB_DEBUG_GUI
    GDBDialog gdbDialog
@@ -1587,7 +1651,7 @@ class IDEWorkSpace : Window
       master = this, parent = this;
       //anchor = { left = 100, top = 100, right = 100, bottom = 100 };
 
-      void OnCommand(char * string)
+      void OnCommand(const char * string)
       {
          if(ide)
             ide.debugger.SendGDBCommand(string);
@@ -1606,6 +1670,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;
@@ -1635,7 +1701,7 @@ class IDEWorkSpace : Window
          }
    }
 
-   ProjectView CreateProjectView(Workspace workspace, char * fileName)
+   ProjectView CreateProjectView(Workspace workspace, const char * fileName)
    {
       Project project = workspace.projects.firstIterator.data;
       projectView = ProjectView
@@ -1701,7 +1767,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;
@@ -1724,7 +1789,8 @@ class IDEWorkSpace : Window
                   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)
             {
@@ -1735,6 +1801,7 @@ class IDEWorkSpace : Window
          }
          // 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?
       }
    }
 
@@ -1754,7 +1821,7 @@ class IDEWorkSpace : Window
       return false;
    }
 
-   void DocumentSaved(Window document, char * fileName)
+   void DocumentSaved(Window document, const char * fileName)
    {
       ideSettings.AddRecentFile(fileName);
       ide.UpdateRecentMenus();
@@ -1762,7 +1829,7 @@ class IDEWorkSpace : Window
       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"
@@ -1795,7 +1862,7 @@ class IDEWorkSpace : Window
    {
       if(workspace)
       {
-         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.activeCompiler);
          for(prj : workspace.projects)
             projectView.ProjectUpdateMakefileForAllConfigs(prj);
          delete compiler;
@@ -1808,7 +1875,7 @@ class IDEWorkSpace : Window
       if(workspace)
       {
          bool silent = mute || (ide.projectView.buildInProgress == none ? false : true);
-         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
+         CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.activeCompiler);
          if(!silent)
          {
             projectView.ShowOutputBuildLog(true);
@@ -1826,11 +1893,12 @@ class IDEWorkSpace : Window
       for(compiler : ideSettings.compilerConfigs)
       {
          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)
@@ -2068,7 +2136,6 @@ class IDEWorkSpace : Window
       bool running = isDebuggerRunning;
       bool stopped = isDebuggerStopped;
       bool active = debugger.isActive;
-      bool noBreakpointToggle = !project;
 
       bool isNotRunning    = unavailable || !running;
       bool isNotNotRunning = unavailable || running;
@@ -2119,7 +2186,7 @@ class IDEWorkSpace : Window
       }
    }
 
-   void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
+   void ChangeFileDialogsDirectory(const char * directory, bool saveSettings)
    {
       char tempString[MAX_LOCATION];
       strcpy(tempString, directory);
@@ -2140,14 +2207,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;
@@ -2158,7 +2225,7 @@ class IDEWorkSpace : Window
       return null;
    }
 
-   bool DontTerminateDebugSession(char * title)
+   bool DontTerminateDebugSession(const char * title)
    {
       if(debugger.isActive)
       {
@@ -2182,14 +2249,14 @@ class IDEWorkSpace : Window
       return false;
    }
 
-   Window OpenFile(char * origFilePath, bool dontMaximize, 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)
@@ -2204,7 +2271,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;
@@ -2231,7 +2298,6 @@ class IDEWorkSpace : Window
                {
                   for(;;)
                   {
-                     Project project;
                      Workspace workspace = null;
 
                      if(FileExists(filePath))
@@ -2247,12 +2313,10 @@ 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;
 
@@ -2266,7 +2330,7 @@ class IDEWorkSpace : Window
 
                         ide.projectView.ShowOutputBuildLog(true);
                         {
-                           CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+                           CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.activeCompiler);
                            ide.projectView.DisplayCompiler(compiler, false);
                            delete compiler;
                         }
@@ -2285,23 +2349,8 @@ class IDEWorkSpace : Window
                         // this crashes on starting ide with epj file, solution please?
                         // app.UpdateDisplay();
 
+                        workspace.OpenPreviouslyOpenedFiles(noParsing);
                         workspace.holdTracking = true;
-                        for(ofi : workspace.openedFiles)
-                        {
-                           if(ofi.state != closed)
-                           {
-                              Window file = OpenFile(ofi.path, false, true, null, no, normal, noParsing);
-                              if(file)
-                              {
-                                 char fileName[MAX_LOCATION];
-                                 ProjectNode node;
-                                 GetLastDirectory(ofi.path, fileName);
-                                 node = projectView.project.topNode.Find(fileName, true);
-                                 if(node)
-                                    node.EnsureVisible();
-                              }
-                           }
-                        }
                         ide.RepositionWindows(false);
                         workspace.holdTracking = false;
 
@@ -2383,10 +2432,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 = ideSettings.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;
@@ -2509,21 +2558,7 @@ class IDEWorkSpace : Window
       if(document)
       {
          if(projectView && document._class == class(CodeEditor) && workspace)
-         {
-            int lineNumber, position;
-            Point scroll;
-            CodeEditor editor = (CodeEditor)document;
-            editor.openedFileInfo = workspace.UpdateOpenedFileInfo(filePath, opened);
-            editor.openedFileInfo.holdTracking = true;
-            lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
-            position = Max(editor.openedFileInfo.position - 1, 0);
-            if(editor.editBox.GoToLineNum(lineNumber))
-               editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
-            scroll.x = Max(editor.openedFileInfo.scroll.x, 0);
-            scroll.y = Max(editor.openedFileInfo.scroll.y, 0);
-            editor.editBox.scroll = scroll;
-            editor.openedFileInfo.holdTracking = false;
-         }
+            workspace.RestorePreviouslyOpenedFileState((CodeEditor)document);
 
          if(needFileModified)
             document.OnFileModified = OnFileModified;
@@ -2609,16 +2644,75 @@ class IDEWorkSpace : Window
       return true;
    }
 
-   void GoToError(const char * line, bool noParsing)
+   void GoToError(const char * line, bool noParsing, const char * objectFileExt)
    {
       if(projectView)
-         projectView.GoToError(line, noParsing);
+         projectView.GoToError(line, noParsing, objectFileExt);
+   }
+
+   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);
+                     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 *s = null;
-      char *path = text;
+      const char *path = text;
       char *colon = strchr(text, ':');
       char filePath[MAX_LOCATION] = "";
       char completePath[MAX_LOCATION];
@@ -2628,7 +2722,7 @@ class IDEWorkSpace : Window
       FileAttribs fileAttribs;
 
       // support for valgrind output
-      if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
+      if((s = strstr(text, "==")) && s == text && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
       {
          colon = s;
          for(; s>text; s--)
@@ -2710,50 +2804,8 @@ class IDEWorkSpace : Window
          strcpy(filePath, path);
       }
 
-      if(filePath[0])
-      {
-         if(prj)
-            strcpy(completePath, prj.topNode.path);
-         else if(dir && dir[0])
-            strcpy(completePath, dir);
-         else
-            completePath[0] = '\0';
-         PathCat(completePath, filePath);
-
-         if((fileAttribs = FileExists(completePath)))
-            CodeLocationGoTo(completePath, fileAttribs, line, col);
-         else if(ide.workspace)
-         {
-            bool done = false;
-            for(p : ide.workspace.projects)
-            {
-               strcpy(completePath, p.topNode.path);
-               PathCat(completePath, filePath);
-               if((fileAttribs = FileExists(completePath)).isFile)
-               {
-                  CodeLocationGoTo(completePath, fileAttribs, line, col);
-                  done = true;
-                  break;
-               }
-            }
-            if(!done)
-            {
-               for(p : ide.workspace.projects)
-               {
-                  ProjectNode node = p.topNode.Find(filePath, false);
-                  if(node)
-                  {
-                     node.GetFullFilePath(completePath);
-                     if((fileAttribs = FileExists(completePath)).isFile)
-                     {
-                        CodeLocationGoTo(completePath, fileAttribs, line, col);
-                        break;
-                     }
-                  }
-               }
-            }
-         }
-      }
+      if((fileAttribs = GoToCodeSelectFile(filePath, dir, prj, null, completePath, objectFileExt)))
+         CodeLocationGoTo(completePath, fileAttribs, line, col);
    }
 
    void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
@@ -2765,7 +2817,9 @@ class IDEWorkSpace : Window
          strlwr(ext);
          if(binaryDocExt.Find(ext))
             ShellOpen(path);
-         else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
+         else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "bc") ||
+               !strcmp(ext, "lib") || !strcmp(ext, "dll") ||
+               !strcmp(ext, "exe") || !strcmp(ext, "nexe") || !strcmp(ext, "pexe"))
          {
             char dirPath[MAX_LOCATION];
             StripLastDirectory(path, dirPath);
@@ -2773,8 +2827,8 @@ class IDEWorkSpace : Window
          }
          else
          {
-            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, ext, no, normal, false);
-            if(codeEditor && line)
+            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, !strcmpi(ext, "epj") ? "txt" : ext, no, normal, false);
+            if(codeEditor && codeEditor._class == class(CodeEditor) && line)
             {
                EditBox editBox = codeEditor.editBox;
                editBox.GoToLineNum(line - 1);
@@ -2995,7 +3049,7 @@ class IDEWorkSpace : Window
                               {
                                  List<ProjectNode> nodes { };
                                  nodes.Add(node);
-                                 projectView.Compile(node.project, nodes, mods.ctrl && mods.shift, isCObject ? cObject : normal);
+                                 projectView.Compile(node.project, nodes, false, false, isCObject ? cObject : normal);
                                  delete nodes;
                               }
                            }
@@ -3036,7 +3090,6 @@ class IDEWorkSpace : Window
    {
       int c;
       bool passThrough = false;
-      bool debugStart = false;
       bool debugWorkDir = false;
       char * passDebugWorkDir = null;
       bool openAsText = false;
@@ -3047,7 +3100,7 @@ class IDEWorkSpace : Window
       {
          if(passThrough)
          {
-            char * arg = app.argv[c];
+            const char * arg = app.argv[c];
             char * buf = new char[strlen(arg)*2+1];
             if(ptArg++ > 0)
                passArgs.concat(" ");
@@ -3066,7 +3119,9 @@ class IDEWorkSpace : Window
          else if(!strcmp(app.argv[c], "-no-parsing"))
             ide.noParsing = true;
          else if(!strcmp(app.argv[c], "-debug-start"))
-            debugStart = true;
+            ide.debugStart = true;
+         else if(!strcmp(app.argv[c], "-debug-hide-ide"))
+            ide.debugHideIDE = true;
          else if(!strcmp(app.argv[c], "-debug-work-dir"))
             debugWorkDir = true;
          else if(!strcmp(app.argv[c], "-@"))
@@ -3084,7 +3139,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) + (ide.debugHideIDE ? 1 : 0)) continue;
 
             // Create directory for projects (only)
             if(((dirAttribs = FileExists(parentPath)) && dirAttribs.isDirectory) || isProject)
@@ -3137,8 +3192,6 @@ class IDEWorkSpace : Window
          workspace.debugDir = passDebugWorkDir;
          delete passDebugWorkDir;
       }
-      if(debugStart)
-         ;//MenuDebugStart(debugStartResumeItem, 0); // <-- how TODO this without getting into the app.Wait lock
 
       UpdateToolBarActiveConfigs(false);
       UpdateToolBarActiveCompilers();
@@ -3233,17 +3286,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);
@@ -3362,7 +3419,6 @@ class IDEWorkSpace : Window
    IDEWorkSpace()
    {
       // Graphics Driver Menu
-      int c;
 
       /*
       app.currentSkin.selectionColor = selectionColor;
@@ -3415,7 +3471,6 @@ class IDEWorkSpace : Window
       Menu recentProjects = fileMenu.FindMenu($"Recent Projects");
       char * itemPath = new char[MAX_LOCATION];
       char * itemName = new char[MAX_LOCATION+4];
-      MenuItem item;
 
       recentFiles.Clear();
       c = 0;
@@ -3472,159 +3527,154 @@ 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("ProgramData", 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])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); 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, name); 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, name); 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++)
+   v[0] = '\0';
+   if(found)
    {
-      strncpy(p, tokens[c], 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;
    }
-#endif
-   for(path : paths)
+#if defined(__WIN32__)
+   if(!found)
    {
-      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      if(FileExists(p).isDirectory)
+      GetEnvironment("ECERE_SDK_SRC", v, maxPathLen);
+      if(v[0])
       {
-         ShellOpen(p);
-         break;
+         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
       }
    }
-   delete p;
-   delete v;
-   paths.Free();
-   delete paths;
-}
-
-void FindAndShellOpenInstalledFile(char * subdir, 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])
+   if(!found)
    {
-      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(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, sdkDirName);
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
+      }
    }
-   GetEnvironment("AppData", v, maxPathLen);
-   if(v[0])
+   if(!found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+      GetEnvironment("ProgramData", v, maxPathLen);
+      if(v[0])
+      {
+         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, sdkDirName);
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
+      }
    }
-   GetEnvironment("ProgramFiles", v, maxPathLen);
-   if(v[0])
+   if(!found)
    {
-      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(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, sdkDirName);
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
+      }
    }
-   GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
-   if(v[0])
+   if(!found)
    {
-      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(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, sdkDirName);
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
+      }
    }
-   GetEnvironment("SystemDrive", v, maxPathLen);
-   if(v[0])
+   if(!found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+      GetEnvironment("SystemDrive", v, maxPathLen);
+      if(v[0])
+      {
+         strncpy(path, v, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+         PathCat(path, "Program Files");
+         PathCat(path, sdkDirName);
+         PathCat(path, subDir);
+         if(name) PathCat(path, name);
+         if(FileExists(path) & attribs) found = true;
+      }
    }
 #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)
+   if(!found)
    {
-      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, name);
-      if(FileExists(p).isFile)
+      char * p = null;
+      char * tokens[256];
+      int c, numTokens;
+
+      GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
+      numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
+      if(!numTokens)
+      {
+         p = new char[MAX_LOCATION];
+         p[0] = '\0';
+         strcat(p, "/usr/share");
+         tokens[0] = p;
+         numTokens = 1;
+      }
+      for(c=0; c<numTokens; c++)
       {
-         ShellOpen(p);
-         break;
+         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 FindAndShellOpenInstalledFolder(const char * name)
+{
+   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;
@@ -3651,7 +3701,7 @@ class IDEApp : GuiApplication
 
       if(ideSettings.language)
       {
-         String language = GetLanguageString();
+         const String language = GetLanguageString();
          if(ideSettings.language.OnCompare(language))
          {
             LanguageRestart(ideSettings.language, app, null, null, null, null, true);
@@ -3672,6 +3722,7 @@ class IDEApp : GuiApplication
             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-hide-ide"));
             else if(!strcmp(app.argv[c], "-debug-work-dir"))
                debugWorkDir = true;
             else if(!strcmp(app.argv[c], "-@"))
@@ -3696,6 +3747,17 @@ class IDEApp : GuiApplication
          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.caption = titleECEREIDE;
@@ -3758,7 +3820,7 @@ class IDEApp : GuiApplication
       }
 
       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
       {
@@ -3823,7 +3885,7 @@ class IDEApp : GuiApplication
             char genericLocale[256];
             i = 0;
             strncpy(genericLocale, language, sizeof(genericLocale));
-            genericLocale[sizeof(genericLocale)] = 0;
+            genericLocale[sizeof(genericLocale)-1] = 0;
 
             under = strchr(genericLocale, '_');
             if(under)