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