ide; fixed ide rendered useless at compiling when ide binary that's being run is...
[sdk] / ide / src / project / ProjectView.ec
index 7a1df6d..57b7118 100644 (file)
@@ -45,17 +45,10 @@ static Array<FileFilter> resourceFilters
 { [
    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png,*.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
    { $"3D Studio Model Files (*.3ds)", "3ds" },
+   { $"Translations (*.mo)", "mo" },
    { $"All files", null }
 ] };
 
-static Array<FileType> fileTypes
-{ [
-   { $"Based on extension", null },
-   { $"Text",               "txt" },
-   { $"Image",              "jpg" },
-   { $"3D Studio Model",    "3ds" }
-] };
-
 static Array<FileFilter> projectFilters
 { [
    { $"Project Files (*.epj)", ProjectExtension },
@@ -77,6 +70,7 @@ static char * iconNames[] =
    "<:ecere>status/folderOpen.png",                /*openFolder*/
    "<:ecere>mimeTypes/textEcereSource.png",        /*ecFile*/
    "<:ecere>mimeTypes/textEcereHeader.png",        /*ehFile*/
+   "<:ecere>mimeTypes/textCSource.png",            /*sFile*/ // TODO: change sFile icon to differentiate from cFile icon
    "<:ecere>mimeTypes/textCSource.png",            /*cFile*/
    "<:ecere>mimeTypes/textCHeader.png",            /*hFile*/
    "<:ecere>mimeTypes/textC++Source.png",          /*cppFile*/
@@ -93,7 +87,7 @@ static char * iconNames[] =
 
 enum PrepareMakefileMethod { normal, force, forceExists };
 
-enum BuildType { build, rebuild, relink, run, start, restart };
+enum BuildType { build, rebuild, relink, run, start, restart, clean };
 enum BuildState
 {
    none, buildingMainProject, buildingSecondaryProject, compilingFile;
@@ -155,48 +149,34 @@ class ProjectView : Window
       get { return workspace; }
    }
    
-   /*property Project project
-   {
-      set
-      {
-         if(project)
-         {
-            DeleteNode(project.topNode);
-            project.Free();
-         }
-         project = value;
-         if(project)
-         {
-            AddNode(project.topNode, null);
-            fileDialog.currentDirectory = project.topNode.path;
-            resourceFileDialog.currentDirectory = project.topNode.path;
-
-            // Make sure this is done already...
-            {
-               char filePath[MAX_LOCATION];
-               strcpy(filePath, project.topNode.path);
-               PathCat(filePath, project.topNode.name);
-               strcat(filePath, ".epj");
-               fileName = filePath;
-            }
-
-            ide.statusBar.text = "Generating Makefile & Dependencies...";
-            app.UpdateDisplay();
-            // REDJ set makefile generation flag so generation occurs only when compiling instead of generating on the spot
-            project.config.makingModified = true;
-            ide.statusBar.text = "Initializing Debugger";
-            ide.statusBar.text = null;
-            app.UpdateDisplay();
-         }
-      }
-      get { return project; }
-   }*/
-
    bool drawingInProjectSettingsDialog;
    bool drawingInProjectSettingsDialogHeader;
    ProjectSettings projectSettingsDialog;
 
    bool stopBuild;
+   PopupMenu popupMenu;
+
+   ProjectView()
+   {
+      NodeIcons c;
+      for(c = 0; c < NodeIcons::enumSize; c++)
+      {
+         icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
+         AddResource(icons[c]);
+      }
+      fileList.AddField(DataField { dataType = class(ProjectNode), freeData = false, userData = this });
+   }
+
+   ~ProjectView()
+   {
+      DebugStop();
+      ide.DestroyTemporaryProjectDir();
+      if(project)
+      {
+         workspace.Free();
+         delete workspace;
+      }
+   }
 
    ListBox fileList
    {
@@ -230,7 +210,6 @@ class ProjectView : Window
             if(node.type == NodeTypes::project || node.type == resources || node.type == file || node.type == folder)
             {
                bool buildMenuUnavailable = buildInProgress;
-               PopupMenu popupMenu;
                Menu popupContent { };
                
                if(node.type == NodeTypes::project)
@@ -241,9 +220,12 @@ class ProjectView : Window
                      MenuItem { popupContent, $"Relink", l, NotifySelect = ProjectLink }.disabled = buildMenuUnavailable;
                      MenuItem { popupContent, $"Rebuild", r, NotifySelect = ProjectRebuild }.disabled = buildMenuUnavailable;
                      MenuItem { popupContent, $"Clean", c, NotifySelect = ProjectClean }.disabled = buildMenuUnavailable;
+                     MenuItem { popupContent, $"Real Clean", d, NotifySelect = ProjectRealClean }.disabled = buildMenuUnavailable;
                      MenuItem { popupContent, $"Regenerate Makefile", m, NotifySelect = ProjectRegenerate }.disabled = buildMenuUnavailable;
                      MenuDivider { popupContent };
                   }
+                  MenuItem { popupContent, $"Debug Generate Symbols", l, NotifySelect = FileDebugGenerateSymbols }.disabled = buildMenuUnavailable;
+                  MenuDivider { popupContent };
                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
@@ -257,7 +239,6 @@ class ProjectView : Window
                      MenuItem { popupContent, $"Remove project from workspace", r, NotifySelect = ProjectRemove }.disabled = buildMenuUnavailable;
                      MenuDivider { popupContent };
                   }
-                  MenuItem { popupContent, $"Active Configuration...", s, Key { f5, alt = true } , NotifySelect = MenuConfig };
                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
                   MenuDivider { popupContent };
                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
@@ -270,6 +251,7 @@ class ProjectView : Window
                {
                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
+                  MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
                   MenuItem { popupContent, $"Add Resources to Project...", f, NotifySelect = ResourcesAddFiles };
                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
                   MenuDivider { popupContent };
@@ -279,8 +261,13 @@ class ProjectView : Window
                else if(node.type == file)
                {
                   MenuItem { popupContent, $"Open", o, NotifySelect = FileOpenFile };
+                  MenuDivider { popupContent };
+                  MenuItem { popupContent, $"Clean", l, NotifySelect = FileClean }.disabled = buildMenuUnavailable;
                   MenuItem { popupContent, $"Compile", c, Key { f7, ctrl = true}, NotifySelect = FileCompile }.disabled = buildMenuUnavailable;
                   MenuDivider { popupContent };
+                  MenuItem { popupContent, $"Debug Precompile", l, NotifySelect = FileDebugPrecompile }.disabled = buildMenuUnavailable;
+                  MenuItem { popupContent, $"Debug Compile", l, NotifySelect = FileDebugCompile }.disabled = buildMenuUnavailable;
+                  MenuDivider { popupContent };
                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
                   MenuDivider { popupContent };
                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
@@ -310,6 +297,9 @@ class ProjectView : Window
                      MenuItem { popupContent, $"Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
                   }
                   MenuDivider { popupContent };
+                  MenuItem { popupContent, $"Clean", l, NotifySelect = FileClean }.disabled = buildMenuUnavailable;
+                  MenuItem { popupContent, $"Compile", c, Key { f7, ctrl = true}, NotifySelect = FileCompile }.disabled = buildMenuUnavailable;
+                  MenuDivider { popupContent };
                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
                   MenuDivider { popupContent };
                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
@@ -324,6 +314,11 @@ class ProjectView : Window
                   position = {
                      x + clientStart.x + absPosition.x - app.desktop.position.x, 
                      y + clientStart.y + absPosition.y - app.desktop.position.y };
+
+                  void NotifyDestroyed(Window window, DialogResult result)
+                  {
+                     popupMenu = null;
+                  }
                };
                popupMenu.Create();
             }
@@ -331,6 +326,16 @@ class ProjectView : Window
          return true;
       }
 
+      bool NotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch)
+      {
+         if(key == altUp || key == altDown)
+         {
+            SelectNextProject(key == altUp);
+            return false;
+         }
+         return true;
+      }
+
       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
       {
          if(row)
@@ -447,12 +452,10 @@ class ProjectView : Window
    FileDialog fileDialog
    {
       autoCreate = false, mayNotExist = true, filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
-      types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType);
    };
    FileDialog resourceFileDialog
    {
       autoCreate = false, mayNotExist = true, filters = resourceFilters.array, sizeFilters = resourceFilters.count * sizeof(FileFilter);
-      types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType);
    };
 
    Menu fileMenu { menu, $"File", f };
