ide: Make Escape in CodeEditor only fill up space if a ProjectView is opened
[sdk] / ide / src / ide.ec
index 8ce9871..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
@@ -404,7 +412,7 @@ class IDEWorkSpace : Window
    menu = Menu {  };
    IDEToolbar toolBar;
 
-   MenuItem * driverItems, * skinItems;
+   MenuItem * driverItems, * skinItems, * languageItems;
    StatusField pos { width = 150 };
    StatusField ovr, caps, num;
    DualPipe documentor;
@@ -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
@@ -607,8 +615,8 @@ class IDEWorkSpace : Window
    };
    BreakpointsView breakpointsView { parent = this };
 
-   ToolBox toolBox { parent = this };
-   Sheet sheet { parent = this };
+   ToolBox toolBox { parent = this, visible = false };
+   Sheet sheet { parent = this, visible = false };
 
    char * tmpPrjDir;
    property char * tmpPrjDir { set { delete tmpPrjDir; if(value) tmpPrjDir = CopyString(value); } get { return tmpPrjDir; } };
@@ -620,11 +628,14 @@ class IDEWorkSpace : Window
          bitmap = { ":actions/docNew.png" };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            Window document = (Window)NewCodeEditor(this, normal, false);
+            Window currentDoc = activeClient;
+            bool maximizeDoc = ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
+            Window document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, false);
+            RepositionWindows(false);
             document.NotifySaved = DocumentSaved;
             return true;
          }
