ide: Fixed zombie documentor processes
[sdk] / ide / src / ide.ec
index 5d49553..30d3837 100644 (file)
@@ -246,6 +246,9 @@ class IDEToolbar : ToolBar
    // Compile actual file
    // Execute
    ToolButton buttonRun { this, toolTip = $"Run", menuItemPtr = IDEItem(projectRunItem), disabled = true; };
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+   ToolButton buttonInstall { this, toolTip = $"Install", menuItemPtr = IDEItem(projectInstallItem), disabled = true; };
+#endif
 
    ToolSeparator separator4 { this };
 
@@ -279,21 +282,7 @@ class IDEToolbar : ToolBar
       bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
       {
          if(row)
-         {
-            for(prj : ide.workspace.projects)
-            {
-               for(cfg : prj.configurations)
-               {
-                  if(cfg.name && !strcmp(cfg.name, row.string))
-                  {
-                     prj.config = cfg;
-                     break;
-                  }
-               }
-            }
-            ide.UpdateToolBarActiveConfigs(true);
-            ide.projectView.Update(null);
-         }
+            ide.workspace.SelectActiveConfig(row.string);
          return true;
       }
    };
@@ -418,6 +407,7 @@ class IDEWorkSpace : Window
    MenuItem * driverItems, * skinItems;
    StatusField pos { width = 150 };
    StatusField ovr, caps, num;
+   DualPipe documentor;
 
    BitmapResource back                 { ":ecereBack.jpg", window = this };
    BitmapResource bmpBp                { ":codeMarks/breakpoint.png", window = this };
@@ -466,13 +456,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -486,16 +469,11 @@ class IDEWorkSpace : Window
    {
       parent = this, font = { panelFont.faceName, panelFont.size };
 
-      void OnGotoLine(char * line)
+      void OnSelectFrame(int frameIndex)
       {
-         int stackLvl;
-         stackLvl = atoi(line);
-         ide.debugger.GoToStackFrameLine(stackLvl, true);
-      }
-
-      void OnSelectFrame(int lineNumber)
-      {
-         ide.debugger.SelectFrame(lineNumber);
+         ide.debugger.GoToStackFrameLine(frameIndex, true);
+         if(frameIndex >= 0)
+            ide.debugger.SelectFrame(frameIndex);
       }
 
       void OnToggleBreakpoint()
@@ -548,13 +526,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -635,13 +606,6 @@ class IDEWorkSpace : Window
          return true;
       }
 
-      bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
-      {
-         if(active)
-            ide.RepositionWindows(false);
-         return true;
-      }
-
       bool OnClose(bool parentClosing)
       {
          visible = false;
@@ -715,6 +679,7 @@ class IDEWorkSpace : Window
                   {
                      if(!projectView && gotWhatWeWant)
                         ChangeFileDialogsDirectory(ideFileDialog.currentDirectory, true);
+                     ide.RepositionWindows(false);
                      break;
                   }
                }
@@ -783,7 +748,7 @@ class IDEWorkSpace : Window
       MenuDivider { fileMenu };
       MenuItem exitItem
       {
-         fileMenu, $"Exit", x, altF4, NotifySelect = MenuFileExit;
+         fileMenu, $"Exit", x, altF4;
 
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
@@ -810,7 +775,10 @@ class IDEWorkSpace : Window
                   delete command;
                }
                else
+               {
                   OpenFile(file, normal, true, isProjectFile ? "txt" : null, no, normal, mods.ctrl && mods.shift);
+                  ide.RepositionWindows(false);
+               }
                break;
             }
             id++;
@@ -850,26 +818,26 @@ class IDEWorkSpace : Window
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
             if(!DontTerminateDebugSession($"New Project"))
-               if(MenuWindowCloseAll(null, 0))
+            {
+               DialogResult result;
+               NewProjectDialog newProjectDialog { master = this };
+               incref newProjectDialog;
+               result = newProjectDialog.Modal();
+               if(result == ok)
                {
-                  NewProjectDialog newProjectDialog;
-
-                  if(projectView)
+                  if(ProjectClose())
                   {
-                     projectView.visible = false;
-                     if(!projectView.Destroy(0))
-                        return true;
-                  }
-                  
-                  newProjectDialog = { master = this };
-                  newProjectDialog.Modal();
-                  if(projectView)
-                  {
-                     ideSettings.AddRecentProject(projectView.fileName);
-                     ide.UpdateRecentMenus();
-                     settingsContainer.Save();
+                     newProjectDialog.CreateNewProject();
+                     if(projectView)
+                     {
+                        ideSettings.AddRecentProject(projectView.fileName);
+                        ide.UpdateRecentMenus();
+                        settingsContainer.Save();
+                     }
                   }
                }
+               delete newProjectDialog;
+            }
             return true;
          }
       }