@@ -468,6 +471,35 @@ class ProjectView : Window
       }
       if(buildInProgress)
          return false;
+
+      if(modifiedDocument)
+      {
+         DialogResult dialogRes;
+         char msg[2048];
+         bool first = true;
+         strcpy(msg, $"You have modified projects.\nSave changes to ");
+         for(p : ide.workspace.projects)
+         {
+            if(p.topNode.modified)
+            {
+               if(!first) strcat(msg, ", ");
+               strcat(msg, p.name);
+               first = false;
+            }
+         }
+         strcat(msg, "?");
+
+         dialogRes = MessageBox { master = master, type = yesNoCancel, text = parent.caption ? parent.caption : rootWindow.caption, contents = msg }.Modal();
+
+         if(dialogRes == yes)
+         {
+            // TOFIX: Precomp error if brackets are taken out
+            return (DialogResult)MenuFileSave(null, 0) != cancel;
+         }
+         else if(dialogRes == cancel)
+            return false;
+         modifiedDocument = false;
+      }
       return true;
    }
 
@@ -490,10 +522,12 @@ class ProjectView : Window
    {
       for(prj : ide.workspace.projects)
       {
-         if(prj.topNode.modified && prj.Save(prj.filePath))
+         if(prj.topNode.modified)
          {
-            // ProjectUpdateMakefileForAllConfigs(prj, true, true);
-            prj.topNode.modified = false;
+            prj.StopMonitoring();
+            if(prj.Save(prj.filePath))
+               prj.topNode.modified = false;
+            prj.StartMonitoring();
          }
       }
       modifiedDocument = false;
@@ -510,570 +544,585 @@ class ProjectView : Window
 
    bool GetRelativePath(char * filePath, char * relativePath)
    {
-      /*ProjectNode node;
-      char moduleName[MAX_FILENAME]; //, modulePath[MAX_LOCATION];
-      GetLastDirectory(filePath, moduleName);
-      
-      // try with workspace dir first?
-      if((node = project.topNode.Find(moduleName, false)))
-      {
-         strcpy(relativePath, node.path);
-         PathCatSlash(relativePath, node.name);
-         return true;
-      }
-      // WARNING: On failure, relative path is uninitialized
-      return false;   */
       return project.GetRelativePath(filePath, relativePath);
    }
 
-   ProjectNode GetNodeFromWindow(Window document, Project project)
+   ProjectNode GetNodeFromWindow(Window document, Project project, bool isCObject)
    {
       if(document.fileName)
       {
          char winFileName[MAX_LOCATION];
          char * documentFileName = GetSlashPathBuffer(winFileName, document.fileName);
-         for(p : ide.workspace.projects)
+         ProjectNode node;
+         Project prj;
+         if(isCObject)
          {
-            Project prj = project ? project : p;
-            ProjectNode node;
-            char moduleName[MAX_FILENAME], modulePath[MAX_LOCATION];
-            GetLastDirectory(document.fileName, moduleName);
-
-            if((node = prj.topNode.Find(moduleName, false)))
+            char name[MAX_FILENAME];
+            GetLastDirectory(documentFileName, name);
+            ChangeExtension(name, "ec", name);
+            for(p : ide.workspace.projects)
             {
-               strcpy(modulePath, prj.topNode.path);
-               PathCatSlash(modulePath, node.path);
-               PathCatSlash(modulePath, node.name);
-               if(!fstrcmp(documentFileName, modulePath))
-               {
+               prj = project ? project : p;
+               if((node = prj.topNode.Find(name, false)))
                   return node;
-               }
+               if(project) break;
+            }
+         }
+         else
+         {
+            for(p : ide.workspace.projects)
+            {
+               prj = project ? project : p;
+               if((node = prj.topNode.FindByFullPath(documentFileName, false)))
+                  return node;
+               if(project) break;
             }
-            if(project) break;
          }
       }
       return null;
    }
 
-   void Compile(ProjectNode node)
+   //                          ((( UTILITY FUNCTIONS )))
+   //
+   //  ************************************************************************
+   //  *** These methods below are part of a sequence of events, and as     ***
+   //  *** such they must be passed the current compiler and project config ***
+   //  ************************************************************************
+   bool DisplayCompiler(CompilerConfig compiler, bool cleanLog)
    {
-      char fileName[MAX_LOCATION];
-      char extension[MAX_EXTENSION];
-      Window document;
-      Project prj = node.project;
-      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
-      DirExpression objDir = prj.objDir;
-
-      strcpy(fileName, prj.topNode.path);
-      PathCatSlash(fileName, objDir.dir);
-      PathCatSlash(fileName, node.name);
-      StripExtension(fileName);
-      strcat(fileName, ".o");
-      if(FileExists(fileName))
-         DeleteFile(fileName);
-
-      GetExtension(node.name, extension);
-      if(!strcmp(extension, "ec"))
-      {
-         // Delete generated C file
-         strcpy(fileName, prj.topNode.path);
-         PathCat(fileName, objDir.dir);
-         PathCat(fileName, node.name);
-         StripExtension(fileName);
-         strcat(fileName, ".c");
-         if(FileExists(fileName))
-            DeleteFile(fileName);
-
-         // Delete symbol file
-         strcpy(fileName, prj.topNode.path);
-         PathCat(fileName, node.path);
-         PathCat(fileName, node.name);
-         StripExtension(fileName);
-         strcat(fileName, ".sym");
-         if(FileExists(fileName))
-            DeleteFile(fileName);
-      }
+      ide.outputView.buildBox.Logf($"%s Compiler\n", compiler ? compiler.name : $"{problem with compiler selection}");
+      return true;
+   }
 
-      stopBuild = false;
+   bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler,
+      CompilerConfig compiler, ProjectConfig config)
+   {
+      bool isReady = true;
+      char message[MAX_F_STRING];
+      LogBox logBox = ide.outputView.buildBox;
 
-      // Check if we have to save
-      strcpy(fileName, prj.topNode.path);
-      PathCatSlash(fileName, node.path);
-      PathCatSlash(fileName, node.name);
-      for(document = ide.firstChild; document; document = document.next)
+      ShowOutputBuildLog(cleanLog);
+
+      if(displayCompiler)
+         DisplayCompiler(compiler, false);
+
+      ProjectPrepareCompiler(project, compiler);
+      ProjectPrepareMakefile(project, method, compiler, config);
+      return true;
+   }
+
+   bool ProjectPrepareCompiler(Project project, CompilerConfig compiler)
+   {
+      if(!project.GenerateCrossPlatformMk(app.includeFile) || !project.GenerateCompilerCf(compiler))
+         ide.outputView.buildBox.Logf($"Error generating compiler configuration (Is the project/config directory writable?)\n");
+      return true;
+   }
+
+   // Note: Compiler is only passed in to for VisualStudio support
+   bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, CompilerConfig compiler, ProjectConfig config)
+   {
+      if(compiler.type.isVC)
       {
-         if(document.modifiedDocument)
-         {
-            char documentFileName[MAX_LOCATION];
-            if(!fstrcmp(GetSlashPathBuffer(documentFileName, document.fileName), fileName))
-               if(!document.MenuFileSave(null, 0))
-                  return;
-         }
+         // I'm guessing we'll want to support generating VS files on Linux as well...
+         ide.statusBar.text = $"Generating Visual Studio Solution...";
+         app.UpdateDisplay();
+         //GenerateVSSolutionFile(project, compiler);
+         ide.statusBar.text = $"Generating Visual Studio Project...";
+         app.UpdateDisplay();
+         //GenerateVCProjectFile(project, compiler);
+         ide.statusBar.text = null;
+         app.UpdateDisplay();
+         return true;
       }
-
-      if(ProjectPrepareForToolchain(prj, normal, true, true))
+      else
       {
-         if(!node.isExcluded)
+         char makefilePath[MAX_LOCATION];
+         char makefileName[MAX_LOCATION];
+         bool exists;
+         LogBox logBox = ide.outputView.buildBox;
+         
+         strcpy(makefilePath, project.topNode.path);
+         project.CatMakeFileName(makefileName, config);
+         PathCatSlash(makefilePath, makefileName);
+
+         exists = FileExists(makefilePath);
+         if(method == force ||
+           (method == forceExists && exists) ||
+           (method == normal && (!exists || (config && config.makingModified))))
          {
-            buildInProgress = compilingFile;
-            ide.AdjustBuildMenus();
+            char * reason;
+            char * action;
+            ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
+            app.UpdateDisplay();
 
-            //ide.outputView.ShowClearSelectTab(build);
-            // this stuff doesn't even appear
-            //ide.outputView.buildBox.Logf("%s Compiler\n", compiler.name);
-            if(prj.config)
-               ide.outputView.buildBox.Logf($"Compiling single file %s in project %s using the %s configuration...\n", node.name, prj.name, prj.config.name);
+            if((method == normal && !exists) || (method == force && !exists))
+               action = $"Generating ";
+            else if(method == force)
+               action = $"Regenerating ";
+            else if(method == normal || method == forceExists)
+               action = $"Updating ";
+            else
+               action = "";
+            if(!exists)
+               reason = $"Makefile doesn't exist. ";
+            else if(project.topNode.modified)
+               reason = $"Project has been modified. ";
             else
-               ide.outputView.buildBox.Logf($"Compiling single file %s in project %s...\n", node.name, prj.name);
+               reason = "";
 
-            prj.Compile(node);
-            buildInProgress = none;
-            ide.AdjustBuildMenus();
+            //logBox.Logf("%s\n", makefileName);
+            logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, GetConfigName(config));
+
+            if(!project.GenerateMakefile(null, false, null, config))
+               ide.outputView.buildBox.Logf($"Error generating makefile (Is the project directory writable?)\n");
+
+            ide.statusBar.text = null;
+            app.UpdateDisplay();
+            return true;
          }
-         else
-            ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
       }
