Fixed up some i18n strings
[sdk] / ide / src / project / ProjectView.ec
1 import "ide"
2
3 import "FileSystemIterator"
4
5 class ImportFolderFSI : NormalFileSystemIterator
6 {
7    ProjectView projectView;
8    Array<ProjectNode> stack { };
9
10    bool OnFolder(char * folderPath)
11    {
12       char name[MAX_LOCATION];
13       ProjectNode parentNode = stack.lastIterator.data;
14       ProjectNode folder;
15       GetLastDirectory(folderPath, name);
16       folder = parentNode.FindSpecial(name, false, true, true);
17       if(!folder)
18          folder = projectView.NewFolder(parentNode, name, false);
19       stack.Add(folder);
20       return true;
21    }
22
23    void OutFolder(char * folderPath, bool isRoot)
24    {
25       stack.lastIterator.Remove(); //stack.Remove();
26    }
27
28    bool OnFile(char * filePath)
29    {
30       ProjectNode parentNode = stack.lastIterator.data;
31       projectView.AddFile(parentNode, filePath, parentNode.isInResources, false);
32       return true;
33    }
34 }
35
36 static Array<FileFilter> fileFilters
37 { [
38    { $"eC/C/C++ Files (*.ec, *.eh, *.c, *.cpp, *.cc, *.cxx, *.h, *.hpp, *.hh, *.hxx)", "ec, eh, c, cpp, cc, cxx, h, hpp, hh, hxx" },
39    { $"eC/C/C++ Source Files (*.ec, *.c, *.cpp, *.cc, *.cxx)", "ec, eh, c, cpp, cc, cxx" },
40    { $"Header Files for eC/C/C++ (*.eh, *.h, *.hpp, *.hh, *.hxx)", "eh, h, hpp, hh, hxx" },
41    { $"All files", null }
42 ] };
43
44 static Array<FileFilter> resourceFilters
45 { [
46    { $"Image Files (*.jpg, *.jpeg, *.bmp, *.pcx, *.png,*.gif)", "jpg, jpeg, bmp, pcx, png, gif" },
47    { $"3D Studio Model Files (*.3ds)", "3ds" },
48    { $"All files", null }
49 ] };
50
51 static Array<FileType> fileTypes
52 { [
53    { $"Based on extension", null },
54    { $"Text",               "txt" },
55    { $"Image",              "jpg" },
56    { $"3D Studio Model",    "3ds" }
57 ] };
58
59 static Array<FileFilter> projectFilters
60 { [
61    { $"Project Files (*.epj)", ProjectExtension },
62    { $"Workspace Files (*.ews)", WorkspaceExtension }
63 ] };
64
65 static Array<FileType> projectTypes
66 { [
67    { $"Ecere IDE Project", ProjectExtension },
68    { $"Ecere IDE Workspace", WorkspaceExtension }
69 ] };
70
71 static char * iconNames[] = 
72 {
73    "<:ecere>mimeTypes/file.png",                   /*genFile*/
74    "<:ecere>mimeTypes/textEcereWorkspace.png",     /*ewsFile*/
75    "<:ecere>mimeTypes/textEcereProject.png",       /*epjFile*/
76    "<:ecere>places/folder.png",                    /*folder*/
77    "<:ecere>status/folderOpen.png",                /*openFolder*/
78    "<:ecere>mimeTypes/textEcereSource.png",        /*ecFile*/
79    "<:ecere>mimeTypes/textEcereHeader.png",        /*ehFile*/
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 mfile
92 };
93
94 enum PrepareMakefileMethod { normal, force, forceExists };
95
96 enum BuildType { build, rebuild, relink, run, start, restart };
97 enum BuildState
98 {
99    none, buildingMainProject, buildingSecondaryProject, compilingFile;
100
101    property bool { get { return this != none; } }
102    //property bool actualBuild { get { return this == buildingMainProject || this == buildingSecondaryProject;  } }
103 };
104
105 class ProjectView : Window
106 {
107    isDocument = true;
108    //hasMinimize = true;
109    hasClose = true;
110    borderStyle = sizable;
111    hasHorzScroll = true;
112    hasVertScroll = true;
113    background = white;
114    size = { 300 };
115    anchor = Anchor { left = 0, top = 0, bottom = 0 };
116    menu = Menu { };
117    
118    //hasMinimize = true;
119    saveDialog = projectFileDialog;
120    
121    DataRow resourceRow;
122    BuildState buildInProgress;
123    BitmapResource icons[NodeIcons];
124    Project project;
125    Workspace workspace;
126    property Workspace workspace
127    {
128       set
129       {
130          if(workspace)
131          {
132             for(prj : workspace.projects)
133             {
134                DeleteNode(prj.topNode);
135             }
136             workspace.projects.Free();
137             ide.debugger.CleanUp();
138          }
139          workspace = value;
140          if(workspace)
141          {
142             fileDialog.currentDirectory = workspace.workspaceDir;
143             resourceFileDialog.currentDirectory = workspace.workspaceDir;
144             for(prj : workspace.projects)
145                AddNode(prj.topNode, null);
146             ide.statusBar.text = $"Generating Makefile & Dependencies...";
147             app.UpdateDisplay();
148             for(prj : workspace.projects)
149                prj.ModifiedAllConfigs(true, false, false, false);
150             ide.statusBar.text = $"Initializing Debugger"; app.UpdateDisplay();
151             ide.statusBar.text = null;
152             app.UpdateDisplay();
153          }
154       }
155       get { return workspace; }
156    }
157    
158    /*property Project project
159    {
160       set
161       {
162          if(project)
163          {
164             DeleteNode(project.topNode);
165             project.Free();
166          }
167          project = value;
168          if(project)
169          {
170             AddNode(project.topNode, null);
171             fileDialog.currentDirectory = project.topNode.path;
172             resourceFileDialog.currentDirectory = project.topNode.path;
173
174             // Make sure this is done already...
175             {
176                char filePath[MAX_LOCATION];
177                strcpy(filePath, project.topNode.path);
178                PathCat(filePath, project.topNode.name);
179                strcat(filePath, ".epj");
180                fileName = filePath;
181             }
182
183             ide.statusBar.text = "Generating Makefile & Dependencies...";
184             app.UpdateDisplay();
185             // REDJ set makefile generation flag so generation occurs only when compiling instead of generating on the spot
186             project.config.makingModified = true;
187             ide.statusBar.text = "Initializing Debugger";
188             ide.statusBar.text = null;
189             app.UpdateDisplay();
190          }
191       }
192       get { return project; }
193    }*/
194
195    bool drawingInProjectSettingsDialog;
196    bool drawingInProjectSettingsDialogHeader;
197    ProjectSettings projectSettingsDialog;
198
199    bool stopBuild;
200
201    ListBox fileList
202    {
203       multiSelect = true, fullRowSelect = false, hasVertScroll = true, hasHorzScroll = true;
204       borderStyle = deep, parent = this, collapseControl = true, treeBranches = true;
205       anchor = Anchor { left = 0, right = 0, top = 0 , bottom = 0 };
206
207       background = projectViewBackground;
208       foreground = projectViewText;
209       selectionColor = selectionColor, selectionText = selectionText;
210       stippleColor = skyBlue;
211
212       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
213       {
214          if(!active) Update(null);
215          return ListBox::OnActivate(active, previous, goOnWithActivation, direct);
216       }
217       
218       bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
219       {
220          // Prevent the double click from reactivating the project view (returns false if we opened something)
221          return !OpenSelectedNodes();
222       }
223
224       bool NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods)
225       {
226          DataRow row = listBox.currentRow;
227          if(row)
228          {
229             ProjectNode node = (ProjectNode)row.tag;
230             if(node.type == NodeTypes::project || node.type == resources || node.type == file || node.type == folder)
231             {
232                bool buildMenuUnavailable = buildInProgress;
233                PopupMenu popupMenu;
234                Menu popupContent { };
235                
236                if(node.type == NodeTypes::project)
237                {
238                   //if(node == ((Project)workspace.projects.first).topNode)
239                   {
240                      MenuItem { popupContent, $"Build", b, NotifySelect = ProjectBuild }.disabled = buildMenuUnavailable;
241                      MenuItem { popupContent, $"Relink", l, NotifySelect = ProjectLink }.disabled = buildMenuUnavailable;
242                      MenuItem { popupContent, $"Rebuild", r, NotifySelect = ProjectRebuild }.disabled = buildMenuUnavailable;
243                      MenuItem { popupContent, $"Clean", c, NotifySelect = ProjectClean }.disabled = buildMenuUnavailable;
244                      MenuItem { popupContent, $"Regenerate Makefile", m, NotifySelect = ProjectRegenerate }.disabled = buildMenuUnavailable;
245                      MenuDivider { popupContent };
246                   }
247                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
248                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
249                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
250                   MenuItem { popupContent, $"Add Files to Project...", f, NotifySelect = ProjectAddFiles };
251                   MenuDivider { popupContent };
252                   MenuItem { popupContent, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
253                   // MenuItem { popupContent, "Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
254                   MenuDivider { popupContent };
255                   if(node != ((Project)workspace.projects.first).topNode)
256                   {
257                      MenuItem { popupContent, $"Remove project from workspace", r, NotifySelect = ProjectRemove }.disabled = buildMenuUnavailable;
258                      MenuDivider { popupContent };
259                   }
260                   MenuItem { popupContent, $"Active Configuration...", s, Key { f5, alt = true } , NotifySelect = MenuConfig };
261                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
262                   MenuDivider { popupContent };
263                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
264                   MenuDivider { popupContent };
265                   MenuItem { popupContent, $"Save", v, Key { s, ctrl = true }, NotifySelect = ProjectSave }.disabled = !node.modified;
266                   MenuDivider { popupContent };
267                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
268                }
269                else if(node.type == resources)
270                {
271                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
272                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
273                   MenuItem { popupContent, $"Add Resources to Project...", f, NotifySelect = ResourcesAddFiles };
274                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
275                   MenuDivider { popupContent };
276                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
277                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
278                }
279                else if(node.type == file)
280                {
281                   MenuItem { popupContent, $"Open", o, NotifySelect = FileOpenFile };
282                   MenuItem { popupContent, $"Compile", c, Key { f7, ctrl = true}, NotifySelect = FileCompile }.disabled = buildMenuUnavailable;
283                   MenuDivider { popupContent };
284                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
285                   MenuDivider { popupContent };
286                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
287                   MenuDivider { popupContent };
288                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
289                   MenuItem { popupContent, $"Properties..", p, Key { enter, alt = true }, NotifySelect = FileProperties };
290                }
291                else if(node.type == folder)
292                {
293                   bool isInResources = node.isInResources;
294
295                   MenuItem { popupContent, $"New File...", l, Key { l, ctrl = true }, NotifySelect = ProjectNewFile };
296                   MenuItem { popupContent, $"New Folder...", n, Key { f, ctrl = true }, NotifySelect = ProjectNewFolder };
297                   MenuItem { popupContent, $"Import Folder...", i, NotifySelect = ProjectImportFolder };
298                   if(isInResources)
299                   {
300                      MenuItem { popupContent, $"Add Resources to Folder...", f, NotifySelect = ResourcesAddFiles };
301                   }
302                   else
303                   {
304                      MenuItem { popupContent, $"Add Files to Folder...", f, NotifySelect = ProjectAddFiles };
305                   }
306                   if(!isInResources)
307                   {
308                      MenuDivider { popupContent };
309                      MenuItem { popupContent, $"Add New Form...", o, NotifySelect = ProjectAddNewForm };
310                      MenuItem { popupContent, $"Add New Behavior Graph...", g, NotifySelect = ProjectAddNewGraph };
311                   }
312                   MenuDivider { popupContent };
313                   MenuItem { popupContent, $"Remove", r, NotifySelect = FileRemoveFile };
314                   MenuDivider { popupContent };
315                   MenuItem { popupContent, $"Browse Folder", w, NotifySelect = MenuBrowseFolder };
316                   MenuDivider { popupContent };
317                   MenuItem { popupContent, $"Settings...", s, Key { f7, alt = true } , NotifySelect = MenuSettings };
318                   MenuItem { popupContent, $"Properties...", p, Key { enter, alt = true }, NotifySelect = FileProperties };
319                }
320
321                popupMenu = 
322                {
323                   master = this, menu = popupContent;
324                   position = {
325                      x + clientStart.x + absPosition.x - app.desktop.position.x, 
326                      y + clientStart.y + absPosition.y - app.desktop.position.y };
327                };
328                popupMenu.Create();
329             }
330          }
331          return true;
332       }
333
334       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
335       {
336          if(row)
337          {
338             ProjectNode node = (ProjectNode)row.tag;
339             switch(key)
340             {
341                case altEnter: case Key { keyPadEnter, alt = true }:
342                {
343                   NodeProperties { parent = parent, master = this, 
344                      position = { position.x + 100, position.y + 100 }, node = node }.Create();
345                   return false;
346                }
347                case enter: case keyPadEnter:
348                {
349                   ProjectNode resNode;
350                   for(resNode = node.parent; resNode; resNode = resNode.parent)
351                      if(resNode.type == resources)
352                         break;
353                   if(node.type == project || (node.type == folder && !resNode))
354                   {
355                      AddFiles(false);
356                      return false;
357                   }
358                   else if(node.type == resources || node.type == folder)
359                   {
360                      AddFiles(true);
361                      return false;
362                   }
363                   break;
364                }
365                case ctrlI:
366                {
367                   if(node.type == project || node.type == folder || node.type == resources)
368                   {
369                      ImportFolder(node);
370                      return false;
371                   }
372                   break;
373                }
374                case ctrlF:
375                {
376                   if(node.type == project || node.type == folder || node.type == resources)
377                   {
378                      NewFolder(node, null, true);
379                      return false;
380                   }
381                   break;
382                }
383                case ctrlF7:
384                {
385                   if(node.type == file)
386                   {
387                      FileCompile(null, (Modifiers)key);
388                      return false;
389                   }
390                   break;
391                }
392                case Key { space, shift = true }:
393                case space:
394                {
395                   if(node.type == NodeTypes::project)
396                   {
397                      Project prj = null;
398                      for(p : workspace.projects)
399                      {
400                         if(p.topNode == node)
401                         {
402                            prj = p;
403                            break;
404                         }
405                      }
406                      prj.RotateActiveConfig(!key.shift);
407                      if(prj == project)
408                         ide.AdjustMenus();
409                      return false;
410                   }
411                   break;
412                }
413             }
414          }
415          switch(key)
416          {
417             case enter: case keyPadEnter:  OpenSelectedNodes();   break;
418             case del:                      RemoveSelectedNodes(); break;
419             case escape:                      
420             {
421                Window activeClient = ide.activeClient;
422                if(activeClient)
423                   activeClient.Activate();
424                else
425                   ide.RepositionWindows(true);
426                break;
427             }
428          }
429          return true;
430       }
431
432       bool NotifyCollapse(ListBox listBox, DataRow row, bool collapsed)
433       {
434          ProjectNode node = (ProjectNode)row.tag;
435          if(node.type == folder)
436             node.icon = collapsed ? folder : openFolder;
437          return true;
438       }
439    };
440
441    FileDialog importFileDialog { autoCreate = false, type = selectDir, text = $"Import Folder" };
442    FileDialog projectFileDialog
443    {
444       autoCreate = false, filters = projectFilters.array, sizeFilters = projectFilters.count * sizeof(FileFilter);
445       types = projectTypes.array, sizeTypes = projectTypes.count * sizeof(FileType);
446    };
447    FileDialog fileDialog
448    {
449       autoCreate = false, mayNotExist = true, filters = fileFilters.array, sizeFilters = fileFilters.count * sizeof(FileFilter);
450       types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType);
451    };
452    FileDialog resourceFileDialog
453    {
454       autoCreate = false, mayNotExist = true, filters = resourceFilters.array, sizeFilters = resourceFilters.count * sizeof(FileFilter);
455       types = fileTypes.array, sizeTypes = fileTypes.count * sizeof(FileType);
456    };
457
458    Menu fileMenu { menu, $"File", f };
459    MenuItem { fileMenu, $"Save", s, Key { s, ctrl = true }, NotifySelect = MenuFileSave };
460    // MenuItem { fileMenu, "Save As...", a, NotifySelect = MenuFileSaveAs };
461
462    bool OnClose(bool parentClosing)
463    {
464       if(!parentClosing && visible)
465       {
466          visible = false;
467          return false;
468       }
469       if(buildInProgress)
470          return false;
471       return true;
472    }
473
474    void OnDestroy(void)
475    {
476       //if(ide.findInFilesDialog && ide.findInFilesDialog.created && ide.findInFilesDialog.mode != directory)
477       //   ide.findInFilesDialog.SearchStop();
478
479       ide.outputView.buildBox.Clear();
480       ide.outputView.debugBox.Clear();
481       //ide.outputView.findBox.Clear();
482       ide.callStackView.Clear();
483       ide.watchesView.Clear();
484       ide.threadsView.Clear();
485       ide.breakpointsView.Clear();
486       ide.outputView.ShowClearSelectTab(find); // why this? 
487    }
488
489    bool OnSaveFile(char * fileName)
490    {
491       for(prj : ide.workspace.projects)
492       {
493          if(prj.topNode.modified && prj.Save(prj.filePath))
494          {
495             // ProjectUpdateMakefileForAllConfigs(prj, true, true);
496             prj.topNode.modified = false;
497          }
498       }
499       modifiedDocument = false;
500       Update(null);
501       return true;
502    }
503
504    bool IsModuleInProject(char * filePath)
505    {
506       char moduleName[MAX_FILENAME]; //, modulePath[MAX_LOCATION];
507       GetLastDirectory(filePath, moduleName);
508       return project.topNode.Find(moduleName, false) != null;
509    }
510
511    bool GetRelativePath(char * filePath, char * relativePath)
512    {
513       /*ProjectNode node;
514       char moduleName[MAX_FILENAME]; //, modulePath[MAX_LOCATION];
515       GetLastDirectory(filePath, moduleName);
516       
517       // try with workspace dir first?
518       if((node = project.topNode.Find(moduleName, false)))
519       {
520          strcpy(relativePath, node.path);
521          PathCatSlash(relativePath, node.name);
522          return true;
523       }
524       // WARNING: On failure, relative path is uninitialized
525       return false;   */
526       return project.GetRelativePath(filePath, relativePath);
527    }
528
529    ProjectNode GetNodeFromWindow(Window document, Project project)
530    {
531       if(document.fileName)
532       {
533          char winFileName[MAX_LOCATION];
534          char * documentFileName = GetSlashPathBuffer(winFileName, document.fileName);
535          for(p : ide.workspace.projects)
536          {
537             Project prj = project ? project : p;
538             ProjectNode node;
539             char moduleName[MAX_FILENAME], modulePath[MAX_LOCATION];
540             GetLastDirectory(document.fileName, moduleName);
541
542             if((node = prj.topNode.Find(moduleName, false)))
543             {
544                strcpy(modulePath, prj.topNode.path);
545                PathCatSlash(modulePath, node.path);
546                PathCatSlash(modulePath, node.name);
547                if(!fstrcmp(documentFileName, modulePath))
548                {
549                   return node;
550                }
551             }
552             if(project) break;
553          }
554       }
555       return null;
556    }
557
558    void Compile(ProjectNode node)
559    {
560       char fileName[MAX_LOCATION];
561       char extension[MAX_EXTENSION];
562       Window document;
563       Project prj = node.project;
564       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
565       DirExpression objDir = prj.objDir;
566
567       strcpy(fileName, prj.topNode.path);
568       PathCatSlash(fileName, objDir.dir);
569       PathCatSlash(fileName, node.name);
570       StripExtension(fileName);
571       strcat(fileName, ".o");
572       if(FileExists(fileName))
573          DeleteFile(fileName);
574
575       GetExtension(node.name, extension);
576       if(!strcmp(extension, "ec"))
577       {
578          // Delete generated C file
579          strcpy(fileName, prj.topNode.path);
580          PathCat(fileName, objDir.dir);
581          PathCat(fileName, node.name);
582          StripExtension(fileName);
583          strcat(fileName, ".c");
584          if(FileExists(fileName))
585             DeleteFile(fileName);
586
587          // Delete symbol file
588          strcpy(fileName, prj.topNode.path);
589          PathCat(fileName, node.path);
590          PathCat(fileName, node.name);
591          StripExtension(fileName);
592          strcat(fileName, ".sym");
593          if(FileExists(fileName))
594             DeleteFile(fileName);
595       }
596
597       stopBuild = false;
598
599       // Check if we have to save
600       strcpy(fileName, prj.topNode.path);
601       PathCatSlash(fileName, node.path);
602       PathCatSlash(fileName, node.name);
603       for(document = ide.firstChild; document; document = document.next)
604       {
605          if(document.modifiedDocument)
606          {
607             char documentFileName[MAX_LOCATION];
608             if(!fstrcmp(GetSlashPathBuffer(documentFileName, document.fileName), fileName))
609                if(!document.MenuFileSave(null, 0))
610                   return;
611          }
612       }
613
614       if(ProjectPrepareForToolchain(prj, normal, true, true))
615       {
616          if(!node.isExcluded)
617          {
618             buildInProgress = compilingFile;
619             ide.AdjustBuildMenus();
620
621             //ide.outputView.ShowClearSelectTab(build);
622             // this stuff doesn't even appear
623             //ide.outputView.buildBox.Logf("%s Compiler\n", compiler.name);
624             if(prj.config)
625                ide.outputView.buildBox.Logf($"Compiling single file %s in project %s using the %s configuration...\n", node.name, prj.name, prj.config.name);
626             else
627                ide.outputView.buildBox.Logf($"Compiling single file %s in project %s...\n", node.name, prj.name);
628
629             prj.Compile(node);
630             buildInProgress = none;
631             ide.AdjustBuildMenus();
632          }
633          else
634             ide.outputView.buildBox.Logf($"File %s is excluded from current build configuration.\n", node.name);
635       }
636       delete objDir;
637       delete compiler;
638    }
639
640    void GoToError(const char * line)
641    {
642       char * colon;
643       
644       while(isspace(*line)) line++;
645       colon = strstr(line, ":");
646
647       {
648          int lineNumber = 0;
649          int col = 1;
650          bool lookForLineNumber = true;
651
652          // deal with linking error
653          if(colon && colon[1] == ' ')
654          {
655             colon = strstr(colon + 1, ":");
656             if(colon && !colon[1])
657             {
658                colon = strstr(line, ":");
659                lookForLineNumber = false;
660             }
661             else if(colon && !isdigit(colon[1]))
662             {
663                line = colon + 1;
664                colon = strstr(line, ":");
665             }
666          }
667          // Don't be mistaken by the drive letter colon
668          if(colon && (colon[1] == '/' || colon[1] == '\\'))
669             colon = strstr(colon + 1, ":");
670          if(colon && lookForLineNumber)
671          {
672             char * comma;
673             // MSVS Errors
674             char * par = RSearchString(line, "(", colon - line, true, false);
675             if(par && strstr(par, ")"))
676                colon = par;
677             else if((colon+1)[0] == ' ')
678             {
679                // NOTE: This is the same thing as saying 'colon = line'
680                for( ; colon != line; colon--)
681                   /*if(*colon == '(')
682                      break*/;
683             }
684             lineNumber = atoi(colon + 1);
685             /*
686             comma = strchr(colon, ',');
687             if(comma)
688                col = atoi(comma+1);
689             */
690             comma = strchr(colon+1, ':');
691             if(comma)
692                col = atoi(comma+1);
693          }
694          
695          {
696             char moduleName[MAX_LOCATION], filePath[MAX_LOCATION];
697             char * bracket;
698             if(colon)
699             {
700                // Cut module name
701                strncpy(moduleName, line, colon - line);
702                moduleName[colon - line] = '\0';
703             }
704             else
705                strcpy(moduleName, line);
706
707             // Remove stuff in brackets
708             /*
709             bracket = strstr(moduleName, "(");
710             if(bracket) *bracket = '\0';
711             */
712             MakeSlashPath(moduleName);
713
714             if(!colon)
715             {
716                // Check if it's one of our modules
717                ProjectNode node = project.topNode.Find(moduleName, false);
718                if(node)
719                {
720                   strcpy(moduleName, node.path);
721                   PathCatSlash(moduleName, node.name);
722                }
723                else
724                   moduleName[0] = '\0';
725             }
726             if(moduleName[0])
727             {
728                CodeEditor codeEditor;
729                strcpy(filePath, project.topNode.path);
730                PathCatSlash(filePath, moduleName);
731       
732                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
733                if(!codeEditor)
734                {
735                   char name[MAX_LOCATION];
736                   // TOFIX: Improve on this, don't use only filename, make a function
737                   if(ide && ide.workspace)
738                   {
739                      for(prj : ide.workspace.projects)
740                      {
741                         if(prj.topNode.FindWithPath(moduleName, false))
742                         {
743                            strcpy(filePath, prj.topNode.path);
744                            PathCatSlash(filePath, moduleName);
745                            codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, true, null, no, normal);
746                            if(codeEditor)
747                               break;
748                         }
749                      }
750                   }
751                }
752                if(codeEditor && lineNumber)
753                {
754                   EditBox editBox = codeEditor.editBox;
755                   editBox.GoToLineNum(lineNumber - 1);
756                   editBox.GoToPosition(editBox.line, lineNumber - 1, col ? (col - 1) : 0);
757                }
758             }
759          }
760       }
761    }
762
763    bool OpenNode(ProjectNode node)
764    {
765       char filePath[MAX_LOCATION];
766       node.GetFullFilePath(filePath);
767       return ide.OpenFile(filePath, normal, true/*false Why was it opening hidden?*/, null, something, normal) ? true : false;
768    }
769
770    void AddNode(ProjectNode node, DataRow addTo)
771    {
772       DataRow row = addTo ? addTo.AddRow() : fileList.AddRow();
773
774       row.tag = (int)node;
775       node.row = row;
776
777       if(node.type == resources)
778          resourceRow = row;
779
780       row.SetData(null, node);
781
782       if(node.files && node.files.first && node.parent && 
783             !(!node.parent.parent && 
784                (!strcmpi(node.name, "notes") || !strcmpi(node.name, "sources") || 
785                   !strcmpi(node.name, "src") || !strcmpi(node.name, "tools"))))
786          row.collapsed = true;
787       else if(node.type == folder)
788          node.icon = openFolder;
789
790       if(node.files)
791       {
792          for(child : node.files)
793             AddNode(child, row);
794       }
795    }
796
797    void DeleteNode(ProjectNode projectNode)
798    {
799       if(projectNode.files)
800       {
801          ProjectNode child;
802          while(child = projectNode.files.first)
803             DeleteNode(child);
804       }
805       fileList.DeleteRow(projectNode.row);
806       projectNode.Delete();
807    }
808
809    ProjectView()
810    {
811       NodeIcons c;
812       for(c = 0; c < NodeIcons::enumSize; c++)
813       {
814          icons[c] = BitmapResource { iconNames[c], alphaBlend = true };
815          AddResource(icons[c]);
816       }
817       fileList.AddField(DataField { dataType = class(ProjectNode), freeData = false, userData = this });
818    }
819
820    ~ProjectView()
821    {
822       DebugStop();
823       ide.DestroyTemporaryProjectDir();
824       if(project)
825       {
826          workspace.Free();
827          delete workspace;
828       }
829    }
830
831    bool ProjectSave(MenuItem selection, Modifiers mods)
832    {
833       DataRow row = fileList.currentRow;
834       ProjectNode node = row ? (ProjectNode)row.tag : null;
835       Project prj = node ? node.project : null;
836       if(prj)
837       {
838          if(prj.Save(prj.filePath))
839          {
840             // ProjectUpdateMakefileForAllConfigs(prj, true, true);
841             prj.topNode.modified = false;
842             prj = null;
843             for(p : ide.workspace.projects)
844             {
845                if(p.topNode.modified)
846                { 
847                   prj = p;
848                   break;
849                }
850             }
851             if(!prj)
852                modifiedDocument = false;
853             Update(null);
854          }
855       }
856       return true;
857    }
858
859    bool ShowOutputBuildLog(bool cleanLog)
860    {
861       OutputView output = ide.outputView;
862       if(cleanLog)
863          output.ShowClearSelectTab(build);
864       else
865       {
866          output.SelectTab(build);
867          output.Show();
868       }
869    }
870
871    bool DisplayCompiler(bool cleanLog)
872    {
873       CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
874       ShowOutputBuildLog(cleanLog);
875       ide.outputView.buildBox.Logf($"%s Compiler\n", compiler ? compiler.name : $"{problem with compiler selection}");
876       delete compiler;
877    }
878
879    bool ProjectUpdateMakefileForAllConfigs(Project project, bool cleanLog, bool displayCompiler)
880    {
881       ProjectConfig currentConfig = project.config;
882       ShowOutputBuildLog(cleanLog);
883
884       if(displayCompiler)
885          DisplayCompiler(false);
886       
887       for(config : project.configurations)
888       {
889          project.config = config;
890          ProjectPrepareMakefile(project, forceExists, false, false);
891       }
892
893       project.config = currentConfig;
894
895       ide.Update(null);
896    }
897
898    bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
899    {
900       bool isReady = true;
901       char message[MAX_F_STRING];
902       LogBox logBox = ide.outputView.buildBox;
903
904       ShowOutputBuildLog(cleanLog);
905
906       if(displayCompiler)
907          DisplayCompiler(false);
908
909       ProjectPrepareMakefile(project, method, false, false);
910       return true;
911    }
912
913    bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
914    {
915       char makefilePath[MAX_LOCATION];
916       char makefileName[MAX_LOCATION];
917       bool exists;
918       LogBox logBox = ide.outputView.buildBox;
919       
920       ShowOutputBuildLog(cleanLog);
921
922       if(displayCompiler)
923          DisplayCompiler(false);
924
925       strcpy(makefilePath, project.topNode.path);
926       project.CatMakeFileName(makefileName);
927       PathCatSlash(makefilePath, makefileName);
928
929       exists = FileExists(makefilePath);
930       if((method == normal && (!exists || project.config.makingModified/*|| project.topNode.modified*/)) ||
931             (method == forceExists && exists) || 
932             method == force) // || project.config.makingModified || makefileDirty
933       {
934          char * reason;
935          char * action;
936          ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
937          app.UpdateDisplay();
938          
939          if((method == normal && !exists) || (method == force && !exists))
940             action = $"Generating ";
941          else if(method == force)
942             action = $"Regenerating ";
943          else if(method == normal || method == forceExists)
944             action = $"Updating ";
945          else
946             action = "";
947          if(!exists)
948             reason = $"Makefile doesn't exist. ";
949          else if(project.topNode.modified)
950             reason = $"Project has been modified. ";
951          else
952             reason = "";
953
954          //logBox.Logf("%s\n", makefileName);
955          logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, project.configName);
956          project.GenerateMakefile(null, false, null);
957
958          ide.statusBar.text = null;
959          app.UpdateDisplay();
960          return true;
961       }
962       return false;
963    }
964    
965    bool ProjectBuild(MenuItem selection, Modifiers mods)
966    {
967       Project prj = project;
968       if(selection || !ide.activeClient || activeClient == this)
969       {
970          DataRow row = fileList.currentRow;
971          ProjectNode node = row ? (ProjectNode)row.tag : null;
972          if(node) prj = node.project;
973       }
974       // Added this code here until dependencies work:
975       else //if(ide.activeClient)
976       {
977          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
978          if(node)
979             prj = node.project;
980       }
981       if(/*prj != project || */!prj.configIsInDebugSession || !ide.DontTerminateDebugSession($"Project Build"))
982          BuildInterrim(prj, build);
983       return true;
984    }
985
986    bool BuildInterrim(Project prj, BuildType buildType)
987    {
988       if(ProjectPrepareForToolchain(prj, normal, true, true))
989       {
990          ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, prj.configName);
991          return Build(prj, buildType);
992       }
993       return false;
994    }
995
996    bool ProjectLink(MenuItem selection, Modifiers mods)
997    {
998       Project prj = project;
999       if(selection || !ide.activeClient || activeClient == this)
1000       {
1001          DataRow row = fileList.currentRow;
1002          ProjectNode node = row ? (ProjectNode)row.tag : null;
1003          if(node) prj = node.project;
1004       }
1005       // Added this code here until dependencies work:
1006       else //if(ide.activeClient)
1007       {
1008          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
1009          if(node)
1010             prj = node.project;
1011       }
1012       if(ProjectPrepareForToolchain(prj, normal, true, true))
1013       {
1014          ide.outputView.buildBox.Logf("Relinking project %s using the %s configuration...\n", prj.name, prj.configName);
1015          if(prj.config)
1016             prj.config.linkingModified = true;
1017          Build(prj, relink);
1018       }
1019       return true;
1020    }
1021
1022    bool ProjectRebuild(MenuItem selection, Modifiers mods)
1023    {
1024       Project prj = GetSelectedProject((bool)selection);
1025       if(ProjectPrepareForToolchain(prj, normal, true, true))
1026       {
1027          ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, prj.configName);
1028          /*if(prj.config)
1029          {
1030             prj.config.compilingModified = true;
1031             prj.config.makingModified = true;
1032          }*/ // -- should this still be used depite the new solution of BuildType?
1033          Build(prj, rebuild);
1034       }
1035       return true;
1036    }
1037
1038    bool ProjectClean(MenuItem selection, Modifiers mods)
1039    {
1040       Project prj = GetSelectedProject((bool)selection);
1041       if(ProjectPrepareForToolchain(prj, normal, true, true))
1042       {
1043          ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, prj.configName);
1044          
1045          buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1046          ide.AdjustBuildMenus();
1047
1048          prj.Clean();
1049          buildInProgress = none;
1050          ide.AdjustBuildMenus();
1051       }
1052       return true;
1053    }
1054
1055    bool ProjectRegenerate(MenuItem selection, Modifiers mods)
1056    {
1057       Project prj = project;
1058       if(selection || !ide.activeClient || activeClient == this)
1059       {
1060          DataRow row = fileList.currentRow;
1061          ProjectNode node = row ? (ProjectNode)row.tag : null;
1062          if(node)
1063             prj = node.project;
1064       }
1065       // Added this code here until dependencies work:
1066       else //if(ide.activeClient)
1067       {
1068          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
1069          if(node)
1070             prj = node.project;
1071       }
1072
1073       ProjectPrepareMakefile(prj, force, true, true);
1074       return true;
1075    }
1076
1077    bool ProjectNewFile(MenuItem selection, Modifiers mods)
1078    {
1079       DataRow row = fileList.currentRow;
1080       if(row)
1081       {
1082          char fileName[1024];
1083          char filePath[MAX_LOCATION];
1084          ProjectNode parentNode = (ProjectNode)row.tag;
1085          ProjectNode n, fileNode;
1086          parentNode.GetFileSysMatchingPath(filePath);
1087          MakePathRelative(filePath, parentNode.project.topNode.path, filePath);
1088          for(n = parentNode; n && n != parentNode.project.resNode; n = n.parent);
1089          sprintf(fileName, $"Untitled %d", documentID);
1090          fileNode = AddFile(parentNode, fileName, (bool)n, true);
1091          fileNode.path = CopyUnixPath(filePath);
1092          if(fileNode)
1093          {
1094             NodeProperties nodeProperties
1095             {
1096                parent, this;
1097                position = { position.x + 100, position.y + 100 };
1098                mode = newFile;
1099                node = fileNode;
1100             };
1101             nodeProperties.Create(); // not modal?
1102          }
1103       }
1104       return true;
1105    }
1106
1107    bool ProjectNewFolder(MenuItem selection, Modifiers mods)
1108    {
1109       DataRow row = fileList.currentRow;
1110       if(row)
1111       {
1112          ProjectNode parentNode = (ProjectNode)row.tag;
1113          NewFolder(parentNode, null, true);
1114       }
1115       return true;
1116    }
1117
1118    bool ResourcesAddFiles(MenuItem selection, Modifiers mods)
1119    {
1120       AddFiles(true);
1121       return true;
1122    }
1123
1124    bool ProjectAddFiles(MenuItem selection, Modifiers mods)
1125    {
1126       AddFiles(false);
1127       return true;
1128    }
1129
1130    bool ProjectImportFolder(MenuItem selection, Modifiers mods)
1131    {
1132       DataRow row = fileList.currentRow;
1133       if(row)
1134       {
1135          ProjectNode toNode = (ProjectNode)row.tag;
1136          ImportFolder(toNode);
1137       }
1138       return true;
1139    }
1140
1141    bool ProjectAddNewForm(MenuItem selection, Modifiers mods)
1142    {
1143       CodeEditor codeEditor = CreateNew("Form", "form", "Window", null);
1144       codeEditor.EnsureUpToDate();
1145       return true;
1146    }
1147
1148    bool ProjectAddNewGraph(MenuItem selection, Modifiers mods)
1149    {
1150       CodeEditor codeEditor = CreateNew("Graph", "graph", "Block", null);
1151       if(codeEditor)
1152          codeEditor.EnsureUpToDate();
1153       return true;
1154    }
1155
1156    bool ProjectRemove(MenuItem selection, Modifiers mods)
1157    {
1158       DataRow row = fileList.currentRow;
1159       if(row)
1160       {
1161          ProjectNode node = (ProjectNode)row.tag;
1162          if(node.type == project)
1163             RemoveSelectedNodes();
1164       }
1165       return true;
1166    }
1167
1168    bool MenuConfig(MenuItem selection, Modifiers mods)
1169    {
1170       if(ProjectActiveConfig { parent = parent.parent, master = parent, project = project }.Modal() == ok)
1171          ide.AdjustMenus();
1172       return true;
1173    }
1174
1175    bool MenuCompiler(MenuItem selection, Modifiers mods)
1176    {
1177       ActiveCompilerDialog compilerDialog
1178       {
1179          parent = parent.parent, master = parent;
1180          ideSettings = ideSettings, workspaceActiveCompiler = ide.workspace.compiler;
1181       };
1182       incref compilerDialog;
1183       if(compilerDialog.Modal() == ok && strcmp(compilerDialog.workspaceActiveCompiler, ide.workspace.compiler))
1184       {
1185          ide.workspace.compiler = compilerDialog.workspaceActiveCompiler;
1186          for(prj : ide.workspace.projects)
1187             ide.projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
1188       }
1189       delete compilerDialog;
1190       return true;
1191    }
1192
1193    bool MenuSettings(MenuItem selection, Modifiers mods)
1194    {
1195       ProjectNode node = GetSelectedNode(true);
1196       Project prj = node ? node.project : project;
1197       projectSettingsDialog = ProjectSettings { parent = parent.parent, master = parent, project = prj, projectNode = node };
1198       projectSettingsDialog.Modal();
1199
1200       Update(null);
1201       ide.AdjustMenus();
1202       return true;
1203    }
1204
1205    bool FileProperties(MenuItem selection, Modifiers mods)
1206    {
1207       DataRow row = fileList.currentRow;
1208       if(row)
1209       {
1210          ProjectNode node = (ProjectNode)row.tag;
1211          NodeProperties { parent = parent, master = this, node = node, 
1212                position = { position.x + 100, position.y + 100 } }.Create();
1213       }
1214       return true;
1215    }
1216
1217    bool FileOpenFile(MenuItem selection, Modifiers mods)
1218    {
1219       OpenSelectedNodes();
1220       return true;
1221    }
1222
1223    bool FileRemoveFile(MenuItem selection, Modifiers mods)
1224    {
1225       RemoveSelectedNodes();
1226       return true;
1227    }
1228
1229    bool FileCompile(MenuItem selection, Modifiers mods)
1230    {
1231       DataRow row = fileList.currentRow;
1232       if(row)
1233       {
1234          ProjectNode node = (ProjectNode)row.tag;
1235          Compile(node);
1236       }
1237       return true;
1238    }
1239
1240    /*bool IsProjectModified()
1241    {
1242       Window document;
1243
1244       for(document = master.firstChild; document; document = document.next)
1245       {
1246          if(document.modifiedDocument)
1247             if(GetNodeFromWindow(document), project)
1248                return true;
1249       }
1250       return false;
1251    }
1252    */
1253
1254    bool Build(Project prj, BuildType buildType)
1255    {
1256       bool result = true;
1257       Window document;
1258
1259       stopBuild = false;
1260       for(document = master.firstChild; document; document = document.next)
1261       {
1262          if(document.modifiedDocument)
1263          {
1264             ProjectNode node = GetNodeFromWindow(document, prj);
1265             if(node && !document.MenuFileSave(null, 0))
1266             {
1267                result = false;
1268                break;
1269             }
1270          }
1271       }
1272       if(result)
1273       {
1274          DirExpression targetDir = prj.targetDir;
1275
1276          // TOFIX: DebugStop is being abused and backfiring on us.
1277          //        It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
1278
1279          //        configIsInDebugSession seems to be used for two OPPOSITE things:
1280          //        If we're debugging another config, we need to unload the executable!
1281          //        In building, we want to stop if we're debugging the 'same' executable
1282          if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
1283          {
1284             if(buildType == start || buildType == restart)
1285             {
1286                if(ide.debugger && ide.debugger.isPrepared)
1287                {
1288                   DebugStop();
1289                }
1290             }
1291             else
1292             {
1293                if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == prj.config && ide.debugger.isPrepared)
1294                {
1295                   DebugStop();
1296                }
1297             }
1298          }
1299          
1300          // TODO: Disabled until problems fixed... is it fixed?
1301          if(buildType == rebuild || (prj.config && prj.config.compilingModified))
1302             prj.Clean();
1303          else
1304          {
1305             if(buildType == relink || (prj.config && prj.config.linkingModified))
1306             {
1307                char target[MAX_LOCATION];
1308
1309                strcpy(target, prj.topNode.path);
1310                PathCat(target, targetDir.dir);
1311                prj.CatTargetFileName(target);
1312                if(FileExists(target))
1313                   DeleteFile(target);
1314             }
1315             if(prj.config && prj.config.symbolGenModified)
1316             {
1317                DirExpression objDir = prj.objDir;
1318                char fileName[MAX_LOCATION];
1319                char moduleName[MAX_FILENAME];
1320                strcpy(fileName, prj.topNode.path);
1321                PathCatSlash(fileName, objDir.dir);
1322                strcpy(moduleName, prj.moduleName);
1323                strcat(moduleName, ".main.ec");
1324                PathCatSlash(fileName, moduleName);
1325                if(FileExists(fileName))
1326                   DeleteFile(fileName);
1327                ChangeExtension(fileName, "c", fileName);
1328                if(FileExists(fileName))
1329                   DeleteFile(fileName);
1330                ChangeExtension(fileName, "o", fileName);
1331                if(FileExists(fileName))
1332                   DeleteFile(fileName);
1333
1334                delete objDir;
1335             }
1336          }
1337          buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1338          ide.AdjustBuildMenus();
1339          ide.AdjustDebugMenus();
1340
1341          result = prj.Build(buildType == run, null);
1342
1343          if(prj.config)
1344          {
1345             prj.config.compilingModified = false;
1346             if(!ide.ShouldStopBuild())
1347                prj.config.linkingModified = false;
1348
1349             prj.config.symbolGenModified = false;
1350          }
1351          buildInProgress = none;
1352          ide.AdjustBuildMenus();
1353          ide.AdjustDebugMenus();
1354
1355          ide.workspace.modified = true;
1356
1357          delete targetDir;
1358       }
1359       return result;
1360    }
1361
1362    Project GetSelectedProject(bool useSelection)
1363    {
1364       Project prj = project;
1365       if(useSelection)
1366       {
1367          DataRow row = fileList.currentRow;
1368          ProjectNode node = row ? (ProjectNode)row.tag : null;
1369          if(node)
1370             prj = node.project;
1371       }
1372       return prj;
1373    }
1374    
1375    ProjectNode GetSelectedNode(bool useSelection)
1376    {
1377       ProjectNode node = null;
1378       if(useSelection)
1379       {
1380          DataRow row = fileList.currentRow;
1381          if(row)
1382             node = (ProjectNode)row.tag;
1383       }
1384       return node;
1385    }
1386
1387    bool MenuBrowseFolder(MenuItem selection, Modifiers mods)
1388    {
1389       char folder[MAX_LOCATION];
1390       Project prj;
1391       ProjectNode node = GetSelectedNode(true);
1392       if(!node)
1393          node = project.topNode;
1394       prj = node.project;
1395
1396       strcpy(folder, prj.topNode.path);
1397       if(node != prj.topNode)
1398          PathCatSlash(folder, node.path);
1399       ShellOpen(folder);
1400    }
1401
1402    bool Run(MenuItem selection, Modifiers mods)
1403    {
1404       char args[MAX_LOCATION * 64];
1405       args[0] = '\0';
1406       if(ide.workspace.commandLineArgs)
1407          //ide.debugger.GetCommandLineArgs(args);
1408          strcpy(args, ide.workspace.commandLineArgs);
1409       if(ide.debugger.isActive)
1410          project.Run(args);
1411       /*else if(project.config.targetType == sharedLibrary || project.config.targetType == staticLibrary)
1412          MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
1413       else if(BuildInterrim(project, run))
1414          project.Run(args);
1415       return true;
1416    }
1417
1418    bool DebugStart()
1419    {
1420       bool result = false;
1421       if(project.targetType == sharedLibrary || project.targetType == staticLibrary)
1422          MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
1423       else if(project.compress)
1424          MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
1425       else if(project.debug ||
1426          MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
1427       {
1428          if(/*!IsProjectModified() ||*/ BuildInterrim(project, start))
1429          {
1430             CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1431             if(compiler.type.isVC)
1432             {
1433                //bool result = false;
1434                char oldwd[MAX_LOCATION];
1435                char oldPath[MAX_LOCATION * 65];
1436                char command[MAX_LOCATION];
1437
1438                GetEnvironment("PATH", oldPath, sizeof(oldPath));
1439                ide.SetPath(false); //true
1440                
1441                GetWorkingDir(oldwd, sizeof(oldwd));
1442                ChangeWorkingDir(project.topNode.path);
1443
1444                sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, project.config.name, "Debug.Start");
1445                //ide.outputView.buildBox.Logf("command: %s\n", command);
1446                Execute(command);
1447                ChangeWorkingDir(oldwd);
1448                SetEnvironment("PATH", oldPath);
1449             }
1450             else
1451             {
1452                ide.debugger.Start();
1453                result = true;
1454             }
1455
1456             delete compiler;
1457          }
1458       }
1459       return result;      
1460    }
1461
1462    bool DebugRestart()
1463    {
1464       if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart))
1465       {
1466          ide.debugger.Restart();
1467          return true;
1468       }
1469       return false;
1470    }
1471
1472    bool DebugResume()
1473    {
1474       ide.debugger.Resume();
1475       return true;
1476    }
1477
1478    bool DebugBreak()
1479    {
1480       ide.debugger.Break();
1481       return true;
1482    }
1483
1484    bool DebugStop()
1485    {
1486       ide.debugger.Stop();
1487       return true;
1488    }
1489
1490    bool DebugStepInto()
1491    {
1492       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
1493          ide.debugger.StepInto();
1494       return true;
1495    }
1496
1497    bool DebugStepOver(bool skip)
1498    {
1499       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
1500          ide.debugger.StepOver(skip);
1501       return true;
1502    }
1503
1504    bool DebugStepOut(bool skip)
1505    {
1506       ide.debugger.StepOut(skip);
1507       return true;
1508    }
1509
1510    void ImportFolder(ProjectNode toNode)
1511    {
1512       if(toNode)
1513       {
1514          //bool isFolder = toNode.type == folder;
1515          //bool isRes = toNode.isInResources;
1516          
1517          FileDialog fileDialog = importFileDialog;
1518          fileDialog.master = parent;
1519          if(fileDialog.Modal() == ok)
1520          {
1521             ImportFolderFSI fsi { projectView = this };
1522             fsi.stack.Add(toNode);
1523             fsi.Iterate(fileDialog.filePath);
1524             delete fsi;
1525          }
1526       }
1527    }
1528
1529    ProjectNode NewFolder(ProjectNode parentNode, char * name, bool showProperties)
1530    {
1531       if(parentNode)
1532       {
1533          ProjectNode folderNode;
1534          Project prj = parentNode.project;
1535          int c;
1536          ProjectNode after = null;
1537          for(node : parentNode.files)
1538          {
1539             if(node.type != folder)
1540                break;
1541             after = node;
1542          }
1543          
1544          if(name && name[0])
1545             folderNode = parentNode.Add(prj, name, after, folder, folder, true);
1546          else
1547          {
1548             for(c = 0; c < 100; c++)
1549             {
1550                char string[16];
1551                sprintf(string, c ? "New Folder (%d)" : "New Folder", c);
1552                if((folderNode = parentNode.Add(prj, string, after, folder, folder, true)))
1553                   break;
1554             }
1555          }
1556
1557          if(folderNode)
1558          {
1559             NodeProperties nodeProperties;
1560             if(!showProperties)
1561             {
1562                modifiedDocument = true;
1563                prj.topNode.modified = true;
1564             }
1565             Update(null);
1566             folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1567             folderNode.row.tag = (int)folderNode;
1568                
1569             folderNode.row.SetData(null, folderNode);
1570             fileList.currentRow = folderNode.row;
1571             
1572             if(showProperties)
1573             {
1574                nodeProperties = NodeProperties
1575                {
1576                   parent, this, mode = newFolder, node = folderNode;
1577                   position = { position.x + 100, position.y + 100 };
1578                };
1579                nodeProperties.Create();   // Modal?
1580             }
1581             return folderNode;
1582          }
1583       }
1584       return null;
1585    }
1586
1587    void AddFiles(bool resources)
1588    {
1589       FileDialog fileDialog = (!resources) ? this.fileDialog : resourceFileDialog;
1590       fileDialog.type = multiOpen;
1591       fileDialog.text = !resources ? $"Add Files to Project" : $"Add Resources to Project";
1592       fileDialog.master = parent;
1593
1594       if(fileDialog.Modal() == ok)
1595       {
1596          int c;
1597          DataRow row = fileList.currentRow;
1598          ProjectNode parentNode = (ProjectNode)row.tag;
1599          bool addFailed = false;
1600          int numSelections = fileDialog.numSelections;
1601          char ** multiFilePaths = fileDialog.multiFilePaths;
1602
1603          Array<String> nameConflictFiles { };
1604
1605          for(c = 0; c < numSelections; c++)
1606          {
1607             char * filePath = multiFilePaths[c];
1608             FileAttribs exists = FileExists(filePath);
1609             bool addThisFile = true;
1610
1611             if(exists.isDirectory)
1612                addThisFile = false;
1613             else if(!exists)
1614             {
1615                if(MessageBox { master = ide, type = yesNo, text = filePath, 
1616                      contents = $"File doesn't exist. Create?" }.Modal() == yes)
1617                {
1618                   File f = FileOpen(filePath, write);
1619                   if(f)
1620                   {
1621                      addThisFile = true;
1622                      delete f;
1623                   }
1624                   else
1625                   {
1626                      MessageBox { master = ide, type = ok, text = filePath, 
1627                            contents = $"Couldn't create file."}.Modal();
1628                      addThisFile = false;
1629                   }
1630                }
1631             }
1632
1633             if(addThisFile)
1634             {
1635                /*addFailed = */if(!AddFile(parentNode, filePath, resources, false))//;
1636                {
1637                   nameConflictFiles.Add(CopyString(filePath));
1638                   addFailed = true;
1639                }
1640             }
1641          }
1642          if(addFailed)
1643          {
1644             int len = 0;
1645             char * part1 = $"The following file";
1646             char * opt1 = $" was ";
1647             char * opt2 = $"s were ";
1648             char * part2 = $"not added because of identical file name conflict within the project.\n\n";
1649             char * message;
1650             len += strlen(part1);
1651             len += strlen(part2);
1652             len += nameConflictFiles.count > 1 ? strlen(opt2) : strlen(opt1);
1653             for(s : nameConflictFiles)
1654                len += strlen(s) + 1;
1655             message = new char[len + 1];
1656             strcpy(message, part1);
1657             strcat(message, nameConflictFiles.count > 1 ? opt2 : opt1);
1658             strcat(message, part2);
1659             for(s : nameConflictFiles)
1660             {
1661                strcat(message, s);
1662                strcat(message, "\n");
1663             }
1664             MessageBox { master = ide, type = ok, text = $"Name Conflict", 
1665                   contents = message }.Modal();
1666             delete message;
1667          }
1668          nameConflictFiles.Free();
1669          delete nameConflictFiles;
1670       }
1671    }
1672
1673    ProjectNode AddFile(ProjectNode parentNode, char * filePath, bool resources, bool isTemporary)
1674    {
1675       ProjectNode result = null;
1676       ProjectNode after = null;
1677       for(node : parentNode.files)
1678       {
1679          if(node.type != folder && node.type != file && node.type)
1680             break;
1681          after = node;
1682       }
1683
1684       result = parentNode.Add(parentNode.project, filePath, after, file, NodeIcons::SelectFileIcon(filePath), !resources);
1685
1686       if(result)
1687       {
1688          if(!isTemporary)
1689          {
1690             modifiedDocument = true;
1691             parentNode.project.topNode.modified = true;
1692             parentNode.project.ModifiedAllConfigs(true, false, true, true);
1693          }
1694          Update(null);
1695          result.row = parentNode.row.AddRowAfter(after ? after.row : null);
1696          result.row.tag = (int)result;
1697          result.row.SetData(null, result);
1698       }
1699       return result;
1700    }
1701
1702    CodeEditor CreateNew(char * upper, char * lower, char * base, char * className)
1703    {
1704       CodeEditor codeEditor = null;
1705       ProjectNode projectNode;
1706       ProjectNode after = null;
1707       DataRow row = fileList.currentRow;
1708       ProjectNode parentNode;
1709       int c;
1710
1711       if(!row) row = project.topNode.row;
1712
1713       parentNode = (ProjectNode)row.tag;
1714
1715       for(node : parentNode.files)
1716       {
1717          if(node.type != folder && node.type != file && node.type)
1718             break;
1719          after = node;
1720       }
1721       for(c = 1; c < 100; c++)
1722       {
1723          char string[16];
1724          sprintf(string, c ? "%s%d.ec" : "%s.ec", lower, c);
1725          if((projectNode = parentNode.Add(project, string, after, file, genFile, true)))
1726             break;
1727       }
1728       if(projectNode)
1729       {
1730          char name[256];
1731          char filePath[MAX_LOCATION];
1732
1733          modifiedDocument = true;
1734          project.topNode.modified = true;
1735          Update(null);
1736          project.ModifiedAllConfigs(true, false, false, true);
1737          projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1738          projectNode.row.tag =(int)projectNode;
1739             
1740          projectNode.row.SetData(null, projectNode);
1741          fileList.currentRow = projectNode.row;
1742
1743          strcpy(filePath, project.topNode.path);
1744          PathCat(filePath, projectNode.path);
1745          PathCat(filePath, projectNode.name);
1746
1747          codeEditor = (CodeEditor)ide.FindWindow(filePath);
1748          if(!codeEditor)
1749          {
1750             Class baseClass = eSystem_FindClass(__thisModule, base);
1751             subclass(ClassDesignerBase) designerClass = eClass_GetDesigner(baseClass);
1752             if(designerClass)
1753             {
1754                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
1755                strcpy(name, projectNode.name);
1756                sprintf(name, "%s%d", upper, c);
1757                if(className)
1758                   strcpy(className, name);
1759
1760                designerClass.CreateNew(codeEditor.editBox, codeEditor.clientSize, name, base);
1761
1762                //codeEditor.modifiedDocument = false;
1763                //codeEditor.designer.modifiedDocument = false;
1764
1765                //Code_EnsureUpToDate(codeEditor);
1766             }
1767          }
1768          else // TODO: fix no symbols generated when ommiting {} for following else
1769          {
1770             codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
1771          }
1772          if(codeEditor)
1773          {
1774             codeEditor.ViewDesigner();
1775             codeEditor.codeModified = true;
1776          }
1777       }
1778       visible = false;
1779       return codeEditor;   
1780    }
1781
1782    // Returns true if we opened something
1783    bool OpenSelectedNodes()
1784    {
1785       OldList selection;
1786       OldLink item;
1787
1788       fileList.GetMultiSelection(selection);
1789       for(item = selection.first; item; item = item.next)
1790       {
1791          DataRow row = item.data;
1792          ProjectNode node = (ProjectNode)row.tag;
1793          if(node.type == file)
1794          {
1795             OpenNode(node);
1796             return true;
1797          }
1798       }
1799       selection.Free(null);
1800       return false;
1801    }
1802
1803    void RemoveSelectedNodes()
1804    {
1805       OldList selection;
1806       OldLink item, next;
1807       
1808       fileList.GetMultiSelection(selection);
1809
1810       // Remove children of parents we're deleting
1811       for(item = selection.first; item; item = next)
1812       {
1813          OldLink i;
1814          DataRow row = item.data;
1815          ProjectNode n, node = (ProjectNode)row.tag;
1816          bool remove = false;
1817
1818          next = item.next;
1819          for(i = selection.first; i && !remove; i = i.next)
1820          {
1821             ProjectNode iNode = (ProjectNode)((DataRow)i.data).tag;
1822             for(n = node.parent; n; n = n.parent)
1823             {
1824                if(iNode == n)
1825                {
1826                   remove = true;
1827                   break;
1828                }
1829             }
1830          }
1831          if(remove)
1832             selection.Delete(item);
1833       }
1834
1835       for(item = selection.first; item; item = item.next)
1836       {
1837          DataRow row = item.data;
1838          ProjectNode node = (ProjectNode)row.tag;
1839          ProjectNode resNode;
1840          for(resNode = node.parent; resNode; resNode = resNode.parent)
1841             if(resNode.type == resources)
1842                break;
1843          if(node.type == file)
1844          {
1845             Project prj = node.project;
1846             DeleteNode(node);
1847             modifiedDocument = true;
1848             prj.topNode.modified = true;
1849             Update(null);
1850             prj.ModifiedAllConfigs(true, false, true, true);
1851          }
1852          else if(node.type == folder)
1853          {
1854             char message[1024];
1855             sprintf(message, $"Are you sure you want to remove the folder \"%s\"\n"
1856                   "and all of its contents from the project?", node.name);
1857             if(MessageBox { master = ide, type = yesNo, text = $"Delete Folder", contents = message }.Modal() == yes)
1858             {
1859                Project prj = node.project;
1860                if(node.containsFile)
1861                   prj.ModifiedAllConfigs(true, false, true, true);
1862                DeleteNode(node);
1863                modifiedDocument = true;
1864                prj.topNode.modified = true;
1865             }
1866          }
1867          else if(node.type == project && node != project.topNode && !buildInProgress)
1868          {
1869             char message[1024];
1870             Project prj = null;
1871             for(p : workspace.projects)
1872             {
1873                if(p.topNode == node)
1874                {
1875                   prj = p;
1876                   break;
1877                }
1878             }
1879             sprintf(message, $"Are you sure you want to remove the \"%s\" project\n" "from this workspace?", node.name);
1880             if(MessageBox { master = ide, type = yesNo, text = $"Remove Project", contents = message }.Modal() == yes)
1881             {
1882                // THIS GOES FIRST!
1883                DeleteNode(node);
1884                if(prj)
1885                   workspace.RemoveProject(prj);
1886                //modifiedDocument = true; // when project view is a workspace view
1887             }
1888          }
1889       }
1890       selection.Free(null);
1891    }
1892 }