@@ -938,20 +906,7 @@ class IDEWorkSpace : Window
             if(projectView)
             {
                if(!ide.DontTerminateDebugSession($"Project Close"))
-               {
-                  if(findInFilesDialog)
-                     findInFilesDialog.SearchStop();
-                  projectView.visible = false;
-                  if(projectView.Destroy(0))
-                     MenuWindowCloseAll(null, 0);
-                  {
-                     char workingDir[MAX_LOCATION];
-                     GetWorkingDir(workingDir, MAX_LOCATION);
-                     findInFilesDialog.currentDirectory = workingDir;
-                     ideMainFrame.text = titleECEREIDE;
-                  }
-                  ide.AdjustMenus();
-               }
+                  ProjectClose();
             }
             return true;
          }
@@ -1080,6 +1035,19 @@ class IDEWorkSpace : Window
             return true;
          }
       }
+      MenuItem projectInstallItem
+      {
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+         projectMenu, $"Install", t, disabled = true;
+#endif
+         bitmap = { ":status/software-update-available.png" };
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(projectView)
+               projectView.ProjectInstall(projectView.active ? selection : null, mods);
+            return true;
+         }
+      }
       MenuItem projectCompileItem;
    Menu debugMenu { menu, $"Debug", d, hasMargin = true };
       MenuItem debugStartResumeItem
@@ -1139,6 +1107,94 @@ class IDEWorkSpace : Window
             return true;
          }
       }