-      delete objDir;
-      delete compiler;
+      return false;
+   }
+   
+   bool BuildInterrim(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, bool justPrint)
+   {
+      if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
+      {
+         ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, GetConfigName(config));
+         return Build(prj, buildType, compiler, config, justPrint);
+      }
+      return false;
    }
 
-   void GoToError(const char * line)
+   bool DebugStopForMake(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config)
    {
-      char * colon;
-      
-      while(isspace(*line)) line++;
-      colon = strstr(line, ":");
+      bool result = false;
+      // TOFIX: DebugStop is being abused and backfiring on us.
+      //        It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
 
+      //        configIsInDebugSession seems to be used for two OPPOSITE things:
+      //        If we're debugging another config, we need to unload the executable!
+      //        In building, we want to stop if we're debugging the 'same' executable
+      if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
       {
-         int lineNumber = 0;
-         int col = 1;
-         bool lookForLineNumber = true;
-
-         // deal with linking error
-         if(colon && colon[1] == ' ')
+         if(buildType == start || buildType == restart)
          {
-            colon = strstr(colon + 1, ":");
-            if(colon && !colon[1])
-            {
-               colon = strstr(line, ":");
-               lookForLineNumber = false;
-            }
-            else if(colon && !isdigit(colon[1]))
-            {
-               line = colon + 1;
-               colon = strstr(line, ":");
-            }
+            if(ide.debugger && ide.debugger.isPrepared)
+               result = DebugStop();
          }
-         // Don't be mistaken by the drive letter colon
-         if(colon && (colon[1] == '/' || colon[1] == '\\'))
-            colon = strstr(colon + 1, ":");
-         if(colon && lookForLineNumber)
+         else
          {
-            char * comma;
-            // MSVS Errors
-            char * par = RSearchString(line, "(", colon - line, true, false);
-            if(par && strstr(par, ")"))
-               colon = par;
-            else if((colon+1)[0] == ' ')
-            {
-               // NOTE: This is the same thing as saying 'colon = line'
-               for( ; colon != line; colon--)
-                  /*if(*colon == '(')
-                     break*/;
-            }
-            lineNumber = atoi(colon + 1);
-            /*
-            comma = strchr(colon, ',');
-            if(comma)
-               col = atoi(comma+1);
-            */
-            comma = strchr(colon+1, ':');
-            if(comma)
-               col = atoi(comma+1);
+            if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared)
+               result = DebugStop();
          }
-         
+      }
+      return result;
+   }
+
+   bool Build(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, bool justPrint)
+   {
+      bool result = true;
+      Window document;
+
+      stopBuild = false;
+      for(document = master.firstChild; document; document = document.next)
+      {
+         if(document.modifiedDocument)
          {
-            char moduleName[MAX_LOCATION], filePath[MAX_LOCATION];
-            char * bracket;
-            if(colon)
+            ProjectNode node = GetNodeFromWindow(document, prj, false);
+            if(node && !document.MenuFileSave(null, 0))
             {
-               // Cut module name
-               strncpy(moduleName, line, colon - line);
-               moduleName[colon - line] = '\0';
+               result = false;
+               break;
             }
-            else
-               strcpy(moduleName, line);
+         }
+      }
+      if(result)
+      {
+         DirExpression targetDir = prj.GetTargetDir(compiler, config);
 
-            // Remove stuff in brackets
-            /*
-            bracket = strstr(moduleName, "(");
-            if(bracket) *bracket = '\0';
-            */
-            MakeSlashPath(moduleName);
+         DebugStopForMake(prj, buildType, compiler, config);
 
-            if(!colon)
+         // TODO: Disabled until problems fixed... is it fixed?
+         if(buildType == rebuild || (config && config.compilingModified))
+            prj.Clean(compiler, config, false, justPrint);
+         else
+         {
+            if(buildType == relink || (config && config.linkingModified))
             {
-               // Check if it's one of our modules
-               ProjectNode node = project.topNode.Find(moduleName, false);
-               if(node)
-               {
-                  strcpy(moduleName, node.path);
-                  PathCatSlash(moduleName, node.name);
-               }
-               else
-                  moduleName[0] = '\0';
+               char target[MAX_LOCATION];
+
+               strcpy(target, prj.topNode.path);
+               PathCat(target, targetDir.dir);
+               prj.CatTargetFileName(target, compiler, config);
+               if(FileExists(target))
+                  DeleteFile(target);
             }
-            if(moduleName[0])
+            if(config && config.symbolGenModified)
             {
-               CodeEditor codeEditor;
-               strcpy(filePath, project.topNode.path);
-               PathCatSlash(filePath, moduleName);
-      
-               codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
-               if(!codeEditor)
-               {
-                  char name[MAX_LOCATION];
-                  // TOFIX: Improve on this, don't use only filename, make a function
-                  if(ide && ide.workspace)
-                  {
-                     for(prj : ide.workspace.projects)
-                     {
-                        if(prj.topNode.FindWithPath(moduleName, false))
-                        {
-                           strcpy(filePath, prj.topNode.path);
-                           PathCatSlash(filePath, moduleName);
-                           codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
-                           if(codeEditor)
-                              break;
-                        }
-                     }
-                  }
-               }
-               if(codeEditor && lineNumber)
-               {
-                  EditBox editBox = codeEditor.editBox;
-                  editBox.GoToLineNum(lineNumber - 1);
-                  editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
-               }
+               DirExpression objDir = prj.GetObjDir(compiler, config);
+               char fileName[MAX_LOCATION];
+               char moduleName[MAX_FILENAME];
+               strcpy(fileName, prj.topNode.path);
+               PathCatSlash(fileName, objDir.dir);
+               strcpy(moduleName, prj.moduleName);
+               strcat(moduleName, ".main.ec");
+               PathCatSlash(fileName, moduleName);
+               if(FileExists(fileName))
+                  DeleteFile(fileName);
+               ChangeExtension(fileName, "c", fileName);
+               if(FileExists(fileName))
+                  DeleteFile(fileName);
+               ChangeExtension(fileName, "o", fileName);
+               if(FileExists(fileName))
+                  DeleteFile(fileName);
+
+               delete objDir;
             }
          }
-      }
-   }
-
-   bool OpenNode(ProjectNode node)
-   {
-      char filePath[MAX_LOCATION];
-      node.GetFullFilePath(filePath);
-      return ide.OpenFile(filePath, normal, true/*false Why was it opening hidden?*/, null, something, normal) ? true : false;
-   }
-
-   void AddNode(ProjectNode node, DataRow addTo)
-   {
-      DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
+         buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
+         ide.AdjustBuildMenus();
+         ide.AdjustDebugMenus();
 
-      row.tag = (int)node;
-      node.row = row;
+         result = prj.Build(buildType == run, null, compiler, config, justPrint, normal);
 
-      if(node.type == resources)
-         resourceRow = row;
+         if(config)
+         {
+            config.compilingModified = false;
+            if(!ide.ShouldStopBuild())
+               config.linkingModified = false;
 
-      row.SetData(null, node);
+            config.symbolGenModified = false;
+         }
+         buildInProgress = none;
+         ide.AdjustBuildMenus();
+         ide.AdjustDebugMenus();
 
-      if(node.files && node.files.first && node.parent && 
-            !(!node.parent.parent && 
-               (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
-                  !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
-         row.collapsed = true;
-      else if(node.type == folder)
-         node.icon = openFolder;
+         ide.workspace.modified = true;
 
-      if(node.files)
-      {
-         for(child : node.files)
-            AddNode(child, row);
+         delete targetDir;
       }
+      return result;
    }
 
-   void DeleteNode(ProjectNode projectNode)
-   {
-      if(projectNode.files)
-      {
-         ProjectNode child;
-         while(child = projectNode.files.first)
-            DeleteNode(child);
-      }
-      fileList.DeleteRow(projectNode.row);
-      projectNode.Delete();
-   }
+   //                          ((( USER ACTIONS )))
+   //
+   //  ************************************************************************
+   //  *** Methods below should atomically start a process, and as such     ***
+   //  *** they can query compiler and config directly from ide and project ***
+   //  *** but ONLY ONCE!!!                                                 ***
+   //  ************************************************************************
 
-   ProjectView()
+   bool ProjectBuild(MenuItem selection, Modifiers mods)
    {
-      NodeIcons c;
-      for(c = 0; c < NodeIcons::enumSize; c++)
+      Project prj = project;
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config;
+      if(selection || !ide.activeClient)
       {
-         icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
-         AddResource(icons[c]);
+         DataRow row = fileList.currentRow;
+         ProjectNode node = row ? (ProjectNode)row.tag : null;
+         if(node) prj = node.project;
       }
-      fileList.AddField(DataField { dataType = class(ProjectNode), freeData = false, userData = this });
-   }
-
-   ~ProjectView()
-   {
-      DebugStop();
-      ide.DestroyTemporaryProjectDir();
-      if(project)
+      else
       {
-         workspace.Free();
-         delete workspace;
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
+         if(node)
+            prj = node.project;
       }
-   }
-
-   bool ProjectSave(MenuItem selection, Modifiers mods)
-   {
-      DataRow row = fileList.currentRow;
-      ProjectNode node = row ? (ProjectNode)row.tag : null;
-      Project prj = node ? node.project : null;
-      if(prj)
+      config = prj.config;
+      if(/*prj != project || */!prj.GetConfigIsInDebugSession(config) || !ide.DontTerminateDebugSession($"Project Build"))
       {
-         if(prj.Save(prj.filePath))
-         {
-            // ProjectUpdateMakefileForAllConfigs(prj, true, true);
-            prj.topNode.modified = false;
-            prj = null;
-            for(p : ide.workspace.projects)
-            {
-               if(p.topNode.modified)
-               { 
-                  prj = p;
-                  break;
-               }
-            }
-            if(!prj)
-               modifiedDocument = false;
-            Update(null);
-         }
+         BuildInterrim(prj, build, compiler, config, mods.ctrl && mods.shift);
       }
+      delete compiler;
       return true;
    }
 
-   bool ShowOutputBuildLog(bool cleanLog)
+   bool ProjectLink(MenuItem selection, Modifiers mods)
    {
-      OutputView output = ide.outputView;
-      if(cleanLog)
-         output.ShowClearSelectTab(build);
+      Project prj = project;
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config;
+      if(selection || !ide.activeClient)
+      {
+         DataRow row = fileList.currentRow;
+         ProjectNode node = row ? (ProjectNode)row.tag : null;
+         if(node) prj = node.project;
+      }
       else
       {
-         output.SelectTab(build);
-         output.Show();
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
+         if(node)
+            prj = node.project;
       }
-   }
-
-   bool DisplayCompiler(bool cleanLog)
-   {
-      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
-      ShowOutputBuildLog(cleanLog);
-      ide.outputView.buildBox.Logf($"%s Compiler\n", compiler ? compiler.name : $"{problem with compiler selection}");
-      delete compiler;
-   }
-
-   bool ProjectUpdateMakefileForAllConfigs(Project project, bool cleanLog, bool displayCompiler)
-   {
-      ProjectConfig currentConfig = project.config;
-      ShowOutputBuildLog(cleanLog);
-
-      if(displayCompiler)
-         DisplayCompiler(false);
-      
-      for(config : project.configurations)
+      config = prj.config;
+      if(!prj.GetConfigIsInDebugSession(config) ||
+            (!ide.DontTerminateDebugSession($"Project Link") && DebugStopForMake(prj, relink, compiler, config)))
       {
-         project.config = config;
-         ProjectPrepareMakefile(project, forceExists, false, false);
+         if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
+         {
+            ide.outputView.buildBox.Logf($"Relinking project %s using the %s configuration...\n", prj.name, GetConfigName(config));
+            if(config)
+               config.linkingModified = true;
+            Build(prj, relink, compiler, config, mods.ctrl && mods.shift);
+         }
       }
-
-      project.config = currentConfig;
-
-      ide.Update(null);
-   }
-
-   bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
-   {
-      bool isReady = true;
-      char message[MAX_F_STRING];
-      LogBox logBox = ide.outputView.buildBox;
-
-      ShowOutputBuildLog(cleanLog);
-
-      if(displayCompiler)
-         DisplayCompiler(false);
-
-      ProjectPrepareMakefile(project, method, false, false);
+      delete compiler;
       return true;
    }
 
-   bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
-   {
-      char makefilePath[MAX_LOCATION];
-      char makefileName[MAX_LOCATION];
-      bool exists;
-      LogBox logBox = ide.outputView.buildBox;
-      
-      ShowOutputBuildLog(cleanLog);
-
-      if(displayCompiler)
-         DisplayCompiler(false);
-
-      strcpy(makefilePath, project.topNode.path);
-      project.CatMakeFileName(makefileName);
-      PathCatSlash(makefilePath, makefileName);
-
-      exists = FileExists(makefilePath);
-      if((method == normal && (!exists || project.config.makingModified/*|| project.topNode.modified*/)) ||
-            (method == forceExists && exists) || 
-            method == force) // || project.config.makingModified || makefileDirty
-      {
-         char * reason;
-         char * action;
-         ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
-         app.UpdateDisplay();
-         
-         if((method == normal && !exists) || (method == force && !exists))
-            action = $"Generating ";
-         else if(method == force)
-            action = $"Regenerating ";
-         else if(method == normal || method == forceExists)
-            action = $"Updating ";
-         else
-            action = "";
-         if(!exists)
-            reason = $"Makefile doesn't exist. ";
-         else if(project.topNode.modified)
-            reason = $"Project has been modified. ";
-         else
-            reason = "";
-
-         //logBox.Logf("%s\n", makefileName);
-         logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, project.configName);
-         project.GenerateMakefile(null, false, null);
-
-         ide.statusBar.text = null;
-         app.UpdateDisplay();
-         return true;
-      }
-      return false;
-   }
-   
-   bool ProjectBuild(MenuItem selection, Modifiers mods)
+   bool ProjectRebuild(MenuItem selection, Modifiers mods)
    {
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
       Project prj = project;
-      if(selection || !ide.activeClient || activeClient == this)
+      ProjectConfig config;
+      if(selection || !ide.activeClient)
       {
          DataRow row = fileList.currentRow;
          ProjectNode node = row ? (ProjectNode)row.tag : null;
          if(node) prj = node.project;
       }
-      // Added this code here until dependencies work:
-      else //if(ide.activeClient)
+      else
       {
-         ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
          if(node)
             prj = node.project;
       }
-      if(/*prj != project || */!prj.configIsInDebugSession || !ide.DontTerminateDebugSession($"Project Build"))
-         BuildInterrim(prj, build);
-      return true;
-   }
-
-   bool BuildInterrim(Project prj, BuildType buildType)
-   {
-      if(ProjectPrepareForToolchain(prj, normal, true, true))
+      config = prj.config;
+      if(!prj.GetConfigIsInDebugSession(config) ||
+            (!ide.DontTerminateDebugSession($"Project Rebuild") && DebugStopForMake(prj, rebuild, compiler, config)))
       {
-         ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, prj.configName);
-         return Build(prj, buildType);
+         if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
+         {
+            ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, GetConfigName(config));
+            /*if(config)
+            {
+               config.compilingModified = true;
+               config.makingModified = true;
+            }*/ // -- should this still be used depite the new solution of BuildType?
+            Build(prj, rebuild, compiler, config, mods.ctrl && mods.shift);
+         }
       }
-      return false;
+      delete compiler;
+      return true;
    }
 
