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