+#ifndef __WIN32__
+      MenuDivider { debugMenu };
+      MenuItem debugUseValgrindItem
+      {
+         debugMenu, $"Use Valgrind", d, disabled = true, checkable = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               ide.workspace.useValgrind = selection.checked;
+               ide.workspace.Save();
+            }
+            ide.AdjustValgrindMenus();
+            return true;
+         }
+      }
+      Menu debugValgrindLeakCheckItem { debugMenu, $"Valgrind Leak Check", h };
+         MenuItem debugValgrindNoLeakCheckItem      { debugValgrindLeakCheckItem, $"No"     , f, id = ValgrindLeakCheck::no     , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; }
+         MenuItem debugValgrindSummaryLeakCheckItem { debugValgrindLeakCheckItem, $"Summary", f, id = ValgrindLeakCheck::summary, checkable = true, disabled = true; NotifySelect = ValgrindLCSelect, checked = true; }
+         MenuItem debugValgrindYesLeakCheckItem     { debugValgrindLeakCheckItem, $"Yes"    , f, id = ValgrindLeakCheck::yes    , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; }
+         MenuItem debugValgrindFullLeakCheckItem    { debugValgrindLeakCheckItem, $"Full"   , f, id = ValgrindLeakCheck::full   , checkable = true, disabled = true; NotifySelect = ValgrindLCSelect; }
+         bool ValgrindLCSelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               if(selection.checked)
+               {
+                  ValgrindLeakCheck vgLeakCheck = (ValgrindLeakCheck)selection.id;
+
+                  debugValgrindNoLeakCheckItem.checked      = debugValgrindNoLeakCheckItem.id      == vgLeakCheck;
+                  debugValgrindSummaryLeakCheckItem.checked = debugValgrindSummaryLeakCheckItem.id == vgLeakCheck;
+                  debugValgrindYesLeakCheckItem.checked     = debugValgrindYesLeakCheckItem.id     == vgLeakCheck;
+                  debugValgrindFullLeakCheckItem.checked    = debugValgrindFullLeakCheckItem.id    == vgLeakCheck;
+
+                  ide.workspace.vgLeakCheck = vgLeakCheck;
+                  ide.workspace.Save();
+               }
+               else
+                  selection.checked = true;
+            }
+            return true;
+         }
+      Menu debugValgrindRedzoneSizeItem { debugMenu, $"Valgrind Redzone Size", z };
+         MenuItem debugValgrindRS0Item   { debugValgrindRedzoneSizeItem, $"0"  , f, id =   0, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect, checked = true; }
+         MenuItem debugValgrindRS16Item  { debugValgrindRedzoneSizeItem, $"16" , f, id =  16, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         MenuItem debugValgrindRS32Item  { debugValgrindRedzoneSizeItem, $"32" , f, id =  32, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         MenuItem debugValgrindRS64Item  { debugValgrindRedzoneSizeItem, $"64" , f, id =  64, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         MenuItem debugValgrindRS128Item { debugValgrindRedzoneSizeItem, $"128", f, id = 128, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         MenuItem debugValgrindRS256Item { debugValgrindRedzoneSizeItem, $"256", f, id = 256, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         MenuItem debugValgrindRS512Item { debugValgrindRedzoneSizeItem, $"512", f, id = 512, checkable = true, disabled = true; NotifySelect = ValgrindRSSelect; }
+         bool ValgrindRSSelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               if(selection.checked)
+               {
+                  int vgRedzoneSize = (int)selection.id;
+
+                  debugValgrindRS0Item.checked   = debugValgrindRS0Item.id   == vgRedzoneSize;
+                  debugValgrindRS16Item.checked  = debugValgrindRS16Item.id  == vgRedzoneSize;
+                  debugValgrindRS32Item.checked  = debugValgrindRS32Item.id  == vgRedzoneSize;
+                  debugValgrindRS64Item.checked  = debugValgrindRS64Item.id  == vgRedzoneSize;
+                  debugValgrindRS128Item.checked = debugValgrindRS128Item.id == vgRedzoneSize;
+                  debugValgrindRS256Item.checked = debugValgrindRS256Item.id == vgRedzoneSize;
+                  debugValgrindRS512Item.checked = debugValgrindRS512Item.id == vgRedzoneSize;
+
+                  ide.workspace.vgRedzoneSize = vgRedzoneSize;
+                  ide.workspace.Save();
+               }
+               else
+                  selection.checked = true;
+            }
+            return true;
+         }
+      MenuItem debugValgrindTrackOriginsItem
+      {
+         debugMenu, $"Valgrind Track Origins", k, checkable = true, disabled = true;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            if(ide.workspace)
+            {
+               ide.workspace.vgTrackOrigins = selection.checked;
+               ide.workspace.Save();
+            }
+            return true;
+         }
+      };
+#endif
       MenuDivider { debugMenu };
       MenuItem debugStepIntoItem
       {
@@ -1196,6 +1252,7 @@ class IDEWorkSpace : Window
          }
       }
       MenuPlacement debugSkipRunToCursorItem { debugMenu, $"Run To Cursor Skipping Breakpoints", u };
+      MenuPlacement debugSkipRunToCursorAtSameLevelItem { debugMenu, $"Run To Cursor At Same Level Skipping Breakpoints", l };
       //MenuDivider { debugMenu };
       //MenuPlacement debugToggleBreakpoint { debugMenu, "Toggle Breakpoint", t };
    MenuPlacement imageMenu { menu, $"Image", i };
@@ -1398,7 +1455,74 @@ class IDEWorkSpace : Window
          helpMenu, $"API Reference", r, f1;
          bool NotifySelect(MenuItem selection, Modifiers mods)
          {
-            Execute("documentor");
+            if(!documentor)
+            {
+               char * p = new char[MAX_LOCATION];
+               p[0] = '\0';
+               strncpy(p, settingsContainer.moduleLocation, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+               PathCat(p, "documentor");
+   #if defined(__WIN32__)
+               ChangeExtension(p, "exe", p);
+   #endif
+               if(!FileExists(p).isFile)
+                  strcpy(p, "documentor");
+
+               documentor = DualPipeOpen({ input = true, output = true, showWindow = true }, p);
+               delete p;
+            }
+            else
+            {
+               Process_ShowWindows(documentor.GetProcessID());
+               // documentor.Puts("Activate\n");
+            }
+            return true;
+         }
+      }
+      MenuDivider { helpMenu };
+      MenuItem
+      {
+         helpMenu, $"Ecere Tao of Programming [work in progress]", t;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            FindAndShellOpenInstalledFile("doc", "Ecere Tao of Programming [work in progress].pdf");
+            return true;
+         }
+      }
+      MenuDivider { helpMenu };
+      MenuItem
+      {
+         helpMenu, $"Documentation Folder", d;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            FindAndShellOpenInstalledFolder("doc");
+            return true;
+         }
+      }
+      MenuItem
+      {
+         helpMenu, $"Samples Folder", s;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            FindAndShellOpenInstalledFolder("samples");
+            return true;
+         }
+      }
+      MenuItem
+      {
+         helpMenu, $"Extras Folder", x;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            FindAndShellOpenInstalledFolder("extras");
+            return true;
+         }
+      }
+      MenuDivider { helpMenu };
+      MenuItem
+      {
+         helpMenu, $"Community Forums", f;
+         bool NotifySelect(MenuItem selection, Modifiers mods)
+         {
+            ShellOpen("http://ecere.com/forums");
             return true;
          }
       }