-   bool ProjectLink(MenuItem selection, Modifiers mods)
+   bool ProjectClean(MenuItem selection, Modifiers mods)
    {
       Project prj = project;
-      if(selection || !ide.activeClient || activeClient == this)
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config;
+      if(selection || !ide.activeClient)
       {
          DataRow row = fileList.currentRow;
          ProjectNode node = row ? (ProjectNode)row.tag : null;
          if(node) prj = node.project;
       }
-      // Added this code here until dependencies work:
-      else //if(ide.activeClient)
+      else
       {
-         ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
          if(node)
             prj = node.project;
       }
-      if(ProjectPrepareForToolchain(prj, normal, true, true))
+      config = prj.config;
+      if(!prj.GetConfigIsInDebugSession(config) ||
+            (!ide.DontTerminateDebugSession($"Project Clean") && DebugStopForMake(prj, clean, compiler, config)))
       {
-         ide.outputView.buildBox.Logf("Relinking project %s using the %s configuration...\n", prj.name, prj.configName);
-         if(prj.config)
-            prj.config.linkingModified = true;
-         Build(prj, relink);
+         if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
+         {
+            ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, GetConfigName(config));
+
+            buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
+            ide.AdjustBuildMenus();
+            ide.AdjustDebugMenus();
+
+            prj.Clean(compiler, config, false, mods.ctrl && mods.shift);
+            buildInProgress = none;
+            ide.AdjustBuildMenus();
+            ide.AdjustDebugMenus();
+         }
       }
