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