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