ide; added Debug Precompile, Debug Compile and Debug Generate Symbols support to...
[sdk] / ide / src / project / ProjectView.ec
1 import "ide"
2
3 import "FileSystemIterator"
4
5 class ImportFolderFSI : NormalFileSystemIterator
6 {
7    ProjectView projectView;
8    Array<ProjectNode> stack { };
9
10    bool OnFolder(char * folderPath)
11    {
12       char name[MAX_LOCATION];
13       ProjectNode parentNode = stack.lastIterator.data;
14       ProjectNode folder;
15       GetLastDirectory(folderPath, name);
16       folder = parentNode.FindSpecial(name, false, true, true);
17       if(!folder)
18          folder = projectView.NewFolder(parentNode, name, false);
19       stack.Add(folder);
20       return true;
21    }
22
23    void OutFolder(char * folderPath, bool isRoot)
24    {
25       stack.lastIterator.Remove(); //stack.Remove();
26    }
27
28    bool OnFile(char * filePath)
29    {
30       ProjectNode parentNode = stack.lastIterator.data;
31       projectView.AddFile(parentNode, filePath, parentNode.isInResources, false);
32       return true;
33    }
34 }
35
36 static Array<FileFilter> fileFilters
37 { [
38    { $"eC/C/C++ Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
39    { $"eC/C/C++ Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, eh, c, cpp, cc, cxx" },
40    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
41    { $"All files", null }
42 ] };
43
44 static Array<FileFilter> resourceFilters
45 { [
46    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png,*.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
47    { $"3D Studio Model Files (*.3ds)", "3ds" },
48    { $"Translations (*.mo)", "mo" },
49    { $"All files", null }
50 ] };
51
52 static Array<FileFilter> projectFilters
53 { [
54    { $"Project Files (*.epj)", ProjectExtension },
55    { $"Workspace Files (*.ews)", WorkspaceExtension }
56 ] };
57
58 static Array<FileType> projectTypes
59 { [
60    { $"Ecere IDE Project", ProjectExtension },
61    { $"Ecere IDE Workspace", WorkspaceExtension }
62 ] };
63
64 static char * iconNames[] = 
65 {
66    "<:ecere>mimeTypes/file.png",                   /*genFile*/
67    "<:ecere>mimeTypes/textEcereWorkspace.png",     /*ewsFile*/
68    "<:ecere>mimeTypes/textEcereProject.png",       /*epjFile*/
69    "<:ecere>places/folder.png",                    /*folder*/
70    "<:ecere>status/folderOpen.png",                /*openFolder*/
71    "<:ecere>mimeTypes/textEcereSource.png",        /*ecFile*/
72    "<:ecere>mimeTypes/textEcereHeader.png",        /*ehFile*/
73    "<:ecere>mimeTypes/textCSource.png",            /*cFile*/
74    "<:ecere>mimeTypes/textCHeader.png",            /*hFile*/
75    "<:ecere>mimeTypes/textC++Source.png",          /*cppFile*/
76    "<:ecere>mimeTypes/textC++Header.png",          /*hppFile*/
77    "<:ecere>mimeTypes/text.png",                   /*textFile*/
78    "<:ecere>mimeTypes/textHyperTextMarkup.png",    /*webFile*/
79    "<:ecere>mimeTypes/image.png",                  /*pictureFile*/
80    "<:ecere>status/audioVolumeHigh.png",           /*soundFile*/
81    "<:ecere>mimeTypes/package.png",                /*archiveFile*/
82    "<:ecere>mimeTypes/packageSoftware.png",        /*packageFile*/
83    "<:ecere>mimeTypes/packageOpticalDisc.png",     /*opticalMediaImageFile*/
84    "<:ecere>mimeTypes/file.png"                    /*mFile*/ //TODO: create icon for mfile
85 };
86
87 enum PrepareMakefileMethod { normal, force, forceExists };
88
89 enum BuildType { build, rebuild, relink, run, start, restart, clean };
90 enum BuildState
91 {
92    none, buildingMainProject, buildingSecondaryProject, compilingFile;
93
94    property bool { get { return this != none; } }
95    //property bool actualBuild { get { return this == buildingMainProject || this == buildingSecondaryProject;  } }
96 };
97
98 class ProjectView : Window
99 {
100    isDocument = true;
101    //hasMinimize = true;
102    hasClose = true;
103    borderStyle = sizable;
104    hasHorzScroll = true;
105    hasVertScroll = true;
106    background = white;
107    size = { 300 };
108    anchor = Anchor { left = 0, top = 0, bottom = 0 };
109    menu = Menu { };
110    
111    //hasMinimize = true;
112    saveDialog = projectFileDialog;
113    
114    DataRow resourceRow;
115    BuildState buildInProgress;
116    BitmapResource icons[NodeIcons];
117    Project project;
118    Workspace workspace;
119    property Workspace workspace
120    {
121       set
122       {
123          if(workspace)
124          {
125             for(prj : workspace.projects)
126             {
127                DeleteNode(prj.topNode);
128             }
129             workspace.projects.Free();
130             ide.debugger.CleanUp();
131          }
132          workspace = value;
133          if(workspace)
134          {
135             fileDialog.currentDirectory = workspace.workspaceDir;
136             resourceFileDialog.currentDirectory = workspace.workspaceDir;
137             for(prj : workspace.projects)
138                AddNode(prj.topNode, null);
139             ide.statusBar.text = $"Generating Makefile & Dependencies...";
140             app.UpdateDisplay();
141             for(prj : workspace.projects)
142                prj.ModifiedAllConfigs(true, false, false, false);
143             ide.statusBar.text = $"Initializing Debugger"; app.UpdateDisplay();
144             ide.statusBar.text = null;
145             app.UpdateDisplay();
146          }
147       }
148       get { return workspace; }
149    }
150    
151    bool drawingInProjectSettingsDialog;
152    bool drawingInProjectSettingsDialogHeader;
153    ProjectSettings projectSettingsDialog;
154
155    bool stopBuild;
156    PopupMenu popupMenu;
157
158    ProjectView()
159    {
160       NodeIcons c;
161       for(c = 0; c < NodeIcons::enumSize; c++)
162       {
163          icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
164          AddResource(icons[c]);
165       }
166       fileList.AddField(DataField { dataType = class(ProjectNode), freeData = false, userData = this });
167    }
168
169    ~ProjectView()
170    {
171       DebugStop();
172       ide.DestroyTemporaryProjectDir();
173       if(project)
174       {
175          workspace.Free();
176          delete workspace;
177       }
178    }
179
180    ListBox fileList
181    {
182       multiSelect = true, fullRowSelect = false, hasVertScroll = true, hasHorzScroll = true;
183       borderStyle = deep, parent = this, collapseControl = true, treeBranches = true;
184       anchor = Anchor { left = 0, right = 0, top = 0 , bottom = 0 };
185
186       background = projectViewBackground;
187       foreground = projectViewText;
188       selectionColor = selectionColor, selectionText = selectionText;
189       stippleColor = skyBlue;
190
191       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
192       {
193          if(!active) Update(null);
194          return ListBox::OnActivate(active, previous, goOnWithActivation, direct);
195       }
196       
197       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
198       {
199          // Prevent the double click from reactivating the project view (returns false if we opened something)
200          return !OpenSelectedNodes();
201       }
202
203       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
204       {
205          DataRow row = listBox.currentRow;
206          if(row)
207          {
208             ProjectNode node = (ProjectNode)row.tag;
209             if(node.type == NodeTypes::project || node.type == resources || node.type == file || node.type == folder)
210             {
211                bool buildMenuUnavailable = buildInProgress;
212                Menu popupContent { };
213                
214                if(node.type == NodeTypes::project)
215                {
216                   //if(node == ((Project)workspace.projects.first).topNode)
217                   {
218                      MenuItem { popupContent, $"Build", b, NotifySelect = ProjectBuild }.disabled = buildMenuUnavailable;
219                      MenuItem { popupContent, $"Relink", l, NotifySelect = ProjectLink }.disabled = buildMenuUnavailable;
220                      MenuItem { popupContent, $"Rebuild", r, NotifySelect = ProjectRebuild }.disabled = buildMenuUnavailable;
221                      MenuItem { popupContent, $"Clean", c, NotifySelect = ProjectClean }.disabled = buildMenuUnavailable;
222                      MenuItem { popupContent, $"Real Clean", d, NotifySelect = ProjectRealClean }.disabled = buildMenuUnavailable;
223                      MenuItem { popupContent, $"Regenerate Makefile", m, NotifySelect = ProjectRegenerate }.disabled = buildMenuUnavailable;
224                      MenuDivider { popupContent };
225                   }
226                   MenuItem { popupContent, $"Debug Generate Symbols", l, NotifySelect = FileDebugGenerateSymbols }.disabled = buildMenuUnavailable;
227                   MenuDivider { popupContent };
228                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
229                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
230                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
231                   MenuItem { popupContent, $"Add Files to Project...", f, NotifySelect = ProjectAddFiles };
232                   MenuDivider { popupContent };
233                   MenuItem { popupContent, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
234                   // MenuItem { popupContent, "Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
235                   MenuDivider { popupContent };
236                   if(node != ((Project)workspace.projects.first).topNode)
237                   {
238                      MenuItem { popupContent, $"Remove project from workspace", r, NotifySelect = ProjectRemove }.disabled = buildMenuUnavailable;
239                      MenuDivider { popupContent };
240                   }
241                   MenuItem { popupContent, $"Active Configuration...", s, Key { f5, alt = true } , NotifySelect = MenuConfig };
242                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
243                   MenuDivider { popupContent };
244                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
245                   MenuDivider { popupContent };
246                   MenuItem { popupContent, $"Save", v, Key { s, ctrl = true }, NotifySelect = ProjectSave }.disabled = !node.modified;
247                   MenuDivider { popupContent };
248                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
249                }
250                else if(node.type == resources)
251                {
252                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
253                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
254                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
255                   MenuItem { popupContent, $"Add Resources to Project...", f, NotifySelect = ResourcesAddFiles };
256                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
257                   MenuDivider { popupContent };
258                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
259                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
260                }
261                else if(node.type == file)
262                {
263                   MenuItem { popupContent, $"Open", o, NotifySelect = FileOpenFile };
264                   MenuDivider { popupContent };
265                   MenuItem { popupContent, $"Clean", l, NotifySelect = FileClean }.disabled = buildMenuUnavailable;
266                   MenuItem { popupContent, $"Compile", c, Key { f7, ctrl = true}, NotifySelect = FileCompile }.disabled = buildMenuUnavailable;
267                   MenuDivider { popupContent };
268                   MenuItem { popupContent, $"Debug Precompile", l, NotifySelect = FileDebugPrecompile }.disabled = buildMenuUnavailable;
269                   MenuItem { popupContent, $"Debug Compile", l, NotifySelect = FileDebugCompile }.disabled = buildMenuUnavailable;
270                   MenuDivider { popupContent };
271                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
272                   MenuDivider { popupContent };
273                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
274                   MenuDivider { popupContent };
275                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
276                   MenuItem { popupContent, $"Properties..", p, Key { enter, alt = true }, NotifySelect = FileProperties };
277                }
278                else if(node.type == folder)
279                {
280                   bool isInResources = node.isInResources;
281
282                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
283                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
284                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
285                   if(isInResources)
286                   {
287                      MenuItem { popupContent, $"Add Resources to Folder...", f, NotifySelect = ResourcesAddFiles };
288                   }
289                   else
290                   {
291                      MenuItem { popupContent, $"Add Files to Folder...", f, NotifySelect = ProjectAddFiles };
292                   }
293                   if(!isInResources)
294                   {
295                      MenuDivider { popupContent };
296                      MenuItem { popupContent, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
297                      MenuItem { popupContent, $"Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
298                   }
299                   MenuDivider { popupContent };
300                   MenuItem { popupContent, $"Clean", l, NotifySelect = FileClean }.disabled = buildMenuUnavailable;
301                   MenuItem { popupContent, $"Compile", c, Key { f7, ctrl = true}, NotifySelect = FileCompile }.disabled = buildMenuUnavailable;
302                   MenuDivider { popupContent };
303                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
304                   MenuDivider { popupContent };
305                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
306                   MenuDivider { popupContent };
307                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
308                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
309                }
310
311                popupMenu = 
312                {
313                   master = this, menu = popupContent;
314                   position = {
315                      x + clientStart.x + absPosition.x - app.desktop.position.x, 
316                      y + clientStart.y + absPosition.y - app.desktop.position.y };
317
318                   void NotifyDestroyed(Window window, DialogResult result)
319                   {
320                      popupMenu = null;
321                   }
322                };
323                popupMenu.Create();
324             }
325          }
326          return true;
327       }
328
329       bool NotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch)
330       {
331          if(key == altUp || key == altDown)
332          {
333             SelectNextProject(key == altUp);
334             return false;
335          }
336          return true;
337       }
338
339       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
340       {
341          if(row)
342          {
343             ProjectNode node = (ProjectNode)row.tag;
344             switch(key)
345             {
346                case altEnter: case Key { keyPadEnter, alt = true }:
347                {
348                   NodeProperties { parent = parent, master = this, 
349                      position = { position.x + 100, position.y + 100 }, node = node }.Create();
350                   return false;
351                }
352                case enter: case keyPadEnter:
353                {
354                   ProjectNode resNode;
355                   for(resNode = node.parent; resNode; resNode = resNode.parent)
356                      if(resNode.type == resources)
357                         break;
358                   if(node.type == project || (node.type == folder && !resNode))
359                   {
360                      AddFiles(false);
361                      return false;
362                   }
363                   else if(node.type == resources || node.type == folder)
364                   {
365                      AddFiles(true);
366                      return false;
367                   }
368                   break;
369                }
370                case ctrlI:
371                {
372                   if(node.type == project || node.type == folder || node.type == resources)
373                   {
374                      ImportFolder(node);
375                      return false;
376                   }
377                   break;
378                }
379                case ctrlF:
380                {
381                   if(node.type == project || node.type == folder || node.type == resources)
382                   {
383                      NewFolder(node, null, true);
384                      return false;
385                   }
386                   break;
387                }
388                case ctrlF7:
389                {
390                   if(node.type == file)
391                   {
392                      FileCompile(null, (Modifiers)key);
393                      return false;
394                   }
395                   break;
396                }
397                case Key { space, shift = true }:
398                case space:
399                {
400                   if(node.type == NodeTypes::project)
401                   {
402                      Project prj = null;
403                      for(p : workspace.projects)
404                      {
405                         if(p.topNode == node)
406                         {
407                            prj = p;
408                            break;
409                         }
410                      }
411                      prj.RotateActiveConfig(!key.shift);
412                      if(prj == project)
413                         ide.AdjustMenus();
414                      return false;
415                   }
416                   break;
417                }
418             }
419          }
420          switch(key)
421          {
422             case enter: case keyPadEnter:  OpenSelectedNodes();   break;
423             case del:                      RemoveSelectedNodes(); break;
424             case escape:                      
425             {
426                Window activeClient = ide.activeClient;
427                if(activeClient)
428                   activeClient.Activate();
429                else
430                   ide.RepositionWindows(true);
431                break;
432             }
433          }
434          return true;
435       }
436
437       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
438       {
439          ProjectNode node = (ProjectNode)row.tag;
440          if(node.type == folder)
441             node.icon = collapsed ? folder : openFolder;
442          return true;
443       }
444    };
445
446    FileDialog importFileDialog { autoCreate = false, type = selectDir, text = $"Import Folder" };
447    FileDialog projectFileDialog
448    {
449       autoCreate = false, filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
450       types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType);
451    };
452    FileDialog fileDialog
453    {
454       autoCreate = false, mayNotExist = true, filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
455    };
456    FileDialog resourceFileDialog
457    {
458       autoCreate = false, mayNotExist = true, filters = resourceFilters.array, sizeFilters = resourceFilters.count * sizeof(FileFilter);
459    };
460
461    Menu fileMenu { menu, $"File", f };
462    MenuItem { fileMenu, $"Save", s, Key { s, ctrl = true }, NotifySelect = MenuFileSave };
463    // MenuItem { fileMenu, "Save As...", a, NotifySelect = MenuFileSaveAs };
464
465    bool OnClose(bool parentClosing)
466    {
467       if(!parentClosing && visible)
468       {
469          visible = false;
470          return false;
471       }
472       if(buildInProgress)
473          return false;
474
475       if(modifiedDocument)
476       {
477          DialogResult dialogRes;
478          char msg[2048];
479          bool first = true;
480          strcpy(msg, $"You have modified projects.\nSave changes to ");
481          for(p : ide.workspace.projects)
482          {
483             if(p.topNode.modified)
484             {
485                if(!first) strcat(msg, ", ");
486                strcat(msg, p.name);
487                first = false;
488             }
489          }
490          strcat(msg, "?");
491
492          dialogRes = MessageBox { master = master, type = yesNoCancel, text = parent.caption ? parent.caption : rootWindow.caption, contents = msg }.Modal();
493
494          if(dialogRes == yes)
495          {
496             // TOFIX: Precomp error if brackets are taken out
497             return (DialogResult)MenuFileSave(null, 0) != cancel;
498          }
499          else if(dialogRes == cancel)
500             return false;
501          modifiedDocument = false;
502       }
503       return true;
504    }
505
506    void OnDestroy(void)
507    {
508       //if(ide.findInFilesDialog && ide.findInFilesDialog.created && ide.findInFilesDialog.mode != directory)
509       //   ide.findInFilesDialog.SearchStop();
510
511       ide.outputView.buildBox.Clear();
512       ide.outputView.debugBox.Clear();
513       //ide.outputView.findBox.Clear();
514       ide.callStackView.Clear();
515       ide.watchesView.Clear();
516       ide.threadsView.Clear();
517       ide.breakpointsView.Clear();
518       ide.outputView.ShowClearSelectTab(find); // why this? 
519    }
520
521    bool OnSaveFile(char * fileName)
522    {
523       for(prj : ide.workspace.projects)
524       {
525          if(prj.topNode.modified)
526          {
527             prj.StopMonitoring();
528             if(prj.Save(prj.filePath))
529                prj.topNode.modified = false;
530             prj.StartMonitoring();
531          }
532       }
533       modifiedDocument = false;
534       Update(null);
535       return true;
536    }
537
538    bool IsModuleInProject(char * filePath)
539    {
540       char moduleName[MAX_FILENAME]; //, modulePath[MAX_LOCATION];
541       GetLastDirectory(filePath, moduleName);
542       return project.topNode.Find(moduleName, false) != null;
543    }
544
545    bool GetRelativePath(char * filePath, char * relativePath)
546    {
547       return project.GetRelativePath(filePath, relativePath);
548    }
549
550    ProjectNode GetNodeFromWindow(Window document, Project project)
551    {
552       if(document.fileName)
553       {
554          char winFileName[MAX_LOCATION];
555          char * documentFileName = GetSlashPathBuffer(winFileName, document.fileName);
556          for(p : ide.workspace.projects)
557          {
558             Project prj = project ? project : p;
559             ProjectNode node;
560             if((node = prj.topNode.FindByFullPath(documentFileName, false)))
561                return node;
562             if(project) break;
563          }
564       }
565       return null;
566    }
567
568    //                          ((( UTILITY FUNCTIONS )))
569    //
570    //  ************************************************************************
571    //  *** These methods below are part of a sequence of events, and as     ***
572    //  *** such they must be passed the current compiler and project config ***
573    //  ************************************************************************
574    bool DisplayCompiler(CompilerConfig compiler, bool cleanLog)
575    {
576       ide.outputView.buildBox.Logf($"%s Compiler\n", compiler ? compiler.name : $"{problem with compiler selection}");
577       return true;
578    }
579
580    bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler,
581       CompilerConfig compiler, ProjectConfig config)
582    {
583       bool isReady = true;
584       char message[MAX_F_STRING];
585       LogBox logBox = ide.outputView.buildBox;
586
587       ShowOutputBuildLog(cleanLog);
588
589       if(displayCompiler)
590          DisplayCompiler(compiler, false);
591
592       ProjectPrepareCompiler(project, compiler);
593       ProjectPrepareMakefile(project, method, compiler, config);
594       return true;
595    }
596
597    bool ProjectPrepareCompiler(Project project, CompilerConfig compiler)
598    {
599       if(!project.GenerateCrossPlatformMk() || !project.GenerateCompilerCf(compiler))
600          ide.outputView.buildBox.Logf($"Error generating compiler configuration (Is the project/config directory writable?)\n");
601       return true;
602    }
603
604    // Note: Compiler is only passed in to for VisualStudio support
605    bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, CompilerConfig compiler, ProjectConfig config)
606    {
607       if(compiler.type.isVC)
608       {
609          // I'm guessing we'll want to support generating VS files on Linux as well...
610          ide.statusBar.text = $"Generating Visual Studio Solution...";
611          app.UpdateDisplay();
612          //GenerateVSSolutionFile(project, compiler);
613          ide.statusBar.text = $"Generating Visual Studio Project...";
614          app.UpdateDisplay();
615          //GenerateVCProjectFile(project, compiler);
616          ide.statusBar.text = null;
617          app.UpdateDisplay();
618          return true;
619       }
620       else
621       {
622          char makefilePath[MAX_LOCATION];
623          char makefileName[MAX_LOCATION];
624          bool exists;
625          LogBox logBox = ide.outputView.buildBox;
626          
627          strcpy(makefilePath, project.topNode.path);
628          project.CatMakeFileName(makefileName, config);
629          PathCatSlash(makefilePath, makefileName);
630
631          exists = FileExists(makefilePath);
632          if(method == force ||
633            (method == forceExists && exists) ||
634            (method == normal && (!exists || (config && config.makingModified))))
635          {
636             char * reason;
637             char * action;
638             ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
639             app.UpdateDisplay();
640
641             if((method == normal && !exists) || (method == force && !exists))
642                action = $"Generating ";
643             else if(method == force)
644                action = $"Regenerating ";
645             else if(method == normal || method == forceExists)
646                action = $"Updating ";
647             else
648                action = "";
649             if(!exists)
650                reason = $"Makefile doesn't exist. ";
651             else if(project.topNode.modified)
652                reason = $"Project has been modified. ";
653             else
654                reason = "";
655
656             //logBox.Logf("%s\n", makefileName);
657             logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, GetConfigName(config));
658
659             if(!project.GenerateMakefile(null, false, null, config))
660                ide.outputView.buildBox.Logf($"Error generating makefile (Is the project directory writable?)\n");
661
662             ide.statusBar.text = null;
663             app.UpdateDisplay();
664             return true;
665          }
666       }
667       return false;
668    }
669    
670    bool BuildInterrim(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, bool justPrint)
671    {
672       if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
673       {
674          ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, GetConfigName(config));
675          return Build(prj, buildType, compiler, config, justPrint);
676       }
677       return false;
678    }
679
680    bool DebugStopForMake(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config)
681    {
682       bool result = false;
683       // TOFIX: DebugStop is being abused and backfiring on us.
684       //        It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
685
686       //        configIsInDebugSession seems to be used for two OPPOSITE things:
687       //        If we're debugging another config, we need to unload the executable!
688       //        In building, we want to stop if we're debugging the 'same' executable
689       if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
690       {
691          if(buildType == start || buildType == restart)
692          {
693             if(ide.debugger && ide.debugger.isPrepared)
694                result = DebugStop();
695          }
696          else
697          {
698             if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == config && ide.debugger.isPrepared)
699                result = DebugStop();
700          }
701       }
702       return result;
703    }
704
705    bool Build(Project prj, BuildType buildType, CompilerConfig compiler, ProjectConfig config, bool justPrint)
706    {
707       bool result = true;
708       Window document;
709
710       stopBuild = false;
711       for(document = master.firstChild; document; document = document.next)
712       {
713          if(document.modifiedDocument)
714          {
715             ProjectNode node = GetNodeFromWindow(document, prj);
716             if(node && !document.MenuFileSave(null, 0))
717             {
718                result = false;
719                break;
720             }
721          }
722       }
723       if(result)
724       {
725          DirExpression targetDir = prj.GetTargetDir(compiler, config);
726
727          DebugStopForMake(prj, buildType, compiler, config);
728
729          // TODO: Disabled until problems fixed... is it fixed?
730          if(buildType == rebuild || (config && config.compilingModified))
731             prj.Clean(compiler, config, false, justPrint);
732          else
733          {
734             if(buildType == relink || (config && config.linkingModified))
735             {
736                char target[MAX_LOCATION];
737
738                strcpy(target, prj.topNode.path);
739                PathCat(target, targetDir.dir);
740                prj.CatTargetFileName(target, compiler, config);
741                if(FileExists(target))
742                   DeleteFile(target);
743             }
744             if(config && config.symbolGenModified)
745             {
746                DirExpression objDir = prj.GetObjDir(compiler, config);
747                char fileName[MAX_LOCATION];
748                char moduleName[MAX_FILENAME];
749                strcpy(fileName, prj.topNode.path);
750                PathCatSlash(fileName, objDir.dir);
751                strcpy(moduleName, prj.moduleName);
752                strcat(moduleName, ".main.ec");
753                PathCatSlash(fileName, moduleName);
754                if(FileExists(fileName))
755                   DeleteFile(fileName);
756                ChangeExtension(fileName, "c", fileName);
757                if(FileExists(fileName))
758                   DeleteFile(fileName);
759                ChangeExtension(fileName, "o", fileName);
760                if(FileExists(fileName))
761                   DeleteFile(fileName);
762
763                delete objDir;
764             }
765          }
766          buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
767          ide.AdjustBuildMenus();
768          ide.AdjustDebugMenus();
769
770          result = prj.Build(buildType == run, null, compiler, config, justPrint, normal);
771
772          if(config)
773          {
774             config.compilingModified = false;
775             if(!ide.ShouldStopBuild())
776                config.linkingModified = false;
777
778             config.symbolGenModified = false;
779          }
780          buildInProgress = none;
781          ide.AdjustBuildMenus();
782          ide.AdjustDebugMenus();
783
784          ide.workspace.modified = true;
785
786          delete targetDir;
787       }
788       return result;
789    }
790
791    //                          ((( USER ACTIONS )))
792    //
793    //  ************************************************************************
794    //  *** Methods below should atomically start a process, and as such     ***
795    //  *** they can query compiler and config directly from ide and project ***
796    //  *** but ONLY ONCE!!!                                                 ***
797    //  ************************************************************************
798
799    bool ProjectBuild(MenuItem selection, Modifiers mods)
800    {
801       Project prj = project;
802       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
803       ProjectConfig config;
804       if(selection || !ide.activeClient)
805       {
806          DataRow row = fileList.currentRow;
807          ProjectNode node = row ? (ProjectNode)row.tag : null;
808          if(node) prj = node.project;
809       }
810       else
811       {
812          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
813          if(node)
814             prj = node.project;
815       }
816       config = prj.config;
817       if(/*prj != project || */!prj.GetConfigIsInDebugSession(config) || !ide.DontTerminateDebugSession($"Project Build"))
818       {
819          BuildInterrim(prj, build, compiler, config, mods.ctrl && mods.shift);
820       }
821       delete compiler;
822       return true;
823    }
824
825    bool ProjectLink(MenuItem selection, Modifiers mods)
826    {
827       Project prj = project;
828       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
829       ProjectConfig config;
830       if(selection || !ide.activeClient)
831       {
832          DataRow row = fileList.currentRow;
833          ProjectNode node = row ? (ProjectNode)row.tag : null;
834          if(node) prj = node.project;
835       }
836       else
837       {
838          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
839          if(node)
840             prj = node.project;
841       }
842       config = prj.config;
843       if(!prj.GetConfigIsInDebugSession(config) ||
844             (!ide.DontTerminateDebugSession($"Project Link") && DebugStopForMake(prj, relink, compiler, config)))
845       {
846          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
847          {
848             ide.outputView.buildBox.Logf($"Relinking project %s using the %s configuration...\n", prj.name, GetConfigName(config));
849             if(config)
850                config.linkingModified = true;
851             Build(prj, relink, compiler, config, mods.ctrl && mods.shift);
852          }
853       }
854       delete compiler;
855       return true;
856    }
857
858    bool ProjectRebuild(MenuItem selection, Modifiers mods)
859    {
860       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
861       Project prj = project;
862       ProjectConfig config;
863       if(selection || !ide.activeClient)
864       {
865          DataRow row = fileList.currentRow;
866          ProjectNode node = row ? (ProjectNode)row.tag : null;
867          if(node) prj = node.project;
868       }
869       else
870       {
871          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
872          if(node)
873             prj = node.project;
874       }
875       config = prj.config;
876       if(!prj.GetConfigIsInDebugSession(config) ||
877             (!ide.DontTerminateDebugSession($"Project Rebuild") && DebugStopForMake(prj, rebuild, compiler, config)))
878       {
879          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
880          {
881             ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, GetConfigName(config));
882             /*if(config)
883             {
884                config.compilingModified = true;
885                config.makingModified = true;
886             }*/ // -- should this still be used depite the new solution of BuildType?
887             Build(prj, rebuild, compiler, config, mods.ctrl && mods.shift);
888          }
889       }
890       delete compiler;
891       return true;
892    }
893
894    bool ProjectClean(MenuItem selection, Modifiers mods)
895    {
896       Project prj = project;
897       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
898       ProjectConfig config;
899       if(selection || !ide.activeClient)
900       {
901          DataRow row = fileList.currentRow;
902          ProjectNode node = row ? (ProjectNode)row.tag : null;
903          if(node) prj = node.project;
904       }
905       else
906       {
907          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
908          if(node)
909             prj = node.project;
910       }
911       config = prj.config;
912       if(!prj.GetConfigIsInDebugSession(config) ||
913             (!ide.DontTerminateDebugSession($"Project Clean") && DebugStopForMake(prj, clean, compiler, config)))
914       {
915          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
916          {
917             ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, GetConfigName(config));
918
919             buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
920             ide.AdjustBuildMenus();
921             ide.AdjustDebugMenus();
922
923             prj.Clean(compiler, config, false, mods.ctrl && mods.shift);
924             buildInProgress = none;
925             ide.AdjustBuildMenus();
926             ide.AdjustDebugMenus();
927          }
928       }
929       delete compiler;
930       return true;
931    }
932
933    bool ProjectRealClean(MenuItem selection, Modifiers mods)
934    {
935       Project prj = project;
936       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
937       ProjectConfig config;
938       if(selection || !ide.activeClient)
939       {
940          DataRow row = fileList.currentRow;
941          ProjectNode node = row ? (ProjectNode)row.tag : null;
942          if(node) prj = node.project;
943       }
944       else
945       {
946          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
947          if(node)
948             prj = node.project;
949       }
950       config = prj.config;
951       if(!prj.GetConfigIsInDebugSession(config) ||
952             (!ide.DontTerminateDebugSession($"Project Real Clean") && DebugStopForMake(prj, clean, compiler, config)))
953       {
954          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
955          {
956             ide.outputView.buildBox.Logf($"Removing intermediate objects directory for project %s using the %s configuration...\n", prj.name, GetConfigName(config));
957
958             buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
959             ide.AdjustBuildMenus();
960             ide.AdjustDebugMenus();
961
962             prj.Clean(compiler, config, true, mods.ctrl && mods.shift);
963             buildInProgress = none;
964             ide.AdjustBuildMenus();
965             ide.AdjustDebugMenus();
966          }
967       }
968       delete compiler;
969       return true;
970    }
971
972    bool ProjectRegenerate(MenuItem selection, Modifiers mods)
973    {
974       Project prj = project;
975       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
976       ShowOutputBuildLog(true);
977       if(selection || !ide.activeClient)
978       {
979          DataRow row = fileList.currentRow;
980          ProjectNode node = row ? (ProjectNode)row.tag : null;
981          if(node)
982             prj = node.project;
983       }
984       else
985       {
986          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
987          if(node)
988             prj = node.project;
989       }
990
991       DisplayCompiler(compiler, false);
992       ProjectPrepareCompiler(project, compiler);
993       ProjectPrepareMakefile(prj, force, compiler, prj.config);
994       delete compiler;
995       return true;
996    }
997
998    bool Compile(Project project, List<ProjectNode> nodes, bool justPrint, SingleFileCompileMode mode)
999    {
1000       bool result = true;
1001       char fileName[MAX_LOCATION];
1002       Window document;
1003       ProjectConfig config = project.config;
1004
1005       stopBuild = false;
1006
1007       for(document = ide.firstChild; document; document = document.next)
1008       {
1009          if(document.modifiedDocument)
1010          {
1011             ProjectNode n = GetNodeFromWindow(document, project);
1012             for(node : nodes)
1013             {
1014                if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1015                {
1016                   ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1017                   result = false;
1018                   break;
1019                }
1020             }
1021          }
1022       }
1023
1024       if(result)
1025       {
1026          CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1027          result = false;
1028          if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1029          {
1030             if(config)
1031                ide.outputView.buildBox.Logf($"%s specific file(s) in project %s using the %s configuration...\n",
1032                      mode == normal ? $"Compiling" : $"Debug compiling", project.name, config.name);
1033             else
1034                ide.outputView.buildBox.Logf($"%s specific file(s) in project %s...\n",
1035                      mode == normal ? $"Compiling" : $"Debug compiling", project.name);
1036
1037             buildInProgress = compilingFile;
1038             ide.AdjustBuildMenus();
1039             project.Compile(nodes, compiler, config, justPrint, mode);
1040             buildInProgress = none;
1041             ide.AdjustBuildMenus();
1042
1043             result = true;
1044          }
1045          delete compiler;
1046       }
1047       return result;
1048    }
1049
1050    bool Clean(Project project, List<ProjectNode> nodes, bool justPrint)
1051    {
1052       bool result = true;
1053       char fileName[MAX_LOCATION];
1054       Window document;
1055       ProjectConfig config = project.config;
1056
1057       stopBuild = false;
1058
1059       for(document = ide.firstChild; document; document = document.next)
1060       {
1061          if(document.modifiedDocument)
1062          {
1063             ProjectNode n = GetNodeFromWindow(document, project);
1064             for(node : nodes)
1065             {
1066                if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1067                {
1068                   ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1069                   result = false;
1070                   break;
1071                }
1072             }
1073          }
1074       }
1075
1076       if(result)
1077       {
1078          CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1079          result = false;
1080          if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1081          {
1082             for(node : nodes)
1083             {
1084                if(node.GetIsExcluded(config))
1085                   ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
1086                else
1087                {
1088                   if(config)
1089                      ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s using the %s configuration...\n",
1090                            node.type == file ? $"single file" : $"folder", node.name, project.name, config.name);
1091                   else
1092                      ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s...\n",
1093                            node.type == file ? $"single file" : $"folder", node.name, project.name);
1094
1095                   node.DeleteIntermediateFiles(compiler, config);
1096                   result = true;
1097                }
1098             }
1099          }
1100          delete compiler;
1101       }
1102       return result;
1103    }
1104
1105    bool ProjectNewFile(MenuItem selection, Modifiers mods)
1106    {
1107       DataRow row = fileList.currentRow;
1108       if(row)
1109       {
1110          char fileName[1024];
1111          char filePath[MAX_LOCATION];
1112          ProjectNode parentNode = (ProjectNode)row.tag;
1113          ProjectNode n, fileNode;
1114          parentNode.GetFileSysMatchingPath(filePath);
1115          MakePathRelative(filePath, parentNode.project.topNode.path, filePath);
1116          for(n = parentNode; n && n != parentNode.project.resNode; n = n.parent);
1117          sprintf(fileName, $"Untitled %d", documentID);
1118          fileNode = AddFile(parentNode, fileName, (bool)n, true);
1119          fileNode.path = CopyUnixPath(filePath);
1120          if(fileNode)
1121          {
1122             NodeProperties nodeProperties
1123             {
1124                parent, this;
1125                position = { position.x + 100, position.y + 100 };
1126                mode = newFile;
1127                node = fileNode;
1128             };
1129             nodeProperties.Create(); // not modal?
1130          }
1131       }
1132       return true;
1133    }
1134
1135    bool ProjectNewFolder(MenuItem selection, Modifiers mods)
1136    {
1137       DataRow row = fileList.currentRow;
1138       if(row)
1139       {
1140          ProjectNode parentNode = (ProjectNode)row.tag;
1141          NewFolder(parentNode, null, true);
1142       }
1143       return true;
1144    }
1145
1146    bool ResourcesAddFiles(MenuItem selection, Modifiers mods)
1147    {
1148       AddFiles(true);
1149       return true;
1150    }
1151
1152    bool ProjectAddFiles(MenuItem selection, Modifiers mods)
1153    {
1154       AddFiles(false);
1155       return true;
1156    }
1157
1158    bool ProjectImportFolder(MenuItem selection, Modifiers mods)
1159    {
1160       DataRow row = fileList.currentRow;
1161       if(row)
1162       {
1163          ProjectNode toNode = (ProjectNode)row.tag;
1164          ImportFolder(toNode);
1165       }
1166       return true;
1167    }
1168
1169    bool ProjectAddNewForm(MenuItem selection, Modifiers mods)
1170    {
1171       CodeEditor codeEditor = CreateNew("Form", "form", "Window", null);
1172       codeEditor.EnsureUpToDate();
1173       return true;
1174    }
1175
1176    bool ProjectAddNewGraph(MenuItem selection, Modifiers mods)
1177    {
1178       CodeEditor codeEditor = CreateNew("Graph", "graph", "Block", null);
1179       if(codeEditor)
1180          codeEditor.EnsureUpToDate();
1181       return true;
1182    }
1183
1184    bool ProjectRemove(MenuItem selection, Modifiers mods)
1185    {
1186       DataRow row = fileList.currentRow;
1187       if(row)
1188       {
1189          ProjectNode node = (ProjectNode)row.tag;
1190          if(node.type == project)
1191             RemoveSelectedNodes();
1192       }
1193       return true;
1194    }
1195
1196    bool ProjectUpdateMakefileForAllConfigs(Project project)
1197    {
1198       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1199
1200       for(config : project.configurations)
1201          ProjectPrepareMakefile(project, forceExists, compiler, config);
1202
1203       ide.Update(null);
1204       delete compiler;
1205       return true;
1206    }
1207
1208    bool MenuConfig(MenuItem selection, Modifiers mods)
1209    {
1210       if(ProjectActiveConfig { master = parent, project = project }.Modal() == ok)
1211          ide.AdjustMenus();
1212       return true;
1213    }
1214
1215    bool MenuCompiler(MenuItem selection, Modifiers mods)
1216    {
1217       ActiveCompilerDialog compilerDialog
1218       {
1219          master = parent;
1220          ideSettings = ideSettings, workspaceActiveCompiler = ide.workspace.compiler;
1221       };
1222       incref compilerDialog;
1223       if(compilerDialog.Modal() == ok && strcmp(compilerDialog.workspaceActiveCompiler, ide.workspace.compiler))
1224       {
1225          CompilerConfig compiler = ideSettings.GetCompilerConfig(compilerDialog.workspaceActiveCompiler);
1226          ide.workspace.compiler = compilerDialog.workspaceActiveCompiler;
1227          ide.projectView.ShowOutputBuildLog(true);
1228          ide.projectView.DisplayCompiler(compiler, false);
1229
1230          for(prj : ide.workspace.projects)
1231             ide.projectView.ProjectPrepareCompiler(prj, compiler);
1232          delete compiler;
1233       }
1234       delete compilerDialog;
1235       return true;
1236    }
1237
1238    bool MenuSettings(MenuItem selection, Modifiers mods)
1239    {
1240       ProjectNode node = GetSelectedNode(true);
1241       Project prj = node ? node.project : project;
1242       projectSettingsDialog = ProjectSettings { master = parent, project = prj, projectNode = node };
1243       projectSettingsDialog.Modal();
1244
1245       Update(null);
1246       ide.AdjustMenus();
1247       return true;
1248    }
1249
1250    bool FileProperties(MenuItem selection, Modifiers mods)
1251    {
1252       DataRow row = fileList.currentRow;
1253       if(row)
1254       {
1255          ProjectNode node = (ProjectNode)row.tag;
1256          NodeProperties { parent = parent, master = this, node = node, 
1257                position = { position.x + 100, position.y + 100 } }.Create();
1258       }
1259       return true;
1260    }
1261
1262    bool FileOpenFile(MenuItem selection, Modifiers mods)
1263    {
1264       OpenSelectedNodes();
1265       return true;
1266    }
1267
1268    bool FileRemoveFile(MenuItem selection, Modifiers mods)
1269    {
1270       RemoveSelectedNodes();
1271       return true;
1272    }
1273
1274    bool FileCompile(MenuItem selection, Modifiers mods)
1275    {
1276       OldLink item;
1277       OldList selectedRows;
1278       Project project = null;
1279       List<ProjectNode> nodes { };
1280       fileList.GetMultiSelection(selectedRows);
1281       for(item = selectedRows.first; item; item = item.next)
1282       {
1283          OldLink i;
1284          DataRow row = item.data;
1285          ProjectNode node = (ProjectNode)row.tag;
1286          if(!project)
1287             project = node.project;
1288          else if(node.project != project)
1289          {
1290             project = null;
1291             break;
1292          }
1293          nodes.Add(node);
1294       }
1295       selectedRows.Free(null);
1296       if(project)
1297          Compile(project, nodes, mods.ctrl && mods.shift, normal);
1298       else
1299          ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1300       delete nodes;
1301       return true;
1302    }
1303
1304    bool FileClean(MenuItem selection, Modifiers mods)
1305    {
1306       OldLink item;
1307       OldList selectedRows;
1308       Project project = null;
1309       List<ProjectNode> nodes { };
1310       fileList.GetMultiSelection(selectedRows);
1311       for(item = selectedRows.first; item; item = item.next)
1312       {
1313          OldLink i;
1314          DataRow row = item.data;
1315          ProjectNode node = (ProjectNode)row.tag;
1316          if(!project)
1317             project = node.project;
1318          else if(node.project != project)
1319          {
1320             project = null;
1321             break;
1322          }
1323          nodes.Add(node);
1324       }
1325       selectedRows.Free(null);
1326       if(project)
1327          Clean(project, nodes, mods.ctrl && mods.shift);
1328       else
1329          ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1330       delete nodes;
1331       return true;
1332    }
1333
1334    bool FileDebugPrecompile(MenuItem selection, Modifiers mods)
1335    {
1336       DataRow row = fileList.currentRow;
1337       ProjectNode node = row ? (ProjectNode)row.tag : null;
1338       if(node)
1339       {
1340          List<ProjectNode> nodes { };
1341          nodes.Add(node);
1342          Compile(node.project, nodes, mods.ctrl && mods.shift, debugPrecompile);
1343          delete nodes;
1344       }
1345    }
1346
1347    bool FileDebugCompile(MenuItem selection, Modifiers mods)
1348    {
1349       DataRow row = fileList.currentRow;
1350       ProjectNode node = row ? (ProjectNode)row.tag : null;
1351       if(node)
1352       {
1353          List<ProjectNode> nodes { };
1354          nodes.Add(node);
1355          Compile(node.project, nodes, mods.ctrl && mods.shift, debugCompile);
1356          delete nodes;
1357       }
1358    }
1359
1360    bool FileDebugGenerateSymbols(MenuItem selection, Modifiers mods)
1361    {
1362       DataRow row = fileList.currentRow;
1363       ProjectNode node = row ? (ProjectNode)row.tag : null;
1364       if(node)
1365       {
1366          List<ProjectNode> nodes { };
1367          nodes.Add(node);
1368          Compile(node.project, nodes, mods.ctrl && mods.shift, debugGenerateSymbols);
1369          delete nodes;
1370       }
1371    }
1372
1373    Project GetSelectedProject(bool useSelection)
1374    {
1375       Project prj = project;
1376       if(useSelection)
1377       {
1378          DataRow row = fileList.currentRow;
1379          ProjectNode node = row ? (ProjectNode)row.tag : null;
1380          if(node)
1381             prj = node.project;
1382       }
1383       return prj;
1384    }
1385    
1386    void SelectNextProject(bool backwards)
1387    {
1388       DataRow row = fileList.currentRow;
1389       DataRow currentRow = row;
1390       ProjectNode node = (ProjectNode)row.tag;
1391       if(node.type != project)
1392          row = node.project.topNode.row;
1393       else if(backwards)
1394          row = row.previous ? row.previous : fileList.lastRow;
1395       if(!backwards)
1396          row = row.next ? row.next : fileList.firstRow;
1397       if(row && row != currentRow)
1398       {
1399          fileList.SelectRow(row);
1400          fileList.currentRow = row;
1401       }
1402    }
1403
1404    ProjectNode GetSelectedNode(bool useSelection)
1405    {
1406       ProjectNode node = null;
1407       if(useSelection)
1408       {
1409          DataRow row = fileList.currentRow;
1410          if(row)
1411             node = (ProjectNode)row.tag;
1412       }
1413       return node;
1414    }
1415
1416    bool MenuBrowseFolder(MenuItem selection, Modifiers mods)
1417    {
1418       char folder[MAX_LOCATION];
1419       Project prj;
1420       ProjectNode node = GetSelectedNode(true);
1421       if(!node)
1422          node = project.topNode;
1423       prj = node.project;
1424
1425       strcpy(folder, prj.topNode.path);
1426       if(node != prj.topNode)
1427          PathCatSlash(folder, node.path);
1428       ShellOpen(folder);
1429       return true;
1430    }
1431
1432    bool Run(MenuItem selection, Modifiers mods)
1433    {
1434       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1435       ProjectConfig config = project.config;
1436       String args = new char[maxPathLen];
1437       args[0] = '\0';
1438       if(ide.workspace.commandLineArgs)
1439          //ide.debugger.GetCommandLineArgs(args);
1440          strcpy(args, ide.workspace.commandLineArgs);
1441       if(ide.debugger.isActive)
1442          project.Run(args, compiler, config);
1443       /*else if(config.targetType == sharedLibrary || config.targetType == staticLibrary)
1444          MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
1445       else if(BuildInterrim(project, run, compiler, config, false))
1446          project.Run(args, compiler, config);
1447       delete args;
1448       delete compiler;
1449       return true;
1450    }
1451
1452    bool DebugStart()
1453    {
1454       bool result = false;
1455       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1456       ProjectConfig config = project.config;
1457       TargetTypes targetType = project.GetTargetType(config);
1458       if(targetType == sharedLibrary || targetType == staticLibrary)
1459          MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
1460       else if(project.GetCompress(config))
1461          MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
1462       else if(project.GetDebug(config) ||
1463          MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
1464       {
1465          if(/*!IsProjectModified() ||*/ BuildInterrim(project, start, compiler, config, false))
1466          {
1467             if(compiler.type.isVC)
1468             {
1469                //bool result = false;
1470                char oldwd[MAX_LOCATION];
1471                PathBackup pathBackup { };
1472                char command[MAX_LOCATION];
1473
1474                ide.SetPath(false, compiler, config);
1475                
1476                GetWorkingDir(oldwd, sizeof(oldwd));
1477                ChangeWorkingDir(project.topNode.path);
1478
1479                sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, config.name, "Debug.Start");
1480                Execute(command);
1481                ChangeWorkingDir(oldwd);
1482
1483                delete pathBackup;
1484             }
1485             else
1486             {
1487                ide.debugger.Start(compiler, config);
1488                result = true;
1489             }
1490          }
1491       }
1492       delete compiler;
1493       return result;      
1494    }
1495
1496    void GoToError(const char * line)
1497    {
1498       char * colon;
1499       
1500       while(isspace(*line)) line++;
1501       colon = strstr(line, ":");
1502
1503       {
1504          int lineNumber = 0;
1505          int col = 1;
1506          bool lookForLineNumber = true;
1507
1508          // deal with linking error
1509          if(colon && colon[1] == ' ')
1510          {
1511             colon = strstr(colon + 1, ":");
1512             if(colon && !colon[1])
1513             {
1514                colon = strstr(line, ":");
1515                lookForLineNumber = false;
1516             }
1517             else if(colon && !isdigit(colon[1]))
1518             {
1519                line = colon + 1;
1520                colon = strstr(line, ":");
1521             }
1522          }
1523          // Don't be mistaken by the drive letter colon
1524          if(colon && (colon[1] == '/' || colon[1] == '\\'))
1525             colon = strstr(colon + 1, ":");
1526          if(colon && lookForLineNumber)
1527          {
1528             char * comma;
1529             // MSVS Errors
1530             char * par = RSearchString(line, "(", colon - line, true, false);
1531             if(par && strstr(par, ")"))
1532                colon = par;
1533             else if((colon+1)[0] == ' ')
1534             {
1535                // NOTE: This is the same thing as saying 'colon = line'
1536                for( ; colon != line; colon--)
1537                   /*if(*colon == '(')
1538                      break*/;
1539             }
1540             lineNumber = atoi(colon + 1);
1541             /*
1542             comma = strchr(colon, ',');
1543             if(comma)
1544                col = atoi(comma+1);
1545             */
1546             comma = strchr(colon+1, ':');
1547             if(comma)
1548                col = atoi(comma+1);
1549          }
1550          
1551          {
1552             char moduleName[MAX_LOCATION], filePath[MAX_LOCATION];
1553             char * bracket;
1554             if(colon)
1555             {
1556                char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
1557                char * start = inFileIncludedFrom ? line + strlen(stringInFileIncludedFrom) : line;
1558                int len = (int)(colon - start);
1559                len = Min(len, MAX_LOCATION-1);
1560                // Cut module name
1561                strncpy(moduleName, start, len);
1562                moduleName[len] = '\0';
1563             }
1564             else
1565                strcpy(moduleName, line);
1566
1567             // Remove stuff in brackets
1568             /*
1569             bracket = strstr(moduleName, "(");
1570             if(bracket) *bracket = '\0';
1571             */
1572             MakeSlashPath(moduleName);
1573
1574             if(!colon)
1575             {
1576                // Check if it's one of our modules
1577                ProjectNode node = project.topNode.Find(moduleName, false);
1578                if(node)
1579                {
1580                   strcpy(moduleName, node.path);
1581                   PathCatSlash(moduleName, node.name);
1582                }
1583                else
1584                   moduleName[0] = '\0';
1585             }
1586             if(moduleName[0])
1587             {
1588                CodeEditor codeEditor;
1589                strcpy(filePath, project.topNode.path);
1590                PathCatSlash(filePath, moduleName);
1591       
1592                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
1593                if(!codeEditor)
1594                {
1595                   char name[MAX_LOCATION];
1596                   // TOFIX: Improve on this, don't use only filename, make a function
1597                   if(ide && ide.workspace)
1598                   {
1599                      for(prj : ide.workspace.projects)
1600                      {
1601                         if(prj.topNode.FindWithPath(moduleName, false))
1602                         {
1603                            strcpy(filePath, prj.topNode.path);
1604                            PathCatSlash(filePath, moduleName);
1605                            codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
1606                            if(codeEditor)
1607                               break;
1608                         }
1609                      }
1610                   }
1611                }
1612                if(codeEditor && lineNumber)
1613                {
1614                   EditBox editBox = codeEditor.editBox;
1615                   editBox.GoToLineNum(lineNumber - 1);
1616                   editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
1617                }
1618             }
1619          }
1620       }
1621    }
1622
1623    bool OpenNode(ProjectNode node)
1624    {
1625       char filePath[MAX_LOCATION];
1626       node.GetFullFilePath(filePath);
1627       return ide.OpenFile(filePath, normal, true/*false Why was it opening hidden?*/, null, something, normal) ? true : false;
1628    }
1629
1630    void AddNode(ProjectNode node, DataRow addTo)
1631    {
1632       DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
1633
1634       row.tag = (int)node;
1635       node.row = row;
1636
1637       if(node.type == resources)
1638          resourceRow = row;
1639
1640       row.SetData(null, node);
1641
1642       if(node.files && node.files.first && node.parent && 
1643             !(!node.parent.parent && 
1644                (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
1645                   !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
1646          row.collapsed = true;
1647       else if(node.type == folder)
1648          node.icon = openFolder;
1649
1650       if(node.files)
1651       {
1652          for(child : node.files)
1653             AddNode(child, row);
1654       }
1655    }
1656
1657    void DeleteNode(ProjectNode projectNode)
1658    {
1659       if(projectNode.files)
1660       {
1661          ProjectNode child;
1662          while(child = projectNode.files.first)
1663             DeleteNode(child);
1664       }
1665       fileList.DeleteRow(projectNode.row);
1666       projectNode.Delete();
1667    }
1668
1669    bool ProjectSave(MenuItem selection, Modifiers mods)
1670    {
1671       DataRow row = fileList.currentRow;
1672       ProjectNode node = row ? (ProjectNode)row.tag : null;
1673       Project prj = node ? node.project : null;
1674       if(prj)
1675       {
1676          prj.StopMonitoring();
1677          if(prj.Save(prj.filePath))
1678          {
1679             Project modPrj = null;
1680             prj.topNode.modified = false;
1681             for(p : ide.workspace.projects)
1682             {
1683                if(p.topNode.modified)
1684                { 
1685                   modPrj = p;
1686                   break;
1687                }
1688             }
1689             if(!modPrj)
1690                modifiedDocument = false;
1691             Update(null);
1692          }
1693          prj.StartMonitoring();
1694       }
1695       return true;
1696    }
1697
1698    bool ShowOutputBuildLog(bool cleanLog)
1699    {
1700       OutputView output = ide.outputView;
1701       if(cleanLog)
1702          output.ShowClearSelectTab(build);
1703       else
1704       {
1705          output.SelectTab(build);
1706          output.Show();
1707       }
1708       return true;
1709    }
1710
1711    bool DebugRestart()
1712    {
1713       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1714       ProjectConfig config = project.config;
1715
1716       bool result = false;
1717       if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart, compiler, config, false))
1718       {
1719          // For Restart, compiler and config will only be used if for
1720          // whatever reason (if at all possible) the Debugger is in a
1721          // 'terminated' or 'none' state
1722          ide.debugger.Restart(compiler, config);
1723          result = true;
1724       }
1725
1726       delete compiler;
1727       return result;
1728    }
1729
1730    bool DebugResume()
1731    {
1732       ide.debugger.Resume();
1733       return true;
1734    }
1735
1736    bool DebugBreak()
1737    {
1738       ide.debugger.Break();
1739       return true;
1740    }
1741
1742    bool DebugStop()
1743    {
1744       ide.debugger.Stop();
1745       return true;
1746    }
1747
1748    bool DebugStepInto()
1749    {
1750       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1751       ProjectConfig config = project.config;
1752
1753       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, false)))
1754          ide.debugger.StepInto(compiler, config);
1755       delete compiler;
1756       return true;
1757    }
1758
1759    bool DebugStepOver(bool skip)
1760    {
1761       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1762       ProjectConfig config = project.config;
1763
1764       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, false)))
1765          ide.debugger.StepOver(compiler, config, skip);
1766
1767       delete compiler;
1768       return true;
1769    }
1770
1771    bool DebugStepOut(bool skip)
1772    {
1773       ide.debugger.StepOut(skip);
1774       return true;
1775    }
1776
1777    void ImportFolder(ProjectNode toNode)
1778    {
1779       if(toNode)
1780       {
1781          //bool isFolder = toNode.type == folder;
1782          //bool isRes = toNode.isInResources;
1783          
1784          FileDialog fileDialog = importFileDialog;
1785          fileDialog.master = parent;
1786          if(fileDialog.Modal() == ok)
1787          {
1788             ImportFolderFSI fsi { projectView = this };
1789             fsi.stack.Add(toNode);
1790             fsi.Iterate(fileDialog.filePath);
1791             delete fsi;
1792          }
1793       }
1794    }
1795
1796    ProjectNode NewFolder(ProjectNode parentNode, char * name, bool showProperties)
1797    {
1798       if(parentNode)
1799       {
1800          ProjectNode folderNode;
1801          Project prj = parentNode.project;
1802          int c;
1803          ProjectNode after = null;
1804          for(node : parentNode.files)
1805          {
1806             if(node.type != folder)
1807                break;
1808             after = node;
1809          }
1810          
1811          if(name && name[0])
1812             folderNode = parentNode.Add(prj, name, after, folder, folder, true);
1813          else
1814          {
1815             for(c = 0; c < 100; c++)
1816             {
1817                char string[16];
1818                sprintf(string, c ? "New Folder (%d)" : "New Folder", c);
1819                if((folderNode = parentNode.Add(prj, string, after, folder, folder, true)))
1820                   break;
1821             }
1822          }
1823
1824          if(folderNode)
1825          {
1826             NodeProperties nodeProperties;
1827             if(!showProperties)
1828             {
1829                modifiedDocument = true;
1830                prj.topNode.modified = true;
1831             }
1832             Update(null);
1833             folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1834             folderNode.row.tag = (int)folderNode;
1835                
1836             folderNode.row.SetData(null, folderNode);
1837             fileList.currentRow = folderNode.row;
1838             
1839             if(showProperties)
1840             {
1841                nodeProperties = NodeProperties
1842                {
1843                   parent, this, mode = newFolder, node = folderNode;
1844                   position = { position.x + 100, position.y + 100 };
1845                };
1846                nodeProperties.Create();   // Modal?
1847             }
1848             return folderNode;
1849          }
1850       }
1851       return null;
1852    }
1853
1854    void AddFiles(bool resources)
1855    {
1856       FileDialog fileDialog = (!resources) ? this.fileDialog : resourceFileDialog;
1857       fileDialog.type = multiOpen;
1858       fileDialog.text = !resources ? $"Add Files to Project" : $"Add Resources to Project";
1859       fileDialog.master = parent;
1860
1861       if(fileDialog.Modal() == ok)
1862       {
1863          int c;
1864          DataRow row = fileList.currentRow;
1865          ProjectNode parentNode = (ProjectNode)row.tag;
1866          bool addFailed = false;
1867          int numSelections = fileDialog.numSelections;
1868          char ** multiFilePaths = fileDialog.multiFilePaths;
1869
1870          Array<String> nameConflictFiles { };
1871
1872          for(c = 0; c < numSelections; c++)
1873          {
1874             char * filePath = multiFilePaths[c];
1875             FileAttribs exists = FileExists(filePath);
1876             bool addThisFile = true;
1877
1878             if(exists.isDirectory)
1879                addThisFile = false;
1880             else if(!exists)
1881             {
1882                if(MessageBox { master = ide, type = yesNo, text = filePath, 
1883                      contents = $"File doesn't exist. Create?" }.Modal() == yes)
1884                {
1885                   File f = FileOpen(filePath, write);
1886                   if(f)
1887                   {
1888                      addThisFile = true;
1889                      delete f;
1890                   }
1891                   else
1892                   {
1893                      MessageBox { master = ide, type = ok, text = filePath, 
1894                            contents = $"Couldn't create file."}.Modal();
1895                      addThisFile = false;
1896                   }
1897                }
1898             }
1899
1900             if(addThisFile)
1901             {
1902                /*addFailed = */if(!AddFile(parentNode, filePath, resources, false))//;
1903                {
1904                   nameConflictFiles.Add(CopyString(filePath));
1905                   addFailed = true;
1906                }
1907             }
1908          }
1909          if(addFailed)
1910          {
1911             int len = 0;
1912             char * part1 = $"The following file";
1913             char * opt1 = $" was ";
1914             char * opt2 = $"s were ";
1915             char * part2 = $"not added because of identical file name conflict within the project.\n\n";
1916             char * message;
1917             len += strlen(part1);
1918             len += strlen(part2);
1919             len += nameConflictFiles.count > 1 ? strlen(opt2) : strlen(opt1);
1920             for(s : nameConflictFiles)
1921                len += strlen(s) + 1;
1922             message = new char[len + 1];
1923             strcpy(message, part1);
1924             strcat(message, nameConflictFiles.count > 1 ? opt2 : opt1);
1925             strcat(message, part2);
1926             for(s : nameConflictFiles)
1927             {
1928                strcat(message, s);
1929                strcat(message, "\n");
1930             }
1931             MessageBox { master = ide, type = ok, text = $"Name Conflict", 
1932                   contents = message }.Modal();
1933             delete message;
1934          }
1935          nameConflictFiles.Free();
1936          delete nameConflictFiles;
1937       }
1938    }
1939
1940    ProjectNode AddFile(ProjectNode parentNode, char * filePath, bool resources, bool isTemporary)
1941    {
1942       ProjectNode result = null;
1943       ProjectNode after = null;
1944       for(node : parentNode.files)
1945       {
1946          if(node.type != folder && node.type != file && node.type)
1947             break;
1948          after = node;
1949       }
1950
1951       result = parentNode.Add(parentNode.project, filePath, after, file, NodeIcons::SelectFileIcon(filePath), !resources);
1952
1953       if(result)
1954       {
1955          if(!isTemporary)
1956          {
1957             modifiedDocument = true;
1958             parentNode.project.topNode.modified = true;
1959             parentNode.project.ModifiedAllConfigs(true, false, true, true);
1960          }
1961          Update(null);
1962          result.row = parentNode.row.AddRowAfter(after ? after.row : null);
1963          result.row.tag = (int)result;
1964          result.row.SetData(null, result);
1965       }
1966       return result;
1967    }
1968
1969    CodeEditor CreateNew(char * upper, char * lower, char * base, char * className)
1970    {
1971       CodeEditor codeEditor = null;
1972       ProjectNode projectNode;
1973       ProjectNode after = null;
1974       DataRow row = fileList.currentRow;
1975       ProjectNode parentNode;
1976       int c;
1977
1978       if(!row) row = project.topNode.row;
1979
1980       parentNode = (ProjectNode)row.tag;
1981
1982       for(node : parentNode.files)
1983       {
1984          if(node.type != folder && node.type != file && node.type)
1985             break;
1986          after = node;
1987       }
1988       for(c = 1; c < 100; c++)
1989       {
1990          char string[16];
1991          sprintf(string, c ? "%s%d.ec" : "%s.ec", lower, c);
1992          if((projectNode = parentNode.Add(project, string, after, file, genFile, true)))
1993             break;
1994       }
1995       if(projectNode)
1996       {
1997          char name[256];
1998          char filePath[MAX_LOCATION];
1999
2000          modifiedDocument = true;
2001          project.topNode.modified = true;
2002          Update(null);
2003          project.ModifiedAllConfigs(true, false, false, true);
2004          projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
2005          projectNode.row.tag =(int)projectNode;
2006             
2007          projectNode.row.SetData(null, projectNode);
2008          fileList.currentRow = projectNode.row;
2009
2010          strcpy(filePath, project.topNode.path);
2011          PathCat(filePath, projectNode.path);
2012          PathCat(filePath, projectNode.name);
2013
2014          codeEditor = (CodeEditor)ide.FindWindow(filePath);
2015          if(!codeEditor)
2016          {
2017             Class baseClass = eSystem_FindClass(__thisModule, base);
2018             subclass(ClassDesignerBase) designerClass = eClass_GetDesigner(baseClass);
2019             if(designerClass)
2020             {
2021                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
2022                strcpy(name, projectNode.name);
2023                sprintf(name, "%s%d", upper, c);
2024                if(className)
2025                   strcpy(className, name);
2026
2027                designerClass.CreateNew(codeEditor.editBox, codeEditor.clientSize, name, base);
2028
2029                //codeEditor.modifiedDocument = false;
2030                //codeEditor.designer.modifiedDocument = false;
2031
2032                //Code_EnsureUpToDate(codeEditor);
2033             }
2034          }
2035          else // TODO: fix no symbols generated when ommiting {} for following else
2036          {
2037             codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
2038          }
2039          if(codeEditor)
2040          {
2041             codeEditor.ViewDesigner();
2042             codeEditor.codeModified = true;
2043          }
2044       }
2045       visible = false;
2046       return codeEditor;   
2047    }
2048
2049    // Returns true if we opened something
2050    bool OpenSelectedNodes()
2051    {
2052       bool result = false;
2053       OldList selection;
2054       OldLink item;
2055
2056       fileList.GetMultiSelection(selection);
2057       for(item = selection.first; item; item = item.next)
2058       {
2059          DataRow row = item.data;
2060          ProjectNode node = (ProjectNode)row.tag;
2061          if(node.type == file)
2062          {
2063             OpenNode(node);
2064             result = true;
2065             break;
2066          }
2067       }
2068       selection.Free(null);
2069       return result;
2070    }
2071
2072    void RemoveSelectedNodes()
2073    {
2074       OldList selection;
2075       OldLink item, next;
2076       
2077       fileList.GetMultiSelection(selection);
2078
2079       // Remove children of parents we're deleting
2080       for(item = selection.first; item; item = next)
2081       {
2082          OldLink i;
2083          DataRow row = item.data;
2084          ProjectNode n, node = (ProjectNode)row.tag;
2085          bool remove = false;
2086
2087          next = item.next;
2088          for(i = selection.first; i && !remove; i = i.next)
2089          {
2090             ProjectNode iNode = (ProjectNode)((DataRow)i.data).tag;
2091             for(n = node.parent; n; n = n.parent)
2092             {
2093                if(iNode == n)
2094                {
2095                   remove = true;
2096                   break;
2097                }
2098             }
2099          }
2100          if(remove)
2101             selection.Delete(item);
2102       }
2103
2104       for(item = selection.first; item; item = item.next)
2105       {
2106          DataRow row = item.data;
2107          ProjectNode node = (ProjectNode)row.tag;
2108          ProjectNode resNode;
2109          for(resNode = node.parent; resNode; resNode = resNode.parent)
2110             if(resNode.type == resources)
2111                break;
2112          if(node.type == file)
2113          {
2114             Project prj = node.project;
2115             DeleteNode(node);
2116             modifiedDocument = true;
2117             prj.topNode.modified = true;
2118             Update(null);
2119             prj.ModifiedAllConfigs(true, false, true, true);
2120          }
2121          else if(node.type == folder)
2122          {
2123             char message[1024];
2124             sprintf(message, $"Are you sure you want to remove the folder \"%s\"\n"
2125                   "and all of its contents from the project?", node.name);
2126             if(MessageBox { master = ide, type = yesNo, text = $"Delete Folder", contents = message }.Modal() == yes)
2127             {
2128                Project prj = node.project;
2129                if(node.containsFile)
2130                   prj.ModifiedAllConfigs(true, false, true, true);
2131                DeleteNode(node);
2132                modifiedDocument = true;
2133                prj.topNode.modified = true;
2134             }
2135          }
2136          else if(node.type == project && node != project.topNode && !buildInProgress)
2137          {
2138             char message[1024];
2139             Project prj = null;
2140             for(p : workspace.projects)
2141             {
2142                if(p.topNode == node)
2143                {
2144                   prj = p;
2145                   break;
2146                }
2147             }
2148             sprintf(message, $"Are you sure you want to remove the \"%s\" project\n" "from this workspace?", node.name);
2149             if(MessageBox { master = ide, type = yesNo, text = $"Remove Project", contents = message }.Modal() == yes)
2150             {
2151                // THIS GOES FIRST!
2152                DeleteNode(node);
2153                if(prj)
2154                   workspace.RemoveProject(prj);
2155                //modifiedDocument = true; // when project view is a workspace view
2156             }
2157          }
2158       }
2159       selection.Free(null);
2160    }
2161 }