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