ide; fixed toolbar quick compiler does not reflect reloaded settings.
[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, bitDepth);
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, int bitDepth, 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, bitDepth, 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, int bitDepth, 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, bitDepth);
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, bitDepth, clean, justPrint);
751          else
752          {
753             if(buildType == relink || (config && config.linkingModified))
754                prj.Clean(compiler, config, bitDepth, cleanTarget, false);
755             if(config && config.symbolGenModified)
756             {
757                DirExpression objDir = prj.GetObjDir(compiler, config, bitDepth);
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, bitDepth, 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          int bitDepth = ide.workspace.bitDepth;
817          ProjectConfig config;
818          if(selection || !ide.activeClient)
819          {
820             DataRow row = fileList.currentRow;
821             ProjectNode node = row ? (ProjectNode)row.tag : null;
822             if(node) prj = node.project;
823          }
824          else
825          {
826             ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
827             if(node)
828                prj = node.project;
829          }
830          config = prj.config;
831          if(/*prj != project || */!prj.GetConfigIsInDebugSession(config) || !ide.DontTerminateDebugSession($"Project Build"))
832          {
833             BuildInterrim(prj, build, compiler, config, bitDepth, mods.ctrl && mods.shift);
834          }
835          delete compiler;
836       }
837       else
838          stopBuild = true;
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       int bitDepth = ide.workspace.bitDepth;
847       ProjectConfig config;
848       if(selection || !ide.activeClient)
849       {
850          DataRow row = fileList.currentRow;
851          ProjectNode node = row ? (ProjectNode)row.tag : null;
852          if(node) prj = node.project;
853       }
854       else
855       {
856          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
857          if(node)
858             prj = node.project;
859       }
860       config = prj.config;
861       if(!prj.GetConfigIsInDebugSession(config) ||
862             (!ide.DontTerminateDebugSession($"Project Link") && DebugStopForMake(prj, relink, compiler, config)))
863       {
864          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
865          {
866             ide.outputView.buildBox.Logf($"Relinking project %s using the %s configuration...\n", prj.name, GetConfigName(config));
867             if(config)
868                config.linkingModified = true;
869             Build(prj, relink, compiler, config, bitDepth, mods.ctrl && mods.shift);
870          }
871       }
872       delete compiler;
873       return true;
874    }
875
876    bool ProjectRebuild(MenuItem selection, Modifiers mods)
877    {
878       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
879       int bitDepth = ide.workspace.bitDepth;
880       Project prj = project;
881       ProjectConfig config;
882       if(selection || !ide.activeClient)
883       {
884          DataRow row = fileList.currentRow;
885          ProjectNode node = row ? (ProjectNode)row.tag : null;
886          if(node) prj = node.project;
887       }
888       else
889       {
890          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
891          if(node)
892             prj = node.project;
893       }
894       config = prj.config;
895       if(!prj.GetConfigIsInDebugSession(config) ||
896             (!ide.DontTerminateDebugSession($"Project Rebuild") && DebugStopForMake(prj, rebuild, compiler, config)))
897       {
898          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
899          {
900             ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, GetConfigName(config));
901             /*if(config)
902             {
903                config.compilingModified = true;
904                config.makingModified = true;
905             }*/ // -- should this still be used depite the new solution of BuildType?
906             Build(prj, rebuild, compiler, config, bitDepth, mods.ctrl && mods.shift);
907          }
908       }
909       delete compiler;
910       return true;
911    }
912
913 #if 0
914    bool ProjectCleanTarget(MenuItem selection, Modifiers mods)
915    {
916       Project prj = project;
917       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
918       ProjectConfig config;
919       if(selection || !ide.activeClient)
920       {
921          DataRow row = fileList.currentRow;
922          ProjectNode node = row ? (ProjectNode)row.tag : null;
923          if(node) prj = node.project;
924       }
925       else
926       {
927          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
928          if(node)
929             prj = node.project;
930       }
931       config = prj.config;
932       if(!prj.GetConfigIsInDebugSession(config) ||
933             (!ide.DontTerminateDebugSession($"Project Clean Target") && DebugStopForMake(prj, clean, compiler, config)))
934       {
935          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
936          {
937             ide.outputView.buildBox.Logf($"Cleaning target for project %s using the %s configuration...\n", prj.name, GetConfigName(config));
938
939             buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
940             ide.AdjustBuildMenus();
941             ide.AdjustDebugMenus();
942
943             prj.Clean(compiler, config, cleanTarget, mods.ctrl && mods.shift);
944             buildInProgress = none;
945             ide.AdjustBuildMenus();
946             ide.AdjustDebugMenus();
947          }
948       }
949       delete compiler;
950       return true;
951    }
952 #endif
953
954    bool ProjectClean(MenuItem selection, Modifiers mods)
955    {
956       Project prj = project;
957       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
958       ProjectConfig config;
959       int bitDepth = ide.workspace.bitDepth;
960       if(selection || !ide.activeClient)
961       {
962          DataRow row = fileList.currentRow;
963          ProjectNode node = row ? (ProjectNode)row.tag : null;
964          if(node) prj = node.project;
965       }
966       else
967       {
968          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
969          if(node)
970             prj = node.project;
971       }
972       config = prj.config;
973       if(!prj.GetConfigIsInDebugSession(config) ||
974             (!ide.DontTerminateDebugSession($"Project Clean") && DebugStopForMake(prj, clean, compiler, config)))
975       {
976          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
977          {
978             ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, GetConfigName(config));
979
980             buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
981             ide.AdjustBuildMenus();
982             ide.AdjustDebugMenus();
983
984             prj.Clean(compiler, config, bitDepth, clean, mods.ctrl && mods.shift);
985             buildInProgress = none;
986             ide.AdjustBuildMenus();
987             ide.AdjustDebugMenus();
988          }
989       }
990       delete compiler;
991       return true;
992    }
993
994    bool ProjectRealClean(MenuItem selection, Modifiers mods)
995    {
996       Project prj = project;
997       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
998       ProjectConfig config;
999       int bitDepth = ide.workspace.bitDepth;
1000       if(selection || !ide.activeClient)
1001       {
1002          DataRow row = fileList.currentRow;
1003          ProjectNode node = row ? (ProjectNode)row.tag : null;
1004          if(node) prj = node.project;
1005       }
1006       else
1007       {
1008          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
1009          if(node)
1010             prj = node.project;
1011       }
1012       config = prj.config;
1013       if(!prj.GetConfigIsInDebugSession(config) ||
1014             (!ide.DontTerminateDebugSession($"Project Real Clean") && DebugStopForMake(prj, clean, compiler, config)))
1015       {
1016          if(ProjectPrepareForToolchain(prj, normal, true, true, compiler, config))
1017          {
1018             ide.outputView.buildBox.Logf($"Removing intermediate objects directory for project %s using the %s configuration...\n", prj.name, GetConfigName(config));
1019
1020             buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1021             ide.AdjustBuildMenus();
1022             ide.AdjustDebugMenus();
1023
1024             prj.Clean(compiler, config, bitDepth, realClean, mods.ctrl && mods.shift);
1025             buildInProgress = none;
1026             ide.AdjustBuildMenus();
1027             ide.AdjustDebugMenus();
1028          }
1029       }
1030       delete compiler;
1031       return true;
1032    }
1033
1034    bool ProjectRegenerate(MenuItem selection, Modifiers mods)
1035    {
1036       Project prj = project;
1037       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1038       ShowOutputBuildLog(true);
1039       if(selection || !ide.activeClient)
1040       {
1041          DataRow row = fileList.currentRow;
1042          ProjectNode node = row ? (ProjectNode)row.tag : null;
1043          if(node)
1044             prj = node.project;
1045       }
1046       else
1047       {
1048          ProjectNode node = GetNodeFromWindow(ide.activeClient, null, false);
1049          if(node)
1050             prj = node.project;
1051       }
1052
1053       DisplayCompiler(compiler, false);
1054       ProjectPrepareCompiler(project, compiler, false);
1055       ProjectPrepareMakefile(prj, force, compiler, prj.config);
1056       delete compiler;
1057       return true;
1058    }
1059
1060    bool Compile(Project project, List<ProjectNode> nodes, bool justPrint, SingleFileCompileMode mode)
1061    {
1062       bool result = true;
1063       char fileName[MAX_LOCATION];
1064       Window document;
1065       ProjectConfig config = project.config;
1066
1067       stopBuild = false;
1068
1069       for(document = ide.firstChild; document; document = document.next)
1070       {
1071          if(document.modifiedDocument)
1072          {
1073             ProjectNode n = GetNodeFromWindow(document, project, mode == cObject ? true : false);
1074             for(node : nodes)
1075             {
1076                if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1077                {
1078                   ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1079                   result = false;
1080                   break;
1081                }
1082             }
1083          }
1084       }
1085
1086       if(result)
1087       {
1088          CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1089          int bitDepth = ide.workspace.bitDepth;
1090          result = false;
1091          if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1092          {
1093             if(config)
1094                ide.outputView.buildBox.Logf($"%s specific file(s) in project %s using the %s configuration...\n",
1095                      mode == normal ? $"Compiling" : $"Debug compiling", project.name, config.name);
1096             else
1097                ide.outputView.buildBox.Logf($"%s specific file(s) in project %s...\n",
1098                      mode == normal ? $"Compiling" : $"Debug compiling", project.name);
1099
1100             buildInProgress = compilingFile;
1101             ide.AdjustBuildMenus();
1102             project.Compile(nodes, compiler, config, bitDepth, justPrint, mode);
1103             buildInProgress = none;
1104             ide.AdjustBuildMenus();
1105
1106             result = true;
1107          }
1108          delete compiler;
1109       }
1110       return result;
1111    }
1112
1113    bool Clean(Project project, List<ProjectNode> nodes, bool justPrint)
1114    {
1115       bool result = true;
1116       char fileName[MAX_LOCATION];
1117       Window document;
1118       ProjectConfig config = project.config;
1119
1120       stopBuild = false;
1121
1122       for(document = ide.firstChild; document; document = document.next)
1123       {
1124          if(document.modifiedDocument)
1125          {
1126             ProjectNode n = GetNodeFromWindow(document, project, false);
1127             for(node : nodes)
1128             {
1129                if(n && n.IsInNode(node) && !document.MenuFileSave(null, 0))
1130                {
1131                   ide.outputView.buildBox.Logf($"Unable to save %s file.\n", node.name);
1132                   result = false;
1133                   break;
1134                }
1135             }
1136          }
1137       }
1138
1139       if(result)
1140       {
1141          CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1142          int bitDepth = ide.workspace.bitDepth;
1143          result = false;
1144          if(ProjectPrepareForToolchain(project, normal, true, true, compiler, config))
1145          {
1146             Map<String, NameCollisionInfo> namesInfo { };
1147             project.topNode.GenMakefileGetNameCollisionInfo(namesInfo, config);
1148             for(node : nodes)
1149             {
1150                if(node.GetIsExcluded(config))
1151                   ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
1152                else
1153                {
1154                   if(config)
1155                      ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s using the %s configuration...\n",
1156                            node.type == file ? $"single file" : $"folder", node.name, project.name, config.name);
1157                   else
1158                      ide.outputView.buildBox.Logf($"Deleteing intermediate objects for %s %s in project %s...\n",
1159                            node.type == file ? $"single file" : $"folder", node.name, project.name);
1160
1161                   node.DeleteIntermediateFiles(compiler, config, bitDepth, namesInfo, false);
1162                   result = true;
1163                }
1164             }
1165             namesInfo.Free();
1166             delete namesInfo;
1167          }
1168          delete compiler;
1169       }
1170       return result;
1171    }
1172
1173    bool ProjectNewFile(MenuItem selection, Modifiers mods)
1174    {
1175       DataRow row = fileList.currentRow;
1176       if(row)
1177       {
1178          char fileName[1024];
1179          char filePath[MAX_LOCATION];
1180          ProjectNode parentNode = (ProjectNode)row.tag;
1181          ProjectNode n, fileNode;
1182          parentNode.GetFileSysMatchingPath(filePath);
1183          MakePathRelative(filePath, parentNode.project.topNode.path, filePath);
1184          for(n = parentNode; n && n != parentNode.project.resNode; n = n.parent);
1185          sprintf(fileName, $"Untitled %d", documentID);
1186          fileNode = AddFile(parentNode, fileName, (bool)n, true);
1187          fileNode.path = CopyUnixPath(filePath);
1188          if(fileNode)
1189          {
1190             NodeProperties nodeProperties
1191             {
1192                parent, this;
1193                position = { position.x + 100, position.y + 100 };
1194                mode = newFile;
1195                node = fileNode;
1196             };
1197             nodeProperties.Create(); // not modal?
1198          }
1199       }
1200       return true;
1201    }
1202
1203    bool ProjectNewFolder(MenuItem selection, Modifiers mods)
1204    {
1205       DataRow row = fileList.currentRow;
1206       if(row)
1207       {
1208          ProjectNode parentNode = (ProjectNode)row.tag;
1209          NewFolder(parentNode, null, true);
1210       }
1211       return true;
1212    }
1213
1214    bool ResourcesAddFiles(MenuItem selection, Modifiers mods)
1215    {
1216       AddFiles(true);
1217       return true;
1218    }
1219
1220    bool ProjectAddFiles(MenuItem selection, Modifiers mods)
1221    {
1222       AddFiles(false);
1223       return true;
1224    }
1225
1226    bool ProjectImportFolder(MenuItem selection, Modifiers mods)
1227    {
1228       DataRow row = fileList.currentRow;
1229       if(row)
1230       {
1231          ProjectNode toNode = (ProjectNode)row.tag;
1232          ImportFolder(toNode);
1233       }
1234       return true;
1235    }
1236
1237    bool ProjectAddNewForm(MenuItem selection, Modifiers mods)
1238    {
1239       CodeEditor codeEditor = CreateNew("Form", "form", "Window", null);
1240       codeEditor.EnsureUpToDate();
1241       return true;
1242    }
1243
1244    bool ProjectAddNewGraph(MenuItem selection, Modifiers mods)
1245    {
1246       CodeEditor codeEditor = CreateNew("Graph", "graph", "Block", null);
1247       if(codeEditor)
1248          codeEditor.EnsureUpToDate();
1249       return true;
1250    }
1251
1252    bool ProjectRemove(MenuItem selection, Modifiers mods)
1253    {
1254       DataRow row = fileList.currentRow;
1255       if(row)
1256       {
1257          ProjectNode node = (ProjectNode)row.tag;
1258          if(node.type == project)
1259             RemoveSelectedNodes();
1260       }
1261       return true;
1262    }
1263
1264    bool ProjectUpdateMakefileForAllConfigs(Project project)
1265    {
1266       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1267
1268       // This call really does not belong here:
1269       ide.UpdateToolBarActiveConfigs(false);
1270       for(config : project.configurations)
1271          ProjectPrepareMakefile(project, forceExists, compiler, config);
1272
1273       ide.Update(null);
1274       delete compiler;
1275       return true;
1276    }
1277
1278    bool MenuSettings(MenuItem selection, Modifiers mods)
1279    {
1280       ProjectNode node = GetSelectedNode(true);
1281       Project prj = node ? node.project : project;
1282       projectSettingsDialog = ProjectSettings { master = parent, project = prj, projectNode = node };
1283       projectSettingsDialog.Modal();
1284       ide.UpdateToolBarActiveConfigs(false);
1285       Update(null);
1286       ide.AdjustMenus();
1287       return true;
1288    }
1289
1290    bool FileProperties(MenuItem selection, Modifiers mods)
1291    {
1292       DataRow row = fileList.currentRow;
1293       if(row)
1294       {
1295          ProjectNode node = (ProjectNode)row.tag;
1296          NodeProperties { parent = parent, master = this, node = node, 
1297                position = { position.x + 100, position.y + 100 } }.Create();
1298       }
1299       return true;
1300    }
1301
1302    bool FileOpenFile(MenuItem selection, Modifiers mods)
1303    {
1304       OpenSelectedNodes();
1305       return true;
1306    }
1307
1308    bool FileRemoveFile(MenuItem selection, Modifiers mods)
1309    {
1310       RemoveSelectedNodes();
1311       return true;
1312    }
1313
1314    bool FileCompile(MenuItem selection, Modifiers mods)
1315    {
1316       OldLink item;
1317       OldList selectedRows;
1318       Project project = null;
1319       List<ProjectNode> nodes { };
1320       fileList.GetMultiSelection(selectedRows);
1321       for(item = selectedRows.first; item; item = item.next)
1322       {
1323          OldLink i;
1324          DataRow row = item.data;
1325          ProjectNode node = (ProjectNode)row.tag;
1326          if(!project)
1327             project = node.project;
1328          else if(node.project != project)
1329          {
1330             project = null;
1331             break;
1332          }
1333          nodes.Add(node);
1334       }
1335       selectedRows.Free(null);
1336       if(project)
1337          Compile(project, nodes, mods.ctrl && mods.shift, normal);
1338       else
1339          ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1340       delete nodes;
1341       return true;
1342    }
1343
1344    bool FileClean(MenuItem selection, Modifiers mods)
1345    {
1346       OldLink item;
1347       OldList selectedRows;
1348       Project project = null;
1349       List<ProjectNode> nodes { };
1350       fileList.GetMultiSelection(selectedRows);
1351       for(item = selectedRows.first; item; item = item.next)
1352       {
1353          OldLink i;
1354          DataRow row = item.data;
1355          ProjectNode node = (ProjectNode)row.tag;
1356          if(!project)
1357             project = node.project;
1358          else if(node.project != project)
1359          {
1360             project = null;
1361             break;
1362          }
1363          nodes.Add(node);
1364       }
1365       selectedRows.Free(null);
1366       if(project)
1367          Clean(project, nodes, mods.ctrl && mods.shift);
1368       else
1369          ide.outputView.buildBox.Logf($"Please select files from a single project.\n");
1370       delete nodes;
1371       return true;
1372    }
1373
1374    bool FileDebugPrecompile(MenuItem selection, Modifiers mods)
1375    {
1376       DataRow row = fileList.currentRow;
1377       ProjectNode node = row ? (ProjectNode)row.tag : null;
1378       if(node)
1379       {
1380          List<ProjectNode> nodes { };
1381          nodes.Add(node);
1382          Compile(node.project, nodes, mods.ctrl && mods.shift, debugPrecompile);
1383          delete nodes;
1384       }
1385    }
1386
1387    bool FileDebugCompile(MenuItem selection, Modifiers mods)
1388    {
1389       DataRow row = fileList.currentRow;
1390       ProjectNode node = row ? (ProjectNode)row.tag : null;
1391       if(node)
1392       {
1393          List<ProjectNode> nodes { };
1394          nodes.Add(node);
1395          Compile(node.project, nodes, mods.ctrl && mods.shift, debugCompile);
1396          delete nodes;
1397       }
1398    }
1399
1400    bool FileDebugGenerateSymbols(MenuItem selection, Modifiers mods)
1401    {
1402       DataRow row = fileList.currentRow;
1403       ProjectNode node = row ? (ProjectNode)row.tag : null;
1404       if(node)
1405       {
1406          List<ProjectNode> nodes { };
1407          nodes.Add(node);
1408          Compile(node.project, nodes, mods.ctrl && mods.shift, debugGenerateSymbols);
1409          delete nodes;
1410       }
1411    }
1412
1413    Project GetSelectedProject(bool useSelection)
1414    {
1415       Project prj = project;
1416       if(useSelection)
1417       {
1418          DataRow row = fileList.currentRow;
1419          ProjectNode node = row ? (ProjectNode)row.tag : null;
1420          if(node)
1421             prj = node.project;
1422       }
1423       return prj;
1424    }
1425    
1426    void SelectNextProject(bool backwards)
1427    {
1428       DataRow row = fileList.currentRow;
1429       DataRow currentRow = row;
1430       ProjectNode node = (ProjectNode)row.tag;
1431       if(node.type != project)
1432          row = node.project.topNode.row;
1433       else if(backwards)
1434          row = row.previous ? row.previous : fileList.lastRow;
1435       if(!backwards)
1436          row = row.next ? row.next : fileList.firstRow;
1437       if(row && row != currentRow)
1438       {
1439          fileList.SelectRow(row);
1440          fileList.currentRow = row;
1441       }
1442    }
1443
1444    ProjectNode GetSelectedNode(bool useSelection)
1445    {
1446       ProjectNode node = null;
1447       if(useSelection)
1448       {
1449          DataRow row = fileList.currentRow;
1450          if(row)
1451             node = (ProjectNode)row.tag;
1452       }
1453       return node;
1454    }
1455
1456    bool MenuBrowseFolder(MenuItem selection, Modifiers mods)
1457    {
1458       char folder[MAX_LOCATION];
1459       Project prj;
1460       ProjectNode node = GetSelectedNode(true);
1461       if(!node)
1462          node = project.topNode;
1463       prj = node.project;
1464
1465       strcpy(folder, prj.topNode.path);
1466       if(node != prj.topNode)
1467          PathCatSlash(folder, node.path);
1468       ShellOpen(folder);
1469       return true;
1470    }
1471
1472    bool Run(MenuItem selection, Modifiers mods)
1473    {
1474       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1475       ProjectConfig config = project.config;
1476       int bitDepth = ide.workspace.bitDepth;
1477       String args = new char[maxPathLen];
1478       args[0] = '\0';
1479       if(ide.workspace.commandLineArgs)
1480          //ide.debugger.GetCommandLineArgs(args);
1481          strcpy(args, ide.workspace.commandLineArgs);
1482       if(ide.debugger.isActive)
1483          project.Run(args, compiler, config, bitDepth);
1484       /*else if(config.targetType == sharedLibrary || config.targetType == staticLibrary)
1485          MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
1486       else if(BuildInterrim(project, run, compiler, config, bitDepth, false))
1487          project.Run(args, compiler, config, bitDepth);
1488       delete args;
1489       delete compiler;
1490       return true;
1491    }
1492
1493    bool DebugStart()
1494    {
1495       bool result = false;
1496       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1497       ProjectConfig config = project.config;
1498       int bitDepth = ide.workspace.bitDepth;
1499       TargetTypes targetType = project.GetTargetType(config);
1500       if(targetType == sharedLibrary || targetType == staticLibrary)
1501          MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
1502       else if(project.GetCompress(config))
1503          MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
1504       else if(project.GetDebug(config) ||
1505          MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
1506       {
1507          if(/*!IsProjectModified() ||*/ BuildInterrim(project, start, compiler, config, bitDepth, false))
1508          {
1509             if(compiler.type.isVC)
1510             {
1511                //bool result = false;
1512                char oldwd[MAX_LOCATION];
1513                PathBackup pathBackup { };
1514                char command[MAX_LOCATION];
1515
1516                ide.SetPath(false, compiler, config, bitDepth);
1517                
1518                GetWorkingDir(oldwd, sizeof(oldwd));
1519                ChangeWorkingDir(project.topNode.path);
1520
1521                sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, config.name, "Debug.Start");
1522                Execute(command);
1523                ChangeWorkingDir(oldwd);
1524
1525                delete pathBackup;
1526             }
1527             else
1528             {
1529                ide.debugger.Start(compiler, config, bitDepth);
1530                result = true;
1531             }
1532          }
1533       }
1534       delete compiler;
1535       return result;      
1536    }
1537
1538    void GoToError(const char * line)
1539    {
1540       char * colon;
1541       
1542       while(isspace(*line)) line++;
1543       colon = strstr(line, ":");
1544
1545       {
1546          int lineNumber = 0;
1547          int col = 1;
1548          bool lookForLineNumber = true;
1549
1550          // deal with linking error
1551          if(colon && colon[1] == ' ')
1552          {
1553             colon = strstr(colon + 1, ":");
1554             if(colon && !colon[1])
1555             {
1556                colon = strstr(line, ":");
1557                lookForLineNumber = false;
1558             }
1559             else if(colon && !isdigit(colon[1]))
1560             {
1561                line = colon + 1;
1562                colon = strstr(line, ":");
1563             }
1564          }
1565          // Don't be mistaken by the drive letter colon
1566          if(colon && (colon[1] == '/' || colon[1] == '\\'))
1567             colon = strstr(colon + 1, ":");
1568          if(colon && lookForLineNumber)
1569          {
1570             char * comma;
1571             // MSVS Errors
1572             char * par = RSearchString(line, "(", colon - line, true, false);
1573             if(par && strstr(par, ")"))
1574                colon = par;
1575             else if((colon+1)[0] == ' ')
1576             {
1577                // NOTE: This is the same thing as saying 'colon = line'
1578                for( ; colon != line; colon--)
1579                   /*if(*colon == '(')
1580                      break*/;
1581             }
1582             lineNumber = atoi(colon + 1);
1583             /*
1584             comma = strchr(colon, ',');
1585             if(comma)
1586                col = atoi(comma+1);
1587             */
1588             comma = strchr(colon+1, ':');
1589             if(comma)
1590                col = atoi(comma+1);
1591          }
1592          
1593          {
1594             char moduleName[MAX_LOCATION], filePath[MAX_LOCATION];
1595             char * bracket;
1596             if(colon)
1597             {
1598                char * inFileIncludedFrom = strstr(line, stringInFileIncludedFrom);
1599                char * start = inFileIncludedFrom ? line + strlen(stringInFileIncludedFrom) : line;
1600                int len = (int)(colon - start);
1601                len = Min(len, MAX_LOCATION-1);
1602                // Cut module name
1603                strncpy(moduleName, start, len);
1604                moduleName[len] = '\0';
1605             }
1606             else
1607                strcpy(moduleName, line);
1608
1609             // Remove stuff in brackets
1610             /*
1611             bracket = strstr(moduleName, "(");
1612             if(bracket) *bracket = '\0';
1613             */
1614             MakeSlashPath(moduleName);
1615
1616             if(!colon)
1617             {
1618                // Check if it's one of our modules
1619                ProjectNode node = project.topNode.Find(moduleName, false);
1620                if(node)
1621                {
1622                   strcpy(moduleName, node.path);
1623                   PathCatSlash(moduleName, node.name);
1624                }
1625                else
1626                   moduleName[0] = '\0';
1627             }
1628             if(moduleName[0])
1629             {
1630                CodeEditor codeEditor;
1631                strcpy(filePath, project.topNode.path);
1632                PathCatSlash(filePath, moduleName);
1633       
1634                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
1635                if(!codeEditor)
1636                {
1637                   char name[MAX_LOCATION];
1638                   // TOFIX: Improve on this, don't use only filename, make a function
1639                   if(ide && ide.workspace)
1640                   {
1641                      for(prj : ide.workspace.projects)
1642                      {
1643                         if(prj.topNode.FindWithPath(moduleName, false))
1644                         {
1645                            strcpy(filePath, prj.topNode.path);
1646                            PathCatSlash(filePath, moduleName);
1647                            codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
1648                            if(codeEditor)
1649                               break;
1650                         }
1651                      }
1652                   }
1653                }
1654                if(codeEditor && lineNumber)
1655                {
1656                   EditBox editBox = codeEditor.editBox;
1657                   editBox.GoToLineNum(lineNumber - 1);
1658                   editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
1659                }
1660             }
1661          }
1662       }
1663    }
1664
1665    bool OpenNode(ProjectNode node)
1666    {
1667       char filePath[MAX_LOCATION];
1668       node.GetFullFilePath(filePath);
1669       return ide.OpenFile(filePath, normal, true/*false Why was it opening hidden?*/, null, something, normal) ? true : false;
1670    }
1671
1672    void AddNode(ProjectNode node, DataRow addTo)
1673    {
1674       DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
1675
1676       row.tag = (int64)node;
1677       node.row = row;
1678
1679       if(node.type == resources)
1680          resourceRow = row;
1681
1682       row.SetData(null, node);
1683
1684       if(node.files && node.files.first && node.parent && 
1685             !(!node.parent.parent && 
1686                (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
1687                   !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
1688          row.collapsed = true;
1689       else if(node.type == folder)
1690          node.icon = openFolder;
1691
1692       if(node.files)
1693       {
1694          for(child : node.files)
1695             AddNode(child, row);
1696       }
1697    }
1698
1699    void DeleteNode(ProjectNode projectNode)
1700    {
1701       if(projectNode.files)
1702       {
1703          ProjectNode child;
1704          while(child = projectNode.files.first)
1705             DeleteNode(child);
1706       }
1707       fileList.DeleteRow(projectNode.row);
1708       projectNode.Delete();
1709    }
1710
1711    bool ProjectSave(MenuItem selection, Modifiers mods)
1712    {
1713       DataRow row = fileList.currentRow;
1714       ProjectNode node = row ? (ProjectNode)row.tag : null;
1715       Project prj = node ? node.project : null;
1716       if(prj)
1717       {
1718          prj.StopMonitoring();
1719          if(prj.Save(prj.filePath))
1720          {
1721             Project modPrj = null;
1722             prj.topNode.modified = false;
1723             for(p : ide.workspace.projects)
1724             {
1725                if(p.topNode.modified)
1726                { 
1727                   modPrj = p;
1728                   break;
1729                }
1730             }
1731             if(!modPrj)
1732                modifiedDocument = false;
1733             Update(null);
1734          }
1735          prj.StartMonitoring();
1736       }
1737       return true;
1738    }
1739
1740    bool ShowOutputBuildLog(bool cleanLog)
1741    {
1742       OutputView output = ide.outputView;
1743       if(cleanLog)
1744          output.ShowClearSelectTab(build);
1745       else
1746       {
1747          output.SelectTab(build);
1748          output.Show();
1749       }
1750       return true;
1751    }
1752
1753    bool DebugRestart()
1754    {
1755       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1756       ProjectConfig config = project.config;
1757       int bitDepth = ide.workspace.bitDepth;
1758
1759       bool result = false;
1760       if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart, compiler, config, bitDepth, false))
1761       {
1762          // For Restart, compiler and config will only be used if for
1763          // whatever reason (if at all possible) the Debugger is in a
1764          // 'terminated' or 'none' state
1765          ide.debugger.Restart(compiler, config, bitDepth);
1766          result = true;
1767       }
1768
1769       delete compiler;
1770       return result;
1771    }
1772
1773    bool DebugResume()
1774    {
1775       ide.debugger.Resume();
1776       return true;
1777    }
1778
1779    bool DebugBreak()
1780    {
1781       ide.debugger.Break();
1782       return true;
1783    }
1784
1785    bool DebugStop()
1786    {
1787       ide.debugger.Stop();
1788       return true;
1789    }
1790
1791    bool DebugStepInto()
1792    {
1793       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1794       ProjectConfig config = project.config;
1795       int bitDepth = ide.workspace.bitDepth;
1796
1797       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, bitDepth, false)))
1798          ide.debugger.StepInto(compiler, config, bitDepth);
1799       delete compiler;
1800       return true;
1801    }
1802
1803    bool DebugStepOver(bool skip)
1804    {
1805       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1806       ProjectConfig config = project.config;
1807       int bitDepth = ide.workspace.bitDepth;
1808
1809       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start, compiler, config, bitDepth, false)))
1810          ide.debugger.StepOver(compiler, config, skip, bitDepth);
1811
1812       delete compiler;
1813       return true;
1814    }
1815
1816    bool DebugStepOut(bool skip)
1817    {
1818       ide.debugger.StepOut(skip);
1819       return true;
1820    }
1821
1822    void ImportFolder(ProjectNode toNode)
1823    {
1824       if(toNode)
1825       {
1826          //bool isFolder = toNode.type == folder;
1827          //bool isRes = toNode.isInResources;
1828          
1829          FileDialog fileDialog = importFileDialog;
1830          fileDialog.master = parent;
1831          if(fileDialog.Modal() == ok)
1832          {
1833             ImportFolderFSI fsi { projectView = this };
1834             fsi.stack.Add(toNode);
1835             fsi.Iterate(fileDialog.filePath);
1836             delete fsi;
1837          }
1838       }
1839    }
1840
1841    ProjectNode NewFolder(ProjectNode parentNode, char * name, bool showProperties)
1842    {
1843       if(parentNode)
1844       {
1845          ProjectNode folderNode;
1846          Project prj = parentNode.project;
1847          int c;
1848          ProjectNode after = null;
1849          for(node : parentNode.files)
1850          {
1851             if(node.type != folder)
1852                break;
1853             after = node;
1854          }
1855          
1856          if(name && name[0])
1857             folderNode = parentNode.Add(prj, name, after, folder, folder, true);
1858          else
1859          {
1860             for(c = 0; c < 100; c++)
1861             {
1862                char string[16];
1863                sprintf(string, c ? "New Folder (%d)" : "New Folder", c);
1864                if((folderNode = parentNode.Add(prj, string, after, folder, folder, true)))
1865                   break;
1866             }
1867          }
1868
1869          if(folderNode)
1870          {
1871             NodeProperties nodeProperties;
1872             if(!showProperties)
1873             {
1874                modifiedDocument = true;
1875                prj.topNode.modified = true;
1876             }
1877             Update(null);
1878             folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1879             folderNode.row.tag = (int64)folderNode;
1880                
1881             folderNode.row.SetData(null, folderNode);
1882             fileList.currentRow = folderNode.row;
1883             
1884             if(showProperties)
1885             {
1886                nodeProperties = NodeProperties
1887                {
1888                   parent, this, mode = newFolder, node = folderNode;
1889                   position = { position.x + 100, position.y + 100 };
1890                };
1891                nodeProperties.Create();   // Modal?
1892             }
1893             return folderNode;
1894          }
1895       }
1896       return null;
1897    }
1898
1899    void AddFiles(bool resources)
1900    {
1901       FileDialog fileDialog = (!resources) ? this.fileDialog : resourceFileDialog;
1902       fileDialog.type = multiOpen;
1903       fileDialog.text = !resources ? $"Add Files to Project" : $"Add Resources to Project";
1904       fileDialog.master = parent;
1905
1906       if(fileDialog.Modal() == ok)
1907       {
1908          int c;
1909          DataRow row = fileList.currentRow;
1910          ProjectNode parentNode = (ProjectNode)row.tag;
1911          bool addFailed = false;
1912          int numSelections = fileDialog.numSelections;
1913          char ** multiFilePaths = fileDialog.multiFilePaths;
1914
1915          Array<String> nameConflictFiles { };
1916
1917          for(c = 0; c < numSelections; c++)
1918          {
1919             char * filePath = multiFilePaths[c];
1920             FileAttribs exists = FileExists(filePath);
1921             bool addThisFile = true;
1922
1923             if(exists.isDirectory)
1924                addThisFile = false;
1925             else if(!exists)
1926             {
1927                if(MessageBox { master = ide, type = yesNo, text = filePath, 
1928                      contents = $"File doesn't exist. Create?" }.Modal() == yes)
1929                {
1930                   File f = FileOpen(filePath, write);
1931                   if(f)
1932                   {
1933                      addThisFile = true;
1934                      delete f;
1935                   }
1936                   else
1937                   {
1938                      MessageBox { master = ide, type = ok, text = filePath, 
1939                            contents = $"Couldn't create file."}.Modal();
1940                      addThisFile = false;
1941                   }
1942                }
1943             }
1944
1945             if(addThisFile)
1946             {
1947                /*addFailed = */if(!AddFile(parentNode, filePath, resources, false))//;
1948                {
1949                   nameConflictFiles.Add(CopyString(filePath));
1950                   addFailed = true;
1951                }
1952             }
1953          }
1954          if(addFailed)
1955          {
1956             int len = 0;
1957             char * part1 = $"The following file";
1958             char * opt1 = $" was ";
1959             char * opt2 = $"s were ";
1960             char * part2 = $"not added because of identical file name conflict within the project.\n\n";
1961             char * message;
1962             len += strlen(part1);
1963             len += strlen(part2);
1964             len += nameConflictFiles.count > 1 ? strlen(opt2) : strlen(opt1);
1965             for(s : nameConflictFiles)
1966                len += strlen(s) + 1;
1967             message = new char[len + 1];
1968             strcpy(message, part1);
1969             strcat(message, nameConflictFiles.count > 1 ? opt2 : opt1);
1970             strcat(message, part2);
1971             for(s : nameConflictFiles)
1972             {
1973                strcat(message, s);
1974                strcat(message, "\n");
1975             }
1976             MessageBox { master = ide, type = ok, text = $"Name Conflict", 
1977                   contents = message }.Modal();
1978             delete message;
1979          }
1980          nameConflictFiles.Free();
1981          delete nameConflictFiles;
1982       }
1983    }
1984
1985    ProjectNode AddFile(ProjectNode parentNode, char * filePath, bool resources, bool isTemporary)
1986    {
1987       ProjectNode result = null;
1988       ProjectNode after = null;
1989       for(node : parentNode.files)
1990       {
1991          if(node.type != folder && node.type != file && node.type)
1992             break;
1993          after = node;
1994       }
1995
1996       result = parentNode.Add(parentNode.project, filePath, after, file, NodeIcons::SelectFileIcon(filePath), !resources);
1997
1998       if(result)
1999       {
2000          if(!isTemporary)
2001          {
2002             modifiedDocument = true;
2003             parentNode.project.topNode.modified = true;
2004             parentNode.project.ModifiedAllConfigs(true, false, true, true);
2005          }
2006          Update(null);
2007          result.row = parentNode.row.AddRowAfter(after ? after.row : null);
2008          result.row.tag = (int64)result;
2009          result.row.SetData(null, result);
2010       }
2011       return result;
2012    }
2013
2014    CodeEditor CreateNew(char * upper, char * lower, char * base, char * className)
2015    {
2016       CodeEditor codeEditor = null;
2017       ProjectNode projectNode;
2018       ProjectNode after = null;
2019       DataRow row = fileList.currentRow;
2020       ProjectNode parentNode;
2021       int c;
2022
2023       if(!row) row = project.topNode.row;
2024
2025       parentNode = (ProjectNode)row.tag;
2026
2027       for(node : parentNode.files)
2028       {
2029          if(node.type != folder && node.type != file && node.type)
2030             break;
2031          after = node;
2032       }
2033       for(c = 1; c < 100; c++)
2034       {
2035          char string[16];
2036          sprintf(string, c ? "%s%d.ec" : "%s.ec", lower, c);
2037          if((projectNode = parentNode.Add(project, string, after, file, genFile, true)))
2038             break;
2039       }
2040       if(projectNode)
2041       {
2042          char name[256];
2043          char filePath[MAX_LOCATION];
2044
2045          modifiedDocument = true;
2046          project.topNode.modified = true;
2047          Update(null);
2048          project.ModifiedAllConfigs(true, false, false, true);
2049          projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
2050          projectNode.row.tag =(int64)projectNode;
2051             
2052          projectNode.row.SetData(null, projectNode);
2053          fileList.currentRow = projectNode.row;
2054
2055          strcpy(filePath, project.topNode.path);
2056          PathCat(filePath, projectNode.path);
2057          PathCat(filePath, projectNode.name);
2058
2059          codeEditor = (CodeEditor)ide.FindWindow(filePath);
2060          if(!codeEditor)
2061          {
2062             Class baseClass = eSystem_FindClass(__thisModule, base);
2063             subclass(ClassDesignerBase) designerClass = eClass_GetDesigner(baseClass);
2064             if(designerClass)
2065             {
2066                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
2067                strcpy(name, projectNode.name);
2068                sprintf(name, "%s%d", upper, c);
2069                if(className)
2070                   strcpy(className, name);
2071
2072                designerClass.CreateNew(codeEditor.editBox, codeEditor.clientSize, name, base);
2073
2074                //codeEditor.modifiedDocument = false;
2075                //codeEditor.designer.modifiedDocument = false;
2076
2077                //Code_EnsureUpToDate(codeEditor);
2078             }
2079          }
2080          else // TODO: fix no symbols generated when ommiting {} for following else
2081          {
2082             codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
2083          }
2084          if(codeEditor)
2085          {
2086             codeEditor.ViewDesigner();
2087             codeEditor.codeModified = true;
2088          }
2089       }
2090       visible = false;
2091       return codeEditor;   
2092    }
2093
2094    // Returns true if we opened something
2095    bool OpenSelectedNodes()
2096    {
2097       bool result = false;
2098       OldList selection;
2099       OldLink item;
2100
2101       fileList.GetMultiSelection(selection);
2102       for(item = selection.first; item; item = item.next)
2103       {
2104          DataRow row = item.data;
2105          ProjectNode node = (ProjectNode)row.tag;
2106          if(node.type == file)
2107          {
2108             OpenNode(node);
2109             result = true;
2110             break;
2111          }
2112       }
2113       selection.Free(null);
2114       return result;
2115    }
2116
2117    void RemoveSelectedNodes()
2118    {
2119       OldList selection;
2120       OldLink item, next;
2121       
2122       fileList.GetMultiSelection(selection);
2123
2124       // Remove children of parents we're deleting
2125       for(item = selection.first; item; item = next)
2126       {
2127          OldLink i;
2128          DataRow row = item.data;
2129          ProjectNode n, node = (ProjectNode)row.tag;
2130          bool remove = false;
2131
2132          next = item.next;
2133          for(i = selection.first; i && !remove; i = i.next)
2134          {
2135             ProjectNode iNode = (ProjectNode)((DataRow)i.data).tag;
2136             for(n = node.parent; n; n = n.parent)
2137             {
2138                if(iNode == n)
2139                {
2140                   remove = true;
2141                   break;
2142                }
2143             }
2144          }
2145          if(remove)
2146             selection.Delete(item);
2147       }
2148
2149       for(item = selection.first; item; item = item.next)
2150       {
2151          DataRow row = item.data;
2152          ProjectNode node = (ProjectNode)row.tag;
2153          ProjectNode resNode;
2154          for(resNode = node.parent; resNode; resNode = resNode.parent)
2155             if(resNode.type == resources)
2156                break;
2157          if(node.type == file)
2158          {
2159             Project prj = node.project;
2160             DeleteNode(node);
2161             modifiedDocument = true;
2162             prj.topNode.modified = true;
2163             Update(null);
2164             prj.ModifiedAllConfigs(true, false, true, true);
2165          }
2166          else if(node.type == folder)
2167          {
2168             char message[1024];
2169             sprintf(message, $"Are you sure you want to remove the folder \"%s\"\n"
2170                   "and all of its contents from the project?", node.name);
2171             if(MessageBox { master = ide, type = yesNo, text = $"Delete Folder", contents = message }.Modal() == yes)
2172             {
2173                Project prj = node.project;
2174                if(node.containsFile)
2175                   prj.ModifiedAllConfigs(true, false, true, true);
2176                DeleteNode(node);
2177                modifiedDocument = true;
2178                prj.topNode.modified = true;
2179             }
2180          }
2181          else if(node.type == project && node != project.topNode && !buildInProgress)
2182          {
2183             char message[1024];
2184             Project prj = null;
2185             for(p : workspace.projects)
2186             {
2187                if(p.topNode == node)
2188                {
2189                   prj = p;
2190                   break;
2191                }
2192             }
2193             sprintf(message, $"Are you sure you want to remove the \"%s\" project\n" "from this workspace?", node.name);
2194             if(MessageBox { master = ide, type = yesNo, text = $"Remove Project", contents = message }.Modal() == yes)
2195             {
2196                // THIS GOES FIRST!
2197                DeleteNode(node);
2198                if(prj)
2199                   workspace.RemoveProject(prj);
2200                //modifiedDocument = true; // when project view is a workspace view
2201             }
2202          }
2203       }
2204       selection.Free(null);
2205    }
2206 }