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