sdk: Fixed many warnings and related problems
[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 const char *
30    {
31       get { return OnGetString(null, null, null); }
32    }
33
34    const 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 const char * commandLineArgs { set { delete commandLineArgs; if(value) commandLineArgs = CopyString(value); } }
112    char * debugDir;
113    property const char * debugDir { set { delete debugDir; if(value) debugDir = CopyString(value); } }
114
115    int bpCount;
116
117    property const 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 const 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 const 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(const 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(const 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    Array<ProjectNode> GetAllProjectNodes(const char *fullPath, bool skipExcluded)
428    {
429       Array<ProjectNode> nodes = null;
430       for(project : projects)
431       {
432          ProjectNode node;
433          if((node = project.topNode.FindByFullPath(fullPath, false)))
434          {
435             if(!skipExcluded || !node.GetIsExcluded(project.config))
436             {
437                if(!nodes) nodes = { };
438                nodes.Add(node);
439             }
440          }
441       }
442       return nodes;
443    }
444
445    OpenedFileInfo UpdateOpenedFileInfo(const char * fileName, OpenedFileState state)
446    {
447       char filePath[MAX_LOCATION];
448       OpenedFileInfo ofi = null;
449       GetSlashPathBuffer(filePath, fileName);
450       for(item : openedFiles)
451       {
452          if(!fstrcmp(item.path, filePath))
453          {
454             ofi = item;
455             break;
456          }
457       }
458       if(state)
459       {
460          if(!ofi)
461          {
462             ofi = OpenedFileInfo { path = CopyString(filePath) };
463             openedFiles.Add(ofi);
464          }
465          ofi.state = state;
466          if(!holdTracking)
467             modified = true;
468       }
469       else if(ofi)
470       {
471          Iterator<OpenedFileInfo> it { openedFiles };
472          if(it.Find(ofi))
473             openedFiles.Delete(it.pointer);
474          if(!holdTracking)
475             modified = true;
476       }
477       return ofi;
478    }
479
480    void UpdateSourceDirsArray(Array<String> dirs)
481    {
482       sourceDirs.Free();
483
484       for(s : dirs)
485          sourceDirs.Add(CopyString(s));
486
487       DropInvalidBreakpoints(null);
488
489       delete dirs;
490    }
491
492    void RemoveProject(Project project)
493    {
494       Iterator<Project> it { projects };
495       if(it.Find(project))
496          it.Remove();
497
498       for(bp : breakpoints)
499       DropInvalidBreakpoints(project);
500       modified = true;
501       ide.findInFilesDialog.RemoveProjectItem(project);
502       ide.UpdateToolBarActiveConfigs(false);
503       Save();
504
505       delete project;
506    }
507
508    void SelectActiveConfig(const char * configName)
509    {
510       bool change = false;
511       for(prj : ide.workspace.projects)
512       {
513          for(cfg : prj.configurations)
514          {
515             if(cfg.name && !strcmp(cfg.name, configName))
516             {
517                prj.config = cfg;
518                change = true;
519                break;
520             }
521          }
522       }
523       if(change)
524       {
525          modified = true;
526          ide.UpdateToolBarActiveConfigs(true);
527          ide.projectView.Update(null);
528          Save();
529       }
530    }
531
532    bool FindPath(ProjectNode node, const char * path)
533    {
534       if(node.type == file)
535       {
536          // TODO: Should this code be moved into a ProjectNode::absolutePath property? Taken from NodeProperties.ec
537          char filePath[MAX_LOCATION];
538          GetSlashPathBuffer(filePath, node.project.topNode.path);
539          PathCatSlash(filePath, node.path);
540          PathCatSlash(filePath, node.name);
541
542          if(!fstrcmp(filePath, path))
543             return true;
544       }
545       if(node.files)
546       {
547          for(n : node.files)
548          {
549             if(FindPath(n, path))
550                return true;
551          }
552       }
553       return false;
554    }
555
556    void ChangeBreakpoint(DataRow row, const char * location)
557    {
558       Breakpoint bp = (Breakpoint)row.tag;
559       if(bp)
560       {
561          char * currentLoc = bp.CopyUserLocationString();
562          if(strcmp(location, currentLoc))
563          {
564             char * newLoc;
565             bp.location = location;
566             bp.ParseLocation();
567             newLoc = bp.CopyUserLocationString();
568             if(strcmp(newLoc, currentLoc))
569             {
570                ide.breakpointsView.UpdateBreakpoint(row);
571                Save();
572             }
573          }
574          delete currentLoc;
575       }
576       else if(location)
577       {
578          // adding a breakpoint by typing it in the breakpoints view
579          // todo, parse location
580          //  if good, make add breakpoint, make sure possibly previously entered ignore and level are reflected in breakpoint
581          //  else
582          //     ...
583          //bp = Breakpoint { };
584          //row.tag = (int64)bp;
585          //breakpoints.Add(bp);
586          //bp.row = row;
587          Save();
588       }
589    }
590
591    void ChangeBreakpointIgnore(DataRow row, int ignore)
592    {
593       Breakpoint bp = (Breakpoint)row.tag;
594       if(bp)
595       {
596          bp.ignore = ignore;
597          Save();
598       }
599    }
600
601    void ChangeBreakpointLevel(DataRow row, int level)
602    {
603       Breakpoint bp = (Breakpoint)row.tag;
604       if(bp)
605       {
606          bp.level = level;
607          Save();
608       }
609    }
610
611    void ChangeBreakpointCondition(DataRow row, const char * condition)
612    {
613       Breakpoint bp = (Breakpoint)row.tag;
614       if(bp && !(!bp.condition && !(condition && condition[0])))
615       {
616          if(!bp.condition)
617          {
618             bp.condition = Watch { };
619             bp.condition.expression = CopyString(condition);
620             Save();
621          }
622          else if(!(condition && condition[0]))
623          {
624             bp.condition.Free();
625             bp.condition = null;
626             Save();
627          }
628          else if(strcmp(condition, bp.condition.expression))
629          {
630             bp.condition.Free();
631             bp.condition = Watch { };
632             bp.condition.expression = CopyString(condition);
633             Save();
634          }
635       }
636    }
637
638    void RemoveBreakpoint(Breakpoint bp)
639    {
640       bpCount--;
641       ide.breakpointsView.RemoveBreakpoint(bp);
642       ide.debugger.UpdateRemovedBreakpoint(bp);
643       {
644          Iterator<Breakpoint> it { breakpoints };
645          if(it.Find(bp))
646             breakpoints.Remove(it.pointer);
647       }
648       {
649          Window document;
650          for(document = ide.firstChild; document; document = document.next)
651          {
652             const char * fileName = document.fileName;
653             if(document.isDocument && fileName && document.created)
654             {
655                char winFilePath[MAX_LOCATION];
656                const char * slashPath = GetSlashPathBuffer(winFilePath, fileName);
657
658                if(!fstrcmp(slashPath, bp.absoluteFilePath))
659                {
660                   CodeEditor codeEditor = (CodeEditor)document;
661                   int boxH = codeEditor.editBox.clientSize.h;
662                   Box box { 0, 0, 19, boxH - 1 };
663                   document.Update(box);
664                   break;
665                }
666             }
667          }
668       }
669       Save();
670       delete bp;
671    }
672
673    void ParseLoadedBreakpoints()
674    {
675       for(bp : breakpoints; bp.location)
676       {
677          bp.ParseLocation();
678          ide.breakpointsView.UpdateBreakpoint(bp.row);
679       }
680    }
681
682    void DropInvalidBreakpoints(Project removedProject)
683    {
684       Link bpLink, next;
685       for(bpLink = breakpoints.first; bpLink; bpLink = next)
686       {
687          Breakpoint bp = (Breakpoint)bpLink.data;
688          next = bpLink.next;
689
690          if(bp.type == user)
691          {
692             if(removedProject)
693             {
694                if(bp.project == removedProject)
695                {
696                   ide.breakpointsView.RemoveBreakpoint(bp);
697                   RemoveBreakpoint(bp);
698                }
699             }
700             else
701             {
702                Project project = bp.project;
703                if(!project)
704                {
705                   for(p : projects)
706                   {
707                      if(FindPath(p.topNode, bp.absoluteFilePath))
708                      {
709                         project = p;
710                         break;
711                      }
712                      // Handle symbol loader modules:
713                      {
714                         char moduleName[MAX_FILENAME];
715                         char * sl;
716                         GetLastDirectory(bp.absoluteFilePath, moduleName);
717                         // Tweak for automatically resolving symbol loader modules
718                         sl = strstr(moduleName, ".main.ec");
719                         if(sl && (*sl = 0, !strcmpi(moduleName, p.name)))
720                         {
721                            project = p;
722                            break;
723                         }
724                      }
725                   }
726                }
727                if(!project)
728                {
729                   bool found = false;
730                   for(dir : sourceDirs)
731                   {
732                      if(IsPathInsideOf(bp.absoluteFilePath, dir))
733                      {
734                         found = true;
735                         break;
736                      }
737                   }
738                   if(!found)
739                   {
740                      ide.breakpointsView.RemoveBreakpoint(bp);
741                      RemoveBreakpoint(bp);
742                   }
743                }
744             }
745          }
746       }
747       ide.breakpointsView.Update(null);
748    }
749
750    void Free()
751    {
752       delete workspaceFile;
753       delete workspaceDir;
754       delete commandLineArgs;
755       delete debugDir;
756
757       //project = null;
758
759       projects.Free();
760       breakpoints.Free();
761       watches.Free();
762    }
763
764    Workspace()
765    {
766       ide.outputView.buildBox.Clear();
767       ide.outputView.debugBox.Clear();
768       ide.callStackView.Clear();
769       ide.watchesView.Clear();
770       ide.threadsView.Clear();
771       ide.breakpointsView.Clear();
772
773       property::debugDir = "";
774
775       SetSourceDirs(sourceDirs);
776    }
777
778    ~Workspace()
779    {
780       Save();
781       timer.Stop();
782
783       sourceDirs.Free();
784       environmentVars.Free();
785       SetSourceDirs(null);
786       Free();
787       openedFiles.Free();
788       delete compiler;
789    }
790
791 }
792
793 Workspace LoadWorkspace(const char * filePath, const char * fromProjectFile)
794 {
795    File file;
796    Workspace workspace = null;
797
798    file = FileOpen(filePath, read);
799    if(file)
800    {
801       OldList openedFilesNotFound { };
802       double version = 0;
803       char section[128];
804       char subSection[128];
805
806       workspace = Workspace { compiler = ideSettings.defaultCompiler, workspaceFile = filePath };
807
808       file.Seek(0, start);
809       while(!file.Eof())
810       {
811          char buffer[65536];
812          char * equal;
813
814          Watch wh;
815          Breakpoint bp;
816
817          file.GetLine(buffer, 65536 - 1);
818          TrimLSpaces(buffer, buffer);
819          TrimRSpaces(buffer, buffer);
820          if(strlen(buffer))
821          {
822             if(buffer[0] == '~')
823             {
824                equal = &buffer[0];
825                equal[0] = ' ';
826                TrimLSpaces(equal, equal);
827                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Watches"))
828                {
829                   wh = Watch { };
830                   workspace.watches.Add(wh);
831                   wh.expression = CopyString(equal);
832                }
833                else if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Breakpoints"))
834                {
835                   if(bp)
836                   {
837                      wh = Watch { };
838                      wh.expression = CopyString(equal);
839                      bp.condition = wh;
840                   }
841                }
842                else if(!strcmpi(section, "Execution Data") && !strcmpi(subSection, "Environment Variables"))
843                {
844                   String value = strchr(equal, '=');
845                   if(value)
846                   {
847                      *value = 0;
848                      value++;
849                      TrimRSpaces(equal, equal);
850                      TrimLSpaces(value, value);
851                      workspace.environmentVars.Add({ equal, value });
852                   }
853                }
854             }
855             else if(buffer[0] == '*')
856             {
857                equal = &buffer[0];
858                equal[0] = ' ';
859                TrimLSpaces(equal, equal);
860                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Breakpoints"))
861                {
862                   char * strEnabled = null;
863                   char * strIgnore = null;
864                   char * strLevel = null;
865                   char * strLine = null;
866                   char * strFile = null;
867
868                   strEnabled = equal;
869                   if(strEnabled && strEnabled[0])
870                   {
871                      strIgnore = strstr(strEnabled, ",");
872                      strIgnore[0] = '\0';
873                      strIgnore++;
874                   }
875                   if(strIgnore && strIgnore[0])
876                   {
877                      strLevel = strstr(strIgnore, ",");
878                      strLevel[0] = '\0';
879                      strLevel++;
880                   }
881                   if(strLevel && strLevel[0])
882                   {
883                      strLine = strstr(strLevel, ",");
884                      strLine[0] = '\0';
885                      strLine++;
886                   }
887                   if(strLine && strLine[0])
888                   {
889                      strFile = strstr(strLine, ",");
890                      strFile[0] = '\0';
891                      strFile++;
892                   }
893                   if(strEnabled && strEnabled[0] && strIgnore && strIgnore[0] &&
894                         strLevel && strLevel[0] && strLine && strLine[0] && strFile && strFile[0])
895                   {
896                      bool enabled;
897                      int ignore;
898                      int level;
899                      int line;
900
901                      TrimLSpaces(strEnabled, strEnabled);
902                      TrimRSpaces(strEnabled, strEnabled);
903                      TrimLSpaces(strIgnore, strIgnore);
904                      TrimRSpaces(strIgnore, strIgnore);
905                      TrimLSpaces(strLevel, strLevel);
906                      TrimRSpaces(strLevel, strLevel);
907                      TrimLSpaces(strLevel, strLevel);
908                      TrimRSpaces(strLevel, strLevel);
909                      TrimLSpaces(strFile, strFile);
910                      TrimRSpaces(strFile, strFile);
911
912                      enabled = (strEnabled[0] == '1');
913                      ignore = atoi(strIgnore);
914                      level = atoi(strLevel);
915                      line = atoi(strLine);
916
917                      bp = { type = user, enabled = enabled, ignore = ignore, level = level, line = line };
918                      workspace.breakpoints.Add(bp);
919                      bp.location = strFile;
920                   }
921                }
922             }
923             else if(buffer[0] == '=' || buffer[0] == '-')
924             {
925                equal = &buffer[0];
926                equal[0] = ' ';
927                TrimLSpaces(equal, equal);
928                if(!strcmpi(section, "Debugger Data") && !strcmpi(subSection, "Source Directories"))
929                   workspace.sourceDirs.Add(CopyString(equal));
930                else if(!strcmpi(section, "Opened Files"))
931                {
932                   OpenedFileState state = opened;
933                   int lineNumber = 0;
934                   int position = 0;
935                   Point scroll { };
936                   char absolutePath[MAX_LOCATION];
937                   strcpy(absolutePath, workspace.workspaceDir);
938                   if(version == 0.01)
939                   {
940                      char * comma = strchr(equal, ',');
941                      if(comma)
942                      {
943                         comma[0] = '\0';
944                         lineNumber = atoi(equal);
945                         equal = comma + 1;
946                      }
947                   }
948                   else if(version >= 0.02)
949                   {
950                      char * column = strchr(equal, ':');
951                      if(column)
952                      {
953                         column[0] = '\0';
954                         if(strcmpi(equal, "O"))
955                            state = closed;
956                         column++;
957                         equal = column;
958                         column = strchr(equal, ':');
959                         if(column)
960                         {
961                            column[0] = '\0';
962                            lineNumber = atoi(equal);
963                            column++;
964                            equal = column;
965                            column = strchr(equal, ':');
966                            if(column)
967                            {
968                               column[0] = '\0';
969                               position = atoi(equal);
970                               column++;
971                               equal = column;
972                               column = strchr(equal, ':');
973                               if(column)
974                               {
975                                  column[0] = '\0';
976                                  scroll.x = atoi(equal);
977                                  column++;
978                                  equal = column;
979                                  column = strchr(equal, ':');
980                                  if(column)
981                                  {
982                                     column[0] = '\0';
983                                     scroll.y = atoi(equal);
984                                     column++;
985                                     equal = column;
986                                  }
987                               }
988                            }
989                         }
990                      }
991                   }
992                   PathCatSlash(absolutePath, equal);
993
994                   if(state == closed || FileExists(absolutePath))
995                      workspace.openedFiles.Add(OpenedFileInfo { path = CopyString(absolutePath), state = state, lineNumber = lineNumber, position = position, scroll = scroll });
996                   else
997                      openedFilesNotFound.Add(NamedItem { name = CopyString(equal) });
998                }
999                else if(!strcmpi(section, "Projects"))
1000                {
1001                   char projectFilePath[MAX_LOCATION];
1002                   Project newProject;
1003                   strcpy(projectFilePath, workspace.workspaceDir);
1004                   PathCatSlash(projectFilePath, equal);
1005                   newProject = LoadProject(projectFilePath, null);
1006                   if(newProject)
1007                   {
1008                      workspace.projects.Add(newProject);
1009                      newProject.StartMonitoring();
1010                   }
1011                   else if(workspace.projects.count == 0)
1012                   {
1013                      delete workspace;
1014                      break;
1015                   }
1016                   else
1017                   {
1018                      // TODO: show message or something when added project fails to load
1019                      // http://ecere.com/mantis/view.php?id=524
1020                   }
1021                }
1022             }
1023             else if(!strcmpi(buffer, "ECERE Workspace File"));
1024             else if(!strcmpi(buffer, "Version 0a"))
1025                version = 0;
1026             else if(!strncmp(buffer, "Version ", 8))
1027                version = atof(&buffer[8]);
1028             else if(!strcmpi(buffer, "Workspace"))
1029                strcpy(section, buffer);
1030             else if(!strcmpi(buffer, "Projects"))
1031                strcpy(section, buffer);
1032             else if(!strcmpi(buffer, "Execution Data"))
1033                strcpy(section, buffer);
1034             else if(!strcmpi(buffer, "Debugger Data"))
1035                strcpy(section, buffer);
1036             else if(!strcmpi(buffer, "Source Directories"))
1037                strcpy(subSection, buffer);
1038             else if(!strcmpi(buffer, "Breakpoints"))
1039                strcpy(subSection, buffer);
1040             else if(!strcmpi(buffer, "Watches"))
1041                strcpy(subSection, buffer);
1042             else if(!strcmpi(buffer, "Environment Variables"))
1043                strcpy(subSection, buffer);
1044             else if(!strcmpi(buffer, "Opened Files"))
1045                strcpy(section, buffer);
1046             else if(!strcmpi(buffer, ""))      // | These two lines were commented out
1047                strcpy(subSection, buffer);     // | Do they serve a purpose? They were there for copy paste when adding a new subsection
1048             else
1049             {
1050                equal = strstr(buffer, "=");
1051                if(equal)
1052                {
1053                   if(!strcmpi(section, "Workspace"))
1054                   {
1055                      equal[0] = '\0';
1056                      TrimRSpaces(buffer, buffer);
1057                      equal++;
1058                      TrimLSpaces(equal, equal);
1059                      if(!strcmpi(buffer, "Active Compiler"))
1060                      {
1061                         CompilerConfig compiler = ideSettings.GetCompilerConfig(equal);
1062                         if(!compiler)
1063                            workspace.compiler = defaultCompilerName;
1064                         else
1065                            workspace.compiler = equal;
1066                         delete compiler;
1067                      }
1068                      if(!strcmpi(buffer, "Active Bit Depth"))
1069                      {
1070                         int bitDepth = atoi(equal);
1071                         if(!(bitDepth == 32 || bitDepth == 64))
1072                            bitDepth = 0;
1073                         workspace.bitDepth = bitDepth;
1074                         ide.toolBar.activeBitDepth.SelectRow(ide.toolBar.activeBitDepth.FindRow(bitDepth));
1075                      }
1076                   }
1077                   else if(!strcmpi(section, "Execution Data"))
1078                   {
1079                      equal[0] = '\0';
1080                      TrimRSpaces(buffer, buffer);
1081                      equal++;
1082                      TrimLSpaces(equal, equal);
1083                      if(!strcmpi(buffer, "Command Line Arguments"))
1084                         workspace.commandLineArgs = equal;
1085
1086                      if(!strcmpi(buffer, "Environment Variables"))
1087                      {
1088                         workspace.environmentVars.Free();
1089                         delete workspace.environmentVars;
1090                         workspace.environmentVars = { };
1091                      }
1092
1093                   }
1094                   else if(!strcmpi(section, "Debugger Data"))
1095                   {
1096                      equal[0] = '\0';
1097                      TrimRSpaces(buffer, buffer);
1098                      equal++;
1099                      TrimLSpaces(equal, equal);
1100                      if(!strcmpi(buffer, "Debug Working Directory"))
1101                         workspace.debugDir = equal;
1102                   }
1103                   else
1104                   {
1105                      equal[0] = '\0';
1106                      TrimRSpaces(buffer, buffer);
1107                      equal++;
1108                      TrimLSpaces(equal, equal);
1109                      if(!strcmpi(buffer, "Active Configuration"))
1110                      {
1111                         Project prj;
1112                         if(workspace.projects.last)
1113                         {
1114                            prj = workspace.projects.lastIterator.data;
1115                            for(cfg : prj.configurations)
1116                            {
1117                               if(!strcmp(cfg.name, equal))
1118                               {
1119                                  prj.config = cfg;
1120                                  break;
1121                               }
1122                            }
1123                         }
1124                      }
1125                      else if(!strcmpi(buffer, "Modified Compiler Config") || !strcmpi(buffer, "Modified Linker Config"))
1126                      {
1127                         Project prj;
1128                         if(workspace.projects.last)
1129                         {
1130                            prj = workspace.projects.lastIterator.data;
1131                            for(cfg : prj.configurations)
1132                            {
1133                               if(!strcmp(cfg.name, equal))
1134                               {
1135                                  if(strstr(buffer, "Compiler"))
1136                                     cfg.compilingModified = true;
1137                                  else
1138                                     cfg.linkingModified = true;
1139                                  break;
1140                               }
1141                            }
1142                         }
1143                      }
1144                      else if(!strcmpi(buffer, "CommandLineArgs"))
1145                         workspace.commandLineArgs = equal;
1146                      else if(!strcmpi(buffer, "Breakpoint"))
1147                      {
1148                         bool enabled;
1149                         char * lineNum = strstr(equal, ",");
1150                         if(lineNum)
1151                         {
1152                            lineNum[0] = '\0';
1153                            lineNum++;
1154                            if(equal[0] == '0')
1155                               enabled = false;
1156                            else
1157                               enabled = true;
1158                            if(lineNum)
1159                            {
1160                               char * absPath = strstr(lineNum, ",");
1161                               if(absPath)
1162                               {
1163                                  absPath[0] = '\0';
1164                                  absPath++;
1165                                  if(absPath)
1166                                  {
1167                                     char * relPath = strstr(absPath, ",");
1168                                     if(relPath)
1169                                     {
1170                                        relPath[0] = '\0';
1171                                        relPath++;
1172                                        if(relPath)
1173                                        {
1174                                           bp = { type = user, enabled = enabled, level = -1 };
1175                                           workspace.breakpoints.Add(bp);
1176                                           bp.line = atoi(lineNum);
1177                                           bp.location = relPath;
1178                                        }
1179                                     }
1180                                  }
1181                               }
1182                            }
1183                         }
1184                      }
1185                      else if(!strcmpi(buffer, "Watch"))
1186                      {
1187                         wh = Watch { };
1188                         workspace.watches.Add(wh);
1189                         wh.expression = CopyString(equal);
1190                      }
1191                      else if(!strcmpi(buffer, "SourceDir"))
1192                      {
1193                         workspace.sourceDirs.Add(CopyString(equal));
1194                      }
1195                      else if(!strcmpi(buffer, "DebugDir"))
1196                      {
1197                         workspace.debugDir = equal;
1198                      }
1199                   }
1200                }
1201             }
1202          }
1203       }
1204
1205       delete file;
1206
1207       if(workspace)
1208       {
1209          if(!workspace.projects.first)
1210          {
1211             Project project;
1212             if(fromProjectFile)
1213                project = LoadProject(fromProjectFile /*projectFilePath*/, null);
1214             else
1215             {
1216                char projectFilePath[MAX_LOCATION];
1217                strcpy(projectFilePath, workspace.workspaceFile);
1218                ChangeExtension(projectFilePath, ProjectExtension, projectFilePath);
1219                project = LoadProject(projectFilePath, null);
1220             }
1221             if(project)
1222             {
1223                project.StartMonitoring();
1224                workspace.projects.Add(project);
1225                workspace.name = CopyString(project.name);
1226             }
1227             else
1228             {
1229                MessageBox { type = ok, master = ide, contents = $"Workspace load file failed", text = $"Workspace Load File Error" }.Modal();
1230                delete workspace;
1231                return null;
1232             }
1233          }
1234
1235          if(openedFilesNotFound.first)
1236          {
1237             int c = 0;
1238             char s[2] = "";
1239             String files = new char[MAX_LOCATION * 16];
1240             char title[512];
1241             String msg = new char[MAX_LOCATION * 16 + 2048];
1242             NamedItem item;
1243             strcpy(files,"\n");
1244
1245             item = openedFilesNotFound.first;
1246             if(item.next)
1247                strcpy(s, "s");
1248
1249             for(item = openedFilesNotFound.first; item; item = item.next)
1250             {
1251                c++;
1252                if(c == 16)
1253                {
1254                   strcat(files, "\n...");
1255                   break;
1256                }
1257                strcat(files, "\n");
1258                strcat(files, item.name);
1259             }
1260
1261             sprintf(title, $"File%s not found", s);
1262             sprintf(msg, $"The following file%s could not be re-opened.%s", s, files);
1263
1264             MessageBox { type = ok, master = ide, contents = msg, text = title }.Modal();
1265
1266             delete files;
1267             delete msg;
1268          }
1269          openedFilesNotFound.Free(OldLink::Free);
1270       }
1271       else
1272          openedFilesNotFound.Free(OldLink::Free);
1273    }
1274    else if(fromProjectFile)
1275    {
1276       //MessageBox { type = Ok, master = ide, contents = "Worspace load file failed", text = "Worspace Load File Error" }.Modal();
1277       //char projectFile[MAX_LOCATION];
1278       Project newProject;
1279
1280       //strcpy(projectFile, filePath);
1281       //ChangeExtension(projectFile, ProjectExtension, projectFile);
1282       newProject = LoadProject(fromProjectFile /*projectFile*/, null);
1283
1284       if(newProject)
1285       {
1286          newProject.StartMonitoring();
1287          workspace = Workspace { workspaceFile = filePath };
1288
1289          workspace.projects.Add(newProject);
1290          workspace.Save();
1291       }
1292    }
1293
1294    if(workspace)
1295    {
1296       ide.ChangeFileDialogsDirectory(workspace.workspaceDir, false);
1297
1298       if(!workspace.compiler || !workspace.compiler[0])
1299          workspace.compiler = defaultCompilerName;
1300    }
1301    return workspace;
1302 }