+      delete compiler;
       return true;
    }
 
-   bool ProjectRebuild(MenuItem selection, Modifiers mods)
+   bool ProjectRealClean(MenuItem selection, Modifiers mods)
    {
-      Project prj = GetSelectedProject((bool)selection);
-      if(ProjectPrepareForToolchain(prj, normal, true, true))
+      Project prj = project;
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config;
+      if(selection || !ide.activeClient)
       {
-         ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, prj.configName);
-         /*if(prj.config)
-         {
-            prj.config.compilingModified = true;
-            prj.config.makingModified = true;
-         }*/ // -- should this still be used depite the new solution of BuildType?
-         Build(prj, rebuild);
+         DataRow row = fileList.currentRow;
+         ProjectNode node = row ? (ProjectNode)row.tag : null;
+         if(node) prj = node.project;
       }
-      return true;
-   }
-
-   bool ProjectClean(MenuItem selection, Modifiers mods)
-   {
-      Project prj = GetSelectedProject((bool)selection);
-      if(ProjectPrepareForToolchain(prj, normal, true, true))
+      else
       {
-         ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, prj.configName);
-         
-         buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
-         ide.AdjustBuildMenus();
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
+         if(node)
+            prj = node.project;
+      }
+      config = prj.config;
+      if(!prj.GetConfigIsInDebugSession(config) ||
+            (!ide.DontTerminateDebugSession($"Project Real Clean") && DebugStopForMake(prj, clean, compiler, config)))
+      {
+         if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
+         {
+            ide.outputView.buildBox.Logf($"Removing intermediate objects directory for project %s using the %s configuration...\n", prj.name, GetConfigName(config));
 
-         prj.Clean();
-         buildInProgress = none;
-         ide.AdjustBuildMenus();
+            buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
+            ide.AdjustBuildMenus();
+            ide.AdjustDebugMenus();
+
+            prj.Clean(compiler, config, true, mods.ctrl && mods.shift);
+            buildInProgress = none;
+            ide.AdjustBuildMenus();
+            ide.AdjustDebugMenus();
+         }
       }
+      delete compiler;
       return true;
    }
 
    bool ProjectRegenerate(MenuItem selection, Modifiers mods)
    {
       Project prj = project;
-      if(selection || !ide.activeClient || activeClient == this)
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ShowOutputBuildLog(true);
+      if(selection || !ide.activeClient)
       {
          DataRow row = fileList.currentRow;
          ProjectNode node = row ? (ProjectNode)row.tag : null;
          if(node)
             prj = node.project;
       }
-      // Added this code here until dependencies work:
-      else //if(ide.activeClient)
+      else
       {
-         ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
+         ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
          if(node)
             prj = node.project;
       }
 
-      ProjectPrepareMakefile(prj, force, true, true);
+      DisplayCompiler(compiler, false);
+      ProjectPrepareCompiler(project, compiler);
+      ProjectPrepareMakefile(prj, force, compiler, prj.config);
+      delete compiler;
       return true;
    }
 