-      }
+      };
       MenuItem fileOpenItem
       {
          fileMenu, $"Open...", o, ctrlO;
@@ -640,11 +651,11 @@ class IDEWorkSpace : Window
                   bool gotWhatWeWant = false;
                   int c;
                   int numSelections = ideFileDialog.numSelections;
-                  char ** multiFilePaths = ideFileDialog.multiFilePaths;
+                  const char * const * multiFilePaths = ideFileDialog.multiFilePaths;
 
                   for(c = 0; c < numSelections; c++)
                   {
-                     if(OpenFile(multiFilePaths[c], normal, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
+                     if(OpenFile(multiFilePaths[c], false, true, fileTypes[ideFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift))
                         gotWhatWeWant = true;
                   }
                   if(gotWhatWeWant ||
@@ -662,7 +673,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem fileCloseItem { fileMenu, $"Close", c, ctrlF4, NotifySelect = MenuFileClose };
       MenuDivider { fileMenu };
       MenuItem fileSaveItem
@@ -690,7 +701,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuItem replaceInFiles
       {
          fileMenu, $"Replace In Files...", e, Key { r, ctrl = true , shift = true };
@@ -700,7 +711,7 @@ class IDEWorkSpace : Window
             findInFilesDialog.Show();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       MenuItem globalSettingsItem
       {
@@ -715,7 +726,7 @@ class IDEWorkSpace : Window
             globalSettingsDialog.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { fileMenu };
       Menu recentFiles { fileMenu, $"Recent Files", r };
       Menu recentProjects { fileMenu, $"Recent Projects", p };
@@ -750,7 +761,7 @@ class IDEWorkSpace : Window
                }
                else
                {
-                  OpenFile(file, normal, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
+                  OpenFile(file, false, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
                   ide.RepositionWindows(false);
                }
                break;
@@ -774,7 +785,7 @@ class IDEWorkSpace : Window
                   delete command;
                }
                else
-                  OpenFile(file, normal, true, null, no, normal, mods.ctrl && mods.shift);
+                  OpenFile(file, false, true, null, no, normal, mods.ctrl && mods.shift);
                break;
             }
             id++;
@@ -814,7 +825,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectOpenItem
       {
          projectMenu, $"Open...", o, Key { o, true, true };
@@ -827,12 +838,12 @@ class IDEWorkSpace : Window
             ideProjectFileDialog.text = openProjectFileDialogTitle;
             if(ideProjectFileDialog.Modal() == ok)
             {
-               OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
+               OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, normal, mods.ctrl && mods.shift);
                //ChangeProjectFileDialogDirectory(ideProjectFileDialog.currentDirectory);
             }
             return true;
          }
-      }
+      };
       MenuItem projectQuickItem
       {
          projectMenu, $"Quick...", q, f7, disabled = true;
@@ -842,7 +853,7 @@ class IDEWorkSpace : Window
                QuickProjectDialog { this }.Modal();
             return true;
          }
-      }
+      };
       MenuItem projectAddItem
       {
          projectMenu, $"Add project to workspace...", a, Key { a, true, true };
@@ -858,7 +869,7 @@ class IDEWorkSpace : Window
             {
                if(ideProjectFileDialog.Modal() == ok)
                {
-                  if(OpenFile(ideProjectFileDialog.filePath, normal, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
+                  if(OpenFile(ideProjectFileDialog.filePath, false, true, projectTypes[ideProjectFileDialog.fileType].typeExtension, no, add, mods.ctrl && mods.shift))
                      break;
                   if(MessageBox { type = yesNo, master = this, text = $"Error opening project file",
                         contents = $"Add a different project?" }.Modal() == no)
@@ -871,7 +882,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCloseItem
       {
          projectMenu, $"Close", c, disabled = true;
@@ -884,7 +895,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectSettingsItem
       {
@@ -894,7 +905,7 @@ class IDEWorkSpace : Window
             projectView.MenuSettings(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectBrowseFolderItem
       {
@@ -905,7 +916,7 @@ class IDEWorkSpace : Window
                projectView.MenuBrowseFolder(null, mods);
             return true;
          }
-      }
+      };
       MenuDivider { projectMenu };
       MenuItem projectRunItem
       {
@@ -917,7 +928,7 @@ class IDEWorkSpace : Window
                projectView.Run(null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectBuildItem
       {
          projectMenu, $"Build", b, f7, disabled = true;
@@ -933,7 +944,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectLinkItem
       {
          projectMenu, $"Relink", l, disabled = true;
@@ -944,7 +955,7 @@ class IDEWorkSpace : Window
                projectView.ProjectLink(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectRebuildItem
       {
          projectMenu, $"Rebuild", d, shiftF7, disabled = true;
@@ -955,7 +966,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRebuild(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectCleanTargetItem
       {
          projectMenu, $"Clean Target", g, disabled = true;
@@ -969,7 +980,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectCleanItem
       {
          projectMenu, $"Clean", e, disabled = true;
@@ -983,7 +994,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRealCleanItem
       {
          projectMenu, $"Real Clean", disabled = true;
@@ -997,7 +1008,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuItem projectRegenerateItem
       {
          projectMenu, $"Regenerate Makefile", m, disabled = true;
@@ -1008,7 +1019,7 @@ class IDEWorkSpace : Window
                projectView.ProjectRegenerate(projectView.active ? selection : null, mods);
             return true;
          }
-      }
+      };
       MenuItem projectInstallItem
       {
 #ifdef IDE_SHOW_INSTALL_MENU_BUTTON
@@ -1021,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
@@ -1029,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)
@@ -1056,7 +1067,7 @@ class IDEWorkSpace : Window
                projectView.DebugRestart();
             return true;
          }
-      }
+      };
       MenuItem debugBreakItem
       {
          debugMenu, $"Break", b, Key { pauseBreak, ctrl = true }, disabled = true;
@@ -1069,7 +1080,7 @@ class IDEWorkSpace : Window
                projectView.DebugBreak();
             return true;
          }
-      }
+      };
       MenuItem debugStopItem
       {
          debugMenu, $"Stop", p, shiftF5, disabled = true;
@@ -1080,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
@@ -1096,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)
@@ -1124,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)
@@ -1181,7 +1246,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepInto();
             return true;
          }
-      }
+      };
       MenuItem debugStepOverItem
       {
          debugMenu, $"Step Over", v, f10, disabled = true;
@@ -1191,7 +1256,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(false);
             return true;
          }
-      }
+      };
       MenuItem debugSkipStepOverItem
       {
          debugMenu, $"Step Over Skipping Breakpoints", e, shiftF10, disabled = true;
@@ -1201,7 +1266,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOver(true);
             return true;
          }
-      }
+      };
       MenuItem debugStepOutItem
       {
          debugMenu, $"Step Out", o, shiftF11, disabled = true;
@@ -1211,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;
@@ -1221,7 +1286,7 @@ class IDEWorkSpace : Window
             if(projectView) projectView.DebugStepOut(true);
             return true;
          }
-      }
+      };
 #if 0
       MenuItem debugStepUntilItem
       {
@@ -1231,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;
@@ -1240,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 };
@@ -1268,7 +1333,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuPlacement { viewMenu, $"View Designer" };
       MenuPlacement { viewMenu, $"View Code" };
       MenuPlacement { viewMenu, $"View Properties" };
