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