+   bool Compile(Project project, List<ProjectNode> nodes, bool justPrint, SingleFileCompileMode mode)
+   {
+      bool result = true;
+      char fileName[MAX_LOCATION];
+      Window document;
+      ProjectConfig config = project.config;
+
+      stopBuild = false;
+
+      for(document = ide.firstChild; document; document = document.next)
+      {
+         if(document.modifiedDocument)
+         {
+            ProjectNode n = GetNodeFromWindow(document, project, mode == cObject ? true : false);
+            for(node : nodes)
+            {
+               if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
+               {
+                  ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
+                  result = false;
+                  break;
+               }
+            }
+         }
+      }
+
+      if(result)
+      {
+         CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+         result = false;
+         if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
+         {
+            if(config)
+               ide.outputView.buildBox.Logf($"%s specific file(s) in project %s using the %s configuration...\n",
+                     mode == normal ? $"Compiling" : $"Debug compiling", project.name, config.name);
+            else
+               ide.outputView.buildBox.Logf($"%s specific file(s) in project %s...\n",
+                     mode == normal ? $"Compiling" : $"Debug compiling", project.name);
+
+            buildInProgress = compilingFile;
+            ide.AdjustBuildMenus();
+            project.Compile(nodes, compiler, config, justPrint, mode);
+            buildInProgress = none;
+            ide.AdjustBuildMenus();
+
+            result = true;
+         }
+         delete compiler;
+      }
+      return result;
+   }
+
+   bool Clean(Project project, List<ProjectNode> nodes, bool justPrint)
+   {
+      bool result = true;
+      char fileName[MAX_LOCATION];
+      Window document;
+      ProjectConfig config = project.config;
+
+      stopBuild = false;
+
+      for(document = ide.firstChild; document; document = document.next)
+      {
+         if(document.modifiedDocument)
+         {
+            ProjectNode n = GetNodeFromWindow(document, project, false);
+            for(node : nodes)
+            {
+               if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
+               {
+                  ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
+                  result = false;
+                  break;
+               }
+            }
+         }
+      }
+
+      if(result)
+      {
+         CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+         result = false;
+         if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
+         {
+            Map<String, NameCollisionInfo> namesInfo { };
+            project.topNode.GenMakefileGetNameCollisionInfo(namesInfo, config);
+            for(node : nodes)
+            {
+               if(node.GetIsExcluded(config))
+                  ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
+               else
+               {
+                  if(config)
+                     ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s using the %s configuration...\n",
+                           node.type == file ? $"single file" : $"folder", node.name, project.name, config.name);
+                  else
+                     ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s...\n",
+                           node.type == file ? $"single file" : $"folder", node.name, project.name);
+
+                  node.DeleteIntermediateFiles(compiler, config, namesInfo, false);
+                  result = true;
+               }
+            }
+            namesInfo.Free();
+            delete namesInfo;
+         }
+         delete compiler;
+      }
+      return result;
+   }
+
    bool ProjectNewFile(MenuItem selection, Modifiers mods)
    {
       DataRow row = fileList.currentRow;
@@ -1165,28 +1214,17 @@ class ProjectView : Window
       return true;
    }
 
-   bool MenuConfig(MenuItem selection, Modifiers mods)
+   bool ProjectUpdateMakefileForAllConfigs(Project project)
    {
-      if(ProjectActiveConfig { parent = parent.parent, master = parent, project = project }.Modal() == ok)
-         ide.AdjustMenus();
-      return true;
-   }
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
 
-   bool MenuCompiler(MenuItem selection, Modifiers mods)
-   {
-      ActiveCompilerDialog compilerDialog
-      {
-         parent = parent.parent, master = parent;
-         ideSettings = ideSettings, workspaceActiveCompiler = ide.workspace.compiler;
-      };
-      incref compilerDialog;
-      if(compilerDialog.Modal() == ok && strcmp(compilerDialog.workspaceActiveCompiler, ide.workspace.compiler))
-      {
-         ide.workspace.compiler = compilerDialog.workspaceActiveCompiler;
-         for(prj : ide.workspace.projects)
-            ide.projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
-      }
-      delete compilerDialog;
+      // This call really does not belong here:
+      ide.UpdateToolBarActiveConfigs(false);
+      for(config : project.configurations)
+         ProjectPrepareMakefile(project, forceExists, compiler, config);
+
+      ide.Update(null);
+      delete compiler;
       return true;
    }
 