@@ -1289,7 +1354,7 @@ class IDEWorkSpace : Window
                ((CodeEditor)client).ViewDesigner();
             return true;
          }
-      }
+      };
       MenuItem viewCodeItem
       {
          viewMenu, $"View Code", c, f8;
@@ -1305,7 +1370,7 @@ class IDEWorkSpace : Window
             client.visible = true;
             return true;
          }
-      }
+      };
       MenuItem viewPropertiesItem
       {
          viewMenu, $"View Properties", p, f4;
@@ -1316,7 +1381,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewMethodsItem
       {
          viewMenu, $"View Methods", m, f4;
@@ -1327,7 +1392,7 @@ class IDEWorkSpace : Window
             sheet.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewToolBoxItem
       {
          viewMenu, $"View Toolbox", x, f12;
@@ -1337,7 +1402,7 @@ class IDEWorkSpace : Window
             toolBox.Activate();
             return true;
          }
-      }
+      };
       MenuItem viewOutputItem
       {
          viewMenu, $"Output", o, alt2;
@@ -1346,7 +1411,7 @@ class IDEWorkSpace : Window
             outputView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewWatchesItem
       {
          viewMenu, $"Watches", w, alt3;
@@ -1355,7 +1420,7 @@ class IDEWorkSpace : Window
             watchesView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewThreadsItem
       {
          viewMenu, $"Threads", t, alt4;
@@ -1364,7 +1429,7 @@ class IDEWorkSpace : Window
             threadsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewBreakpointsItem
       {
          viewMenu, $"Breakpoints", b, alt5;
@@ -1373,7 +1438,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewCallStackItem
       {
          viewMenu, $"Call Stack", s, alt7;
@@ -1382,7 +1447,7 @@ class IDEWorkSpace : Window
             callStackView.Show();
             return true;
          }
-      }
+      };
       MenuItem viewAllDebugViews
       {
          viewMenu, $"All Debug Views", a, alt9;
@@ -1395,7 +1460,7 @@ class IDEWorkSpace : Window
             breakpointsView.Show();
             return true;
          }
-      }
+      };
 #ifdef GDB_DEBUG_GUI
       MenuDivider { viewMenu };
       MenuItem viewGDBItem
@@ -1406,19 +1471,19 @@ class IDEWorkSpace : Window
             gdbDialog.Show();
             return true;
          }
-      }
+      };
 #endif
       MenuDivider { viewMenu };
       MenuItem viewColorPicker
       {
-         viewMenu, $"Color Picker...", l, Key { c, ctrl = true , shift = true };
+         viewMenu, $"Color Picker...", c, Key { c, ctrl = true , shift = true };
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
             ColorPicker colorPicker { master = this };
             colorPicker.Modal();
             return true;
          }
-      }
+      };
       MenuDivider { viewMenu };
       /*
       MenuItem
@@ -1435,6 +1500,11 @@ class IDEWorkSpace : Window
       };
       */
       Menu driversMenu { viewMenu, $"Graphics Driver", v };
