ide: Fixed corruption of LD_LIBRARY_PATH (Only PATH was being restored) with PathBack...
[sdk] / ide / src / project / Workspace.ec
1 import "ide"
2
3 //bool eString_PathInsideOf(char * path, char * of);
4
5 /*static void ParseListValue(List<String> list, char * equal)
6 {
7    char * start, * comma;
8    char * string;
9    string = CopyString(equal);
10    start = string;
11    while(start)
12    {
13       comma = strstr(start, ",");
14       if(comma)
15          comma[0] = '\0';
16       list.Add(CopyString(start));
17       if(comma)
18          comma++;
19       if(comma)
20          comma++;
21       start = comma;
22    }
23    delete string;
24 }*/
25
26 enum OpenedFileState { unknown, opened, closed };
27
28 class OpenedFileInfo
29 {
30    class_no_expansion;
31 //public:
32 //   class_fixed
33    char * path;
34    OpenedFileState state;
35    int lineNumber, position;
36    Point scroll;
37    bool holdTracking;
38
39    property bool trackingAllowed
40    {
41       get { return !holdTracking && ide && ide.workspace && !ide.workspace.holdTracking; }
42    }
43
44    void CaretMove(int line, int charPos)
45    {
46       if(trackingAllowed && (line != lineNumber || position != charPos))
47       {
48          lineNumber = line;
49          position = charPos;
50          ide.workspace.modified = true;
51       }
52    }
53
54    void ScrollChange(Point scroll)
55    {
56       if(trackingAllowed)
57       {
58          this.scroll.x = scroll.x;
59          this.scroll.y = scroll.y;
60          ide.workspace.modified = true;
61       }
62    }
63    
64    void Activate()
65    {
66       if(trackingAllowed)
67       {
68          List<OpenedFileInfo> files = ide.workspace.openedFiles;
69          Iterator<OpenedFileInfo> it { files };
70          if(it.Find(this) && it.pointer != files.GetLast())
71          {
72             files.Move(it.pointer, files.GetPrev(files.GetLast()));
73             ide.workspace.modified = true;
74          } 
75       }
76    }
77    ~OpenedFileInfo()
78    {
79       delete path;
80    }
81 };
82
83 class Workspace
84 {
85 public:
86    char * name;
87    char * workspaceFile;
88    char * workspaceDir;
89    char * commandLineArgs;
90    property char * commandLineArgs { set { delete commandLineArgs; if(value) commandLineArgs = CopyString(value); } }
91    char * debugDir;
92    property char * debugDir { set { delete debugDir; if(value) debugDir = CopyString(value); } }
93
94    int bpCount;
95
96    property char * compiler
97    {
98       set { delete compiler; if(value && value[0]) compiler = CopyString(value); }
99       get { return compiler && compiler[0] ? compiler : null; }
100    }
101
102    List<String> sourceDirs { };
103    Array<NamedString> environmentVars { };
104    List<Breakpoint> breakpoints { };
105    List<Watch> watches { };
106    List<OpenedFileInfo> openedFiles { };
107    List<Project> projects { };
108
109    //Project project;
110
111    bool modified;
112    bool holdTracking;
113
114    Timer timer
115    {
116       userData = this, delay = 2.5;
117       bool DelayExpired()
118       {
119          if(modified)
120             Save();
121          return true;
122       }
123    };
124
125    property char * workspaceFile
126    {
127       set
128       {
129          char dir[MAX_DIRECTORY];
130          if(workspaceFile) delete workspaceFile;
131          if(workspaceDir) delete workspaceDir;
132          workspaceFile = CopyString(value);
133          StripLastDirectory(workspaceFile, dir);
134          workspaceDir = CopyUnixPath(dir);
135       }
136       get { return workspaceFile; }
137    }
138
139    property char * projectDir
140    {
141       get
142       {
143          if(projects.first)
144          {
145             Project prj = projects.firstIterator.data;
146             return prj.topNode ? prj.topNode.path : null;
147          }
148          else
149             return workspaceDir;
150       }
151    }
152
153    /*property Project project
154    {
155       set
156       {
157          if(project)
158          {
159          }
160          project = value;
161          if(project)
162          {
163             projectDir = CopyString(project.topNode.path);
164             
165             if(!project.config && activeConfig && activeConfig[0])
166             {
167                ProjectConfig cfg;
168                for(cfg = project.configurations.first; cfg; cfg = cfg.next)
169                   if(!strcmp(cfg.name, activeConfig))
170                      break;
171                project.config = cfg;
172             }
173             if(!project.config)
174                project.config = project.configurations.first;
175          }
176       }
177       get { return project; }
178    }*/
179
180 private:
181    String compiler;
182
183 public:
184    void Save()
185    {
186       bool bkpts = false;
187       File file;
188       
189       file = FileOpen(workspaceFile, write);
190       if(file)
191       {
192          /*
193          for(bp : breakpoints)
194          {
195             if(bp.type == user)
196             {
197                if(bp.enabled)
198                   file.Printf("Breakpoint=1,%d,%s,%s\n", bp.line, bp.absoluteFilePath, bp.relativeFilePath);
199                else
200                   file.Printf("Breakpoint=0,%d,%s,%s\n", bp.line, bp.absoluteFilePath, bp.relativeFilePath);
201             }
202          }
203          
204          for(wh : watches)
205             file.Printf("Watch=%s\n", wh.expression);
206          
207          for(dir : sourceDirs)
208             file.Printf("SourceDir=%s\n", dir);
209
210          if(debugDir && debugDir[0])
211             file.Printf("DebugDir=%s\n", debugDir);
212          
213          if(commandLineArgs && commandLineArgs[0])
214             file.Printf("CommandLineArgs=%s\n", commandLineArgs);
215          */
216
217          /*
218          char indentation[128*3];
219          char path[MAX_LOCATION];
220          */
221          file.Printf("\nECERE Workspace File\n");
222          file.Printf("\nVersion 0.02\n");
223          file.Printf("\nWorkspace\n");
224          file.Printf("\n   Active Compiler = %s\n", compiler ? compiler : defaultCompilerName);
225          
226          if(projects.first)
227          {
228             file.Printf("\n   Projects\n\n");
229             for(prj : projects)
230             {
231                char location[MAX_LOCATION];
232                MakePathRelative(prj.topNode.path, workspaceDir, location);
233                MakeSlashPath(location);
234                PathCatSlash(location, prj.topNode.name);
235                //strcat(location, ".epj");
236
237                file.Printf("    %s %s\n", "-", location);
238                
239                if(prj.config)
240                   file.Printf("         Active Configuration = %s\n", prj.config.name);
241                for(cfg : prj.configurations)
242                {
243                   if(cfg.compilingModified)
244                      file.Printf("         Modified Compiler Config = %s\n", cfg.name);
245                   else if(cfg.linkingModified)
246                      file.Printf("         Modified Linker Config = %s\n", cfg.name);
247                }
248             }
249          }
250          
251          file.Printf("\n   Execution Data\n");
252          if(commandLineArgs && commandLineArgs[0])
253             file.Printf("\n      Command Line Arguments = %s\n", commandLineArgs);
254
255          if(environmentVars.count)
256          {
257             file.Printf("\n      Environment Variables\n\n");
258             for(v : environmentVars)
259                file.Printf("       ~ %s = %s\n", v.name, v.string);
260          }
261
262          file.Printf("\n   Debugger Data\n");
263          // This really belonged in Execution Data...
264          if(debugDir && debugDir[0])
265             file.Printf("\n      Debug Working Directory = %s\n", debugDir);
266          if(sourceDirs.count)
267          {
268             file.Printf("\n      Source Directories\n");
269             for(dir : sourceDirs)
270                file.Printf("       = %s\n", dir);
271          }
272
273          for(bp : breakpoints)
274          {
275             if(bp.type == user)
276             {
277                if(!bkpts)
278                {
279                   bkpts = true;
280                   file.Printf("\n   Breakpoints\n\n");
281                }
282                bp.Save(file);
283             }
284          }
285
286          if(watches.count)
287          {
288             file.Printf("\n   Watches\n\n");
289             for(wh : watches)
290                wh.Save(file);
291          }
292
293          if(openedFiles.count)
294          {
295             file.Printf("\n   Opened Files\n\n");
296             for(ofi : openedFiles)
297             {
298                char * location;
299                char chr[2];
300                char relativePath[MAX_LOCATION];
301                if(eString_PathInsideOf(ofi.path, workspaceDir))
302                {
303                   MakePathRelative(ofi.path, workspaceDir, relativePath);
304                   MakeSlashPath(relativePath);
305                   location = relativePath;
306                }
307                else
308                   location = ofi.path;
309                strcpy(chr, "=");
310
311                file.Printf("    %s %s:%d:%d:%d:%d:%s\n", chr, ofi.state == closed ? "C" : "O", ofi.lineNumber, ofi.position, ofi.scroll.x, ofi.scroll.y, location);
312             }
313          }
314
315          modified = false;
316          delete file;
317       }
318    }
319
320    char * GetAbsolutePathFromRelative(char * relative)
321    {
322       char name[MAX_LOCATION];
323       char absolute[MAX_LOCATION];
324       Project prj = null;
325
326       GetLastDirectory(relative, name);
327       for(p : projects)
328       {
329          if(p.topNode.Find(name, false))
330          {
331             prj = p;
332             break;
333          }
334       }
335       if(prj)
336       {
337          strcpy(absolute, prj.topNode.path);
338          PathCatSlash(absolute, relative);
339          return CopyString(absolute);
340       }
341
342       prj = null;
343       for(p : projects)
344       {
345          strcpy(absolute, p.topNode.path);
346          PathCatSlash(absolute, relative);
347          if(FileExists(absolute))
348          {
349             prj = p;
350             break;
351          }
352       }
353       if(prj)
354          return CopyString(absolute);
355
356       strcpy(absolute, workspaceDir); //projectDir // CHECK?
357       PathCatSlash(absolute, relative);
358       if(FileExists(absolute))
359          return CopyString(absolute);
360
361       {
362          for(dir : sourceDirs)
363          {
364             strcpy(absolute, dir);
365             PathCatSlash(absolute, relative);
366             if(FileExists(absolute))
367                return CopyString(absolute);
368          }
369       }
370       
371       return null;
372    }
373
374    char * GetPathWorkspaceRelativeOrAbsolute(char * path)
375    {
376       if(eString_PathInsideOf(path, workspaceDir))
377       {
378          char relativePath[MAX_LOCATION];
379          MakePathRelative(path, workspaceDir, relativePath);
380          return CopyUnixPath(relativePath);
381       }
382       else
383          return CopyUnixPath(path);
384    }
385
386    OpenedFileInfo UpdateOpenedFileInfo(char * fileName, OpenedFileState state)
387    {
388       char filePath[MAX_LOCATION];
389       OpenedFileInfo ofi = null;
390       GetSlashPathBuffer(filePath, fileName);
391       for(item : openedFiles)
392       {
393          if(!fstrcmp(item.path, filePath))
394          {
395             ofi = item;
396             break;
397          }
398       }
399       if(state)
400       {
401          if(!ofi)
402          {
403             ofi = OpenedFileInfo { path = CopyString(filePath) };
404             openedFiles.Add(ofi);
405          }
406          ofi.state = state;
407          if(!holdTracking)
408             modified = true;
409       }
410       else if(ofi)
411       {
412          Iterator<OpenedFileInfo> it { openedFiles };
413          if(it.Find(ofi))
414             openedFiles.Delete(it.pointer);
415          if(!holdTracking)
416             modified = true;
417       }
418       return ofi;
419    }
420
421    void UpdateSourceDirsArray(Array<String> dirs)
422    {
423       byte * tokens[256];
424       int c, numTokens;
425
426       sourceDirs.Free();
427       
428       for(s : dirs)
429          sourceDirs.Add(CopyString(s));
430       
431       DropInvalidBreakpoints();
432
433       delete dirs;
434    }
435    
436    void RemoveProject(Project project)
437    {
438       Iterator<Project> it { projects };
439       if(it.Find(project))
440          it.Remove();
441
442       DropInvalidBreakpoints();
443       modified = true;
444       ide.findInFilesDialog.RemoveProjectItem(project);
445       Save();
446
447       delete project;
448    }
449
450    bool FindPath(ProjectNode node, char * path)
451    {
452       if(node.type == file)
453       {
454          // TODO: Should this code be moved into a ProjectNode::absolutePath property? Taken from NodeProperties.ec
455          char filePath[MAX_LOCATION];
456          GetSlashPathBuffer(filePath, node.project.topNode.path);
457          PathCatSlash(filePath, node.path);
458          PathCatSlash(filePath, node.name);
459
460          if(!fstrcmp(filePath, path))
461             return true;
462       }
463       if(node.files)
464       {
465          for(n : node.files)
466          {
467             if(FindPath(n, path))
468                return true;
469          }
470       }
471       return false;
472    }
473
474    void ChangeBreakpoint(DataRow row, char * location)
475    {
476       Breakpoint bp = (Breakpoint)row.tag;
477       if(bp)
478       {
479          char * currentLoc = bp.LocationToString();
480          if(strcmp(location, currentLoc))
481          {
482             // todo, parse location
483             //  if good, make changes to breakpoint, according to execution state delete and place breakpoint
484             ide.breakpointsView.UpdateBreakpoint(row); // this is the else
485             //Save();
486          }
487          delete currentLoc;
488       }
489       else if(location)
490       {
491          // adding a breakpoint by typing it in the breakpoints view
492          // todo, parse location
493          //  if good, make add breakpoint, make sure possibly previously entered ignore and level are reflected in breakpoint
494          //  else
495          //     ...
496          //bp = Breakpoint { };
497          //row.tag = (int)bp;
498          //breakpoints.Add(bp);
499          //bp.row = row;
500          Save();
501       }
502    }
503
504    void ChangeBreakpointIgnore(DataRow row, int ignore)
505    {
506       Breakpoint bp = (Breakpoint)row.tag;
507       if(bp)
508       {
509          bp.ignore = ignore;
510          Save();
511       }
512    }
513
514    void ChangeBreakpointLevel(DataRow row, int level)
515    {
516       Breakpoint bp = (Breakpoint)row.tag;
517       if(bp)
518       {
519          bp.level = level;
520          Save();
521       }
522    }
523
524    void ChangeBreakpointCondition(DataRow row, char * condition)
525    {
526       Breakpoint bp = (Breakpoint)row.tag;
527       if(bp && !(!bp.condition && !(condition && condition[0])))
528       {
529          if(!bp.condition)
530          {
531             bp.condition = Watch { };
532             bp.condition.expression = CopyString(condition);
533             Save();
534          }
535          else if(!(condition && condition[0]))
536          {
537             bp.condition.Free();
538             bp.condition = null;
539             Save();
540          }
541          else if(strcmp(condition, bp.condition.expression))
542          {
543             bp.condition.Free();
544             bp.condition = Watch { };
545             bp.condition.expression = CopyString(condition);
546             Save();
547          }
548       }
549    }
550
551    void RemoveBreakpoint(Breakpoint bp)
552    {
553       bpCount--;
554       ide.breakpointsView.RemoveBreakpoint(bp);
555       ide.debugger.UpdateRemovedBreakpoint(bp);
556       {
557          Iterator<Breakpoint> it { breakpoints };
558          if(it.Find(bp))
559             breakpoints.Remove(it.pointer);
560       }
561       {
562          Window document;
563          for(document = ide.firstChild; document; document = document.next)
564          {
565             char * fileName = document.fileName;
566             if(document.isDocument && fileName && document.created)
567             {
568                char winFilePath[MAX_LOCATION];
569                char * slashPath = GetSlashPathBuffer(winFilePath, fileName);
570
571                if(!fstrcmp(slashPath, bp.absoluteFilePath))
572                {
573                   CodeEditor codeEditor = (CodeEditor)document;
574                   int boxH = codeEditor.editBox.clientSize.h;
575                   Box box { 0, 0, 19, boxH - 1 };
576                   document.Update(box);
577                   break;
578                }
579             }
580          }
581       }
582       Save();
583       delete bp;
584    }
585
586    void DropInvalidBreakpoints()
587    {
588       Link bpLink, next;
589       for(bpLink = breakpoints.first; bpLink; bpLink = next)
590       {
591          Breakpoint bp = (Breakpoint)bpLink.data;
592          next = bpLink.next;
593
594          if(bp.type == user)
595          {
596             Project project = null;
597             for(p : projects)
598             {
599                if(FindPath(p.topNode, bp.absoluteFilePath))
600                {
601                   project = p;
602                   break;
603                }
604             }
605             if(!project)
606             {
607                bool found = false;
608                for(dir : sourceDirs)
609                {
610                   if(eString_PathInsideOf(bp.absoluteFilePath, dir))
611                   {
612                      found = true;
613                      break;
614                   }
615                }
616                if(!found)
617                {
618                   ide.breakpointsView.RemoveBreakpoint(bp);
619                   RemoveBreakpoint(bp);
620                }
621             }
622          }
623       }
624       ide.breakpointsView.Update(null);
625    }
626
627    void Free()
628    {
629       delete workspaceFile;
630       delete workspaceDir;
631       delete commandLineArgs;
632       delete debugDir;
633       
634       //project = null;
635
636       projects.Free();
637       breakpoints.Free();
638       watches.Free();
639    }
640
641    Workspace()
642    {
643       ide.outputView.buildBox.Clear();
644       ide.outputView.debugBox.Clear();
645       ide.callStackView.Clear();
646       ide.watchesView.Clear();
647       ide.threadsView.Clear();
648       ide.breakpointsView.Clear();
649
650       property::debugDir = "";
651       
652       SetSourceDirs(sourceDirs);
653    }
654
655    ~Workspace()
656    {
657       Save();
658       timer.Stop();
659
660       sourceDirs.Free();
661       environmentVars.Free();
662       SetSourceDirs(null);
663       Free();
664       openedFiles.Free();
665       delete compiler;
666    }
667
668 }
669
670 Workspace LoadWorkspace(char * filePath, char * fromProjectFile)
671 {
672    File file;
673    Workspace workspace = null;
674
675    file = FileOpen(filePath, read);
676    if(file)
677    {
678       OldList openedFilesNotFound { };
679
680       workspace = Workspace { compiler = ideSettings.defaultCompiler, workspaceFile = filePath };
681
682       file.Seek(0, start);
683       while(!file.Eof())
684       {
685          char buffer[65536];
686          char section[128];
687          char subSection[128];
688          //char version[16];
689          float version;
690          char * equal;
691          int len;
692          
693          Watch wh;
694          Breakpoint bp;
695          
696          file.GetLine(buffer, 65536 - 1);
697          TrimLSpaces(buffer, buffer);
698          TrimRSpaces(buffer, buffer);
699          if(strlen(buffer))
700          {
701             if(buffer[0] == '~')
702             {
703                equal = &buffer[0];
704                equal[0] = ' ';
705                TrimLSpaces(equal, equal);
706                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Watches"))
707                {
708                   wh = Watch { };
709                   workspace.watches.Add(wh);
710                   wh.expression = CopyString(equal);
711                }
712                else if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Breakpoints"))
713                {
714                   if(bp)
715                   {
716                      wh = Watch { };
717                      wh.expression = CopyString(equal);
718                      bp.condition = wh;
719                   }
720                }
721                else if(!strcmpi(section, "Execution Data") && !strcmpi(subSection, "Environment Variables"))
722                {
723                   String value = strchr(equal, '=');
724                   if(value)
725                   {
726                      *value = 0;
727                      value++;
728                      TrimRSpaces(equal, equal);
729                      TrimLSpaces(value, value);
730                      workspace.environmentVars.Add({ equal, value });
731                   }
732                }
733             }
734             else if(buffer[0] == '*')
735             {
736                equal = &buffer[0];
737                equal[0] = ' ';
738                TrimLSpaces(equal, equal);
739                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Breakpoints"))
740                {
741                   char * strEnabled = null;
742                   char * strIgnore = null;
743                   char * strLevel = null;
744                   char * strLine = null;
745                   char * strFile = null;
746                   
747                   strEnabled = equal;
748                   if(strEnabled && strEnabled[0])
749                   {
750                      strIgnore = strstr(strEnabled, ",");
751                      strIgnore[0] = '\0';
752                      strIgnore++;
753                   }
754                   if(strIgnore && strIgnore[0])
755                   {
756                      strLevel = strstr(strIgnore, ",");
757                      strLevel[0] = '\0';
758                      strLevel++;
759                   }
760                   if(strLevel && strLevel[0])
761                   {
762                      strLine = strstr(strLevel, ",");
763                      strLine[0] = '\0';
764                      strLine++;
765                   }
766                   if(strLine && strLine[0])
767                   {
768                      strFile = strstr(strLine, ",");
769                      strFile[0] = '\0';
770                      strFile++;
771                   }
772                   if(strEnabled && strEnabled[0] && strIgnore && strIgnore[0] && 
773                         strLevel && strLevel[0] && strLine && strLine[0] && strFile && strFile[0])
774                   {
775                      bool enabled;
776                      int ignore;
777                      int level;
778                      int line;
779
780                      TrimLSpaces(strEnabled, strEnabled);
781                      TrimRSpaces(strEnabled, strEnabled);
782                      TrimLSpaces(strIgnore, strIgnore);
783                      TrimRSpaces(strIgnore, strIgnore);
784                      TrimLSpaces(strLevel, strLevel);
785                      TrimRSpaces(strLevel, strLevel);
786                      TrimLSpaces(strLevel, strLevel);
787                      TrimRSpaces(strLevel, strLevel);
788                      TrimLSpaces(strFile, strFile);
789                      TrimRSpaces(strFile, strFile);
790
791                      enabled = (strEnabled[0] == '1');
792                      ignore = atoi(strIgnore);
793                      level = atoi(strLevel);
794                      line = atoi(strLine);
795
796                      bp = { type = user, enabled = enabled, ignore = ignore, level = level, line = line };
797                      workspace.breakpoints.Add(bp);
798                      bp.relativeFilePath = CopyString(strFile);
799                      bp.absoluteFilePath = workspace.GetAbsolutePathFromRelative(strFile);
800                      if(!bp.absoluteFilePath)
801                         bp.absoluteFilePath = CopyString("");
802                   }
803                }
804             }
805             else if(buffer[0] == '=' || buffer[0] == '-')
806             {
807                equal = &buffer[0];
808                equal[0] = ' ';
809                TrimLSpaces(equal, equal);
810                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Source Directories"))
811                   workspace.sourceDirs.Add(CopyString(equal));
812                else if(!strcmpi(section, "Opened Files"))
813                {
814                   OpenedFileState state = opened;
815                   int lineNumber = 0;
816                   int position = 0;
817                   Point scroll { };
818                   char absolutePath[MAX_LOCATION];
819                   strcpy(absolutePath, workspace.workspaceDir);
820                   if(version == 0.01f)
821                   {
822                      char * comma = strchr(equal, ',');
823                      if(comma)
824                      {
825                         comma[0] = '\0';
826                         lineNumber = atoi(equal);
827                         equal = comma + 1;
828                      }
829                   }
830                   else if(version >= 0.02f)
831                   {
832                      char * column = strchr(equal, ':');
833                      if(column)
834                      {
835                         column[0] = '\0';
836                         if(strcmpi(equal, "O"))
837                            state = closed;
838                         column++;
839                         equal = column;
840                         column = strchr(equal, ':');
841                         if(column)
842                         {
843                            column[0] = '\0';
844                            lineNumber = atoi(equal);
845                            column++;
846                            equal = column;
847                            column = strchr(equal, ':');
848                            if(column)
849                            {
850                               column[0] = '\0';
851                               position = atoi(equal);
852                               column++;
853                               equal = column;
854                               column = strchr(equal, ':');
855                               if(column)
856                               {
857                                  column[0] = '\0';
858                                  scroll.x = atoi(equal);
859                                  column++;
860                                  equal = column;
861                                  column = strchr(equal, ':');
862                                  if(column)
863                                  {
864                                     column[0] = '\0';
865                                     scroll.y = atoi(equal);
866                                     column++;
867                                     equal = column;
868                                  }
869                               }
870                            }
871                         }
872                      }
873                   }
874                   PathCatSlash(absolutePath, equal);
875                      
876                   if(state == closed || FileExists(absolutePath))
877                      workspace.openedFiles.Add(OpenedFileInfo { path = CopyString(absolutePath), state = state, lineNumber = lineNumber, position = position, scroll = scroll });
878                   else
879                      openedFilesNotFound.Add(NamedItem { name = CopyString(equal) });
880                }
881                else if(!strcmpi(section, "Projects"))
882                {
883                   char projectFilePath[MAX_LOCATION];
884                   Project newProject;
885                   strcpy(projectFilePath, workspace.workspaceDir);
886                   PathCatSlash(projectFilePath, equal);
887                   newProject = LoadProject(projectFilePath);
888                   if(newProject)
889                      workspace.projects.Add(newProject);
890                   else if(workspace.projects.count == 0)
891                   {
892                      delete workspace;
893                      break;
894                   }
895                   else
896                   {
897                      // TODO: show message or something when added project fails to load
898                      // http://ecere.com/mantis/view.php?id=524
899                   }
900                }
901             }
902             else if(!strcmpi(buffer, "ECERE Workspace File"));
903             else if(!strcmpi(buffer, "Version 0a"))
904                version = 0.0f;
905             else if(!strncmp(buffer, "Version ", 8))
906                version = atof(&buffer[8]);
907             else if(!strcmpi(buffer, "Workspace"))
908                strcpy(section, buffer);
909             else if(!strcmpi(buffer, "Projects"))
910                strcpy(section, buffer);
911             else if(!strcmpi(buffer, "Execution Data"))
912                strcpy(section, buffer);
913             else if(!strcmpi(buffer, "Debugger Data"))
914                strcpy(section, buffer);
915             else if(!strcmpi(buffer, "Source Directories"))
916                strcpy(subSection, buffer);
917             else if(!strcmpi(buffer, "Breakpoints"))
918                strcpy(subSection, buffer);
919             else if(!strcmpi(buffer, "Watches"))
920                strcpy(subSection, buffer);
921             else if(!strcmpi(buffer, "Environment Variables"))
922                strcpy(subSection, buffer);
923             else if(!strcmpi(buffer, "Opened Files"))
924                strcpy(section, buffer);
925             else if(!strcmpi(buffer, ""))      // | These two lines were commented out
926                strcpy(subSection, buffer);     // | Do they serve a purpose? They were there for copy paste when adding a new subsection
927             else
928             {
929                equal = strstr(buffer, "=");
930                if(equal)
931                {
932                   if(!strcmpi(section, "Workspace"))
933                   {
934                      equal[0] = '\0';
935                      TrimRSpaces(buffer, buffer);
936                      equal++;
937                      TrimLSpaces(equal, equal);
938                      if(!strcmpi(buffer, "Active Compiler"))
939                      {
940                         CompilerConfig compiler = ideSettings.GetCompilerConfig(equal);
941                         if(!compiler)
942                            workspace.compiler = defaultCompilerName;
943                         else
944                            workspace.compiler = equal;
945                         delete compiler;
946                      }
947                   }
948                   else if(!strcmpi(section, "Execution Data"))
949                   {
950                      equal[0] = '\0';
951                      TrimRSpaces(buffer, buffer);
952                      equal++;
953                      TrimLSpaces(equal, equal);
954                      if(!strcmpi(buffer, "Command Line Arguments"))
955                         workspace.commandLineArgs = equal;
956
957                      if(!strcmpi(buffer, "Environment Variables"))
958                      {
959                         delete workspace.environmentVars;
960                         workspace.environmentVars = { };
961                      }
962
963                   }
964                   else if(!strcmpi(section, "Debugger Data"))
965                   {
966                      equal[0] = '\0';
967                      TrimRSpaces(buffer, buffer);
968                      equal++;
969                      TrimLSpaces(equal, equal);
970                      if(!strcmpi(buffer, "Debug Working Directory"))
971                         workspace.debugDir = equal;
972                   }
973                   else
974                   {
975                      equal[0] = '\0';
976                      TrimRSpaces(buffer, buffer);
977                      equal++;
978                      TrimLSpaces(equal, equal);
979                      if(!strcmpi(buffer, "Active Configuration"))
980                      {
981                         Project prj;
982                         if(workspace.projects.last)
983                         {
984                            prj = workspace.projects.lastIterator.data;
985                            for(cfg : prj.configurations)
986                            {
987                               if(!strcmp(cfg.name, equal))
988                               {
989                                  prj.config = cfg;
990                                  break;
991                               }
992                            }
993                         }
994                      }
995                      else if(!strcmpi(buffer, "Modified Compiler Config") || !strcmpi(buffer, "Modified Linker Config"))
996                      {
997                         Project prj;
998                         if(workspace.projects.last)
999                         {
1000                            prj = workspace.projects.lastIterator.data;
1001                            for(cfg : prj.configurations)
1002                            {
1003                               if(!strcmp(cfg.name, equal))
1004                               {
1005                                  if(strstr(buffer, "Compiler"))
1006                                     cfg.compilingModified = true;
1007                                  else
1008                                     cfg.linkingModified = true;
1009                                  break;
1010                               }
1011                            }
1012                         }
1013                      }
1014                      else if(!strcmpi(buffer, "CommandLineArgs"))
1015                         workspace.commandLineArgs = equal;
1016                      else if(!strcmpi(buffer, "Breakpoint"))
1017                      {
1018                         bool enabled;
1019                         char * lineNum = strstr(equal, ",");
1020                         if(lineNum)
1021                         {
1022                            lineNum[0] = '\0';
1023                            lineNum++;
1024                            if(equal[0] == '0')
1025                               enabled = false;
1026                            else
1027                               enabled = true;
1028                            if(lineNum)
1029                            {
1030                               char * absPath = strstr(lineNum, ",");
1031                               if(absPath)
1032                               {
1033                                  absPath[0] = '\0';
1034                                  absPath++;
1035                                  if(absPath)
1036                                  {
1037                                     char * relPath = strstr(absPath, ",");
1038                                     if(relPath)
1039                                     {
1040                                        relPath[0] = '\0';
1041                                        relPath++;
1042                                        if(relPath)
1043                                        {
1044                                           bp = { type = user, enabled = enabled, level = -1 };
1045                                           workspace.breakpoints.Add(bp);
1046                                           bp.line = atoi(lineNum);
1047                                           bp.relativeFilePath = CopyString(relPath);
1048                                           bp.absoluteFilePath = workspace.GetAbsolutePathFromRelative(relPath);
1049                                           if(!bp.absoluteFilePath)
1050                                              bp.absoluteFilePath = CopyString("");
1051                                        }
1052                                     }
1053                                  }
1054                               }
1055                            }
1056                         }
1057                      }
1058                      else if(!strcmpi(buffer, "Watch"))
1059                      {
1060                         wh = Watch { };
1061                         workspace.watches.Add(wh);
1062                         wh.expression = CopyString(equal);
1063                      }
1064                      else if(!strcmpi(buffer, "SourceDir"))
1065                      {
1066                         workspace.sourceDirs.Add(CopyString(equal));
1067                      }
1068                      else if(!strcmpi(buffer, "DebugDir"))
1069                      {
1070                         workspace.debugDir = equal;
1071                      }
1072                   }
1073                }
1074             }
1075          }
1076       }
1077
1078       delete file;
1079
1080       if(workspace)
1081       {
1082          if(!workspace.projects.first)
1083          {
1084             Project project;
1085             if(fromProjectFile)
1086                project = LoadProject(fromProjectFile /*projectFilePath*/);
1087             else
1088             {
1089                char projectFilePath[MAX_LOCATION];
1090                strcpy(projectFilePath, workspace.workspaceFile);
1091                ChangeExtension(projectFilePath, ProjectExtension, projectFilePath);
1092                project = LoadProject(projectFilePath);
1093             }
1094             if(project)
1095             {
1096                workspace.projects.Add(project);
1097                workspace.name = CopyString(project.name);
1098             }
1099             else
1100             {
1101                MessageBox { type = ok, master = ide, contents = "Workspace load file failed", text = "Workspace Load File Error" }.Modal();
1102                delete workspace;
1103                return null;
1104             }
1105          }
1106
1107          if(openedFilesNotFound.first)
1108          {
1109             int c = 0;
1110             char s[2] = "";
1111             String files = new char[MAX_LOCATION * 16];
1112             char title[512];
1113             String msg = new char[MAX_LOCATION * 16 + 2048];
1114             NamedItem item;
1115             strcpy(files,"\n");
1116
1117             item = openedFilesNotFound.first;
1118             if(item.next)
1119                strcpy(s, "s");
1120             
1121             for(item = openedFilesNotFound.first; item; item = item.next)
1122             {
1123                c++;
1124                if(c == 16)
1125                {
1126                   strcat(files, "\n...");
1127                   break;
1128                }
1129                strcat(files, "\n");
1130                strcat(files, item.name);
1131             }
1132
1133             sprintf(title, "File%s not found", s);
1134             sprintf(msg, "The following file%s could not be re-opened.%s", s, files);
1135             
1136             MessageBox { type = ok, master = ide, contents = msg, text = title }.Modal();
1137
1138             delete files;
1139             delete msg;
1140          }
1141          openedFilesNotFound.Free(OldLink::Free);
1142       }
1143       else
1144          openedFilesNotFound.Free(OldLink::Free);
1145    }
1146    else if(fromProjectFile)
1147    {
1148       //MessageBox { type = Ok, master = ide, contents = "Worspace load file failed", text = "Worspace Load File Error" }.Modal();
1149       
1150       char projectFile[MAX_LOCATION];
1151       Project newProject;
1152       
1153       //strcpy(projectFile, filePath);
1154       //ChangeExtension(projectFile, ProjectExtension, projectFile);
1155       newProject = LoadProject(fromProjectFile /*projectFile*/);
1156
1157       if(newProject)
1158       {
1159          workspace = Workspace { workspaceFile = filePath };
1160
1161          workspace.projects.Add(newProject);
1162          workspace.Save();
1163       }
1164    }
1165    
1166    if(workspace)
1167    {
1168       ide.ChangeFileDialogsDirectory(workspace.workspaceDir, false);
1169
1170       if(!workspace.compiler || !workspace.compiler[0])
1171          workspace.compiler = defaultCompilerName;
1172    }
1173
1174    return workspace;
1175 }