@@ -1194,7 +1232,7 @@ class ProjectView : Window
    {
       ProjectNode node = GetSelectedNode(true);
       Project prj = node ? node.project : project;
-      projectSettingsDialog = ProjectSettings { parent = parent.parent, master = parent, project = prj, projectNode = node };
+      projectSettingsDialog = ProjectSettings { master = parent, project = prj, projectNode = node };
       projectSettingsDialog.Modal();
 
       Update(null);
@@ -1228,135 +1266,101 @@ class ProjectView : Window
 
    bool FileCompile(MenuItem selection, Modifiers mods)
    {
-      DataRow row = fileList.currentRow;
-      if(row)
+      OldLink item;
+      OldList selectedRows;
+      Project project = null;
+      List<ProjectNode> nodes { };
+      fileList.GetMultiSelection(selectedRows);
+      for(item = selectedRows.first; item; item = item.next)
       {
+         OldLink i;
+         DataRow row = item.data;
          ProjectNode node = (ProjectNode)row.tag;
-         Compile(node);
+         if(!project)
+            project = node.project;
+         else if(node.project != project)
+         {
+            project = null;
+            break;
+         }
+         nodes.Add(node);
       }
+      selectedRows.Free(null);
+      if(project)
+         Compile(project, nodes, mods.ctrl && mods.shift, normal);
+      else
+         ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
+      delete nodes;
       return true;
    }
 
-   /*bool IsProjectModified()
-   {
-      Window document;
-
-      for(document = master.firstChild; document; document = document.next)
-      {
-         if(document.modifiedDocument)
-            if(GetNodeFromWindow(document), project)
-               return true;
-      }
-      return false;
-   }
-   */
-
-   bool Build(Project prj, BuildType buildType)
+   bool FileClean(MenuItem selection, Modifiers mods)
    {
-      bool result = true;
-      Window document;
-
-      stopBuild = false;
-      for(document = master.firstChild; document; document = document.next)
+      OldLink item;
+      OldList selectedRows;
+      Project project = null;
+      List<ProjectNode> nodes { };
+      fileList.GetMultiSelection(selectedRows);
+      for(item = selectedRows.first; item; item = item.next)
       {
-         if(document.modifiedDocument)
+         OldLink i;
+         DataRow row = item.data;
+         ProjectNode node = (ProjectNode)row.tag;
+         if(!project)
+            project = node.project;
+         else if(node.project != project)
          {
-            ProjectNode node = GetNodeFromWindow(document, prj);
-            if(node && !document.MenuFileSave(null, 0))
-            {
-               result = false;
-               break;
-            }
+            project = null;
+            break;
          }
+         nodes.Add(node);
       }
-      if(result)
-      {
-         DirExpression targetDir = prj.targetDir;
-
-         // TOFIX: DebugStop is being abused and backfiring on us.
-         //        It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
-
-         //        configIsInDebugSession seems to be used for two OPPOSITE things:
-         //        If we're debugging another config, we need to unload the executable!
-         //        In building, we want to stop if we're debugging the 'same' executable
-         if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
-         {
-            if(buildType == start || buildType == restart)
-            {
-               if(ide.debugger && ide.debugger.isPrepared)
-               {
-                  DebugStop();
-               }
-            }
-            else
-            {
-               if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == prj.config && ide.debugger.isPrepared)
-               {
-                  DebugStop();
-               }
-            }
-         }
-         
-         // TODO: Disabled until problems fixed... is it fixed?
-         if(buildType == rebuild || (prj.config && prj.config.compilingModified))
-            prj.Clean();
-         else
-         {
-            if(buildType == relink || (prj.config && prj.config.linkingModified))
-            {
-               char target[MAX_LOCATION];
-
-               strcpy(target, prj.topNode.path);
-               PathCat(target, targetDir.dir);
-               prj.CatTargetFileName(target);
-               if(FileExists(target))
-                  DeleteFile(target);
-            }
-            if(prj.config && prj.config.symbolGenModified)
-            {
-               DirExpression objDir = prj.objDir;
-               char fileName[MAX_LOCATION];
-               char moduleName[MAX_FILENAME];
-               strcpy(fileName, prj.topNode.path);
-               PathCatSlash(fileName, objDir.dir);
-               strcpy(moduleName, prj.moduleName);
-               strcat(moduleName, ".main.ec");
-               PathCatSlash(fileName, moduleName);
-               if(FileExists(fileName))
-                  DeleteFile(fileName);
-               ChangeExtension(fileName, "c", fileName);
-               if(FileExists(fileName))
-                  DeleteFile(fileName);
-               ChangeExtension(fileName, "o", fileName);
-               if(FileExists(fileName))
-                  DeleteFile(fileName);
-
-               delete objDir;
-            }
-         }
-         buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
-         ide.AdjustBuildMenus();
-         ide.AdjustDebugMenus();
-
-         result = prj.Build(buildType == run, null);
-
-         if(prj.config)
-         {
-            prj.config.compilingModified = false;
-            if(!ide.ShouldStopBuild())
-               prj.config.linkingModified = false;
+      selectedRows.Free(null);
+      if(project)
+         Clean(project, nodes, mods.ctrl && mods.shift);
+      else
+         ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
+      delete nodes;
+      return true;
+   }
 
-            prj.config.symbolGenModified = false;
-         }
-         buildInProgress = none;
-         ide.AdjustBuildMenus();
-         ide.AdjustDebugMenus();
+   bool FileDebugPrecompile(MenuItem selection, Modifiers mods)
+   {
+      DataRow row = fileList.currentRow;
+      ProjectNode node = row ? (ProjectNode)row.tag : null;
+      if(node)
+      {
+         List<ProjectNode> nodes { };
+         nodes.Add(node);
+         Compile(node.project, nodes, mods.ctrl && mods.shift, debugPrecompile);
+         delete nodes;
+      }
+   }
 
-         ide.workspace.modified = true;
+   bool FileDebugCompile(MenuItem selection, Modifiers mods)
+   {
+      DataRow row = fileList.currentRow;
+      ProjectNode node = row ? (ProjectNode)row.tag : null;
+      if(node)
+      {
+         List<ProjectNode> nodes { };
+         nodes.Add(node);
+         Compile(node.project, nodes, mods.ctrl && mods.shift, debugCompile);
+         delete nodes;
+      }
+   }
 
-         delete targetDir;
+   bool FileDebugGenerateSymbols(MenuItem selection, Modifiers mods)
+   {
+      DataRow row = fileList.currentRow;
+      ProjectNode node = row ? (ProjectNode)row.tag : null;
+      if(node)
+      {
+         List<ProjectNode> nodes { };
+         nodes.Add(node);
+         Compile(node.project, nodes, mods.ctrl && mods.shift, debugGenerateSymbols);
+         delete nodes;
       }
-      return result;
    }
 
    Project GetSelectedProject(bool useSelection)
@@ -1372,6 +1376,24 @@ class ProjectView : Window
       return prj;
    }
    
+   void SelectNextProject(bool backwards)
+   {
+      DataRow row = fileList.currentRow;
+      DataRow currentRow = row;
+      ProjectNode node = (ProjectNode)row.tag;
+      if(node.type != project)
+         row = node.project.topNode.row;
+      else if(backwards)
+         row = row.previous ? row.previous : fileList.lastRow;
+      if(!backwards)
+         row = row.next ? row.next : fileList.firstRow;
+      if(row && row != currentRow)
+      {
+         fileList.SelectRow(row);
+         fileList.currentRow = row;
+      }
+   }
+
    ProjectNode GetSelectedNode(bool useSelection)
    {
       ProjectNode node = null;
@@ -1397,76 +1419,305 @@ class ProjectView : Window
       if(node != prj.topNode)
          PathCatSlash(folder, node.path);
       ShellOpen(folder);
+      return true;
    }
 
    bool Run(MenuItem selection, Modifiers mods)
    {
-      char args[MAX_LOCATION * 64];
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config = project.config;
+      String args = new char[maxPathLen];
       args[0] = '\0';
       if(ide.workspace.commandLineArgs)
          //ide.debugger.GetCommandLineArgs(args);
          strcpy(args, ide.workspace.commandLineArgs);
       if(ide.debugger.isActive)
-         project.Run(args);
-      /*else if(project.config.targetType == sharedLibrary || project.config.targetType == staticLibrary)
+         project.Run(args, compiler, config);
+      /*else if(config.targetType == sharedLibrary || config.targetType == staticLibrary)
          MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
-      else if(BuildInterrim(project, run))
-         project.Run(args);
+      else if(BuildInterrim(project, run, compiler, config, false))
+         project.Run(args, compiler, config);
+      delete args;
+      delete compiler;
       return true;
    }
 
    bool DebugStart()
    {
       bool result = false;
-      if(project.targetType == sharedLibrary || project.targetType == staticLibrary)
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config = project.config;
+      TargetTypes targetType = project.GetTargetType(config);
+      if(targetType == sharedLibrary || targetType == staticLibrary)
          MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
-      else if(project.compress)
+      else if(project.GetCompress(config))
          MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
-      else if(project.debug ||
+      else if(project.GetDebug(config) ||
          MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
       {
-         if(/*!IsProjectModified() ||*/ BuildInterrim(project, start))
+         if(/*!IsProjectModified() ||*/ BuildInterrim(project, start, compiler, config, false))
          {
-            CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
             if(compiler.type.isVC)
             {
                //bool result = false;
                char oldwd[MAX_LOCATION];
-               char oldPath[MAX_LOCATION * 65];
+               PathBackup pathBackup { };
                char command[MAX_LOCATION];
 
-               GetEnvironment("PATH", oldPath, sizeof(oldPath));
-               ide.SetPath(false); //true
+               ide.SetPath(false, compiler, config);
                
                GetWorkingDir(oldwd, sizeof(oldwd));
                ChangeWorkingDir(project.topNode.path);
 
-               sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, project.config.name, "Debug.Start");
-               //ide.outputView.buildBox.Logf("command: %s\n", command);
+               sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, config.name, "Debug.Start");
                Execute(command);
                ChangeWorkingDir(oldwd);
-               SetEnvironment("PATH", oldPath);
+
+               delete pathBackup;
             }
             else
             {
-               ide.debugger.Start();
+               ide.debugger.Start(compiler, config);
                result = true;
             }
-
-            delete compiler;
          }
       }
+      delete compiler;
       return result;      
    }
 