@@ -1446,7 +1570,7 @@ class IDEWorkSpace : Window
    GDBDialog gdbDialog
    {
       master = this, parent = this;
-      anchor = { left = 100, top = 100, right = 100, bottom = 100 };
+      //anchor = { left = 100, top = 100, right = 100, bottom = 100 };
 
       void OnCommand(char * string)
       {
@@ -1535,6 +1659,25 @@ class IDEWorkSpace : Window
       return projectView;
    }
 
+   bool ProjectClose()
+   {
+      projectView.visible = false;
+      if((!projectView || projectView.created == false || projectView.Destroy(0)) && MenuWindowCloseAll(null, 0))
+      {
+         if(findInFilesDialog)
+         {
+            char workingDir[MAX_LOCATION];
+            GetWorkingDir(workingDir, MAX_LOCATION);
+            findInFilesDialog.SearchStop();
+            findInFilesDialog.currentDirectory = workingDir;
+         }
+         ideMainFrame.text = titleECEREIDE;
+         ide.AdjustMenus();
+         return true;
+      }
+      return false;
+   }
+
    void RepositionWindows(bool expand)
    {
       if(this)
@@ -1560,6 +1703,7 @@ class IDEWorkSpace : Window
                anchor.bottom = bottomDistance;
                if(child._class == class(CodeEditor) || child._class == class(Designer))
                {
+                  anchor.left = 300;
                   anchor.right = toolBoxVisible ? 150 : 0;
                }
                child.anchor = anchor;
@@ -1681,7 +1825,7 @@ class IDEWorkSpace : Window
       else
       {
          toolBar.activeConfig.Clear();
-         row = toolBar.activeConfig.AddString("(Mixed)");
+         row = toolBar.activeConfig.AddString($"(Mixed)");
          row.tag = 1;
       }
       if(workspace)
@@ -1764,11 +1908,37 @@ class IDEWorkSpace : Window
       toolBar.activeCompiler.disabled     = unavailable;
       toolBar.activeBitDepth.disabled     = unavailable;
 
+#ifndef __WIN32__
+      debugUseValgrindItem.disabled       = unavailable;
+      AdjustValgrindMenus();
+#endif
+
       AdjustFileMenus();
       AdjustBuildMenus();
       AdjustDebugMenus();
    }
 
+#ifndef __WIN32__
+   void AdjustValgrindMenus()
+   {
+      bool unavailable = !project || !debugUseValgrindItem.checked;
+      debugValgrindNoLeakCheckItem.disabled        = unavailable;
+      debugValgrindSummaryLeakCheckItem.disabled   = unavailable;
+      debugValgrindYesLeakCheckItem.disabled       = unavailable;
+      debugValgrindFullLeakCheckItem.disabled      = unavailable;
+
+      debugValgrindTrackOriginsItem.disabled       = unavailable;
+
+      debugValgrindRS0Item.disabled   = unavailable;
+      debugValgrindRS16Item.disabled  = unavailable;
+      debugValgrindRS32Item.disabled  = unavailable;
+      debugValgrindRS64Item.disabled  = unavailable;
+      debugValgrindRS128Item.disabled = unavailable;
+      debugValgrindRS256Item.disabled = unavailable;
+      debugValgrindRS512Item.disabled = unavailable;
+   }
+#endif
+
    property bool hasOpenedCodeEditors
    {
       get
@@ -1793,6 +1963,7 @@ class IDEWorkSpace : Window
    void AdjustBuildMenus()
    {
       bool unavailable = project && projectView.buildInProgress;
+      bool naForRun = unavailable || !project || project.GetTargetType(project.config) != executable;
 
       projectNewItem.disabled             = unavailable;
       toolBar.buttonNewProject.disabled   = unavailable;
@@ -1804,8 +1975,8 @@ class IDEWorkSpace : Window
       projectCloseItem.disabled           = unavailable;
       // toolBar.buttonCloseProject.disabled = unavailable;
 
-      projectRunItem.disabled    = unavailable || project.GetTargetType(project.config) != executable;
-      toolBar.buttonRun.disabled = unavailable || project.GetTargetType(project.config) != executable;
+      projectRunItem.disabled    = naForRun;
+      toolBar.buttonRun.disabled = naForRun;
 
       projectBuildItem.disabled = false;
       projectBuildItem.text     = unavailable ? $"Stop Build" : $"Build";
@@ -1822,6 +1993,10 @@ class IDEWorkSpace : Window
       // toolBar.buttonRealClean.disabled          = unavailable;
       projectRegenerateItem.disabled            = unavailable;
       toolBar.buttonRegenerateMakefile.disabled = unavailable;
+#ifdef IDE_SHOW_INSTALL_MENU_BUTTON
+      projectInstallItem.disabled               = unavailable;
+      toolBar.buttonInstall.disabled            = unavailable;
+#endif
       projectCompileItem.disabled               = unavailable;
 
       AdjustPopupBuildMenus();
@@ -1848,6 +2023,7 @@ class IDEWorkSpace : Window
          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectClean, 0);             if(menu) menu.disabled = unavailable;
          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRealClean, 0);         if(menu) menu.disabled = unavailable;
          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRegenerate, 0);        if(menu) menu.disabled = unavailable;
