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