c577a00f02aa4d0f13d20c5ee84a988a2b602d90
[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       ProjectNode node = null;
336
337       GetLastDirectory(relative, name);
338       for(p : projects)
339       {
340          if(node = p.topNode.Find(name, false))
341          {
342             prj = p;
343             break;
344          }
345       }
346       if(prj)
347       {
348          node.GetFullFilePath(absolute);
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       ide.UpdateToolBarActiveConfigs(false);
456       Save();
457
458       delete project;
459    }
460
461    bool FindPath(ProjectNode node, char * path)
462    {
463       if(node.type == file)
464       {
465          // TODO: Should this code be moved into a ProjectNode::absolutePath property? Taken from NodeProperties.ec
466          char filePath[MAX_LOCATION];
467          GetSlashPathBuffer(filePath, node.project.topNode.path);
468          PathCatSlash(filePath, node.path);
469          PathCatSlash(filePath, node.name);
470
471          if(!fstrcmp(filePath, path))
472             return true;
473       }
474       if(node.files)
475       {
476          for(n : node.files)
477          {
478             if(FindPath(n, path))
479                return true;
480          }
481       }
482       return false;
483    }
484
485    void ChangeBreakpoint(DataRow row, char * location)
486    {
487       Breakpoint bp = (Breakpoint)row.tag;
488       if(bp)
489       {
490          char * currentLoc = bp.LocationToString();
491          if(strcmp(location, currentLoc))
492          {
493             // todo, parse location
494             //  if good, make changes to breakpoint, according to execution state delete and place breakpoint
495             ide.breakpointsView.UpdateBreakpoint(row); // this is the else
496             //Save();
497          }
498          delete currentLoc;
499       }
500       else if(location)
501       {
502          // adding a breakpoint by typing it in the breakpoints view
503          // todo, parse location
504          //  if good, make add breakpoint, make sure possibly previously entered ignore and level are reflected in breakpoint
505          //  else
506          //     ...
507          //bp = Breakpoint { };
508          //row.tag = (int64)bp;
509          //breakpoints.Add(bp);
510          //bp.row = row;
511          Save();
512       }
513    }
514
515    void ChangeBreakpointIgnore(DataRow row, int ignore)
516    {
517       Breakpoint bp = (Breakpoint)row.tag;
518       if(bp)
519       {
520          bp.ignore = ignore;
521          Save();
522       }
523    }
524
525    void ChangeBreakpointLevel(DataRow row, int level)
526    {
527       Breakpoint bp = (Breakpoint)row.tag;
528       if(bp)
529       {
530          bp.level = level;
531          Save();
532       }
533    }
534
535    void ChangeBreakpointCondition(DataRow row, char * condition)
536    {
537       Breakpoint bp = (Breakpoint)row.tag;
538       if(bp && !(!bp.condition && !(condition && condition[0])))
539       {
540          if(!bp.condition)
541          {
542             bp.condition = Watch { };
543             bp.condition.expression = CopyString(condition);
544             Save();
545          }
546          else if(!(condition && condition[0]))
547          {
548             bp.condition.Free();
549             bp.condition = null;
550             Save();
551          }
552          else if(strcmp(condition, bp.condition.expression))
553          {
554             bp.condition.Free();
555             bp.condition = Watch { };
556             bp.condition.expression = CopyString(condition);
557             Save();
558          }
559       }
560    }
561
562    void RemoveBreakpoint(Breakpoint bp)
563    {
564       bpCount--;
565       ide.breakpointsView.RemoveBreakpoint(bp);
566       ide.debugger.UpdateRemovedBreakpoint(bp);
567       {
568          Iterator<Breakpoint> it { breakpoints };
569          if(it.Find(bp))
570             breakpoints.Remove(it.pointer);
571       }
572       {
573          Window document;
574          for(document = ide.firstChild; document; document = document.next)
575          {
576             char * fileName = document.fileName;
577             if(document.isDocument && fileName && document.created)
578             {
579                char winFilePath[MAX_LOCATION];
580                char * slashPath = GetSlashPathBuffer(winFilePath, fileName);
581
582                if(!fstrcmp(slashPath, bp.absoluteFilePath))
583                {
584                   CodeEditor codeEditor = (CodeEditor)document;
585                   int boxH = codeEditor.editBox.clientSize.h;
586                   Box box { 0, 0, 19, boxH - 1 };
587                   document.Update(box);
588                   break;
589                }
590             }
591          }
592       }
593       Save();
594       delete bp;
595    }
596
597    void DropInvalidBreakpoints()
598    {
599       Link bpLink, next;
600       for(bpLink = breakpoints.first; bpLink; bpLink = next)
601       {
602          Breakpoint bp = (Breakpoint)bpLink.data;
603          next = bpLink.next;
604
605          if(bp.type == user)
606          {
607             Project project = null;
608             for(p : projects)
609             {
610                if(FindPath(p.topNode, bp.absoluteFilePath))
611                {
612                   project = p;
613                   break;
614                }
615                // Handle symbol loader modules:
616                {
617                   char moduleName[MAX_FILENAME];
618                   char * sl;
619                   GetLastDirectory(bp.absoluteFilePath, moduleName);
620                   // Tweak for automatically resolving symbol loader modules
621                   sl = strstr(moduleName, ".main.ec");
622                   if(sl && (*sl = 0, !strcmpi(moduleName, p.name)))
623                   {
624                      project = p;
625                      break;
626                   }
627                }
628             }
629             if(!project)
630             {
631                bool found = false;
632                for(dir : sourceDirs)
633                {
634                   if(IsPathInsideOf(bp.absoluteFilePath, dir))
635                   {
636                      found = true;
637                      break;
638                   }
639                }
640                if(!found)
641                {
642                   ide.breakpointsView.RemoveBreakpoint(bp);
643                   RemoveBreakpoint(bp);
644                }
645             }
646          }
647       }
648       ide.breakpointsView.Update(null);
649    }
650
651    void Free()
652    {
653       delete workspaceFile;
654       delete workspaceDir;
655       delete commandLineArgs;
656       delete debugDir;
657       
658       //project = null;
659
660       projects.Free();
661       breakpoints.Free();
662       watches.Free();
663    }
664
665    Workspace()
666    {
667       ide.outputView.buildBox.Clear();
668       ide.outputView.debugBox.Clear();
669       ide.callStackView.Clear();
670       ide.watchesView.Clear();
671       ide.threadsView.Clear();
672       ide.breakpointsView.Clear();
673
674       property::debugDir = "";
675       
676       SetSourceDirs(sourceDirs);
677    }
678
679    ~Workspace()
680    {
681       Save();
682       timer.Stop();
683
684       sourceDirs.Free();
685       environmentVars.Free();
686       SetSourceDirs(null);
687       Free();
688       openedFiles.Free();
689       delete compiler;
690    }
691
692 }
693
694 Workspace LoadWorkspace(char * filePath, char * fromProjectFile)
695 {
696    File file;
697    Workspace workspace = null;
698
699    file = FileOpen(filePath, read);
700    if(file)
701    {
702       OldList openedFilesNotFound { };
703       double version = 0;
704       char section[128];
705       char subSection[128];
706
707       workspace = Workspace { compiler = ideSettings.defaultCompiler, workspaceFile = filePath };
708
709       file.Seek(0, start);
710       while(!file.Eof())
711       {
712          char buffer[65536];
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.01)
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.02)
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;
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                         workspace.environmentVars.Free();
994                         delete workspace.environmentVars;
995                         workspace.environmentVars = { };
996                      }
997
998                   }
999                   else if(!strcmpi(section, "Debugger Data"))
1000                   {
1001                      equal[0] = '\0';
1002                      TrimRSpaces(buffer, buffer);
1003                      equal++;
1004                      TrimLSpaces(equal, equal);
1005                      if(!strcmpi(buffer, "Debug Working Directory"))
1006                         workspace.debugDir = equal;
1007                   }
1008                   else
1009                   {
1010                      equal[0] = '\0';
1011                      TrimRSpaces(buffer, buffer);
1012                      equal++;
1013                      TrimLSpaces(equal, equal);
1014                      if(!strcmpi(buffer, "Active Configuration"))
1015                      {
1016                         Project prj;
1017                         if(workspace.projects.last)
1018                         {
1019                            prj = workspace.projects.lastIterator.data;
1020                            for(cfg : prj.configurations)
1021                            {
1022                               if(!strcmp(cfg.name, equal))
1023                               {
1024                                  prj.config = cfg;
1025                                  break;
1026                               }
1027                            }
1028                         }
1029                      }
1030                      else if(!strcmpi(buffer, "Modified Compiler Config") || !strcmpi(buffer, "Modified Linker Config"))
1031                      {
1032                         Project prj;
1033                         if(workspace.projects.last)
1034                         {
1035                            prj = workspace.projects.lastIterator.data;
1036                            for(cfg : prj.configurations)
1037                            {
1038                               if(!strcmp(cfg.name, equal))
1039                               {
1040                                  if(strstr(buffer, "Compiler"))
1041                                     cfg.compilingModified = true;
1042                                  else
1043                                     cfg.linkingModified = true;
1044                                  break;
1045                               }
1046                            }
1047                         }
1048                      }
1049                      else if(!strcmpi(buffer, "CommandLineArgs"))
1050                         workspace.commandLineArgs = equal;
1051                      else if(!strcmpi(buffer, "Breakpoint"))
1052                      {
1053                         bool enabled;
1054                         char * lineNum = strstr(equal, ",");
1055                         if(lineNum)
1056                         {
1057                            lineNum[0] = '\0';
1058                            lineNum++;
1059                            if(equal[0] == '0')
1060                               enabled = false;
1061                            else
1062                               enabled = true;
1063                            if(lineNum)
1064                            {
1065                               char * absPath = strstr(lineNum, ",");
1066                               if(absPath)
1067                               {
1068                                  absPath[0] = '\0';
1069                                  absPath++;
1070                                  if(absPath)
1071                                  {
1072                                     char * relPath = strstr(absPath, ",");
1073                                     if(relPath)
1074                                     {
1075                                        relPath[0] = '\0';
1076                                        relPath++;
1077                                        if(relPath)
1078                                        {
1079                                           bp = { type = user, enabled = enabled, level = -1 };
1080                                           workspace.breakpoints.Add(bp);
1081                                           bp.line = atoi(lineNum);
1082                                           bp.relativeFilePath = CopyString(relPath);
1083                                           bp.absoluteFilePath = workspace.GetAbsolutePathFromRelative(relPath);
1084                                           if(!bp.absoluteFilePath)
1085                                              bp.absoluteFilePath = CopyString("");
1086                                        }
1087                                     }
1088                                  }
1089                               }
1090                            }
1091                         }
1092                      }
1093                      else if(!strcmpi(buffer, "Watch"))
1094                      {
1095                         wh = Watch { };
1096                         workspace.watches.Add(wh);
1097                         wh.expression = CopyString(equal);
1098                      }
1099                      else if(!strcmpi(buffer, "SourceDir"))
1100                      {
1101                         workspace.sourceDirs.Add(CopyString(equal));
1102                      }
1103                      else if(!strcmpi(buffer, "DebugDir"))
1104                      {
1105                         workspace.debugDir = equal;
1106                      }
1107                   }
1108                }
1109             }
1110          }
1111       }
1112
1113       delete file;
1114
1115       if(workspace)
1116       {
1117          if(!workspace.projects.first)
1118          {
1119             Project project;
1120             if(fromProjectFile)
1121                project = LoadProject(fromProjectFile /*projectFilePath*/, null);
1122             else
1123             {
1124                char projectFilePath[MAX_LOCATION];
1125                strcpy(projectFilePath, workspace.workspaceFile);
1126                ChangeExtension(projectFilePath, ProjectExtension, projectFilePath);
1127                project = LoadProject(projectFilePath, null);
1128             }
1129             if(project)
1130             {
1131                project.StartMonitoring();
1132                workspace.projects.Add(project);
1133                workspace.name = CopyString(project.name);
1134             }
1135             else
1136             {
1137                MessageBox { type = ok, master = ide, contents = $"Workspace load file failed", text = $"Workspace Load File Error" }.Modal();
1138                delete workspace;
1139                return null;
1140             }
1141          }
1142
1143          if(openedFilesNotFound.first)
1144          {
1145             int c = 0;
1146             char s[2] = "";
1147             String files = new char[MAX_LOCATION * 16];
1148             char title[512];
1149             String msg = new char[MAX_LOCATION * 16 + 2048];
1150             NamedItem item;
1151             strcpy(files,"\n");
1152
1153             item = openedFilesNotFound.first;
1154             if(item.next)
1155                strcpy(s, "s");
1156             
1157             for(item = openedFilesNotFound.first; item; item = item.next)
1158             {
1159                c++;
1160                if(c == 16)
1161                {
1162                   strcat(files, "\n...");
1163                   break;
1164                }
1165                strcat(files, "\n");
1166                strcat(files, item.name);
1167             }
1168
1169             sprintf(title, $"File%s not found", s);
1170             sprintf(msg, $"The following file%s could not be re-opened.%s", s, files);
1171             
1172             MessageBox { type = ok, master = ide, contents = msg, text = title }.Modal();
1173
1174             delete files;
1175             delete msg;
1176          }
1177          openedFilesNotFound.Free(OldLink::Free);
1178       }
1179       else
1180          openedFilesNotFound.Free(OldLink::Free);
1181    }
1182    else if(fromProjectFile)
1183    {
1184       //MessageBox { type = Ok, master = ide, contents = "Worspace load file failed", text = "Worspace Load File Error" }.Modal();
1185       
1186       char projectFile[MAX_LOCATION];
1187       Project newProject;
1188       
1189       //strcpy(projectFile, filePath);
1190       //ChangeExtension(projectFile, ProjectExtension, projectFile);
1191       newProject = LoadProject(fromProjectFile /*projectFile*/, null);
1192
1193       if(newProject)
1194       {
1195          newProject.StartMonitoring();
1196          workspace = Workspace { workspaceFile = filePath };
1197
1198          workspace.projects.Add(newProject);
1199          workspace.Save();
1200       }
1201    }
1202    
1203    if(workspace)
1204    {
1205       ide.ChangeFileDialogsDirectory(workspace.workspaceDir, false);
1206
1207       if(!workspace.compiler || !workspace.compiler[0])
1208          workspace.compiler = defaultCompilerName;
1209    }
1210
1211    return workspace;
1212 }