ide: Make Escape in CodeEditor only fill up space if a ProjectView is opened
[sdk] / ide / src / ide.ec
index 4670aca..82b58e4 100644 (file)
@@ -44,6 +44,15 @@ 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
@@ -195,7 +204,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,7 +218,7 @@ class IDEToolbar : ToolBar
 
    ToolSeparator separator1 { this };
 
-   /* Edit options */
+   // Edit options
    // Cut
    // Copy
    // Paste
@@ -218,7 +227,7 @@ class IDEToolbar : ToolBar
 
    // ToolSeparator separator2 { this };
 
-   /* Project  options */
+   // Project options
    // New project
    ToolButton buttonNewProject { this, toolTip = $"New project", menuItemPtr = IDEItem(projectNewItem) };
    // Open project
@@ -230,7 +239,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
@@ -252,7 +261,7 @@ class IDEToolbar : ToolBar
 
    ToolSeparator separator4 { this };
 
-   /* Debug options */
+   // Debug options
    // Start/Resume
    ToolButton buttonDebugStartResume { this, toolTip = $"Start", menuItemPtr = IDEItem(debugStartResumeItem), disabled = true; };
    // Restart
@@ -344,7 +353,6 @@ class IDEToolbar : ToolBar
       activeBitDepth.AddString("64 bit").tag = 64;
       activeBitDepth.currentRow = row;
    }
-
 }
 
 class IDEMainFrame : Window
@@ -429,12 +437,12 @@ class IDEWorkSpace : Window
    {
       parent = this;
 
-      void OnGotoError(char * line, bool noParsing)
+      void OnGotoError(const char * line, bool noParsing)
       {
          ide.GoToError(line, noParsing);
       }
 
-      void OnCodeLocationParseAndGoTo(char * line)
+      void OnCodeLocationParseAndGoTo(const char * line)
       {
          ide.CodeLocationParseAndGoTo(line, ide.findInFilesDialog.findProject, ide.findInFilesDialog.findDir);
       }
@@ -515,7 +523,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;
@@ -523,8 +531,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
@@ -627,7 +635,7 @@ class IDEWorkSpace : Window
             document.NotifySaved = DocumentSaved;
             return true;
          }
