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