+
+      MenuDivider { viewMenu };
+
+      Menu languageMenu { viewMenu, "Language", l };
+
       //Menu skinsMenu { viewMenu, "GUI Skins", k };
    Menu windowMenu { menu, $"Window", w };
       MenuItem { windowMenu, $"Close All", l, NotifySelect = MenuWindowCloseAll };
@@ -1476,7 +1546,7 @@ class IDEWorkSpace : Window
             }
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1486,7 +1556,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1496,7 +1566,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("doc");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Samples Folder", s;
@@ -1505,7 +1575,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("samples");
             return true;
          }
-      }
+      };
       MenuItem
       {
          helpMenu, $"Extras Folder", x;
@@ -1514,7 +1584,7 @@ class IDEWorkSpace : Window
             FindAndShellOpenInstalledFolder("extras");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1524,7 +1594,7 @@ class IDEWorkSpace : Window
             ShellOpen("http://ecere.com/forums");
             return true;
          }
-      }
+      };
       MenuDivider { helpMenu };
       MenuItem
       {
@@ -1534,7 +1604,7 @@ class IDEWorkSpace : Window
             AboutIDE { master = this }.Modal();
             return true;
          }
-      }
+      };
 
    property ToolBox toolBox
    {
@@ -1571,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);
@@ -1590,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;
@@ -1619,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
@@ -1670,6 +1742,9 @@ class IDEWorkSpace : Window
             findInFilesDialog.SearchStop();
             findInFilesDialog.currentDirectory = workingDir;
          }
+         sheet.visible = false;
+         toolBox.visible = false;
+         outputView.visible = false;
          ideMainFrame.text = titleECEREIDE;
          ide.AdjustMenus();
          return true;
@@ -1682,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;
@@ -1702,10 +1776,11 @@ class IDEWorkSpace : Window
                anchor.bottom = bottomDistance;
                if(child._class == class(CodeEditor) || child._class == class(Designer))
                {
-                  anchor.left = 300;
+                  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)
             {
@@ -1716,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?
       }
    }
 
@@ -1728,15 +1804,14 @@ class IDEWorkSpace : Window
          projectView.visible = true;
          projectView.Activate();
       }
-      else
-      {
-         sheet.visible = true;
+      else if(sheet.visible)
          sheet.Activate();
-      }
+      else
+         outputView.visible = false;
       return false;
    }
 
-   void DocumentSaved(Window document, char * fileName)
+   void DocumentSaved(Window document, const char * fileName)
    {
       ideSettings.AddRecentFile(fileName);
       ide.UpdateRecentMenus();
@@ -1744,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"
@@ -1760,7 +1835,7 @@ class IDEWorkSpace : Window
 
          this.modifiedDocument = false;
          this.Destroy(0);
-         this = ide.OpenFile(fileName, normal, true, null, no, normal, noParsing);
+         this = ide.OpenFile(fileName, false, true, null, no, normal, noParsing);
          if(this)
          {
             this.anchor = anchor;
@@ -2050,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;
@@ -2101,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);
@@ -2122,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;
@@ -2140,7 +2214,7 @@ class IDEWorkSpace : Window
       return null;
    }
 