-      }
+      };
       MenuItem fileOpenItem
       {
          fileMenu, $"Open...", o, ctrlO;
@@ -643,7 +651,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++)
                   {
@@ -665,7 +673,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
       MenuDivider { fileMenu };
       MenuItem fileSaveItem
@@ -693,7 +701,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuItem replaceInFiles
       {
          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
@@ -703,7 +711,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       MenuItem globalSettingsItem
       {
@@ -718,7 +726,7 @@ class IDEWorkSpace : Window
             globalSettingsDialog.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       Menu recentFiles { fileMenu, $"Recent Files", r };
       Menu recentProjects { fileMenu, $"Recent Projects", p };
@@ -817,7 +825,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectOpenItem
       {
          projectMenu, $"Open...", o, Key { o, true, true };
@@ -835,7 +843,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectQuickItem
       {
          projectMenu, $"Quick...", q, f7, disabled = true;
@@ -845,7 +853,7 @@ class IDEWorkSpace : Window
                QuickProjectDialog { this }.Modal();
             return true;
          }
-      }
+      };
       MenuItem projectAddItem
       {
          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
@@ -874,7 +882,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCloseItem
       {
          projectMenu, $"Close", c, disabled = true;
@@ -887,7 +895,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectSettingsItem
       {
@@ -897,7 +905,7 @@ class IDEWorkSpace : Window
             projectView.MenuSettings(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectBrowseFolderItem
       {
@@ -908,7 +916,7 @@ class IDEWorkSpace : Window
                projectView.MenuBrowseFolder(null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectRunItem
       {
@@ -920,7 +928,7 @@ class IDEWorkSpace : Window
                projectView.Run(null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectBuildItem
       {
          projectMenu, $"Build", b, f7, disabled = true;
@@ -936,7 +944,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectLinkItem
       {
          projectMenu, $"Relink", l, disabled = true;
@@ -947,7 +955,7 @@ class IDEWorkSpace : Window
                projectView.ProjectLink(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectRebuildItem
       {
          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
@@ -958,7 +966,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCleanTargetItem
       {
          projectMenu, $"Clean Target", g, disabled = true;
@@ -972,7 +980,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCleanItem
       {
          projectMenu, $"Clean", e, disabled = true;
@@ -986,7 +994,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRealCleanItem
       {
          projectMenu, $"Real Clean", disabled = true;
@@ -1000,7 +1008,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRegenerateItem
       {
          projectMenu, $"Regenerate Makefile", m, disabled = true;
@@ -1011,7 +1019,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectInstallItem
       {
 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
@@ -1024,7 +1032,7 @@ class IDEWorkSpace : Window
                projectView.ProjectInstall(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCompileItem;
    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
       MenuItem debugStartResumeItem
@@ -1032,7 +1040,7 @@ class IDEWorkSpace : Window
          debugMenu, $"Start", s, f5, disabled = true;
          bitmap = { ":actions/debug.png" };
          NotifySelect = MenuDebugStart;
-      }
+      };
       bool MenuDebugStart(MenuItem selection, Modifiers mods)
       {
          if(projectView)
@@ -1059,7 +1067,7 @@ class IDEWorkSpace : Window
                projectView.DebugRestart();
             return true;
          }
-      }
+      };
       MenuItem debugBreakItem
       {
          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
@@ -1072,7 +1080,7 @@ class IDEWorkSpace : Window
                projectView.DebugBreak();
             return true;
          }
-      }
+      };
       MenuItem debugStopItem
       {
          debugMenu, $"Stop", p, shiftF5, disabled = true;
@@ -1083,7 +1091,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
@@ -1099,12 +1161,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)
@@ -1127,14 +1189,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)
@@ -1184,7 +1246,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepInto();
             return true;
          }
-      }
+      };
       MenuItem debugStepOverItem
       {
          debugMenu, $"Step Over", v, f10, disabled = true;
@@ -1194,7 +1256,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(false);
             return true;
          }
-      }
+      };
       MenuItem debugSkipStepOverItem
       {
          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
@@ -1204,7 +1266,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(true);
             return true;
          }
-      }
+      };
       MenuItem debugStepOutItem
       {
          debugMenu, $"Step Out", o, shiftF11, disabled = true;
@@ -1214,7 +1276,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;
@@ -1224,7 +1286,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOut(true);
             return true;
          }
-      }
+      };
 #if 0
       MenuItem debugStepUntilItem
       {
@@ -1234,7 +1296,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;
@@ -1243,7 +1305,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 };
@@ -1271,7 +1333,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuPlacement { viewMenu, $"View Designer" };
       MenuPlacement { viewMenu, $"View Code" };
       MenuPlacement { viewMenu, $"View Properties" };
@@ -1292,7 +1354,7 @@ class IDEWorkSpace : Window
                ((CodeEditor)client).ViewDesigner();
             return true;
          }
-      }
+      };
       MenuItem viewCodeItem
       {
          viewMenu, $"View Code", c, f8;
@@ -1308,7 +1370,7 @@ class IDEWorkSpace : Window
             client.visible = true;
             return true;
          }
-      }
+      };
       MenuItem viewPropertiesItem
       {
          viewMenu, $"View Properties", p, f4;
@@ -1319,7 +1381,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewMethodsItem
       {
          viewMenu, $"View Methods", m, f4;
@@ -1330,7 +1392,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewToolBoxItem
       {
          viewMenu, $"View Toolbox", x, f12;
@@ -1340,7 +1402,7 @@ class IDEWorkSpace : Window
             toolBox.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewOutputItem
       {
          viewMenu, $"Output", o, alt2;
@@ -1349,7 +1411,7 @@ class IDEWorkSpace : Window
             outputView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewWatchesItem
       {
          viewMenu, $"Watches", w, alt3;
@@ -1358,7 +1420,7 @@ class IDEWorkSpace : Window
             watchesView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewThreadsItem
       {
          viewMenu, $"Threads", t, alt4;
@@ -1367,7 +1429,7 @@ class IDEWorkSpace : Window
             threadsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewBreakpointsItem
       {
          viewMenu, $"Breakpoints", b, alt5;
@@ -1376,7 +1438,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewCallStackItem
       {
          viewMenu, $"Call Stack", s, alt7;
@@ -1385,7 +1447,7 @@ class IDEWorkSpace : Window
             callStackView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewAllDebugViews
       {
          viewMenu, $"All Debug Views", a, alt9;
@@ -1398,7 +1460,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
 #ifdef GDB_DEBUG_GUI
       MenuDivider { viewMenu };
       MenuItem viewGDBItem
@@ -1409,7 +1471,7 @@ class IDEWorkSpace : Window
             gdbDialog.Show();
             return true;
          }
-      }
+      };
 #endif
       MenuDivider { viewMenu };
       MenuItem viewColorPicker
@@ -1421,7 +1483,7 @@ class IDEWorkSpace : Window
             colorPicker.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { viewMenu };
       /*
       MenuItem
@@ -1484,7 +1546,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1494,7 +1556,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1504,7 +1566,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("doc");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Samples Folder", s;
@@ -1513,7 +1575,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("samples");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Extras Folder", x;
@@ -1522,7 +1584,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("extras");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1532,7 +1594,7 @@ class IDEWorkSpace : Window
             ShellOpen("http://ecere.com/forums");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1542,7 +1604,7 @@ class IDEWorkSpace : Window
             AboutIDE { master = this }.Modal();
             return true;
          }
-      }
+      };
 
    property ToolBox toolBox
    {
@@ -1579,7 +1641,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);
@@ -1598,6 +1660,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;
@@ -1627,7 +1691,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
@@ -1693,7 +1757,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;
@@ -1716,7 +1779,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)
             {
@@ -1727,6 +1791,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?
       }
    }
 
@@ -1746,7 +1811,7 @@ class IDEWorkSpace : Window
       return false;
    }
 
-   void DocumentSaved(Window document, char * fileName)
+   void DocumentSaved(Window document, const char * fileName)
    {
       ideSettings.AddRecentFile(fileName);
       ide.UpdateRecentMenus();
@@ -1754,7 +1819,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"
@@ -2060,7 +2125,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;
@@ -2111,7 +2175,7 @@ class IDEWorkSpace : Window
       }
    }
 
-   void ChangeFileDialogsDirectory(char * directory, bool saveSettings)
+   void ChangeFileDialogsDirectory(const char * directory, bool saveSettings)
    {
       char tempString[MAX_LOCATION];
       strcpy(tempString, directory);
@@ -2132,14 +2196,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;
@@ -2150,7 +2214,7 @@ class IDEWorkSpace : Window
       return null;
    }
 
-   bool DontTerminateDebugSession(char * title)
+   bool DontTerminateDebugSession(const char * title)
    {
       if(debugger.isActive)
       {
@@ -2174,14 +2238,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)
@@ -2196,7 +2260,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;
@@ -2223,7 +2287,6 @@ class IDEWorkSpace : Window
                {
                   for(;;)
                   {
-                     Project project;
                      Workspace workspace = null;
 
                      if(FileExists(filePath))
@@ -2239,12 +2302,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;
 
@@ -2375,7 +2436,7 @@ class IDEWorkSpace : Window
                   prj = LoadProject(filePath, null);
                   if(prj)
                   {
-                     char * activeConfigName = null;
+                     const char * activeConfigName = null;
                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
                      prj.StartMonitoring();
                      workspace.projects.Add(prj);
@@ -2610,7 +2671,7 @@ class IDEWorkSpace : Window
    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
    {
       char *s = null;
-      char *path = text;
+      const char *path = text;
       char *colon = strchr(text, ':');
       char filePath[MAX_LOCATION] = "";
       char completePath[MAX_LOCATION];
@@ -2755,7 +2816,7 @@ class IDEWorkSpace : Window
          char ext[MAX_EXTENSION];
          GetExtension(path, ext);
          strlwr(ext);
-         if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
+         if(binaryDocExt.Find(ext))
             ShellOpen(path);
          else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
          {
@@ -3039,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(" ");
@@ -3354,7 +3415,6 @@ class IDEWorkSpace : Window
    IDEWorkSpace()
    {
       // Graphics Driver Menu
-      int c;
 
       /*
       app.currentSkin.selectionColor = selectionColor;
@@ -3407,7 +3467,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;
@@ -3464,153 +3523,139 @@ 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])
+   v[0] = '\0';
+   if(found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      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("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])
-   {
-      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++)
-   {
-      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)
+#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++)
+   if(!found)
    {
-      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)
+      char * tokens[256];
+      int c, numTokens;
+
+      GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
+      numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
+      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;
       }
    }
-   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;
@@ -3637,7 +3682,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);
@@ -3667,7 +3712,7 @@ class IDEApp : GuiApplication
          }
       }
 
-      if(app.argFilesCount > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
+      if(app.argFilesCount > 0 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
       {
          app.driver = "OpenGL";
          ide.driverItems[1].checked = true;
@@ -3682,6 +3727,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;
@@ -3744,7 +3800,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
       {
@@ -3809,7 +3865,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)