ecere: Fixed up i18n Support; Started Chinese translation integration
[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       LogBox logBox = ide.outputView.buildBox;
875       ShowOutputBuildLog(cleanLog);
876       logBox.Logf("$%s Compiler\n", compiler ? compiler.name : "${problem with compiler selection}");
877       delete compiler;
878    }
879
880    bool ProjectUpdateMakefileForAllConfigs(Project project, bool cleanLog, bool displayCompiler)
881    {
882       ProjectConfig currentConfig = project.config;
883       ShowOutputBuildLog(cleanLog);
884
885       if(displayCompiler)
886          DisplayCompiler(false);
887       
888       for(config : project.configurations)
889       {
890          project.config = config;
891          ProjectPrepareMakefile(project, forceExists, false, false);
892       }
893
894       project.config = currentConfig;
895
896       ide.Update(null);
897    }
898
899    bool ProjectPrepareForToolchain(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
900    {
901       bool isReady = true;
902       char message[MAX_F_STRING];
903       LogBox logBox = ide.outputView.buildBox;
904
905       ShowOutputBuildLog(cleanLog);
906
907       if(displayCompiler)
908          DisplayCompiler(false);
909
910       ProjectPrepareMakefile(project, method, false, false);
911       return true;
912    }
913
914    bool ProjectPrepareMakefile(Project project, PrepareMakefileMethod method, bool cleanLog, bool displayCompiler)
915    {
916       char makefilePath[MAX_LOCATION];
917       char makefileName[MAX_LOCATION];
918       bool exists;
919       LogBox logBox = ide.outputView.buildBox;
920       
921       ShowOutputBuildLog(cleanLog);
922
923       if(displayCompiler)
924          DisplayCompiler(false);
925
926       strcpy(makefilePath, project.topNode.path);
927       project.CatMakeFileName(makefileName);
928       PathCatSlash(makefilePath, makefileName);
929
930       exists = FileExists(makefilePath);
931       if((method == normal && (!exists || project.config.makingModified/*|| project.topNode.modified*/)) ||
932             (method == forceExists && exists) || 
933             method == force) // || project.config.makingModified || makefileDirty
934       {
935          char * reason;
936          char * action;
937          ide.statusBar.text = $"Generating Makefile & Dependencies..."; // Dependencies?
938          app.UpdateDisplay();
939          
940          if((method == normal && !exists) || (method == force && !exists))
941             action = $"Generating ";
942          else if(method == force)
943             action = $"Regenerating ";
944          else if(method == normal || method == forceExists)
945             action = $"Updating ";
946          else
947             action = "";
948          if(!exists)
949             reason = $"Makefile doesn't exist. ";
950          else if(project.topNode.modified)
951             reason = $"Project has been modified. ";
952          else
953             reason = "";
954
955          //logBox.Logf("%s\n", makefileName);
956          logBox.Logf($"%s - %s%smakefile for %s config...\n", makefileName, reason, action, project.configName);
957          project.GenerateMakefile(null, false, null);
958
959          ide.statusBar.text = null;
960          app.UpdateDisplay();
961          return true;
962       }
963       return false;
964    }
965    
966    bool ProjectBuild(MenuItem selection, Modifiers mods)
967    {
968       Project prj = project;
969       if(selection || !ide.activeClient || activeClient == this)
970       {
971          DataRow row = fileList.currentRow;
972          ProjectNode node = row ? (ProjectNode)row.tag : null;
973          if(node) prj = node.project;
974       }
975       // Added this code here until dependencies work:
976       else //if(ide.activeClient)
977       {
978          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
979          if(node)
980             prj = node.project;
981       }
982       if(/*prj != project || */!prj.configIsInDebugSession || !ide.DontTerminateDebugSession($"Project Build"))
983          BuildInterrim(prj, build);
984       return true;
985    }
986
987    bool BuildInterrim(Project prj, BuildType buildType)
988    {
989       if(ProjectPrepareForToolchain(prj, normal, true, true))
990       {
991          ide.outputView.buildBox.Logf($"Building project %s using the %s configuration...\n", prj.name, prj.configName);
992          return Build(prj, buildType);
993       }
994       return false;
995    }
996
997    bool ProjectLink(MenuItem selection, Modifiers mods)
998    {
999       Project prj = project;
1000       if(selection || !ide.activeClient || activeClient == this)
1001       {
1002          DataRow row = fileList.currentRow;
1003          ProjectNode node = row ? (ProjectNode)row.tag : null;
1004          if(node) prj = node.project;
1005       }
1006       // Added this code here until dependencies work:
1007       else //if(ide.activeClient)
1008       {
1009          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
1010          if(node)
1011             prj = node.project;
1012       }
1013       if(ProjectPrepareForToolchain(prj, normal, true, true))
1014       {
1015          ide.outputView.buildBox.Logf("Relinking project %s using the %s configuration...\n", prj.name, prj.configName);
1016          if(prj.config)
1017             prj.config.linkingModified = true;
1018          Build(prj, relink);
1019       }
1020       return true;
1021    }
1022
1023    bool ProjectRebuild(MenuItem selection, Modifiers mods)
1024    {
1025       Project prj = GetSelectedProject((bool)selection);
1026       if(ProjectPrepareForToolchain(prj, normal, true, true))
1027       {
1028          ide.outputView.buildBox.Logf($"Rebuilding project %s using the %s configuration...\n", prj.name, prj.configName);
1029          /*if(prj.config)
1030          {
1031             prj.config.compilingModified = true;
1032             prj.config.makingModified = true;
1033          }*/ // -- should this still be used depite the new solution of BuildType?
1034          Build(prj, rebuild);
1035       }
1036       return true;
1037    }
1038
1039    bool ProjectClean(MenuItem selection, Modifiers mods)
1040    {
1041       Project prj = GetSelectedProject((bool)selection);
1042       if(ProjectPrepareForToolchain(prj, normal, true, true))
1043       {
1044          ide.outputView.buildBox.Logf($"Cleaning project %s using the %s configuration...\n", prj.name, prj.configName);
1045          
1046          buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1047          ide.AdjustBuildMenus();
1048
1049          prj.Clean();
1050          buildInProgress = none;
1051          ide.AdjustBuildMenus();
1052       }
1053       return true;
1054    }
1055
1056    bool ProjectRegenerate(MenuItem selection, Modifiers mods)
1057    {
1058       Project prj = project;
1059       if(selection || !ide.activeClient || activeClient == this)
1060       {
1061          DataRow row = fileList.currentRow;
1062          ProjectNode node = row ? (ProjectNode)row.tag : null;
1063          if(node)
1064             prj = node.project;
1065       }
1066       // Added this code here until dependencies work:
1067       else //if(ide.activeClient)
1068       {
1069          ProjectNode node = GetNodeFromWindow(ide.activeClient, null);
1070          if(node)
1071             prj = node.project;
1072       }
1073
1074       ProjectPrepareMakefile(prj, force, true, true);
1075       return true;
1076    }
1077
1078    bool ProjectNewFile(MenuItem selection, Modifiers mods)
1079    {
1080       DataRow row = fileList.currentRow;
1081       if(row)
1082       {
1083          char fileName[1024];
1084          char filePath[MAX_LOCATION];
1085          ProjectNode parentNode = (ProjectNode)row.tag;
1086          ProjectNode n, fileNode;
1087          parentNode.GetFileSysMatchingPath(filePath);
1088          MakePathRelative(filePath, parentNode.project.topNode.path, filePath);
1089          for(n = parentNode; n && n != parentNode.project.resNode; n = n.parent);
1090          sprintf(fileName, $"Untitled %d", documentID);
1091          fileNode = AddFile(parentNode, fileName, (bool)n, true);
1092          fileNode.path = CopyUnixPath(filePath);
1093          if(fileNode)
1094          {
1095             NodeProperties nodeProperties
1096             {
1097                parent, this;
1098                position = { position.x + 100, position.y + 100 };
1099                mode = newFile;
1100                node = fileNode;
1101             };
1102             nodeProperties.Create(); // not modal?
1103          }
1104       }
1105       return true;
1106    }
1107
1108    bool ProjectNewFolder(MenuItem selection, Modifiers mods)
1109    {
1110       DataRow row = fileList.currentRow;
1111       if(row)
1112       {
1113          ProjectNode parentNode = (ProjectNode)row.tag;
1114          NewFolder(parentNode, null, true);
1115       }
1116       return true;
1117    }
1118
1119    bool ResourcesAddFiles(MenuItem selection, Modifiers mods)
1120    {
1121       AddFiles(true);
1122       return true;
1123    }
1124
1125    bool ProjectAddFiles(MenuItem selection, Modifiers mods)
1126    {
1127       AddFiles(false);
1128       return true;
1129    }
1130
1131    bool ProjectImportFolder(MenuItem selection, Modifiers mods)
1132    {
1133       DataRow row = fileList.currentRow;
1134       if(row)
1135       {
1136          ProjectNode toNode = (ProjectNode)row.tag;
1137          ImportFolder(toNode);
1138       }
1139       return true;
1140    }
1141
1142    bool ProjectAddNewForm(MenuItem selection, Modifiers mods)
1143    {
1144       CodeEditor codeEditor = CreateNew("Form", "form", "Window", null);
1145       codeEditor.EnsureUpToDate();
1146       return true;
1147    }
1148
1149    bool ProjectAddNewGraph(MenuItem selection, Modifiers mods)
1150    {
1151       CodeEditor codeEditor = CreateNew("Graph", "graph", "Block", null);
1152       if(codeEditor)
1153          codeEditor.EnsureUpToDate();
1154       return true;
1155    }
1156
1157    bool ProjectRemove(MenuItem selection, Modifiers mods)
1158    {
1159       DataRow row = fileList.currentRow;
1160       if(row)
1161       {
1162          ProjectNode node = (ProjectNode)row.tag;
1163          if(node.type == project)
1164             RemoveSelectedNodes();
1165       }
1166       return true;
1167    }
1168
1169    bool MenuConfig(MenuItem selection, Modifiers mods)
1170    {
1171       if(ProjectActiveConfig { parent = parent.parent, master = parent, project = project }.Modal() == ok)
1172          ide.AdjustMenus();
1173       return true;
1174    }
1175
1176    bool MenuCompiler(MenuItem selection, Modifiers mods)
1177    {
1178       ActiveCompilerDialog compilerDialog
1179       {
1180          parent = parent.parent, master = parent;
1181          ideSettings = ideSettings, workspaceActiveCompiler = ide.workspace.compiler;
1182       };
1183       incref compilerDialog;
1184       if(compilerDialog.Modal() == ok && strcmp(compilerDialog.workspaceActiveCompiler, ide.workspace.compiler))
1185       {
1186          ide.workspace.compiler = compilerDialog.workspaceActiveCompiler;
1187          for(prj : ide.workspace.projects)
1188             ide.projectView.ProjectUpdateMakefileForAllConfigs(prj, true, true);
1189       }
1190       delete compilerDialog;
1191       return true;
1192    }
1193
1194    bool MenuSettings(MenuItem selection, Modifiers mods)
1195    {
1196       ProjectNode node = GetSelectedNode(true);
1197       Project prj = node ? node.project : project;
1198       projectSettingsDialog = ProjectSettings { parent = parent.parent, master = parent, project = prj, projectNode = node };
1199       projectSettingsDialog.Modal();
1200
1201       Update(null);
1202       ide.AdjustMenus();
1203       return true;
1204    }
1205
1206    bool FileProperties(MenuItem selection, Modifiers mods)
1207    {
1208       DataRow row = fileList.currentRow;
1209       if(row)
1210       {
1211          ProjectNode node = (ProjectNode)row.tag;
1212          NodeProperties { parent = parent, master = this, node = node, 
1213                position = { position.x + 100, position.y + 100 } }.Create();
1214       }
1215       return true;
1216    }
1217
1218    bool FileOpenFile(MenuItem selection, Modifiers mods)
1219    {
1220       OpenSelectedNodes();
1221       return true;
1222    }
1223
1224    bool FileRemoveFile(MenuItem selection, Modifiers mods)
1225    {
1226       RemoveSelectedNodes();
1227       return true;
1228    }
1229
1230    bool FileCompile(MenuItem selection, Modifiers mods)
1231    {
1232       DataRow row = fileList.currentRow;
1233       if(row)
1234       {
1235          ProjectNode node = (ProjectNode)row.tag;
1236          Compile(node);
1237       }
1238       return true;
1239    }
1240
1241    /*bool IsProjectModified()
1242    {
1243       Window document;
1244
1245       for(document = master.firstChild; document; document = document.next)
1246       {
1247          if(document.modifiedDocument)
1248             if(GetNodeFromWindow(document), project)
1249                return true;
1250       }
1251       return false;
1252    }
1253    */
1254
1255    bool Build(Project prj, BuildType buildType)
1256    {
1257       bool result = true;
1258       Window document;
1259
1260       stopBuild = false;
1261       for(document = master.firstChild; document; document = document.next)
1262       {
1263          if(document.modifiedDocument)
1264          {
1265             ProjectNode node = GetNodeFromWindow(document, prj);
1266             if(node && !document.MenuFileSave(null, 0))
1267             {
1268                result = false;
1269                break;
1270             }
1271          }
1272       }
1273       if(result)
1274       {
1275          DirExpression targetDir = prj.targetDir;
1276
1277          // TOFIX: DebugStop is being abused and backfiring on us.
1278          //        It's supposed to be the 'Debug/Stop' item, not unloading executable or anything else
1279
1280          //        configIsInDebugSession seems to be used for two OPPOSITE things:
1281          //        If we're debugging another config, we need to unload the executable!
1282          //        In building, we want to stop if we're debugging the 'same' executable
1283          if(buildType != run) ///* && prj == project*/ && prj.configIsInDebugSession)
1284          {
1285             if(buildType == start || buildType == restart)
1286             {
1287                if(ide.debugger && ide.debugger.isPrepared)
1288                {
1289                   DebugStop();
1290                }
1291             }
1292             else
1293             {
1294                if(ide.project == prj && ide.debugger && ide.debugger.prjConfig == prj.config && ide.debugger.isPrepared)
1295                {
1296                   DebugStop();
1297                }
1298             }
1299          }
1300          
1301          // TODO: Disabled until problems fixed... is it fixed?
1302          if(buildType == rebuild || (prj.config && prj.config.compilingModified))
1303             prj.Clean();
1304          else
1305          {
1306             if(buildType == relink || (prj.config && prj.config.linkingModified))
1307             {
1308                char target[MAX_LOCATION];
1309
1310                strcpy(target, prj.topNode.path);
1311                PathCat(target, targetDir.dir);
1312                prj.CatTargetFileName(target);
1313                if(FileExists(target))
1314                   DeleteFile(target);
1315             }
1316             if(prj.config && prj.config.symbolGenModified)
1317             {
1318                DirExpression objDir = prj.objDir;
1319                char fileName[MAX_LOCATION];
1320                char moduleName[MAX_FILENAME];
1321                strcpy(fileName, prj.topNode.path);
1322                PathCatSlash(fileName, objDir.dir);
1323                strcpy(moduleName, prj.moduleName);
1324                strcat(moduleName, ".main.ec");
1325                PathCatSlash(fileName, moduleName);
1326                if(FileExists(fileName))
1327                   DeleteFile(fileName);
1328                ChangeExtension(fileName, "c", fileName);
1329                if(FileExists(fileName))
1330                   DeleteFile(fileName);
1331                ChangeExtension(fileName, "o", fileName);
1332                if(FileExists(fileName))
1333                   DeleteFile(fileName);
1334
1335                delete objDir;
1336             }
1337          }
1338          buildInProgress = prj == project ? buildingMainProject : buildingSecondaryProject;
1339          ide.AdjustBuildMenus();
1340          ide.AdjustDebugMenus();
1341
1342          result = prj.Build(buildType == run, null);
1343
1344          if(prj.config)
1345          {
1346             prj.config.compilingModified = false;
1347             if(!ide.ShouldStopBuild())
1348                prj.config.linkingModified = false;
1349
1350             prj.config.symbolGenModified = false;
1351          }
1352          buildInProgress = none;
1353          ide.AdjustBuildMenus();
1354          ide.AdjustDebugMenus();
1355
1356          ide.workspace.modified = true;
1357
1358          delete targetDir;
1359       }
1360       return result;
1361    }
1362
1363    Project GetSelectedProject(bool useSelection)
1364    {
1365       Project prj = project;
1366       if(useSelection)
1367       {
1368          DataRow row = fileList.currentRow;
1369          ProjectNode node = row ? (ProjectNode)row.tag : null;
1370          if(node)
1371             prj = node.project;
1372       }
1373       return prj;
1374    }
1375    
1376    ProjectNode GetSelectedNode(bool useSelection)
1377    {
1378       ProjectNode node = null;
1379       if(useSelection)
1380       {
1381          DataRow row = fileList.currentRow;
1382          if(row)
1383             node = (ProjectNode)row.tag;
1384       }
1385       return node;
1386    }
1387
1388    bool MenuBrowseFolder(MenuItem selection, Modifiers mods)
1389    {
1390       char folder[MAX_LOCATION];
1391       Project prj;
1392       ProjectNode node = GetSelectedNode(true);
1393       if(!node)
1394          node = project.topNode;
1395       prj = node.project;
1396
1397       strcpy(folder, prj.topNode.path);
1398       if(node != prj.topNode)
1399          PathCatSlash(folder, node.path);
1400       ShellOpen(folder);
1401    }
1402
1403    bool Run(MenuItem selection, Modifiers mods)
1404    {
1405       char args[MAX_LOCATION * 64];
1406       args[0] = '\0';
1407       if(ide.workspace.commandLineArgs)
1408          //ide.debugger.GetCommandLineArgs(args);
1409          strcpy(args, ide.workspace.commandLineArgs);
1410       if(ide.debugger.isActive)
1411          project.Run(args);
1412       /*else if(project.config.targetType == sharedLibrary || project.config.targetType == staticLibrary)
1413          MessageBox { master = ide, type = ok, text = "Run", contents = "Shared and static libraries cannot be run like executables." }.Modal();*/
1414       else if(BuildInterrim(project, run))
1415          project.Run(args);
1416       return true;
1417    }
1418
1419    bool DebugStart()
1420    {
1421       bool result = false;
1422       if(project.targetType == sharedLibrary || project.targetType == staticLibrary)
1423          MessageBox { master = ide, type = ok, text = $"Run", contents = $"Shared and static libraries cannot be run like executables." }.Modal();
1424       else if(project.compress)
1425          MessageBox { master = ide, text = $"Starting Debug", contents = $"Debugging compressed applications is not supported\n" }.Modal();
1426       else if(project.debug ||
1427          MessageBox { master = ide, type = okCancel, text = $"Starting Debug", contents = $"Attempting to debug non-debug configuration\nProceed anyways?" }.Modal() == ok)
1428       {
1429          if(/*!IsProjectModified() ||*/ BuildInterrim(project, start))
1430          {
1431             CompilerConfig compiler = ideSettings.GetCompilerConfig(ide.workspace.compiler);
1432             if(compiler.type.isVC)
1433             {
1434                //bool result = false;
1435                char oldwd[MAX_LOCATION];
1436                char oldPath[MAX_LOCATION * 65];
1437                char command[MAX_LOCATION];
1438
1439                GetEnvironment("PATH", oldPath, sizeof(oldPath));
1440                ide.SetPath(false); //true
1441                
1442                GetWorkingDir(oldwd, sizeof(oldwd));
1443                ChangeWorkingDir(project.topNode.path);
1444
1445                sprintf(command, "%s /useenv %s.sln /projectconfig \"%s|Win32\" /command \"%s\"" , "devenv", project.name, project.config.name, "Debug.Start");
1446                //ide.outputView.buildBox.Logf("command: %s\n", command);
1447                Execute(command);
1448                ChangeWorkingDir(oldwd);
1449                SetEnvironment("PATH", oldPath);
1450             }
1451             else
1452             {
1453                ide.debugger.Start();
1454                result = true;
1455             }
1456
1457             delete compiler;
1458          }
1459       }
1460       return result;      
1461    }
1462
1463    bool DebugRestart()
1464    {
1465       if(/*!IsProjectModified() ||*/ BuildInterrim(project, restart))
1466       {
1467          ide.debugger.Restart();
1468          return true;
1469       }
1470       return false;
1471    }
1472
1473    bool DebugResume()
1474    {
1475       ide.debugger.Resume();
1476       return true;
1477    }
1478
1479    bool DebugBreak()
1480    {
1481       ide.debugger.Break();
1482       return true;
1483    }
1484
1485    bool DebugStop()
1486    {
1487       ide.debugger.Stop();
1488       return true;
1489    }
1490
1491    bool DebugStepInto()
1492    {
1493       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
1494          ide.debugger.StepInto();
1495       return true;
1496    }
1497
1498    bool DebugStepOver(bool skip)
1499    {
1500       if((ide.debugger.isActive) || (!buildInProgress && BuildInterrim(project, start)))
1501          ide.debugger.StepOver(skip);
1502       return true;
1503    }
1504
1505    bool DebugStepOut(bool skip)
1506    {
1507       ide.debugger.StepOut(skip);
1508       return true;
1509    }
1510
1511    void ImportFolder(ProjectNode toNode)
1512    {
1513       if(toNode)
1514       {
1515          //bool isFolder = toNode.type == folder;
1516          //bool isRes = toNode.isInResources;
1517          
1518          FileDialog fileDialog = importFileDialog;
1519          fileDialog.master = parent;
1520          if(fileDialog.Modal() == ok)
1521          {
1522             ImportFolderFSI fsi { projectView = this };
1523             fsi.stack.Add(toNode);
1524             fsi.Iterate(fileDialog.filePath);
1525             delete fsi;
1526          }
1527       }
1528    }
1529
1530    ProjectNode NewFolder(ProjectNode parentNode, char * name, bool showProperties)
1531    {
1532       if(parentNode)
1533       {
1534          ProjectNode folderNode;
1535          Project prj = parentNode.project;
1536          int c;
1537          ProjectNode after = null;
1538          for(node : parentNode.files)
1539          {
1540             if(node.type != folder)
1541                break;
1542             after = node;
1543          }
1544          
1545          if(name && name[0])
1546             folderNode = parentNode.Add(prj, name, after, folder, folder, true);
1547          else
1548          {
1549             for(c = 0; c < 100; c++)
1550             {
1551                char string[16];
1552                sprintf(string, c ? "New Folder (%d)" : "New Folder", c);
1553                if((folderNode = parentNode.Add(prj, string, after, folder, folder, true)))
1554                   break;
1555             }
1556          }
1557
1558          if(folderNode)
1559          {
1560             NodeProperties nodeProperties;
1561             if(!showProperties)
1562             {
1563                modifiedDocument = true;
1564                prj.topNode.modified = true;
1565             }
1566             Update(null);
1567             folderNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1568             folderNode.row.tag = (int)folderNode;
1569                
1570             folderNode.row.SetData(null, folderNode);
1571             fileList.currentRow = folderNode.row;
1572             
1573             if(showProperties)
1574             {
1575                nodeProperties = NodeProperties
1576                {
1577                   parent, this, mode = newFolder, node = folderNode;
1578                   position = { position.x + 100, position.y + 100 };
1579                };
1580                nodeProperties.Create();   // Modal?
1581             }
1582             return folderNode;
1583          }
1584       }
1585       return null;
1586    }
1587
1588    void AddFiles(bool resources)
1589    {
1590       FileDialog fileDialog = (!resources) ? this.fileDialog : resourceFileDialog;
1591       fileDialog.type = multiOpen;
1592       fileDialog.text = !resources ? $"Add Files to Project" : $"Add Resources to Project";
1593       fileDialog.master = parent;
1594
1595       if(fileDialog.Modal() == ok)
1596       {
1597          int c;
1598          DataRow row = fileList.currentRow;
1599          ProjectNode parentNode = (ProjectNode)row.tag;
1600          bool addFailed = false;
1601          int numSelections = fileDialog.numSelections;
1602          char ** multiFilePaths = fileDialog.multiFilePaths;
1603
1604          Array<String> nameConflictFiles { };
1605
1606          for(c = 0; c < numSelections; c++)
1607          {
1608             char * filePath = multiFilePaths[c];
1609             FileAttribs exists = FileExists(filePath);
1610             bool addThisFile = true;
1611
1612             if(exists.isDirectory)
1613                addThisFile = false;
1614             else if(!exists)
1615             {
1616                if(MessageBox { master = ide, type = yesNo, text = filePath, 
1617                      contents = $"File doesn't exist. Create?" }.Modal() == yes)
1618                {
1619                   File f = FileOpen(filePath, write);
1620                   if(f)
1621                   {
1622                      addThisFile = true;
1623                      delete f;
1624                   }
1625                   else
1626                   {
1627                      MessageBox { master = ide, type = ok, text = filePath, 
1628                            contents = $"Couldn't create file."}.Modal();
1629                      addThisFile = false;
1630                   }
1631                }
1632             }
1633
1634             if(addThisFile)
1635             {
1636                /*addFailed = */if(!AddFile(parentNode, filePath, resources, false))//;
1637                {
1638                   nameConflictFiles.Add(CopyString(filePath));
1639                   addFailed = true;
1640                }
1641             }
1642          }
1643          if(addFailed)
1644          {
1645             int len = 0;
1646             char * part1 = $"The following file";
1647             char * opt1 = $" was ";
1648             char * opt2 = $"s were ";
1649             char * part2 = $"not added because of identical file name conflict within the project.\n\n";
1650             char * message;
1651             len += strlen(part1);
1652             len += strlen(part2);
1653             len += nameConflictFiles.count > 1 ? strlen(opt2) : strlen(opt1);
1654             for(s : nameConflictFiles)
1655                len += strlen(s) + 1;
1656             message = new char[len + 1];
1657             strcpy(message, part1);
1658             strcat(message, nameConflictFiles.count > 1 ? opt2 : opt1);
1659             strcat(message, part2);
1660             for(s : nameConflictFiles)
1661             {
1662                strcat(message, s);
1663                strcat(message, "\n");
1664             }
1665             MessageBox { master = ide, type = ok, text = $"Name Conflict", 
1666                   contents = message }.Modal();
1667             delete message;
1668          }
1669          nameConflictFiles.Free();
1670          delete nameConflictFiles;
1671       }
1672    }
1673
1674    ProjectNode AddFile(ProjectNode parentNode, char * filePath, bool resources, bool isTemporary)
1675    {
1676       ProjectNode result = null;
1677       ProjectNode after = null;
1678       for(node : parentNode.files)
1679       {
1680          if(node.type != folder && node.type != file && node.type)
1681             break;
1682          after = node;
1683       }
1684
1685       result = parentNode.Add(parentNode.project, filePath, after, file, NodeIcons::SelectFileIcon(filePath), !resources);
1686
1687       if(result)
1688       {
1689          if(!isTemporary)
1690          {
1691             modifiedDocument = true;
1692             parentNode.project.topNode.modified = true;
1693             parentNode.project.ModifiedAllConfigs(true, false, true, true);
1694          }
1695          Update(null);
1696          result.row = parentNode.row.AddRowAfter(after ? after.row : null);
1697          result.row.tag = (int)result;
1698          result.row.SetData(null, result);
1699       }
1700       return result;
1701    }
1702
1703    CodeEditor CreateNew(char * upper, char * lower, char * base, char * className)
1704    {
1705       CodeEditor codeEditor = null;
1706       ProjectNode projectNode;
1707       ProjectNode after = null;
1708       DataRow row = fileList.currentRow;
1709       ProjectNode parentNode;
1710       int c;
1711
1712       if(!row) row = project.topNode.row;
1713
1714       parentNode = (ProjectNode)row.tag;
1715
1716       for(node : parentNode.files)
1717       {
1718          if(node.type != folder && node.type != file && node.type)
1719             break;
1720          after = node;
1721       }
1722       for(c = 1; c < 100; c++)
1723       {
1724          char string[16];
1725          sprintf(string, c ? "%s%d.ec" : "%s.ec", lower, c);
1726          if((projectNode = parentNode.Add(project, string, after, file, genFile, true)))
1727             break;
1728       }
1729       if(projectNode)
1730       {
1731          char name[256];
1732          char filePath[MAX_LOCATION];
1733
1734          modifiedDocument = true;
1735          project.topNode.modified = true;
1736          Update(null);
1737          project.ModifiedAllConfigs(true, false, false, true);
1738          projectNode.row = parentNode.row.AddRowAfter(after ? after.row : null);
1739          projectNode.row.tag =(int)projectNode;
1740             
1741          projectNode.row.SetData(null, projectNode);
1742          fileList.currentRow = projectNode.row;
1743
1744          strcpy(filePath, project.topNode.path);
1745          PathCat(filePath, projectNode.path);
1746          PathCat(filePath, projectNode.name);
1747
1748          codeEditor = (CodeEditor)ide.FindWindow(filePath);
1749          if(!codeEditor)
1750          {
1751             Class baseClass = eSystem_FindClass(__thisModule, base);
1752             subclass(ClassDesignerBase) designerClass = eClass_GetDesigner(baseClass);
1753             if(designerClass)
1754             {
1755                codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
1756                strcpy(name, projectNode.name);
1757                sprintf(name, "%s%d", upper, c);
1758                if(className)
1759                   strcpy(className, name);
1760
1761                designerClass.CreateNew(codeEditor.editBox, codeEditor.clientSize, name, base);
1762
1763                //codeEditor.modifiedDocument = false;
1764                //codeEditor.designer.modifiedDocument = false;
1765
1766                //Code_EnsureUpToDate(codeEditor);
1767             }
1768          }
1769          else // TODO: fix no symbols generated when ommiting {} for following else
1770          {
1771             codeEditor = (CodeEditor)ide.OpenFile(filePath, normal, false, null, whatever, normal);
1772          }
1773          if(codeEditor)
1774          {
1775             codeEditor.ViewDesigner();
1776             codeEditor.codeModified = true;
1777          }
1778       }
1779       visible = false;
1780       return codeEditor;   
1781    }
1782
1783    // Returns true if we opened something
1784    bool OpenSelectedNodes()
1785    {
1786       OldList selection;
1787       OldLink item;
1788
1789       fileList.GetMultiSelection(selection);
1790       for(item = selection.first; item; item = item.next)
1791       {
1792          DataRow row = item.data;
1793          ProjectNode node = (ProjectNode)row.tag;
1794          if(node.type == file)
1795          {
1796             OpenNode(node);
1797             return true;
1798          }
1799       }
1800       selection.Free(null);
1801       return false;
1802    }
1803
1804    void RemoveSelectedNodes()
1805    {
1806       OldList selection;
1807       OldLink item, next;
1808       
1809       fileList.GetMultiSelection(selection);
1810
1811       // Remove children of parents we're deleting
1812       for(item = selection.first; item; item = next)
1813       {
1814          OldLink i;
1815          DataRow row = item.data;
1816          ProjectNode n, node = (ProjectNode)row.tag;
1817          bool remove = false;
1818
1819          next = item.next;
1820          for(i = selection.first; i && !remove; i = i.next)
1821          {
1822             ProjectNode iNode = (ProjectNode)((DataRow)i.data).tag;
1823             for(n = node.parent; n; n = n.parent)
1824             {
1825                if(iNode == n)
1826                {
1827                   remove = true;
1828                   break;
1829                }
1830             }
1831          }
1832          if(remove)
1833             selection.Delete(item);
1834       }
1835
1836       for(item = selection.first; item; item = item.next)
1837       {
1838          DataRow row = item.data;
1839          ProjectNode node = (ProjectNode)row.tag;
1840          ProjectNode resNode;
1841          for(resNode = node.parent; resNode; resNode = resNode.parent)
1842             if(resNode.type == resources)
1843                break;
1844          if(node.type == file)
1845          {
1846             Project prj = node.project;
1847             DeleteNode(node);
1848             modifiedDocument = true;
1849             prj.topNode.modified = true;
1850             Update(null);
1851             prj.ModifiedAllConfigs(true, false, true, true);
1852          }
1853          else if(node.type == folder)
1854          {
1855             char message[1024];
1856             sprintf(message, $"Are you sure you want to remove the folder \"%s\"\n"
1857                   "and all of its contents from the project?", node.name);
1858             if(MessageBox { master = ide, type = yesNo, text = $"Delete Folder", contents = message }.Modal() == yes)
1859             {
1860                Project prj = node.project;
1861                if(node.containsFile)
1862                   prj.ModifiedAllConfigs(true, false, true, true);
1863                DeleteNode(node);
1864                modifiedDocument = true;
1865                prj.topNode.modified = true;
1866             }
1867          }
1868          else if(node.type == project && node != project.topNode && !buildInProgress)
1869          {
1870             char message[1024];
1871             Project prj = null;
1872             for(p : workspace.projects)
1873             {
1874                if(p.topNode == node)
1875                {
1876                   prj = p;
1877                   break;
1878                }
1879             }
1880             sprintf(message, $"Are you sure you want to remove the \"%s\" project\n" "from this workspace?", node.name);
1881             if(MessageBox { master = ide, type = yesNo, text = $"Remove Project", contents = message }.Modal() == yes)
1882             {
1883                // THIS GOES FIRST!
1884                DeleteNode(node);
1885                if(prj)
1886                   workspace.RemoveProject(prj);
1887                //modifiedDocument = true; // when project view is a workspace view
1888             }
1889          }
1890       }
1891       selection.Free(null);
1892    }
1893 }