ide:ProjectView:ProjectNode:NodeProperties: added 'new file' feature. made that and...
[sdk] / ide / src / project / ProjectNode.ec
1 #ifndef MAKEFILE_GENERATOR
2 import "ide"
3 #else
4 #ifdef ECERE_STATIC
5 import static "ecere"
6 #else
7 import "ecere"
8 #endif
9
10 import "Project"
11
12 static define app = ((GuiApplication)__thisModule);
13 #endif
14
15 bool eString_PathInsideOfMore(char * path, char * of, char * pathRest)
16 {
17    if(!path[0] || !of[0])
18       return false;  // What to do here? Ever used?
19    else
20    {
21       char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
22       char pathPart[MAX_FILENAME]; //, pathRest[MAX_LOCATION];
23       strcpy(ofRest, of);
24       strcpy(pathRest, path);
25       for(; ofRest[0] && pathRest[0];)
26       {
27          SplitDirectory(ofRest, ofPart, ofRest);      
28          SplitDirectory(pathRest, pathPart, pathRest);
29          if(fstrcmp(pathPart, ofPart))
30             return false;
31       }
32       if(!ofRest[0] && !pathRest[0])
33          return false;
34       else if(!pathRest[0])           // not inside of, it's the other way around
35          return false;
36       return true;
37    }
38 }
39
40 enum NodeTypes { project, file, folder, resources, folderOpen };
41 enum NodeIcons
42 {
43    genFile, ewsFile, epjFile, folder, openFolder, ecFile, ehFile,
44    cFile, hFile, cppFile, hppFile, textFile, webFile, pictureFile, soundFile,
45    archiveFile, packageFile, opticalMediaImageFile, mFile;
46
47    NodeIcons ::SelectFileIcon(char * filePath)
48    {
49       NodeIcons icon;
50       if(filePath && filePath[0])
51       {
52          char extension[MAX_EXTENSION];
53          GetExtension(filePath, extension);
54          if(strlen(extension))
55          {
56             if(!strcmpi(extension, WorkspaceExtension))
57                icon = ewsFile;
58             else if(!strcmpi(extension, ProjectExtension))
59                icon = epjFile;
60             else if(!strcmpi(extension, "ec"))
61                icon = ecFile;
62             else if(!strcmpi(extension, "eh"))
63                icon = ehFile;
64             else if(!strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
65                   !strcmpi(extension, "cxx"))
66                icon = cppFile;
67             else if(!strcmpi(extension, "hpp") || !strcmpi(extension, "hh") ||
68                   !strcmpi(extension, "hxx"))
69                icon = hppFile;
70             else if(!strcmpi(extension, "c"))
71                icon = cFile;
72             else if(!strcmpi(extension, "h"))
73                icon = hFile;
74             else if(!strcmpi(extension, "m"))
75                icon = mFile;
76             else if(!strcmpi(extension, "txt") || !strcmpi(extension, "text") ||
77                   !strcmpi(extension, "nfo") || !strcmpi(extension, "info"))
78                icon = textFile;
79             else if(!strcmpi(extension, "htm") || !strcmpi(extension, "html") ||
80                   !strcmpi(extension, "css") || !strcmpi(extension, "php") ||
81                   !strcmpi(extension, "js"))
82                icon = webFile;
83             else if(!strcmpi(extension, "bmp") || !strcmpi(extension, "pcx") ||
84                   !strcmpi(extension, "jpg") || !strcmpi(extension, "jpeg") ||
85                   !strcmpi(extension, "gif") || !strcmpi(extension, "png") ||
86                   !strcmpi(extension, "ico"))
87                icon = pictureFile;
88             else if(!strcmpi(extension, "wav") || !strcmpi(extension, "mp3") ||
89                   !strcmpi(extension, "ogg") || !strcmpi(extension, "snd"))
90                icon = soundFile;
91             else if(!strcmpi(extension, "ear") || !strcmpi(extension, "7z") ||
92                   !strcmpi(extension, "rar") || !strcmpi(extension, "zip") ||
93                   !strcmpi(extension, "gz") || !strcmpi(extension, "bz2") ||
94                   !strcmpi(extension, "tar") || !strcmpi(extension, "arj") ||
95                   !strcmpi(extension, "lza") || !strcmpi(extension, "lzh") ||
96                   !strcmpi(extension, "cpio") || !strcmpi(extension, "z"))
97                icon = archiveFile;
98             else if(!strcmpi(extension, "cab") || !strcmpi(extension, "deb") ||
99                   !strcmpi(extension, "rpm"))
100                icon = packageFile;
101             else if(!strcmpi(extension, "iso") || !strcmpi(extension, "mds") ||
102                   !strcmpi(extension, "cue") || !strcmpi(extension, "bin") ||
103                   !strcmpi(extension, "ccd") || !strcmpi(extension, "bwt") ||
104                   !strcmpi(extension, "cdi") || !strcmpi(extension, "nrg"))
105                icon = opticalMediaImageFile;
106             else
107                icon = genFile;
108          }
109          else
110             icon = genFile;
111       }
112       else
113          icon = genFile; // tocheck: error icon?
114       return icon;
115    }
116
117    NodeIcons ::SelectNodeIcon(NodeTypes type)
118    {
119       switch(type)
120       {
121          case project:
122             return epjFile;
123          case file:
124             return genFile;
125          case folder:
126             return folder;
127          case resources:
128             return archiveFile;
129          case folderOpen:
130             return openFolder;
131       }
132       return genFile;
133    }
134 };
135
136 #define SELECTION_COLOR Color { 10, 36, 106 }
137
138 // On Windows & UNIX
139 #define SEPS    "/"
140 #define SEP     '/'
141
142 // this is so not working, why!
143 //struct TwoStrings
144 // return result was not even executed (did not step on while debugging)
145 class TwoStrings : struct
146 {
147    char * a;
148    char * b;
149
150    property bool
151    {
152       get
153       {
154          return a && a[0];
155       }
156    }
157
158    ~TwoStrings()
159    {
160       delete a;
161       delete b;
162    }
163 }
164
165 class ProjectNode : ListItem
166 {
167 public:
168    property String
169    {
170       set { return { fileName = value }; }
171       // TOCHECK: Is this isset necessary at all?
172       isset { return nodeType == file && !options && !configurations && !platforms && !files; }
173    };
174    property String folder
175    {
176       set
177       {
178          nodeType = folder;
179          if(strchr(value, '/'))
180          {
181             char p[MAX_LOCATION];
182             char n[MAX_FILENAME];
183             GetLastDirectory(value, n);
184             StripLastDirectory(value, p);
185             name = CopyString(n);
186             path = CopyString(p);
187          }
188          else
189             name = CopyString(value);
190       }
191       get
192       {
193          // TOCHECK: Non Reentrant
194          static char insidePath[MAX_LOCATION];
195
196          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
197          PathCatSlash(insidePath, name);
198
199          if(!fstrcmp(path, insidePath))
200             return name;
201          else
202          {
203             strcpy(insidePath, path);
204             if(!insidePath[0]) strcpy(insidePath, ".");
205             PathCatSlash(insidePath, name);
206             return insidePath;
207          }
208       }
209       isset { return nodeType == folder; }
210    };
211    property String fileName
212    {
213       set
214       {
215          nodeType = file;
216          if(strchr(value, '/'))
217          {
218             char p[MAX_LOCATION];
219             char n[MAX_FILENAME];
220             GetLastDirectory(value, n);
221             StripLastDirectory(value, p);
222             name = CopyValidateMakefilePath(n);
223             path = CopyValidateMakefilePath(p);
224          }
225          else
226             name = CopyValidateMakefilePath(value);
227       }
228       get
229       {
230          // TOCHECK: Non Reentrant
231          static char insidePath[MAX_LOCATION];
232
233          strcpy(insidePath, (parent.type == project) ? "" : parent.path);
234          if(!fstrcmp(path, insidePath))
235             return name;
236          else
237          {
238             strcpy(insidePath, path);
239             if(!insidePath[0]) strcpy(insidePath, ".");
240             PathCatSlash(insidePath, name);
241             return insidePath;
242          }
243       }
244       isset { return nodeType == file && (options || configurations || platforms); }
245    };
246
247    LinkList<ProjectNode> files;
248    ProjectOptions options;
249    Array<PlatformOptions> platforms;
250    List<ProjectConfig> configurations;
251
252    property ProjectConfig config
253    {
254       get
255       {
256          Project prj;
257          ProjectConfig result = null;
258          /*if(configurations)
259             printf("test\n");
260          if(prj)
261             printf("test\n");*/
262          if(configurations && (prj = property::project) && prj.config)
263          {
264             const char * projectConfigName = prj.config.name;
265             for(config : configurations)
266             {
267                if(!strcmpi(config.name, projectConfigName))
268                {
269                   result = config;
270                   break;
271                }
272             }
273          }
274          return result;
275       }
276    }
277
278    ProjectConfig GetMatchingNodeConfig(ProjectConfig config)
279    {
280       ProjectConfig nodeConfig = null;
281       if(configurations)
282       {
283          const char * configName = config.name;
284          for(cfg : configurations)
285          {
286             if(!strcmpi(cfg.name, configName))
287             {
288                nodeConfig = cfg;
289                break;
290             }
291          }
292       }
293       return nodeConfig;
294    }
295
296    property bool ecflags
297    {
298       get
299       {
300          ProjectConfig config = this.config;
301          ProjectOptions options = this.options;
302          SetBool memoryGuard = localMemoryGuard;
303          String defaultNameSpace = localDefaultNameSpace;
304          SetBool strictNameSpaces = localStrictNameSpaces;
305          SetBool noLineNumbers = localNoLineNumbers;
306
307          if(memoryGuard || defaultNameSpace || strictNameSpaces || noLineNumbers)
308             return true;
309          else if(parent.parent)
310             return parent.ecflags;
311          else
312             return false;
313       }
314    }
315    property bool memoryGuard
316    {
317       get
318       {
319          ProjectConfig config = this.config;
320          ProjectOptions options = this.options;
321          SetBool memoryGuard = localMemoryGuard;
322          if(!memoryGuard)
323          {
324             if(parent)
325                return parent.memoryGuard;
326          }
327          return memoryGuard == true;
328       }
329    }
330    property String defaultNameSpace
331    {
332       get
333       {
334          ProjectConfig config = this.config;
335          ProjectOptions options = this.options;
336          String defaultNameSpace = localDefaultNameSpace;
337          if(!defaultNameSpace)
338          {
339             if(parent)
340                return parent.defaultNameSpace;
341          }
342          return defaultNameSpace;
343       }
344    }
345    property bool strictNameSpaces
346    {
347       get
348       {
349          ProjectConfig config = this.config;
350          ProjectOptions options = this.options;
351          SetBool strictNameSpaces = localStrictNameSpaces;
352          if(!strictNameSpaces)
353          {
354             if(parent)
355                return parent.strictNameSpaces;
356          }
357          return strictNameSpaces == true;
358       }
359    }
360    property bool noLineNumbers
361    {
362       get
363       {
364          ProjectConfig config = this.config;
365          ProjectOptions options = this.options;
366          SetBool noLineNumbers = localNoLineNumbers;
367          if(!noLineNumbers)
368          {
369             if(parent)
370                return parent.noLineNumbers;
371          }
372          return noLineNumbers == true;
373       }
374    }
375
376    property ProjectNode root { get { ProjectNode n; for(n = this; n.parent; n = n.parent); return n; } }
377
378    char * GetFullFilePath(char * buffer)
379    {
380       if(buffer)
381       {
382          strcpy(buffer, root.path);
383          PathCatSlash(buffer, path);
384          PathCatSlash(buffer, name);
385       }
386       return buffer;
387    }
388
389    char * GetFileSysMatchingPath(char * buffer)
390    {
391       if(buffer)
392       {
393          ProjectNode n, root = this.root;
394          for(n = this; n && (n.type == folder || n.type == project); n = n.parent)
395          {
396             strcpy(buffer, root.path);
397             if(n != root)
398                PathCatSlash(buffer, n.path);
399             if(FileExists(buffer).isDirectory)
400                break;
401          }
402          if(!(n && (n.type == folder || n.type == project)))
403             buffer[0] = '\0';
404       }
405       return buffer;
406    }
407
408    void CollectPerFileAndDirOptions(ProjectConfig projectConfig, Array<String> perFilePreprocessorDefs, Array<DirPath> perFileIncludeDirs)
409    {
410       ProjectNode node = null;
411       ProjectConfig config = GetMatchingNodeConfig(projectConfig);
412       List<ProjectNode> nodeStack { };
413       
414       for(node = this; node && node.parent; node = node.parent)
415          nodeStack.Add(node);
416
417       // Should we reverse this stack to give priority to the per-file includes? Does the following technique already reverse? 
418
419       // TODO: Check how to fix duplication of following options when configuration is made per-config-per-file
420       while((node = nodeStack.lastIterator.data))
421       {
422          if(node.options && node.options.preprocessorDefinitions)
423          {
424             for(def : node.options.preprocessorDefinitions)
425                perFilePreprocessorDefs.Add(CopyString(def));
426          }
427          if(config && config.options && config.options.preprocessorDefinitions)
428          {
429             for(def : config.options.preprocessorDefinitions)
430                perFilePreprocessorDefs.Add(CopyString(def));
431          }
432          if(node.options && node.options.includeDirs)
433          {
434             for(dir : node.options.includeDirs)
435                perFileIncludeDirs.Add(CopySystemPath(dir));
436          }
437          if(config && config.options && config.options.includeDirs)
438          {
439             for(dir : config.options.includeDirs)
440                perFileIncludeDirs.Add(CopySystemPath(dir));
441          }
442          nodeStack.lastIterator.Remove();
443       }
444       delete nodeStack;
445    }
446
447 private:
448    ProjectNodeType nodeType;
449    ProjectNode parent;
450    char * name;
451    char * info;
452
453    // This holds the absolute path of the .epj for the project topnode (without the filename)
454    // It holds a relative path to the topNode (project) for other nodes (folders and files)
455    // For folders, it includes the folder it refers to. If there is a name difference between the
456    // file system folder and the grouping folder of the project view, it maps to that folder.
457    char * path;
458    
459    NodeTypes type;
460    NodeIcons icon;
461    int indent;
462    DataRow row;
463
464    bool modified;
465    
466    // This is only set for Top Nodes
467    Project project;
468
469    property Project project
470    {
471       get
472       {
473          ProjectNode n = this;
474          while(n && n.type != project) n = n.parent;
475          return n ? (*&n.project) : null;
476       }
477    }   
478
479    void RenameConfig(char * oldName, char * newName)
480    {
481       if(files)
482       {
483          for(f : files; (f.configurations || f.files)) { f.RenameConfig(oldName, newName); }
484       }
485       if(configurations)
486       {
487          for(c : configurations; !strcmp(c.name, oldName))
488          {
489             delete c.name;
490             c.name = CopyString(newName);
491          }
492       }
493    }
494
495    void DeleteConfig(ProjectConfig configToDelete)
496    {
497       if(files)
498       {
499          for(f : files; (f.configurations || f.files)) { f.DeleteConfig(configToDelete); }
500       }
501       if(configurations)
502       {
503          Iterator<ProjectConfig> c { configurations };
504          while(c.Next())
505          {
506             ProjectConfig config = c.data;
507             if(!strcmp(configToDelete.name, config.name))
508             {               
509                c.Remove();
510                delete config;
511                break;
512             }
513          }
514          if(!configurations.count)
515          {
516             delete configurations;
517          }
518       }
519    }
520
521    ProjectNode Backup()
522    {
523       ProjectNode backupNode { };
524
525       if(files)
526       {
527          backupNode.files = { };
528          for(f : files) backupNode.files.Add(f.Backup());
529       }
530       if(options)
531          backupNode.options = options.Copy();
532
533       if(platforms)
534       {
535          backupNode.platforms = { };
536          for(p : platforms)
537             backupNode.platforms.Add(p.Copy());
538       }
539
540       if(configurations)
541       {
542          backupNode.configurations = { };
543          for(c : configurations)
544             backupNode.configurations.Add(c.Copy());
545       }
546       return backupNode;
547    }
548
549    void Revert(ProjectNode backupNode)
550    {
551       if(files)
552       {
553          Iterator<ProjectNode> it { backupNode.files };
554          for(f : files)
555          {
556             it.Next();
557             f.Revert(it.data);
558          }
559       }
560
561       delete options;
562       if(platforms)
563       {
564          platforms.Free();
565          delete platforms;
566       }
567       if(configurations)
568       {
569          configurations.Free();
570          delete configurations;
571       }
572
573       options = backupNode.options ? backupNode.options.Copy() : null;
574       if(backupNode.platforms)
575       {
576          platforms = { };
577          for(p : backupNode.platforms)
578             platforms.Add(p.Copy());
579       }
580       if(backupNode.configurations)
581       {
582          configurations = { };
583          for(c : backupNode.configurations)
584             configurations.Add(c.Copy());
585       }
586    }
587
588    void FixupNode(char * parentPath)
589    {
590       if(!parent)
591       {
592          type = project;
593       }
594       else if(nodeType == file)
595       {
596          type = file;
597          if(!path)
598          {
599             path = CopyString((parent.type == folder || parent.type == resources) ? parentPath : "");
600          }
601       }
602       else if(nodeType == folder)
603       {
604          type = folder;
605
606          if(!path)
607          {
608             char temp[MAX_LOCATION];
609             strcpy(temp, (parent.type == folder || parent.type == resources) ? parentPath : "");
610             PathCatSlash(temp, name);
611             path = CopyString(temp);
612          }
613       }
614
615       indent = parent ? parent.indent + 1 : 0;
616
617       if(type == file)
618          icon = NodeIcons::SelectFileIcon(name);
619       else
620          icon = NodeIcons::SelectNodeIcon(type);
621
622       if(files)
623       {
624          for(f : files)
625          {
626             f.parent = this;
627
628             if(type == project)
629                parentPath[0] = '\0';
630             else if(type == resources || type == folder)
631                strcpy(parentPath, path);
632
633             f.FixupNode(parentPath);
634          }
635       }
636    }
637
638    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
639    {
640       if(!needClass)
641       {
642          // TOCHECK: Called from JSON writer
643          if(nodeType == file && !options && !configurations && !platforms && name)
644          {
645             strcpy(tempString, "\"");
646             strcat(tempString, property::fileName);
647             strcat(tempString, "\"");
648             return tempString;
649          }
650          else
651             return null;
652       }
653       else
654          // TOCHECK: Called from ProjectView rendering
655          return name ? name : "";
656    }
657
658    ~ProjectNode()
659    {
660       if(files)
661       {
662          files.Free();
663          delete files;
664       }
665       delete options;
666
667       if(platforms)
668       {
669          platforms.Free();
670          delete platforms;
671       };
672       if(configurations)
673       {
674          configurations.Free();
675          delete configurations;
676       }
677
678       /////////////////////////////
679       delete path;
680       delete name;
681       delete info;
682    }
683
684    property bool isInResources
685    {
686       get
687       {
688          ProjectNode node;
689          for(node = this; node; node = node.parent)
690          {
691             if(node.type == resources)
692                return true;
693          }
694          return false;
695       }
696    }
697
698    property TwoStrings platformSpecificExclusionFu
699    {
700       get
701       {
702          // TODO: Proper folder exclusion fix: collect platform names across parents and then build exclusion expression
703          //       Find solution to excluding a drivers folder for all platforms and overriting that exclusion on children files/folders
704          TwoStrings result { };
705          int nestingCount = 0, c, len;
706          Platform platform;
707          ProjectConfig config = property::config;
708          Map<String, bool> platformNames { };
709          if(platforms)
710          {
711             for(p : platforms)
712                if(p.options.excludeFromBuild == true)
713                   platformNames[p.name] = true;
714          }
715          if(config && config.platforms)
716          {
717             for(p : config.platforms)
718                if(p.options.excludeFromBuild == true)
719                   platformNames[p.name] = true;
720          }
721          if(platformNames.count)
722          {
723             len = 0;
724             result.a = new char[1];
725             result.a[0] = '\0';
726             for(s : platformNames)
727             {
728                for(platform = (Platform)1; platform < Platform::enumSize; platform++)
729                   if(!strcmpi(&s, platform))
730                      break;
731                if(platform < Platform::enumSize)
732                {
733                   const char * opening = "$(if $(";
734                   const char * close = "),,";
735                   char * t = PlatformToMakefileVariable(platform);
736                   nestingCount++;
737                   len += strlen(t) + strlen(opening) + strlen(close);
738                   result.a = renew result.a char[len + 1];
739                   strcat(result.a, opening);
740                   strcat(result.a, t);
741                   strcat(result.a, close);
742                }
743             }
744             result.b = new char[nestingCount + 1];
745             for(c = 0; c < nestingCount; c++)
746                result.b[c] = ')';
747             result.b[nestingCount] = '\0';
748          }
749          else
750          {
751             result.a = CopyString("");
752             result.b = CopyString("");
753          }
754          delete platformNames;
755          if(parent)
756          {
757             TwoStrings parentResult = parent.platformSpecificExclusionFu;
758             if(parentResult.a && parentResult.a[0])
759             {
760                len = strlen(result.a) + strlen(parentResult.a);
761                result.a = renew result.a char[len + 1];
762                strcat(result.a, parentResult.a);
763                len = strlen(result.b) + strlen(parentResult.b);
764                result.b = renew result.b char[len + 1];
765                strcat(result.b, parentResult.b);
766             }
767             delete parentResult;
768          }
769          return result;
770       }
771    }
772
773    property bool isExcluded
774    {
775       get
776       {
777          // THIS IS THE GENERIC (ALL PLATFORMS) EXCLUDED FROM BUILD
778          // TODO: We can also have platform specific exclusion...
779          ProjectConfig config = property::config;
780          if(config && config.options && config.options.excludeFromBuild)
781             return config.options.excludeFromBuild == true;            
782          if(options && options.excludeFromBuild)
783             return options.excludeFromBuild == true;
784          if(parent)
785             return parent.isExcluded;
786          return false;
787       }
788    }
789
790    void EnsureVisible()
791    {
792       if(parent)
793          parent.EnsureVisible();
794       row.collapsed = false;
795    }
796
797    void Delete()
798    {
799       if(parent)
800          parent.files.Delete(this);
801    }
802
803    ProjectNode Find(char * name, bool includeResources)
804    {
805       ProjectNode result = null;
806       if(files)
807       {
808          for(child : files)
809          {
810             if(includeResources || child.type != resources)
811             {
812                if(child.type != folder && child.name && !strcmpi(child.name, name))
813                {
814                   result = child;
815                   break;
816                }
817                result = child.Find(name, includeResources);
818                if(result)
819                   break;
820             }
821          }
822       }
823       return result;
824    }
825
826    ProjectNode FindWithPath(char * name, bool includeResources)
827    {
828       ProjectNode result = null;
829       if(files)
830       {
831          for(child : files)
832          {
833             if(includeResources || child.type != resources)
834             {
835                char path[MAX_LOCATION];
836                strcpy(path, child.path);
837                if(child.type != folder && child.name)
838                {
839                   PathCatSlash(path, child.name);
840                   if(!strcmpi(path, name))
841                   {
842                      result = child;
843                      break;
844                   }
845                }
846                result = child.FindWithPath(name, includeResources);
847                if(result)
848                   break;
849             }
850          }
851       }
852       return result;
853    }
854
855    ProjectNode FindSpecial(char * name, bool recursive, bool includeResources, bool includeFolders)
856    {
857       ProjectNode result = null;
858       if(files)
859       {
860          for(child : files)
861          {
862             if(includeResources || child.type != resources)
863             {
864                if((includeFolders || child.type != folder) && child.name && !strcmpi(child.name, name))
865                {
866                   result = child;
867                   break;
868                }
869                if(recursive)
870                   result = child.FindSpecial(name, recursive, includeResources, includeFolders);
871                if(result)
872                   break;
873             }
874          }
875       }
876       return result;
877    }
878
879    ProjectNode Add(Project project, char * filePath, ProjectNode after, NodeTypes type, NodeIcons icon, bool checkIfExists)
880    {
881       ProjectNode node = null;
882       char temp[MAX_LOCATION];
883
884       GetLastDirectory(filePath, temp);
885       if(!checkIfExists || !project.topNode.Find(temp, false))
886       {
887          // Do the check for folder in the same parent or resource files only here
888          if(type == folder || !checkIfExists)
889          {
890             for(node : files)
891             {
892                if(node.name && !strcmpi(node.name, temp))
893                   return null;
894             }
895          }
896
897          node = ProjectNode { parent = this, indent = indent + 1, type = type, icon = icon, name = CopyString(temp) };
898          if(type != file)
899          {
900             node.files = { }; 
901             node.nodeType = folder;
902          }
903          if(type != folder)
904          {
905             if(filePath)
906             {
907                StripLastDirectory(filePath, temp);
908                MakePathRelative(temp, project.topNode.path, temp);
909                node.path = CopyUnixPath(temp);
910             }
911             node.nodeType = file;
912          }
913          else
914          {
915             strcpy(temp, (type == NodeTypes::project) ? "" : path);
916             PathCatSlash(temp, node.name);
917             node.path = CopyString(temp);
918          }
919          files.Insert(after, node);
920       }
921       return node;
922    }
923
924 #ifndef MAKEFILE_GENERATOR
925    void OnDisplay(Surface surface, int x, int y, int width, ProjectView projectView, Alignment alignment, DataDisplayFlags displayFlags)
926    {
927       char label[MAX_FILENAME];
928       int indent = 16;
929       int xStart;
930       int len;
931       int w, h;
932       Bitmap bmp;
933       bool showConfig = true;
934
935       if(!projectView)
936       {
937          showConfig = false;
938          projectView = ide.projectView;
939       }         
940       
941       bmp = projectView.icons[icon].bitmap;
942       xStart = /*indent * indent + */x + (bmp ? (bmp.width + 5) : 0);
943
944       GetLastDirectory(name, label);
945       if(!showConfig || projectView.drawingInProjectSettingsDialogHeader)
946       {
947          if(projectView.drawingInProjectSettingsDialogHeader || (type == project && info))
948          {
949             if(projectView.projectSettingsDialog && projectView.projectSettingsDialog.buildTab)
950             {
951                char * addendum;
952                addendum = projectView.projectSettingsDialog.buildTab.selectedConfigName;
953                if(strlen(addendum))
954                {
955                   strcat(label, " (");
956                   strcat(label, addendum);
957                   strcat(label, ")");
958                }
959                addendum = projectView.projectSettingsDialog.buildTab.selectedPlatformName;
960                if(strlen(addendum))
961                {
962                   strcat(label, " (");
963                   strcat(label, addendum);
964                   strcat(label, ")");
965                }
966             }
967          }
968       }
969       else if(!projectView.drawingInProjectSettingsDialog)
970       {
971          if(modified)
972             strcat(label, " *");
973          if(type == project && info)
974          {
975             int len = strlen(info) + 4;
976             char * more = new char[len];
977             sprintf(more, " (%s)", info);
978             strcat(label, more);
979             delete more;
980          }
981       }
982       len = strlen(label);
983       
984       if(!bmp)
985       {
986          if(type == folder || type == folderOpen)
987             surface.SetForeground(yellow);
988          indent = 8;
989       }
990
991       surface.TextOpacity(false);
992       surface.TextExtent(label, len, &w, &h);
993       h = Max(h, 16);
994     
995       // Draw the current row stipple
996       if(displayFlags.selected)
997          //surface.Area(xStart - 1, y, xStart - 1, y + h - 1);
998          //surface.Area(xStart + w - 1, y, xStart + w + 1, y + h - 1);
999          surface.Area(xStart - 3, y, xStart + w + 1, y + h - 1);
1000       
1001       surface.WriteTextDots(alignment, xStart, y + 2, width, label, len);
1002       
1003       if(!app.textMode)
1004       {
1005          if(displayFlags.current)
1006          {
1007             if(displayFlags.active)
1008             {
1009                surface.LineStipple(0x5555);
1010                if(displayFlags.selected)
1011                   surface.SetForeground(0xFFFFFF80);
1012                else
1013                   surface.SetForeground(black);
1014             }
1015             else
1016             {
1017                surface.SetForeground(SELECTION_COLOR);
1018             }
1019             surface.Rectangle(xStart - 3, y, xStart + w + 1, y + h - 1);
1020             surface.LineStipple(0);
1021          }
1022
1023          if(bmp)
1024          {
1025             surface.SetForeground(white);
1026             surface.Blit(bmp, x /*+ indent * indent*/,y,0,0, bmp.width, bmp.height);
1027          }
1028       }
1029    }
1030 #endif
1031
1032    int OnCompare(ProjectNode b)
1033    {
1034       int result;
1035       if(type == b.type /*|| type >= TYPE_DRIVE*/)
1036          result = strcmpi(name, b.name);
1037       else
1038       {
1039          if(type == folder && b.type == file) result = -1;
1040          else if(type == file && b.type == folder) result = 1;
1041       }
1042       return result;
1043    }
1044
1045    void GenFileFlags(File f, Project project)
1046    {
1047       ProjectNode node = null;
1048       List<ProjectNode> nodeStack { };
1049       
1050       for(node = this; node && node.parent; node = node.parent)
1051          nodeStack.Add(node);
1052
1053       // Should we reverse this stack to give priority to the per-file includes?
1054
1055       while((node = nodeStack.lastIterator.data))
1056       {
1057          ProjectConfig config = node.config;
1058          if(node.options && node.options.preprocessorDefinitions)
1059             OutputListOption(f, "D", node.options.preprocessorDefinitions, inPlace, false);
1060          if(config && config.options && config.options.preprocessorDefinitions)
1061             OutputListOption(f, "D", config.options.preprocessorDefinitions, inPlace, false);
1062          if(node.options && node.options.includeDirs)
1063             OutputListOption(f, "I", node.options.includeDirs, inPlace, true);
1064          if(config && config.options && config.options.includeDirs)
1065             OutputListOption(f, "I", config.options.includeDirs, inPlace, true);
1066
1067          nodeStack.lastIterator.Remove();
1068       }
1069       delete nodeStack;
1070    }
1071
1072    void GenMakefileGetNameCollisionInfo(Map<String, NameCollisionInfo> namesInfo)
1073    {
1074       if(type == file)
1075       {
1076          char extension[MAX_EXTENSION];
1077          GetExtension(name, extension);
1078          if(!strcmpi(extension, "ec") || !strcmpi(extension, "c") ||
1079                !strcmpi(extension, "cpp") || !strcmpi(extension, "cc") ||
1080                !strcmpi(extension, "cxx") || !strcmpi(extension, "m"))
1081          {
1082             char moduleName[MAX_FILENAME];
1083             NameCollisionInfo info;
1084             ReplaceSpaces(moduleName, name);
1085             StripExtension(moduleName);
1086             info = namesInfo[moduleName];
1087             if(!info)
1088                info = NameCollisionInfo { };
1089             info.count++; // += 1; unless this is for a bug?
1090             if(!strcmpi(extension, "ec"))
1091                info.ec = true;
1092             else if(!strcmpi(extension, "c"))
1093                info.c = true;
1094             else if(!strcmpi(extension, "cpp"))
1095                info.cpp = true;
1096             else if(!strcmpi(extension, "cc"))
1097                info.cc = true;
1098             else if(!strcmpi(extension, "cxx"))
1099                info.cxx = true;
1100             else if(!strcmpi(extension, "m"))
1101                info.m = true;
1102             namesInfo[moduleName] = info;
1103          }
1104       }
1105       else if(files)
1106       {
1107          for(child : files)
1108          {
1109             if(child.type != resources && (child.type == folder || !child.isExcluded))
1110                child.GenMakefileGetNameCollisionInfo(namesInfo);
1111          }
1112       }
1113    }
1114    
1115    int GenMakefilePrintNode(File f, Project project, GenMakefilePrintTypes printType, Map<String, NameCollisionInfo> namesInfo, Array<String> items)
1116    {
1117       int count = 0;
1118       if(type == file)
1119       {
1120          char s[2048];
1121          // TOCHECK: Does this take care of parent folder platform specific exclusions?
1122          TwoStrings ts = platformSpecificExclusionFu;
1123          char moduleName[MAX_FILENAME];
1124          char extension[MAX_EXTENSION];
1125          GetExtension(name, extension);
1126          if(printType == resources)
1127          {
1128             bool useRes;
1129             char tempPath[MAX_LOCATION];
1130             char modulePath[MAX_LOCATION];
1131
1132             tempPath[0] = '\0';
1133             if(eString_PathInsideOfMore(path, project.resNode.path, tempPath))
1134             {
1135                useRes = true;
1136                PathCatSlash(tempPath, name);
1137             }
1138             else
1139             {
1140                useRes = false;
1141                strcpy(tempPath, path);
1142                PathCatSlash(tempPath, name);
1143             }
1144             ReplaceSpaces(modulePath, tempPath);
1145             sprintf(s, "%s%s%s%s", ts.a, useRes ? "$(RES)" : "", modulePath, ts.b);
1146             items.Add(CopyString(s));
1147          }
1148          else if(printType == sources)
1149          {
1150             if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1151                   !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1152                   !strcmpi(extension, "ec") || !strcmpi(extension, "m"))
1153             {
1154                char modulePath[MAX_LOCATION];
1155
1156                ReplaceSpaces(modulePath, path);
1157                ReplaceSpaces(moduleName, name);
1158                sprintf(s, "%s%s%s%s%s", ts.a, modulePath, path[0] ? SEPS : "", moduleName, ts.b);
1159                items.Add(CopyString(s));
1160             }
1161          }
1162          else if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1163                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx") ||
1164                !strcmpi(extension, "m"))
1165          {
1166             if(printType == objects)
1167             {
1168                bool collision;
1169                NameCollisionInfo info;
1170                ReplaceSpaces(moduleName, name);
1171                StripExtension(moduleName);
1172                info = namesInfo[moduleName];
1173                collision = info ? info.IsExtensionColliding(extension) : false;
1174                sprintf(s, "%s$(OBJ)%s%s%s.o%s", ts.a, moduleName, collision ? "." : "", collision ? extension : "", ts.b);
1175                items.Add(CopyString(s));
1176             }
1177          }
1178          else if(!strcmpi(extension, "ec"))
1179          {
1180             ReplaceSpaces(moduleName, name);
1181             StripExtension(moduleName);
1182             if(printType == objects)
1183                count++;
1184             s[0] = '\0';
1185             if(printType == objects)
1186                sprintf(s, "%s$(OBJ)%s.o%s", ts.a, moduleName, ts.b);
1187             else if(printType == cObjects)
1188                sprintf(s, "%s$(OBJ)%s.c%s", ts.a, moduleName, ts.b);
1189             else if(printType == symbols)
1190                sprintf(s, "%s$(OBJ)%s.sym%s", ts.a, moduleName, ts.b);
1191             else if(printType == imports)
1192                sprintf(s, "%s$(OBJ)%s.imp%s", ts.a, moduleName, ts.b);
1193             if(s[0])
1194                items.Add(CopyString(s));
1195          }
1196          delete ts;
1197       }
1198       else if(files)
1199       {
1200          for(child : files)
1201          {
1202             if(child.type != resources && (child.type == folder || !child.isExcluded))
1203                count += child.GenMakefilePrintNode(f, project, printType, namesInfo, items);
1204          }
1205       }
1206       return count;
1207    }
1208
1209    void GenMakefilePrintSymbolRules(File f, Project project)
1210    {
1211       //ProjectNode child;
1212       //char objDir[MAX_LOCATION];
1213       CompilerConfig compiler = GetCompilerConfig();
1214       //ReplaceSpaces(objDir, project.config.objDir.dir);
1215
1216       //eSystem_Log("Printing Symbol Rules\n");
1217       if(type == file)
1218       {
1219          char extension[MAX_EXTENSION];
1220          char modulePath[MAX_LOCATION];
1221          char moduleName[MAX_FILENAME];
1222          
1223          GetExtension(name, extension);
1224          if(!strcmpi(extension, "ec"))
1225          {
1226             DualPipe dep;
1227             char command[2048];
1228
1229             ReplaceSpaces(moduleName, name);
1230             StripExtension(moduleName);
1231
1232             ReplaceSpaces(modulePath, path);
1233             if(modulePath[0]) strcat(modulePath, SEPS);
1234
1235 #if 0
1236             // *** Dependency command ***
1237             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s", moduleName,
1238                modulePath, moduleName, extension);
1239
1240             // System Includes (from global settings)
1241             for(item : compiler.dirs[Includes])
1242             {
1243                strcat(command, " -isystem ");
1244                if(strchr(item, ' '))
1245                {
1246                   strcat(command, "\"");
1247                   strcat(command, item);
1248                   strcat(command, "\"");
1249                }
1250                else
1251                   strcat(command, item);
1252             }
1253
1254             for(item : project.includeDirs)
1255             {
1256                strcat(command, " -I");
1257                if(strchr(item, ' '))
1258                {
1259                   strcat(command, "\"");
1260                   strcat(command, item);
1261                   strcat(command, "\"");
1262                }
1263                else
1264                   strcat(command, item);
1265             }
1266             for(item : project.preprocessorDefs)
1267             {
1268                strcat(command, " -D");
1269                strcat(command, item);
1270             }
1271
1272             // Execute it
1273             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1274             {
1275                char line[1024];
1276                bool firstLine = true;
1277                bool result = true;
1278
1279                // To do some time: auto save external dependencies?
1280                while(!dep.Eof())
1281                {
1282                   if(dep.GetLine(line, sizeof(line)-1))
1283                   {
1284                      if(firstLine)
1285                      {
1286                         char * colon = strstr(line, ":");
1287                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1288                         {
1289                            result = false;
1290                            break;
1291                         }
1292                         firstLine = false;
1293                      }
1294                      f.Puts(line);
1295                      f.Puts("\n");
1296                   }
1297                   if(!result) break;
1298                }
1299                delete dep;
1300
1301                // If we failed to generate dependencies...
1302                if(!result)
1303                {
1304 #endif
1305                   f.Printf("$(OBJ)%s.sym: %s%s.%s\n",
1306                      moduleName, modulePath, moduleName, extension);
1307 #if 0
1308                }
1309             }
1310 #endif
1311          /*
1312             f.Printf("\t$(ECP) %s%s.%s %s.sym\n\n",
1313                modulePath, moduleName, extension, moduleName);
1314             */
1315
1316             f.Printf("\t$(ECP) $(CECFLAGS)");
1317             if(ecflags)
1318             {
1319                if(memoryGuard)
1320                   f.Printf(" -memguard");
1321                if(strictNameSpaces)
1322                   f.Printf(" -strictns");
1323                {
1324                   char * s = defaultNameSpace;
1325                   if(s && s[0])
1326                      f.Printf(" -defaultns %s", s);
1327                }
1328             }
1329             else
1330                f.Printf(" $(ECFLAGS)");
1331             f.Printf(" $(CFLAGS)");
1332             GenFileFlags(f, project);
1333             f.Printf(" -c %s%s.%s -o $(OBJ)%s.sym\n\n",
1334                modulePath, moduleName, extension, moduleName);
1335          }
1336       }
1337       if(files)
1338       {
1339          for(child : files)
1340          {
1341             // TODO: Platform specific options
1342             if(child.type != resources && (child.type == folder || !child.isExcluded))
1343                child.GenMakefilePrintSymbolRules(f, project);
1344          }
1345       }
1346       delete compiler;
1347    }
1348
1349    void GenMakefilePrintCObjectRules(File f, Project project)
1350    {
1351       //ProjectNode child;
1352       //char objDir[MAX_LOCATION];
1353       CompilerConfig compiler = GetCompilerConfig();
1354       //ReplaceSpaces(objDir, project.config.objDir.dir);
1355       //eSystem_Log("Printing C Object Rules\n");
1356       if(type == file)
1357       {
1358          char extension[MAX_EXTENSION];
1359          char modulePath[MAX_LOCATION];
1360          char moduleName[MAX_FILENAME];
1361          
1362          GetExtension(name, extension);
1363          if(!strcmpi(extension, "ec"))
1364          {
1365             DualPipe dep;
1366             char command[2048];
1367
1368             ReplaceSpaces(moduleName, name);
1369             StripExtension(moduleName);
1370
1371             ReplaceSpaces(modulePath, path);
1372             if(modulePath[0]) strcat(modulePath, SEPS);
1373
1374 #if 0
1375             // *** Dependency command ***
1376             sprintf(command, "gcc -MT $(OBJ)%s.o -MM %s%s.%s",
1377                moduleName, modulePath, moduleName, extension);
1378
1379             // System Includes (from global settings)
1380             for(item : compiler.dirs[Includes])
1381             {
1382                strcat(command, " -isystem ");
1383                if(strchr(item, ' '))
1384                {
1385                   strcat(command, "\"");
1386                   strcat(command, item);
1387                   strcat(command, "\"");
1388                }
1389                else
1390                   strcat(command, item);
1391             }
1392
1393             for(item : project.config.includeDirs)
1394             {
1395                strcat(command, " -I");
1396                if(strchr(item, ' '))
1397                {
1398                   strcat(command, "\"");
1399                   strcat(command, item);
1400                   strcat(command, "\"");
1401                }
1402                else
1403                   strcat(command, item);
1404             }
1405             for(item : project.config.preprocessorDefs)
1406             {
1407                strcat(command, " -D");
1408                strcat(command, item);
1409             }
1410
1411             // Execute it
1412             if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1413             {
1414                char line[1024];
1415                bool result = true;
1416                bool firstLine = true;
1417
1418                // To do some time: auto save external dependencies?
1419                while(!dep.Eof())
1420                {
1421                   if(dep.GetLine(line, sizeof(line)-1))
1422                   {
1423                      if(firstLine)
1424                      {
1425                         char * colon = strstr(line, ":");
1426                         if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1427                         {
1428                            result = false;
1429                            break;
1430                         }
1431                         firstLine = false;
1432                      }
1433                      f.Puts(line);
1434                      f.Puts("\n");
1435                   }
1436                   if(!result) break;
1437                }
1438                delete dep;
1439
1440                // If we failed to generate dependencies...
1441                if(!result)
1442                {
1443                   /* COMMENTED OUT FOR NOW
1444                   f.Printf("$(OBJ)%s.c: %s%s.%s $(Symbols)\n",
1445                      moduleName, modulePath, moduleName, extension);
1446                   */
1447 #endif
1448                   f.Printf("$(OBJ)%s.c: %s%s.%s $(OBJ)%s.sym | $(SYMBOLS)\n",
1449                      moduleName, modulePath, moduleName, extension, moduleName);
1450 #if 0
1451                }
1452             }
1453 #endif
1454          /*
1455             f.Printf("\t$(ECC) %s%s.%s $(OBJ)%s.c\n\n",
1456                modulePath, moduleName, extension, moduleName);
1457          */
1458
1459             f.Printf("\t$(ECC)");
1460             if(ecflags)
1461             {
1462                f.Printf("%s $(CECFLAGS)", noLineNumbers ? " -nolinenumbers" : "");
1463                if(memoryGuard)
1464                   f.Printf(" -memguard");
1465                if(strictNameSpaces)
1466                   f.Printf(" -strictns");
1467                {
1468                   char * s = defaultNameSpace;
1469                   if(s && s[0])
1470                      f.Printf(" -defaultns %s", s);
1471                }
1472             }
1473             else
1474                f.Printf(" $(CECFLAGS) $(ECFLAGS)");
1475             f.Printf(" $(CFLAGS) $(FVISIBILITY)");
1476             GenFileFlags(f, project);
1477             f.Printf(" -c %s%s.%s -o $(OBJ)%s.c -symbols $(OBJ)\n\n",
1478                modulePath, moduleName, extension, moduleName);
1479          }
1480       }
1481       if(files)
1482       {
1483          for(child : files)
1484          {
1485             // TODO: Platform specific options
1486             if(child.type != resources && (child.type == folder || !child.isExcluded))
1487                child.GenMakefilePrintCObjectRules(f, project);
1488          }
1489       }
1490       delete compiler;
1491    }
1492
1493    void GenMakefilePrintObjectRules(File f, Project project, Map<String, NameCollisionInfo> namesInfo)
1494    {
1495       //ProjectNode child;
1496       //char objDir[MAX_LOCATION];
1497       CompilerConfig compiler = GetCompilerConfig();
1498       //ReplaceSpaces(objDir, project.config.objDir.dir);
1499       //eSystem_Log("Printing Object Rules\n");
1500       if(type == file)
1501       {
1502          bool collision;
1503          char extension[MAX_EXTENSION];
1504          char modulePath[MAX_LOCATION];
1505          char moduleName[MAX_FILENAME];
1506          
1507          GetExtension(name, extension);
1508          /*if(!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1509                !strcmpi(extension, "ec") || !strcmpi(extension, "cc") ||
1510                !strcmpi(extension, "cxx"))*/
1511          if((!strcmpi(extension, "c") || !strcmpi(extension, "cpp") ||
1512                !strcmpi(extension, "cc") || !strcmpi(extension, "cxx")) ||
1513                !strcmpi(extension, "ec"))
1514          {
1515             DualPipe dep;
1516             char command[2048];
1517             NameCollisionInfo info;
1518
1519             ReplaceSpaces(moduleName, name);
1520             StripExtension(moduleName);
1521
1522             info = namesInfo[moduleName];
1523             collision = info ? info.IsExtensionColliding(extension) : false;
1524             
1525             ReplaceSpaces(modulePath, path);
1526             if(modulePath[0]) strcat(modulePath, SEPS);
1527
1528             // *** Dependency command ***
1529             if(!strcmpi(extension, "ec"))
1530                sprintf(command, "%s -MT $(OBJ)%s.o -MM $(OBJ)%s.c", compiler.ccCommand, moduleName, moduleName);
1531             else
1532                sprintf(command, "%s -MT $(OBJ)%s.o -MM %s%s.%s", compiler.ccCommand, moduleName, modulePath, moduleName, extension);
1533
1534             if(!strcmpi(extension, "ec"))
1535                f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1536             else
1537             {
1538 #if 0
1539                // System Includes (from global settings)
1540                for(item : compiler.dirs[includes])
1541                {
1542                   strcat(command, " -isystem ");
1543                   if(strchr(item, ' '))
1544                   {
1545                      strcat(command, "\"");
1546                      strcat(command, item);
1547                      strcat(command, "\"");
1548                   }
1549                   else
1550                      strcat(command, item);
1551                }
1552
1553                for(item : project.config.includeDirs)
1554                {
1555                   strcat(command, " -I");
1556                   if(strchr(item, ' '))
1557                   {
1558                      strcat(command, "\"");
1559                      strcat(command, item);
1560                      strcat(command, "\"");
1561                   }
1562                   else
1563                      strcat(command, item);
1564                }
1565                for(item : project.config.preprocessorDefs)
1566                {
1567                   strcat(command, " -D");
1568                   strcat(command, item);
1569                }
1570
1571                // Execute it
1572                if((dep = DualPipeOpen(PipeOpenMode { output = 1, error = 1, input = 2 }, command)))
1573                {
1574                   char line[1024];
1575                   bool firstLine = true;
1576                   bool result = true;
1577
1578                   // To do some time: auto save external dependencies?
1579
1580                   while(!dep.Eof())
1581                   {
1582                      if(dep.GetLine(line, sizeof(line)-1))
1583                      {
1584                         if(firstLine)
1585                         {
1586                            char * colon = strstr(line, ":");
1587                            if(strstr(line, "No such file") || strstr(line, ",") || (colon && strstr(colon+1, ":")))
1588                            {
1589                               result = false;
1590                               break;
1591                            }
1592                            firstLine = false;
1593                         }
1594                         f.Puts(line);
1595                         f.Puts("\n");
1596                      }
1597                      if(!result) break;
1598                   }
1599                   delete dep;
1600
1601                   // If we failed to generate dependencies...
1602                   if(!result)
1603                   {
1604 #endif
1605                      /*if(!strcmpi(extension, "ec"))
1606                         f.Printf("$(OBJ)%s.o: $(OBJ)%s.c\n", moduleName, moduleName);
1607                      else*/
1608                         f.Printf("$(OBJ)%s%s%s.o: %s%s.%s\n", moduleName, 
1609                               collision ? "." : "", collision ? extension : "", modulePath, moduleName, extension);
1610 #if 0
1611                   }
1612                }
1613 #endif
1614             }
1615             f.Printf("\t$(CC) $(CFLAGS)");
1616             GenFileFlags(f, project);
1617             if(!strcmpi(extension, "ec"))
1618                f.Printf(" $(FVISIBILITY) -c $(OBJ)%s.c -o $(OBJ)%s.o\n\n", moduleName, moduleName);
1619             else
1620                f.Printf(" -c %s%s.%s -o $(OBJ)%s%s%s.o\n\n",
1621                      modulePath, moduleName, !strcmpi(extension, "ec") ? "c" : extension, moduleName,
1622                      collision ? "." : "", collision ? extension : "");
1623          }
1624       }
1625       if(files)
1626       {
1627          for(child : files)
1628          {
1629             // TODO: Platform specific options
1630             if(child.type != resources && (child.type == folder || !child.isExcluded))
1631                child.GenMakefilePrintObjectRules(f, project, namesInfo);
1632          }
1633       }
1634       delete compiler;
1635    }
1636
1637    void GenMakefileAddResources(File f, String resourcesPath)
1638    {
1639       int count = 0;
1640       if(files)
1641       {
1642          int c;
1643          bool prev = false;
1644          //Iterator<ProjectNode> i { files };
1645          //Iterator<ProjectNode> prev { files };
1646          //for(child : files)
1647          //while(i.Next())
1648          for(c = 0; c < files.count; c++)
1649          {
1650             ProjectNode child = files[c];
1651             TwoStrings ts = child.platformSpecificExclusionFu;
1652             if(count > 0 && ts)
1653                prev = true;
1654             if(child.type == file && !child.isExcluded && !(count > 0 && ts))
1655             {
1656                bool useRes;
1657                char tempPath[MAX_LOCATION];
1658                char resPath[MAX_LOCATION];
1659
1660                char * quotes;
1661
1662                // $(EAR) aw%s --- /*quiet ? "q" : */""
1663                if(count == 0)
1664                   f.Printf("\t%s$(EAR) aw $(TARGET)", ts.a);
1665
1666                tempPath[0] = '\0';
1667                if(eString_PathInsideOfMore(child.path, resourcesPath, tempPath))
1668                {
1669                   useRes = true;
1670                   PathCatSlash(tempPath, child.name);
1671                }
1672                else
1673                {
1674                   useRes = false;
1675                   strcpy(tempPath, child.path);
1676                   PathCatSlash(tempPath, child.name);
1677                }
1678                ReplaceSpaces(resPath, tempPath);
1679                if(strchr(tempPath, ' '))
1680                   quotes = "\"";
1681                else
1682                   quotes = "";
1683                f.Printf(" %s%s%s%s", quotes, useRes ? "$(RES)" : "", resPath, quotes);
1684                count++;
1685             }
1686             if(count == 10 || (count > 0 && (ts || !child.next)))
1687             {
1688                char path[MAX_LOCATION] = "", temp[MAX_LOCATION];
1689                ProjectNode parent;
1690
1691                for(parent = this; parent.type == folder; parent = parent.parent)
1692                {
1693                   strcpy(temp, path);
1694                   strcpy(path, parent.name);
1695                   if(temp[0])
1696                   {
1697                      strcat(path, "/");
1698                      strcat(path, temp);
1699                   }
1700                }
1701                f.Printf(" \"%s\"%s\n", path, ts.b);
1702                count = 0;
1703                if(prev)
1704                {
1705                   c--;
1706                   prev = false;
1707                }
1708             }
1709             delete ts;
1710          }
1711          for(child : files)
1712          {
1713             if(child.type == folder)
1714                child.GenMakefileAddResources(f, resourcesPath);
1715          }
1716       }
1717    }
1718 }
1719
1720 class NameCollisionInfo
1721 {
1722    bool ec;
1723    bool c;
1724    bool cpp;
1725    bool cc;
1726    bool cxx;
1727    bool m;
1728    byte count;
1729
1730    bool IsExtensionColliding(char * extension)
1731    {
1732       bool colliding;
1733       if(count > 1 && ((!strcmpi(extension, "c") && ec) ||
1734             (!strcmpi(extension, "cpp") && (ec || c)) ||
1735             (!strcmpi(extension, "cc") && (ec || c || cpp)) ||
1736             (!strcmpi(extension, "cxx") && (ec || c || cpp || cc)) ||
1737             !strcmpi(extension, "m")))
1738          colliding = true;
1739       else
1740          colliding = false;
1741      return colliding;
1742    }
1743 }