+   void GoToError(const char * line)
+   {
+      char * colon;
+      
+      while(isspace(*line)) line++;
+      colon = strstr(line, ":");
+
+      {
+         int lineNumber = 0;
+         int col = 1;
+         bool lookForLineNumber = true;
+
+         // deal with linking error
+         if(colon && colon[1] == ' ')
+         {
+            colon = strstr(colon + 1, ":");
+            if(colon && !colon[1])
+            {
+               colon = strstr(line, ":");
+               lookForLineNumber = false;
+            }
+            else if(colon && !isdigit(colon[1]))
+            {
+               line = colon + 1;
+               colon = strstr(line, ":");
+            }
+         }
+         // Don't be mistaken by the drive letter colon
+         if(colon && (colon[1] == '/' || colon[1] == '\\'))
+            colon = strstr(colon + 1, ":");
+         if(colon && lookForLineNumber)
+         {
+            char * comma;
+            // MSVS Errors
+            char * par = RSearchString(line, "(", colon - line, true, false);
+            if(par && strstr(par, ")"))
+               colon = par;
+            else if((colon+1)[0] == ' ')
+            {
+               // NOTE: This is the same thing as saying 'colon = line'
+               for( ; colon != line; colon--)
+                  /*if(*colon == '(')
+                     break*/;
+            }
+            lineNumber = atoi(colon + 1);
+            /*
+            comma = strchr(colon, ',');
+            if(comma)
+               col = atoi(comma+1);
+            */
+            comma = strchr(colon+1, ':');
+            if(comma)
+               col = atoi(comma+1);
+         }
+         
+         {
+            char moduleName[MAX_LOCATION], filePath[MAX_LOCATION];
+            char * bracket;
+            if(colon)
+            {
+               char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
+               char * start = inFileIncludedFrom ? line + strlen(stringInFileIncludedFrom) : line;
+               int len = (int)(colon - start);
+               len = Min(len, MAX_LOCATION-1);
+               // Cut module name
+               strncpy(moduleName, start, len);
+               moduleName[len] = '\0';
+            }
+            else
+               strcpy(moduleName, line);
+
+            // Remove stuff in brackets
+            /*
+            bracket = strstr(moduleName, "(");
+            if(bracket) *bracket = '\0';
+            */
+            MakeSlashPath(moduleName);
+
+            if(!colon)
+            {
+               // Check if it's one of our modules
+               ProjectNode node = project.topNode.Find(moduleName, false);
+               if(node)
+               {
+                  strcpy(moduleName, node.path);
+                  PathCatSlash(moduleName, node.name);
+               }
+               else
+                  moduleName[0] = '\0';
+            }
+            if(moduleName[0])
+            {
+               CodeEditor codeEditor;
+               strcpy(filePath, project.topNode.path);
+               PathCatSlash(filePath, moduleName);
+      
+               codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
+               if(!codeEditor)
+               {
+                  char name[MAX_LOCATION];
+                  // TOFIX: Improve on this, don't use only filename, make a function
+                  if(ide && ide.workspace)
+                  {
+                     for(prj : ide.workspace.projects)
+                     {
+                        if(prj.topNode.FindWithPath(moduleName, false))
+                        {
+                           strcpy(filePath, prj.topNode.path);
+                           PathCatSlash(filePath, moduleName);
+                           codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
+                           if(codeEditor)
+                              break;
+                        }
+                     }
+                  }
+               }
+               if(codeEditor && lineNumber)
+               {
+                  EditBox editBox = codeEditor.editBox;
+                  editBox.GoToLineNum(lineNumber - 1);
+                  editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
+               }
+            }
+         }
+      }
+   }
+
+   bool OpenNode(ProjectNode node)
+   {
+      char filePath[MAX_LOCATION];
+      node.GetFullFilePath(filePath);
+      return ide.OpenFile(filePath, normal, true/*false Why was it opening hidden?*/, null, something, normal) ? true : false;
+   }
+
+   void AddNode(ProjectNode node, DataRow addTo)
+   {
+      DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
+
+      row.tag = (int64)node;
+      node.row = row;
+
+      if(node.type == resources)
+         resourceRow = row;
+
+      row.SetData(null, node);
+
+      if(node.files && node.files.first && node.parent && 
+            !(!node.parent.parent && 
+               (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
+                  !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
+         row.collapsed = true;
+      else if(node.type == folder)
+         node.icon = openFolder;
+
+      if(node.files)
+      {
+         for(child : node.files)
+            AddNode(child, row);
+      }
+   }
+
+   void DeleteNode(ProjectNode projectNode)
+   {
+      if(projectNode.files)
+      {
+         ProjectNode child;
+         while(child = projectNode.files.first)
+            DeleteNode(child);
+      }
+      fileList.DeleteRow(projectNode.row);
+      projectNode.Delete();
+   }
+
+   bool ProjectSave(MenuItem selection, Modifiers mods)
+   {
+      DataRow row = fileList.currentRow;
+      ProjectNode node = row ? (ProjectNode)row.tag : null;
+      Project prj = node ? node.project : null;
+      if(prj)
+      {
+         prj.StopMonitoring();
+         if(prj.Save(prj.filePath))
+         {
+            Project modPrj = null;
+            prj.topNode.modified = false;
+            for(p : ide.workspace.projects)
+            {
+               if(p.topNode.modified)
+               { 
+                  modPrj = p;
+                  break;
+               }
+            }
+            if(!modPrj)
+               modifiedDocument = false;
+            Update(null);
+         }
+         prj.StartMonitoring();
+      }
+      return true;
+   }
+
+   bool ShowOutputBuildLog(bool cleanLog)
+   {
+      OutputView output = ide.outputView;
+      if(cleanLog)
+         output.ShowClearSelectTab(build);
+      else
+      {
+         output.SelectTab(build);
+         output.Show();
+      }
+      return true;
+   }
+
    bool DebugRestart()
    {
-      if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart))
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config = project.config;
+
+      bool result = false;
+      if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart, compiler, config, false))
       {
-         ide.debugger.Restart();
-         return true;
+         // For Restart, compiler and config will only be used if for
+         // whatever reason (if at all possible) the Debugger is in a
+         // 'terminated' or 'none' state
+         ide.debugger.Restart(compiler, config);
+         result = true;
       }
-      return false;
+
+      delete compiler;
+      return result;
    }
 
    bool DebugResume()
@@ -1489,15 +1740,24 @@ class ProjectView : Window
 
    bool DebugStepInto()
    {
-      if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
-         ide.debugger.StepInto();
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config = project.config;
+
+      if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, false)))
+         ide.debugger.StepInto(compiler, config);
+      delete compiler;
       return true;
    }
 
    bool DebugStepOver(bool skip)
    {
-      if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
-         ide.debugger.StepOver(skip);
+      CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
+      ProjectConfig config = project.config;
+
+      if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, false)))
+         ide.debugger.StepOver(compiler, config, skip);
+
+      delete compiler;
       return true;
    }
 
@@ -1564,7 +1824,7 @@ class ProjectView : Window
             }
             Update(null);
             folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
-            folderNode.row.tag = (int)folderNode;
+            folderNode.row.tag = (int64)folderNode;
                
             folderNode.row.SetData(null, folderNode);
             fileList.currentRow = folderNode.row;
@@ -1693,7 +1953,7 @@ class ProjectView : Window
          }
          Update(null);
          result.row = parentNode.row.AddRowAfter(after ? after.row : null);
-         result.row.tag = (int)result;
+         result.row.tag = (int64)result;
          result.row.SetData(null, result);
       }
       return result;
@@ -1735,7 +1995,7 @@ class ProjectView : Window
          Update(null);
          project.ModifiedAllConfigs(true, false, false, true);
          projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
-         projectNode.row.tag =(int)projectNode;
+         projectNode.row.tag =(int64)projectNode;
             
          projectNode.row.SetData(null, projectNode);
          fileList.currentRow = projectNode.row;