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