+         menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectInstall, 0);           if(menu) menu.disabled = unavailable;
          menu = projectView.popupMenu.menu.FindItem(ProjectView::ProjectRemove, 0);            if(menu) menu.disabled = unavailable;
          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileClean, 0);                if(menu) menu.disabled = unavailable;
          menu = projectView.popupMenu.menu.FindItem(ProjectView::FileCompile, 0);              if(menu) menu.disabled = unavailable;
@@ -2028,14 +2204,8 @@ class IDEWorkSpace : Window
             if(DontTerminateDebugSession($"Open Project"))
                return null;
             isProject = true;
-            if(MenuWindowCloseAll(null, 0))
+            if(ProjectClose())
             {
-               if(projectView)
-               {
-                  projectView.visible = false;
-                  projectView.Destroy(0);
-                  // Where did this come from? projectView = null;
-               }
                if(!projectView)
                {
                   for(;;)
@@ -2058,7 +2228,7 @@ class IDEWorkSpace : Window
                            return null;
                         //project = LoadProject(filePath, null);
                      }
-                     
+
                      if(workspace)
                      {
                         char absolutePath[MAX_LOCATION];
@@ -2106,6 +2276,7 @@ class IDEWorkSpace : Window
                               }
                            }
                         }
+                        ide.RepositionWindows(false);
                         workspace.holdTracking = false;
 
                         workspace.timer.Start();