-   bool DontTerminateDebugSession(char * title)
+   bool DontTerminateDebugSession(const char * title)
    {
       if(debugger.isActive)
       {
@@ -2164,15 +2238,16 @@ class IDEWorkSpace : Window
       return false;
    }
 
-   Window OpenFile(char * origFilePath, WindowState state, bool visible, char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
+   Window OpenFile(const char * origFilePath, bool dontMaximize, bool visible, const char * type, OpenCreateIfFails createIfFails, OpenMethod openMethod, bool noParsing)
    {
       char extension[MAX_EXTENSION] = "";
       Window document = null;
       bool isProject = false;
       bool needFileModified = true;
       char winFilePath[MAX_LOCATION];
-      char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
-
+      const char * filePath = strstr(origFilePath, "http://") == origFilePath ? strcpy(winFilePath, origFilePath) : GetSystemPathBuffer(winFilePath, origFilePath);
+      Window currentDoc = activeClient;
+      bool maximizeDoc = !dontMaximize && ((currentDoc && currentDoc.state == maximized) || (!currentDoc && !projectView));
       if(!type)
       {
          GetExtension(filePath, extension);
@@ -2185,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;
@@ -2212,7 +2287,6 @@ class IDEWorkSpace : Window
                {
                   for(;;)
                   {
-                     Project project;
                      Workspace workspace = null;
 
                      if(FileExists(filePath))
@@ -2228,15 +2302,17 @@ 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;
 
+                        toolBox.visible = true;
+                        sheet.visible = true;
+                        projectView.MakeActive();
+
                         workspace.ParseLoadedBreakpoints();
                         workspace.DropInvalidBreakpoints(null);
                         workspace.Save();
@@ -2267,7 +2343,7 @@ class IDEWorkSpace : Window
                         {
                            if(ofi.state != closed)
                            {
-                              Window file = OpenFile(ofi.path, normal, true, null, no, normal, noParsing);
+                              Window file = OpenFile(ofi.path, false, true, null, no, normal, noParsing);
                               if(file)
                               {
                                  char fileName[MAX_LOCATION];
@@ -2360,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);
@@ -2477,7 +2553,7 @@ class IDEWorkSpace : Window
             createIfFails = yes;
          if(createIfFails == yes || createIfFails == whatever)
          {
-            document = (Window)NewCodeEditor(this, state, true);
+            document = (Window)NewCodeEditor(this, maximizeDoc ? maximized : normal, true);
             if(document)
                document.fileName = filePath;
          }
@@ -2494,8 +2570,8 @@ class IDEWorkSpace : Window
             editor.openedFileInfo.holdTracking = true;
             lineNumber = Max(editor.openedFileInfo.lineNumber - 1, 0);
             position = Max(editor.openedFileInfo.position - 1, 0);
-            editor.editBox.GoToLineNum(lineNumber);
-            editor.editBox.GoToPosition(editor.editBox.line, lineNumber, position);
+            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;
@@ -2505,6 +2581,8 @@ class IDEWorkSpace : Window
          if(needFileModified)
             document.OnFileModified = OnFileModified;
          document.NotifySaved = DocumentSaved;
+         if(maximizeDoc && document.hasMaximize)
+            document.state = maximized;
 
          if(isProject)
             ideSettings.AddRecentProject(document.fileName);
@@ -2593,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];
@@ -2737,7 +2815,8 @@ class IDEWorkSpace : Window
       {
          char ext[MAX_EXTENSION];
          GetExtension(path, ext);
-         if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
+         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"))
          {
@@ -2747,7 +2826,7 @@ class IDEWorkSpace : Window
          }
          else
          {
-            CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
+            CodeEditor codeEditor = (CodeEditor)OpenFile(path, false, true, ext, no, normal, false);
             if(codeEditor && line)
             {
                EditBox editBox = codeEditor.editBox;
@@ -2893,6 +2972,12 @@ class IDEWorkSpace : Window
          }
          else
          {
+            if(!client && !projectView && sheet.visible)
+            {
+               if(sheet)
+                  sheet.visible = false;
+               toolBox.visible = false;
+            }
             if(sheet)
                sheet.codeEditor = null;
             toolBox.codeEditor = null;
@@ -3015,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(" ");
@@ -3092,10 +3177,10 @@ class IDEWorkSpace : Window
                   break;
                }
                else
-                  ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
+                  ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
             }
             else if(strstr(fullPath, "http://") == fullPath)
-               ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, openAsText ? "txt" : null, yes, normal, false);
+               ide.OpenFile(fullPath, app.argFilesCount > 1, true, openAsText ? "txt" : null, yes, normal, false);
          }
       }
       if(passThrough && projectView && projectView.project && workspace)
@@ -3330,7 +3415,6 @@ class IDEWorkSpace : Window
    IDEWorkSpace()
    {
       // Graphics Driver Menu
-      int c;
 
       /*
       app.currentSkin.selectionColor = selectionColor;
@@ -3383,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;
@@ -3416,6 +3499,7 @@ class IDEWorkSpace : Window
    {
       delete driverItems;
       delete skinItems;
+      delete languageItems;
       delete ideSettings;
       if(documentor)
       {
@@ -3439,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])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("AppData", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("ProgramFiles", v, maxPathLen);
-   if(v[0])
+   v[0] = '\0';
+   if(found)
    {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
+      strncpy(path, settingsContainer.moduleLocation, MAX_LOCATION); path[MAX_LOCATION-1] = '\0';
+      StripLastDirectory(path, path);
+      PathCat(path, subDir);
+      if(name) PathCat(path, name);
+      if(FileExists(path) & attribs) found = true;
    }
-   GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
-   if(v[0])
-   {
-      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
-      PathCat(p, sdkDirName); PathCat(p, name); paths.Add(CopyString(p));
-   }
-   GetEnvironment("SystemDrive", v, maxPathLen);
-   if(v[0])
-   {
-      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;
@@ -3600,6 +3670,7 @@ class IDEApp : GuiApplication
    //skin = "TVision";
 
    TempFile includeFile { };
+   int argFilesCount;
 
    bool Init()
    {
@@ -3608,7 +3679,40 @@ class IDEApp : GuiApplication
       //SetLoggingMode(debug, null);
 
       settingsContainer.Load();
-      if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
+
+      if(ideSettings.language)
+      {
+         const String language = GetLanguageString();
+         if(ideSettings.language.OnCompare(language))
+         {
+            LanguageRestart(ideSettings.language, app, null, null, null, null, true);
+            return false;
+         }
+      }
+
+      // First count files arg to decide whether to maximize
+      {
+         bool passThrough = false, debugWorkDir = false;
+         int c;
+         argFilesCount = 0;
+         for(c = 1; c<app.argc; c++)
+         {
+            if(passThrough);
+            else if(debugWorkDir)
+               debugWorkDir = false;
+            else if(!strcmp(app.argv[c], "-t"));
+            else if(!strcmp(app.argv[c], "-no-parsing"));
+            else if(!strcmp(app.argv[c], "-debug-start"));
+            else if(!strcmp(app.argv[c], "-debug-work-dir"))
+               debugWorkDir = true;
+            else if(!strcmp(app.argv[c], "-@"))
+               passThrough = true;
+            else
+               argFilesCount++;
+         }
+      }
+
+      if(app.argFilesCount > 0 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
       {
          app.driver = "OpenGL";
          ide.driverItems[1].checked = true;
@@ -3623,9 +3727,20 @@ 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.text = titleECEREIDE;
+      desktop.caption = titleECEREIDE;
       /*
       int c;
       for(c = 1; c<app.argc; c++)
@@ -3633,13 +3748,164 @@ class IDEApp : GuiApplication
          char fullPath[MAX_LOCATION];
          GetWorkingDir(fullPath, MAX_LOCATION);
          PathCat(fullPath, app.argv[c]);
-         ide.OpenFile(fullPath, (app.argc == 2) * maximized, true, null, yes, normal, false);
+         ide.OpenFile(fullPath, app.argFilesCount > 1, true, null, yes, normal, false);
       }
       */
 
+      // Default to language specified by environment if no language selected
+      if(!ideSettings.language)
+      {
+         ideSettings.language = GetLanguageString();
+         settingsContainer.Save();
+      }
+
+      // Default to home directory if no directory yet set up
+      if(!ideSettings.ideProjectFileDialogLocation[0])
+      {
+         bool found = false;
+         char location[MAX_LOCATION];
+         char * home = getenv("HOME");
+         char * homeDrive = getenv("HOMEDRIVE");
+         char * homePath = getenv("HOMEPATH");
+         char * userProfile = getenv("USERPROFILE");
+         char * systemDrive = getenv("SystemDrive");
+         if(home && FileExists(home).isDirectory)
+         {
+            strcpy(location, home);
+            found = true;
+         }
+         if(!found && homeDrive && homePath)
+         {
+            strcpy(location, homeDrive);
+            PathCat(location, homePath);
+            if(FileExists(location).isDirectory)
+               found = true;
+         }
+         if(!found && FileExists(userProfile).isDirectory)
+         {
+            strcpy(location, userProfile);
+            found = true;
+         }
+         if(!found && FileExists(systemDrive).isDirectory)
+         {
+            strcpy(location, systemDrive);
+            found = true;
+         }
+         if(found)
+         {
+            ideSettings.ideProjectFileDialogLocation = location;
+            if(!ideSettings.ideFileDialogLocation[0])
+               ideSettings.ideFileDialogLocation = location;
+         }
+      }
+
       if(!LoadIncludeFile())
-         PrintLn("error: unable to load :crossplatform.mk file inside ide binary.");
+         PrintLn($"error: unable to load :crossplatform.mk file inside ide binary.");
+
+      // Create language menu
+      {
+         String language = ideSettings.language;
+         int i = 0;
+         bool found = false;
+
+         ide.languageItems = new MenuItem[languages.count];
+         for(l : languages)
+         {
+            ide.languageItems[i] =
+            {
+               ide.languageMenu, l.name;
+               bitmap = { l.bitmap };
+               id = i;
+               isRadio = true;
+
+               bool Window::NotifySelect(MenuItem selection, Modifiers mods)
+               {
+                  if(!LanguageRestart(languages[(int)selection.id].code, app, ideSettings, settingsContainer, ide, ide.projectView, false))
+                  {
+                     // Re-select previous selected language if aborted
+                     String language = ideSettings.language;
+                     int i = 0;
+                     for(l : languages)
+                     {
+                        if(((!language || !language[0]) && i == 0) ||
+                           (language && !strcmpi(l.code, language)))
+                        {
+                           ide.languageItems[i].checked = true;
+                           break;
+                        }
+                        i++;
+                     }
+                  }
+                  return true;
+               }
+            };
+            i++;
+         }
+
+         // Try to find country-specific language first
+         if(language)
+         {
+            i = 0;
+            for(l : languages)
+            {
+               if(!strcmpi(l.code, language) || (i == 0 && !strcmpi("en", language)))
+               {
+                  ide.languageItems[i].checked = true;
+                  found = true;
+                  break;
+               }
+               i++;
+            }
+         }
+
+         // Try generalizing locale
+         if(!found && language)
+         {
+            char * under;
+            char genericLocale[256];
+            i = 0;
+            strncpy(genericLocale, language, sizeof(genericLocale));
+            genericLocale[sizeof(genericLocale)-1] = 0;
+
+            under = strchr(genericLocale, '_');
+            if(under)
+               *under = 0;
+            if(!strcmpi(genericLocale, "zh"))
+               strcpy(genericLocale, "zh_CN");
+            if(strcmp(genericLocale, language))
+            {
+               for(l : languages)
+               {
+                  if(!strcmpi(l.code, genericLocale) || (i == 0 && !strcmpi("en", genericLocale)))
+                  {
+                     ide.languageItems[i].checked = true;
+                     found = true;
+                     break;
+                  }
+                  i++;
+               }
+            }
+         }
+
+         if(!found)
+            ide.languageItems[0].checked = true;
+
+         MenuDivider { ide.languageMenu };
+         MenuItem
+         {
+            ide.languageMenu, "Help Translate";
+
+            bool Window::NotifySelect(MenuItem selection, Modifiers mods)
+            {
+               ShellOpen("http://translations.launchpad.net/ecere");
+               return true;
+            }
+         };
+      }
 
+      ideMainFrame.Create();
+      if(app.argFilesCount > 1)
+         ide.MenuWindowTileVert(null, 0);
       return true;
    }