@@ -2170,9 +2341,24 @@ class IDEWorkSpace : Window
                   prj = LoadProject(filePath, null);
                   if(prj)
                   {
+                     char * activeConfigName = null;
                      CompilerConfig compiler = ideSettings.GetCompilerConfig(workspace.compiler);
                      prj.StartMonitoring();
                      workspace.projects.Add(prj);
+                     if(toolBar.activeConfig.currentRow && toolBar.activeConfig.currentRow != toolBar.activeConfig.firstRow &&
+                           toolBar.activeConfig.currentRow.string && toolBar.activeConfig.currentRow.string[0])
+                        activeConfigName = toolBar.activeConfig.currentRow.string;
+                     if(activeConfigName)
+                     {
+                        for(cfg : prj.configurations)
+                        {
+                           if(cfg.name && !strcmp(cfg.name, activeConfigName))
+                           {
+                              prj.config = cfg;
+                              break;
+                           }
+                        }
+                     }
                      if(projectView)
                         projectView.AddNode(prj.topNode, null);
                      workspace.modified = true;
@@ -2376,20 +2562,55 @@ class IDEWorkSpace : Window
 
    void CodeLocationParseAndGoTo(const char * text, Project project, const char * dir)
    {
+      char *s = null;
       char *path = text;
       char *colon = strchr(text, ':');
-      char filePath[MAX_LOCATION];
+      char filePath[MAX_LOCATION] = "";
       char completePath[MAX_LOCATION];
       int line = 0, col = 0;
+      int len = strlen(text);
       Project prj = null;
       FileAttribs fileAttribs;
 
-      if(colon && (colon[1] == '/' || colon[1] == '\\'))
+      // support for valgrind output
+      if((s = strstr(text, "==")) && (s = strstr(s+2, "==")) && (s = strstr(s+2, ":")) && (s = strstr(s+1, ":")))
       {
-         path = (colon - 1 > path) ? colon - 1 : path;
-         colon = strstr(colon + 1, ":");
+         colon = s;
+         for(; s>text; s--)
+         {
+            if(*s == '(')
+            {
+               path = s+1;
+               break;
+            }
+         }
+         /*for(s=colon; *s; s++)
+         {
+            if(*s == ')')
+            {
+               *s = '\0';;
+               break;
+            }
+         }*/
+         //*colon = '\0';
+         //line = atoi(colon+1);
+      }
+      // support for "Found n match(es) in "file/path";
+      else if(path[len-1] == '\"' && strstr(path, $"Found %d match%s in \"%s\"%s\n\n"."Found") && strstr(path, $"match") && strstr(path, $"in") && (s = strstr(path, "\"")) && s != path+len-1)
+      {
+         path = s+1;
+      }
+      else
+      {
+         if(colon && (colon[1] == '/' || colon[1] == '\\'))
+         {
+            path = (colon - 1 > path) ? colon - 1 : path;
+            colon = strstr(colon + 1, ":");
+         }
+         if(*path == '*' && (s = strchr(path+1, '*')))
+            path = s+1;
+         while(isspace(*path)) path++;
       }
-      while(isspace(*path)) path++;
       if(*path == '(')
       {
          char * close = strchr(path, ')');
@@ -2434,27 +2655,79 @@ class IDEWorkSpace : Window
          strcpy(filePath, path);
       }
 
-      if(prj)
-         strcpy(completePath, prj.topNode.path);
-      else if(dir && dir[0])
-         strcpy(completePath, dir);
-      else
-         completePath[0] = '\0';
-      PathCat(completePath, filePath);
+      if(filePath[0])
+      {
+         if(prj)
+            strcpy(completePath, prj.topNode.path);
+         else if(dir && dir[0])
+            strcpy(completePath, dir);
+         else
+            completePath[0] = '\0';
+         PathCat(completePath, filePath);
+
+         if((fileAttribs = FileExists(completePath)))
+            CodeLocationGoTo(completePath, fileAttribs, line, col);
+         else if(ide.workspace)
+         {
+            bool done = false;
+            for(p : ide.workspace.projects)
+            {
+               strcpy(completePath, p.topNode.path);
+               PathCat(completePath, filePath);
+               if((fileAttribs = FileExists(completePath)).isFile)
+               {
+                  CodeLocationGoTo(completePath, fileAttribs, line, col);
+                  done = true;
+                  break;
+               }
+            }
+            if(!done)
+            {
+               for(p : ide.workspace.projects)
+               {
+                  ProjectNode node = p.topNode.Find(filePath, false);
+                  if(node)
+                  {
+                     node.GetFullFilePath(completePath);
+                     if((fileAttribs = FileExists(completePath)).isFile)
+                     {
+                        CodeLocationGoTo(completePath, fileAttribs, line, col);
+                        break;
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
 
-      fileAttribs = FileExists(completePath);
+   void CodeLocationGoTo(const char * path, const FileAttribs fileAttribs, int line, int col)
+   {
       if(fileAttribs.isFile)
       {
-         CodeEditor codeEditor = (CodeEditor)OpenFile(completePath, normal, true, "", no, normal, false);
-         if(codeEditor && line)
+         char ext[MAX_EXTENSION];
+         GetExtension(path, ext);
+         if(!strcmp(ext, "mp3") || !strcmp(ext, "flac") || !strcmp(ext, "ogg") || !strcmp(ext, "avi") || !strcmp(ext, "mkv"))
+            ShellOpen(path);
+         else if(!strcmp(ext, "a") || !strcmp(ext, "o") || !strcmp(ext, "lib") || !strcmp(ext, "dll") || !strcmp(ext, "exe"))
          {
-            EditBox editBox = codeEditor.editBox;
-            editBox.GoToLineNum(line - 1);
-            editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
+            char dirPath[MAX_LOCATION];
+            StripLastDirectory(path, dirPath);
+            ShellOpen(dirPath);
+         }
+         else
+         {
+            CodeEditor codeEditor = (CodeEditor)OpenFile(path, normal, true, ext, no, normal, false);
+            if(codeEditor && line)
+            {
+               EditBox editBox = codeEditor.editBox;
+               editBox.GoToLineNum(line - 1);
+               editBox.GoToPosition(editBox.line, line - 1, col ? (col - 1) : 0);
+            }
          }
       }
       else if(fileAttribs.isDirectory)
-         ShellOpen(completePath);
+         ShellOpen(path);
    }
 
    void OnRedraw(Surface surface)
@@ -2708,22 +2981,19 @@ class IDEWorkSpace : Window
       char * passDebugWorkDir = null;
       bool openAsText = false;
       DynamicString passArgs { };
+      int ptArg = 0;
+
       for(c = 1; c<app.argc; c++)
       {
-         if(!strcmp(app.argv[c], "-t"))
-            openAsText = true;
-         else if(!strcmp(app.argv[c], "-no-parsing"))
-            ide.noParsing = true;
-         else if(!strcmp(app.argv[c], "-debug-start"))
-            debugStart = true;
-         else if(!strcmp(app.argv[c], "-debug-work-dir"))
-            debugWorkDir = true;
-         else if(!passThrough && !strcmp(app.argv[c], "-@"))
-            passThrough = true;
-         else if(passThrough)
+         if(passThrough)
          {
-            passArgs.concat(" ");
-            passArgs.concat(app.argv[c]);
+            char * arg = app.argv[c];
+            char * buf = new char[strlen(arg)*2+1];
+            if(ptArg++ > 0)
+               passArgs.concat(" ");
+            PassArg(buf, arg);
+            passArgs.concat(buf);
+            delete buf;
          }
          else if(debugWorkDir)
          {
@@ -2731,6 +3001,16 @@ class IDEWorkSpace : Window
             StripQuotes(passDebugWorkDir, passDebugWorkDir);
             debugWorkDir = false;
          }
+         else if(!strcmp(app.argv[c], "-t"))
+            openAsText = true;
+         else if(!strcmp(app.argv[c], "-no-parsing"))
+            ide.noParsing = true;
+         else if(!strcmp(app.argv[c], "-debug-start"))
+            debugStart = true;
+         else if(!strcmp(app.argv[c], "-debug-work-dir"))
+            debugWorkDir = true;
+         else if(!strcmp(app.argv[c], "-@"))
+            passThrough = true;
          else
          {
             char fullPath[MAX_LOCATION];
@@ -2751,42 +3031,35 @@ class IDEWorkSpace : Window
             {
                if(isProject && !FileExists(fullPath))
                {
-                  // The NewProject will handle directory creation
-                  /*if(!dirAttribs.isDirectory)
+                  char name[MAX_LOCATION];
+                  NewProjectDialog newProjectDialog;
+
+                  if(projectView)
                   {
-                     MakeDir(parentPath);
-                     dirAttribs = FileExists(parentPath);
+                     projectView.visible = false;
+                     if(!projectView.Destroy(0))
+                        return true;
                   }
-                  if(dirAttribs.isDirectory)*/
-                  {
-                     char name[MAX_LOCATION];
-                     NewProjectDialog newProjectDialog;
 
-                     if(projectView)
-                     {
-                        projectView.visible = false;
-                        if(!projectView.Destroy(0))
-                           return true;
-                     }
-
-                     newProjectDialog = { master = this };
+                  newProjectDialog = { master = this };
 
-                     strcpy(name, app.argv[c]);
-                     StripExtension(name);
-                     GetLastDirectory(name, name);
-                     newProjectDialog.projectName.contents = name;
-                     newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
-                     newProjectDialog.locationEditBox.path = parentPath;
-                     newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
+                  strcpy(name, app.argv[c]);
+                  StripExtension(name);
+                  GetLastDirectory(name, name);
+                  newProjectDialog.projectName.contents = name;
+                  newProjectDialog.projectName.NotifyModified(newProjectDialog, newProjectDialog.projectName);
+                  newProjectDialog.locationEditBox.path = parentPath;
+                  newProjectDialog.NotifyModifiedLocation(newProjectDialog.locationEditBox);
 
-                     newProjectDialog.Modal();
-                     if(projectView)
-                     {
-                        ideSettings.AddRecentProject(projectView.fileName);
-                        ide.UpdateRecentMenus();
-                        settingsContainer.Save();
-                     }
+                  incref newProjectDialog;
+                  newProjectDialog.Modal();
+                  if(projectView)
+                  {
+                     ideSettings.AddRecentProject(projectView.fileName);
+                     ide.UpdateRecentMenus();
+                     settingsContainer.Save();
                   }
+                  delete newProjectDialog;
                   // Open only one project
                   break;
                }
@@ -2798,49 +3071,8 @@ class IDEWorkSpace : Window
          }
       }
       if(passThrough && projectView && projectView.project && workspace)
-      {
-         char * fixSpacing = new char[Max(passArgs.size * 1.5, 32)];
-         {
-            int c, d = 0;
-            char j = 0;
-            char k = ' ';
-            char l = 0;
-            bool inQuote = false;
-            for(c=0; c<passArgs.size; c++)
-            {
-               l = passArgs[c];
-               if(inQuote && k != '\\' && l == ' ')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = ' ';
-                  inQuote = false;
-               }
-               else if(inQuote && l == '\0')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = '\0';
-                  inQuote = false;
-               }
-               else if(!inQuote && j != '\\' && k == ' ' && l != '-' && l != ' ')
-               {
-                  fixSpacing[d++] = '\"';
-                  fixSpacing[d++] = l;
-                  inQuote = true;
-               }
-               else if(k == '\\' && l == ' ')
-                  fixSpacing[d++] = ' ';
-               else if(k == '\\' && l == '\\')
-                  fixSpacing[d++] = '\\';
-               else if(l != '\\')
-                  fixSpacing[d++] = l;
-               j = k;
-               k = l;
-            }
-            fixSpacing[d] = '\0';
-         }
-         workspace.commandLineArgs = fixSpacing;
-      }
-      if(passDebugWorkDir)
+         workspace.commandLineArgs = passArgs;
+      if(passDebugWorkDir && projectView && projectView.project && workspace)
       {
          workspace.debugDir = passDebugWorkDir;
          delete passDebugWorkDir;
@@ -3000,8 +3232,9 @@ class IDEWorkSpace : Window
       {
          if(!libPathExists[item])  // fstrcmp should be used
          {
-            newLibPaths.Add(item);
-            libPathExists[item] = true;
+            String s = CopyString(item);
+            newLibPaths.Add(s);
+            libPathExists[s] = true;
          }
       }
 
@@ -3018,8 +3251,9 @@ class IDEWorkSpace : Window
       {
          if(!libPathExists[oldPaths[c]])  // fstrcmp should be used
          {
-            newLibPaths.Add(oldPaths[c]);
-            libPathExists[oldPaths[c]] = true;
+            String s = CopyString(oldPaths[c]);
+            newLibPaths.Add(s);
+            libPathExists[s] = true;
          }
       }
 
@@ -3044,6 +3278,7 @@ class IDEWorkSpace : Window
 #endif*/
       delete newList;
 
+      newLibPaths.Free();
       delete newLibPaths;
       delete libPathExists;
 #endif
@@ -3154,6 +3389,12 @@ class IDEWorkSpace : Window
       delete driverItems;
       delete skinItems;
       delete ideSettings;
+      if(documentor)
+      {
+         documentor.Puts("Quit\n");
+         documentor.Wait();
+         delete documentor;
+      }
    }
 }
 
@@ -3164,6 +3405,148 @@ void DestroyDir(char * path)
    delete fsi;
 }
 
+#if defined(__WIN32__)
+define sdkDirName = "Ecere SDK";
+#else
+define sdkDirName = "ecere";
+#endif
+
+void FindAndShellOpenInstalledFolder(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';
+   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])
+   {
+      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)
+   {
+      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      if(FileExists(p).isDirectory)
+      {
+         ShellOpen(p);
+         break;
+      }
+   }
+   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])
+   {
+      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+   GetEnvironment("AppData", v, maxPathLen);
+   if(v[0])
+   {
+      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+   GetEnvironment("ProgramFiles", v, maxPathLen);
+   if(v[0])
+   {
+      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+   GetEnvironment("ProgramFiles(x86)", v, maxPathLen);
+   if(v[0])
+   {
+      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+   GetEnvironment("SystemDrive", v, maxPathLen);
+   if(v[0])
+   {
+      strncpy(p, v, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, "Program Files"); PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+#else
+   GetEnvironment("XDG_DATA_DIRS", v, maxPathLen);
+   numTokens = TokenizeWith(v, sizeof(tokens) / sizeof(byte *), tokens, ":", false);
+   for(c=0; c<numTokens; c++)
+   {
+      strncpy(p, tokens[c], MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, sdkDirName); PathCat(p, subdir); paths.Add(CopyString(p));
+   }
+#endif
+   for(path : paths)
+   {
+      strncpy(p, path, MAX_LOCATION); p[MAX_LOCATION-1] = '\0';
+      PathCat(p, name);
+      if(FileExists(p).isFile)
+      {
+         ShellOpen(p);
+         break;
+      }
+   }
+   delete p;
+   delete v;
+   paths.Free();
+   delete paths;
+}
+
 class RecursiveDeleteFolderFSI : NormalFileSystemIterator
 {
    bool preserveRootFolder;
@@ -3192,16 +3575,25 @@ class IDEApp : GuiApplication
 
    bool Init()
    {
+      char ext[MAX_EXTENSION];
       SetLoggingMode(stdOut, null);
       //SetLoggingMode(debug, null);
 
       settingsContainer.Load();
+      if(argc > 1 && !strcmpi(GetExtension(argv[1], ext), "3ds"))
+      {
+         app.driver = "OpenGL";
+         ide.driverItems[1].checked = true;
+      }
+      else
+      {
 #if defined(__unix__) || defined(__APPLE__)
-      app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
+         app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "X";
 #else
-      app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
+         app.driver = (ideSettings.displayDriver && !strcmp(ideSettings.displayDriver, "OpenGL")) ? ideSettings.displayDriver : "GDI";
 #endif
-      ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
+         ide.driverItems[ideSettings.displayDriver && !strcmp(ideSettings.displayDriver,"OpenGL")].checked = true;
+      }
 
       SetInIDE(true);
 
@@ -3223,6 +3615,33 @@ class IDEApp : GuiApplication
       return true;
    }
 
+   bool Cycle(bool idle)
+   {
+      if(ide.documentor)
+      {
+         if(ide.documentor.Peek())
+         {
+            char line[1024];
+            ide.documentor.GetLine(line, sizeof(line));
+            if(!strcmpi(line, "Exited"))
+            {
+               ide.documentor.CloseInput();
+               ide.documentor.CloseOutput();
+               ide.documentor.Wait();
+               delete ide.documentor;
+            }
+         }
+         if(ide.documentor && ide.documentor.eof)
+         {
+            ide.documentor.CloseInput();
+            ide.documentor.CloseOutput();
+            ide.documentor.Wait();
+            delete ide.documentor;
+         }
+      }
+      return true;
+   }
+
    bool LoadIncludeFile()
    {
       bool result = false;