Fix #7962 - meson write
[roobuilder] / src / Project / Project.vala
1 //<Script type="text/javascript">
2
3 /**
4  * Project Object
5  * 
6  * Projects can only contain one directory... - it can import other projects..(later)
7  * 
8  * we need to sort out that - paths is currently a key/value array..
9  * 
10  * currently we store projects in ~/.Builder/{md5}.json
11    - then for Gtk projects we have a file config1.builder - which contains dependancies, and a list of files for each target
12      (for builder it's ended up in src/Builder/config1.builder? by accident.
13    - should really support something like 
14        roobuilder --build {path_to_cfg} {target} - or local directory if not set..
15        roobuilder --build-errors {path_to_cfg} {target} - or local directory if not set..
16        
17        
18    
19    
20    should really store project data in the directory of the project?
21    
22    
23    // steps:
24    // List of projects - just an array of paths in .Builder/Projects.json
25    
26    // .roobuilder.jcfg  << hidden file with project details?
27    
28    
29    
30  
31  * 
32  * 
33  */
34 namespace Project {
35          public errordomain Error {
36                 INVALID_TYPE,
37                 NEED_IMPLEMENTING,
38                 MISSING_FILE,
39                 INVALID_VALUE,
40                 INVALID_FORMAT
41         }
42
43
44         
45         // static array of all projects.
46         private Gee.ArrayList<Project>  projects;
47         
48         
49         
50         public bool  projects_loaded = false;
51         
52         
53         // used to pass around callbacks with project as a return
54         public class Callback : Object {
55                 public signal void call(Project project);
56         }
57         
58         public abstract class Project : Object {
59                 
60                 public signal void on_changed (); 
61         
62                 //public string id;
63                 //public string fn = ""; // just a md5...
64                 public string name  { 
65                         private set {} 
66                         owned get {
67                                 return GLib.Path.get_basename(path);
68                         }
69                          
70                 }
71                                 
72                 
73                 public string path = "";
74                 private Gee.ArrayList<JsRender.JsRender> sub_paths;
75                 
76                 private Gee.HashMap<string,JsRender.JsRender> files ;  // contains full list of files.
77                 //tree : false,
78                 public  string xtype;
79                 
80                 //public Json.Object json_project_data;
81
82                 public Palete.Palete palete;
83                  
84                 private bool is_scanned = false; 
85                 public  Gee.HashMap<string,Palete.GirObject> gir_cache = null; // used by Gir ??? is this used by Roo?
86                  public Palete.ValaCompileRequest last_request = null;
87                 
88                 protected Project (string path) {
89                         
90                          
91                         //this.json_project_data = new Json.Object();
92                         
93                         this.is_scanned = false;
94                         this.sub_paths = new Gee.ArrayList<JsRender.JsRender>();
95                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
96                         //XObject.extend(this, cfg);
97                         //this.files = { }; 
98                         this.path = path;
99                          
100                         
101                         
102                 }
103                  
104                 
105                 
106                 
107                 public static void loadAll(bool force = false)
108                 {
109                         if (projects_loaded && !force) {
110                                 return;
111                         }
112
113                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
114                         var dir = File.new_for_path(dirname);
115                         if (!dir.query_exists()) {
116                                 try {
117                                         dir.make_directory();
118                                 } catch(GLib.Error e) {
119                                         GLib.error("could not make builder directory");
120                                 }
121                                 return;
122                         }
123                         projects = new  Gee.ArrayList<Project>();
124                           
125                     
126                     if (FileUtils.test(dirname + "/Projects.list", GLib.FileTest.IS_REGULAR)) {
127                         loadProjectList();
128                         projects_loaded = true;
129                         return;
130                 }
131                 convertOldProjects(); // this saves..
132                 foreach(var p in projects) {
133                         p.save();
134                 }
135                         projects_loaded = true;
136  
137         }
138          
139         public static void remove(Project p) {
140                 projects.remove(p);
141                 saveProjectList();
142         
143         }
144         
145         public static void saveProjectList()
146         {
147                         var f = new Json.Object();
148                         foreach(var p in projects) {
149                                 f.set_string_member(p.path, p.xtype);
150                         }
151                         
152                         var  generator = new Json.Generator ();
153                         var  root = new Json.Node(Json.NodeType.OBJECT);
154                         root.init_object(f);
155                         generator.set_root (root);
156                         generator.pretty = true;
157                         generator.indent = 4;
158
159                         var data = generator.to_data (null);
160                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
161                 GLib.debug("Write new Project list\n %s", data);
162                 //Posix.exit(0);
163                 
164                 try {
165                                 //FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
166                                 FileUtils.set_contents(dirname + "/Projects.list", data, data.length);  
167                         } catch (GLib.Error e) {
168                                 GLib.error("failed  to save file %s", e.message);
169                         }
170                 
171         }
172         
173         
174         
175         public static void convertOldProjects()
176         {
177         
178                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
179                         var  dir = File.new_for_path(dirname);
180                         try {
181                                 var file_enum = dir.enumerate_children(
182                                                                 GLib.FileAttribute.STANDARD_DISPLAY_NAME, 
183                                         GLib.FileQueryInfoFlags.NONE, 
184                                         null
185                                 );
186                                  
187                                 FileInfo next_file; 
188                                 while ((next_file = file_enum.next_file(null)) != null) {
189                                         var fn = next_file.get_display_name();
190                                         if (!Regex.match_simple("\\.json$", fn)) {
191                                                 continue;
192                                         }
193                                         Project.factoryFromFileOld(dirname + "/" + fn);
194                                 }       
195                         } catch(GLib.Error e) {
196                                 GLib.warning("oops - something went wrong scanning the projects\n");
197                         }
198                         GLib.debug("Loaded all old Projects - saving");
199                         Project.saveProjectList();
200
201                 }
202
203                 public static Gee.ArrayList<Project> allProjectsByName()
204                 {
205                         
206                         return projects;
207                 
208                 }
209                 
210                 public static Project? getProjectByName(string name)
211                 {
212                         
213                         foreach (var p in projects) {
214                                 if (p.name == name) {
215                                         return p;
216                                 }
217                         }
218                         
219                         return null;
220                 
221                 }
222                 public static Project? getProjectByPath(string path)
223                 {
224                         
225                         foreach (var p in projects) {
226                                 if (p.path == path) {
227                                         return p;
228                                 }
229                         }
230                         
231                         return null;
232                 
233                 }
234                 public static string listAllToString()
235                 {
236                         var all = projects;
237
238                          
239                         
240                         all.sort((fa,fb) => {
241                                 return ((Project)fa).name.collate(((Project)fb).name);
242
243                         });
244
245                         var iter = all.list_iterator();
246                         var ret = "ID\tName\tDirectory\n";
247                         while (iter.next()) {
248                                 ret += "%s\t%s\n".printf(
249                                                  
250                                                 iter.get().name,
251                                                 iter.get().path
252                                                 );
253                          
254                                 
255                         }
256                         
257                         return ret;
258                 
259                 }
260                 
261                 
262                 public static void loadIntoStore(GLib.ListStore st)
263                 {
264                         st.remove_all();
265                         foreach (var p in projects) {
266                                 st.append(p);
267                         }
268                         
269                 }
270                         
271                 
272  
273                 
274                 static void loadProjectList()
275                 {
276                 
277                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
278                          
279                         projects = new  Gee.ArrayList<Project>();
280                           
281                     var pa = new Json.Parser();
282                         try { 
283                                 pa.load_from_file(dirname + "/Projects.list");  
284                         } catch (GLib.Error e) {
285                                 GLib.error("could not load json file %s", e.message);
286                         }
287                         var node = pa.get_root();
288                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
289                                 GLib.error( dirname + "/Projects.list - invalid format?");
290                                  
291                         }
292
293                         
294                         var obj = node.get_object ();
295                         obj.foreach_member((sobj, key, val) => {
296                                 GLib.debug("read ProjectList %s: %s", key, val.get_string());
297                                 // facotry adds project!
298                                 try {
299                                         Project.factory(val.get_string(), key );
300                                 } catch (GLib.Error e ) {
301                                         GLib.debug("error createing project %s", e.message);
302                                 }
303                                  
304                         });
305                         
306                 
307                 }
308                 
309                 // load project data from project file.
310                 public static void   factoryFromFileOld(string jsonfile)
311                 {
312                          
313                         GLib.debug("parse %s", jsonfile);
314
315                         var pa = new Json.Parser();
316                         try { 
317                                 pa.load_from_file(jsonfile);
318                         } catch (GLib.Error e) {
319                                 GLib.error("could not load json file %s", e.message);
320                         }
321                         var node = pa.get_root();
322
323                         
324                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
325                                 GLib.debug("SKIP " + jsonfile + " - invalid format?");
326                                 return;
327                         }
328                         
329                         var obj = node.get_object ();
330                         var xtype =  obj.get_string_member("xtype");
331
332
333                         var paths = obj.get_object_member("paths");
334                         var i = 0;
335                         var fpath = "";
336                         paths.foreach_member((sobj, key, val) => {
337                                 if (i ==0 ) {
338                                         fpath = key;
339                                 }
340                                         
341                         });
342                         
343                         if (fpath.length < 0 || !FileUtils.test(fpath,FileTest.IS_DIR)) {
344                                 return;
345                         }
346                         
347                         Project proj;
348                         try {
349                                 proj = factory(xtype, fpath);
350                         } catch (Error e)  {
351                                 GLib.debug("Skip file - invalid file type");
352                                 return;
353                         }
354
355                         //proj.json_project_data  = obj; // store the original object...
356                         
357                         //proj.fn =  Path.get_basename(jsonfile).split(".")[0];
358
359                         proj.loadJson(obj);
360                         // might not exist?
361  
362                         proj.name = obj.get_string_member("name");
363
364                         // used to load paths..
365                         //proj.initSubDirectories();
366                         
367                          
368                         //proj.initDatabase();
369                         
370                         GLib.debug("Add Project %s", proj.name);
371                         
372                         projects.add(proj);
373                          
374                         
375                 }
376                 
377                 
378                 public static Project factory(string xtype, string path) throws Error
379                 {
380
381                         // check to see if it's already loaded..
382
383                          foreach(var p in projects) {
384                                   if (p.path == path) {
385                                         return p;
386                                  }
387                         }
388
389                         
390                         switch(xtype) {
391                                 case "Gtk":
392                                         var ret =  new Gtk(path);
393                                         projects.add(ret);
394                                         
395                                         return ret;
396                                 case "Roo":
397                                         var ret = new Roo(path);
398                                         projects.add(ret);
399                                  
400                                         return ret;
401                                 //case "Flutter":
402                                 //      return new Flutter(path);
403                         }
404                         throw new Error.INVALID_TYPE("invalid project type");
405                                 
406                 }
407                 
408                 
409          
410
411                 public void save()
412                 {
413                           
414                         var  s =  this.toJSON();
415                         GLib.debug("Save Project %s\n%s", this.name, s);
416                         try {
417                                 //FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
418                                 FileUtils.set_contents(this.path + "/.roobuilder.jcfg", s, s.length);  
419                         } catch (GLib.Error e) {
420                                 GLib.error("failed  to save file %s", e.message);
421                         }
422                         this.onSave();
423                         
424                 }
425
426         
427                 
428                 
429                 public string toJSON( )
430                 {
431                         
432                         var obj = new Json.Object();
433                         obj.set_string_member("xtype", this.xtype);
434                                                 
435                         
436                         this.saveJson(obj);
437                 
438                         var  generator = new Json.Generator ();
439                         var  root = new Json.Node(Json.NodeType.OBJECT);
440                         root.init_object(obj);
441                         generator.set_root (root);
442                         //if (show_all) {
443                                 generator.pretty = true;
444                                 generator.indent = 4;
445                         //}
446
447                         return  generator.to_data (null);
448                           
449                           
450                 }
451                 
452                 // used to check what type a project might be..
453                 // for 'new'
454                 public static string peekProjectType(string fn) 
455                 {
456                         var pa = new Json.Parser();
457                         try { 
458                                 pa.load_from_file(fn);
459                         } catch (GLib.Error e) {
460                                 GLib.debug("could not load json file %s", e.message);
461                                 return "";
462                                 
463                         }
464                         var node = pa.get_root();
465
466                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
467                                 GLib.debug("SKIP %s/.roobuilder.jcfg  - invalid format?",fn);
468                                 return "";
469                         }
470                         
471                         var obj = node.get_object ();
472
473                         var xtype =  obj.get_string_member("xtype");
474                         return xtype == null ? "" : xtype;
475                 
476                 }
477                 
478                 
479                 // this will do a full scan - should only be done on viewing project..
480                 // not initial load.. - may take time.
481                 
482                 public   void   load()
483                 {
484                         if (this.is_scanned) {
485                                 return;
486                         }
487                         GLib.debug("load is_scanned = false");
488                         
489                         if (FileUtils.test(this.path + "/.roobuilder.jcfg", FileTest.EXISTS)) {
490                                   
491                                 var pa = new Json.Parser();
492                                 try { 
493                                         pa.load_from_file(this.path + "/.roobuilder.jcfg");
494                                 } catch (GLib.Error e) {
495                                         GLib.error("could not load json file %s", e.message);
496                                 }
497                                 var node = pa.get_root();
498
499                                 
500                                 if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
501                                         GLib.debug("SKIP %s/.roobuilder.jcfg  - invalid format?",this.path);
502                                         return;
503                                 }
504                                 
505                                 var obj = node.get_object ();
506                                  
507                                 this.loadJson(obj);
508                         } 
509                         // used to load paths..
510                         this.sub_paths = new Gee.ArrayList<JsRender.JsRender>();
511                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
512                         this.loadSubDirectories("", 0);
513                          
514                         this.initDatabase();
515                         this.is_scanned = true; // loaded.. dont need to do it again..
516                          GLib.debug("load is_scanned = true");
517                         
518                 }
519                 
520                 
521                 /*
522                 
523                 public string firstPath()
524                 {
525                         var iter = this.paths.map_iterator();
526                         while (iter.next()) {
527                                 return iter.get_key();
528                         }
529                   
530                         return "";
531                 }
532
533                 public bool hasPath(string path)
534                 {
535                         var iter = this.paths.map_iterator();
536                         while (iter.next()) {
537                                 if (iter.get_key() == path) {
538                                 return true;
539                         }
540                         }
541                   
542                         return false;
543                 }
544
545                 
546                 // returns the first path
547                 public string getName()
548                 {
549                         var iter = this.paths.map_iterator();
550                         while (iter.next()) {
551                                 return GLib.Path.get_basename(iter.get_key());
552                         }
553                   
554                         return "";
555                 }
556                 */
557
558                 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
559                 {
560                         var files = new Gee.ArrayList<JsRender.JsRender>();
561
562                         var fiter = this.files.map_iterator();
563                         while(fiter.next()) {
564                                 files.add(fiter.get_value());
565                         }
566                         files.sort((fa,fb) => {
567                                 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
568
569                         });
570                         return files;
571
572                 }
573                 
574          
575          
576                 public string listAllFilesToString()
577                 {
578                  
579                         var iter = this.sortedFiles().list_iterator();
580                         var ret = "ID\tName\tDirectory\n";
581                         while (iter.next()) {
582                                 ret += "%s\n".printf(
583                                                  
584                                                 iter.get().name
585                                                  
586                                                 );
587                          
588                                 
589                         }
590                         
591                         return ret;
592                 
593                 }
594                 
595          
596          
597          
598                 public JsRender.JsRender? getByName(string name)
599                 {
600                         foreach(var f in this.files.values) {
601                                 if (f.name == name) {
602                                         return f;
603                                 }
604                         };
605                         return null;
606                 }
607                 // this get's a file using the full path ( replaces vala->bjs if they exist);
608                 
609                 public JsRender.JsRender? getByPath(string path)
610                 {
611                  
612                         // keys are not paths...
613                         foreach(var f in this.files.values) {
614                                 GLib.debug("check %s = %s ? %s", path, f.path, f.targetName());
615                                 if (f.path == path || f.targetName() == path) {
616                                         return f;
617                                 }
618                         };
619                         return null;                    
620                 }
621                 
622                 public JsRender.JsRender? getById(string id)
623                 {
624                         foreach(var f in this.files.values) {
625                                 if (f.id == id) {
626                                         return f;
627                                 }
628                         };
629                         return null;
630                 }
631  
632                 // name should include extension.       
633                 /*
634                 public JsRender.JsRender? newFile (string xtype, string sub_dir, string name)
635                 {
636                         try {
637                                 var fp = this.path + (sub_dir.length > 0  ? "/" : "") + sub_dir;
638                                 if (this.files.has_key(fp + "/" +  name)) {
639                                         return null;
640                                 }
641                                  
642                                 
643                                 var ret =  JsRender.JsRender.factory(xtype, 
644                                                                                          this, 
645                                                                                          fp + "/" +  name
646                                                                                          );
647                                 this.files.set(fp + "/" +  name , ret);
648                                 return ret;
649                         } catch (JsRender.Error e) {
650                                 GLib.error("failed to create file %s", e.message);
651                         }
652                 }
653                 */
654          
655                 public JsRender.JsRender loadFileOnly (string path)
656                 {
657                         var xt = this.xtype;
658                         try {
659                                 return JsRender.JsRender.factory(xt, this, path);
660                         } catch (JsRender.Error e) {
661                                 GLib.error("failed to create file %s", e.message);
662                         } 
663                         
664                 } 
665                 
666                 /* 
667                 public JsRender.JsRender create(string filename)
668                 {
669                         var ret = this.loadFileOnly(filename);
670                         ret.save();
671                         this.addFile(ret);
672                         return ret;
673                         
674                 }
675                 */
676                 private void loadSubDirectories(string subdir, int dp) 
677                 {
678                         //dp = dp || 0;
679                         //print("Project.Base: Running scandir on " + dir +"\n");
680                         if (dp > 5) { // no more than 5 deep?
681                                 return;
682                         }
683                         if (subdir == "build") { // cmake!
684                                 return;
685                         }
686                         
687                         if (subdir == "autom4te.cache") { // automake?
688                                 return;
689                         }
690                         if (subdir == "debian") { // debian!?
691                                 return;
692                         }
693
694                         
695                         var dir = this.path + (subdir.length > 0 ? "/" : "") + subdir;
696                         
697                         
698                         GLib.debug("Project %s Scan Dir: %s", this.name, dir);
699                         var jsDir = new JsRender.Dir(this, dir);
700                         this.sub_paths.add(jsDir); // might be ''...
701                         
702                         
703                         // this should be done async -- but since we are getting the proto up ...
704                         var other_files = new Gee.ArrayList<string>();
705                         var bjs_files = new Gee.ArrayList<string>();
706                         var vala_files = new Gee.ArrayList<string>();
707                         var subs = new Gee.ArrayList<string>();
708                         
709                         
710                         var f = File.new_for_path(dir);
711                         try {
712                                 var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
713                                 
714                                  
715                                 FileInfo next_file; 
716                                 while ((next_file = file_enum.next_file(null)) != null) {
717                                         var fn = next_file.get_display_name();
718                         
719                                          
720                                         //print("trying"  + dir + "/" + fn +"\n");
721                                         
722                                         if (fn[0] == '.') { // skip hidden
723                                                 continue;
724                                         }
725                                         
726                                         if (FileUtils.test(dir  + "/" + fn, GLib.FileTest.IS_DIR)) {
727                                                 subs.add(dir  + "/" + fn);
728                                                 continue;
729                                         }
730                                         if (Regex.match_simple("\\.(o|cache|gif|jpg|png|gif|out|stamp|~)$", fn)) { // object..
731                                                 continue;
732                                         }
733                                         if (Regex.match_simple("^(config1.builder|a.out|stamp-h1|depcomp|config.log|config.status)$", fn)) { // object..
734                                                 continue;
735                                         }
736                                         
737                                         
738                                         if (Regex.match_simple("\\.vala$", fn)) {
739                                                 vala_files.add(fn);
740                                                 other_files.add(fn);
741                                                 //print("no a bjs\n");
742                                                 continue;
743                                         }
744                                         if (!Regex.match_simple("\\.bjs$", fn)) {
745                                                 other_files.add(fn);
746                                                 //print("no a bjs\n");
747                                                 continue;
748                                         }
749                                         bjs_files.add(fn.substring(0, fn.length-4));
750                                         
751                                         var xt = this.xtype;
752                                         var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
753                                         this.files.set( dir + "/" + fn, el);
754                                         jsDir.childfiles.append(el);
755                                         
756                                         // parent ?? 
757                                         
758                                          
759                                 }
760                         } catch (Error e) {
761                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
762                         } catch (GLib.Error e) {
763                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
764                         } 
765
766                         foreach(var fn in other_files) {
767                                 var dpos = fn.last_index_of(".");
768                                 var without_ext = fn.substring(0, dpos);
769                                 if (bjs_files.contains(without_ext)) {  // will remove vala and c.
770                                         continue;
771                                 }
772                                 // c with a vala - skip
773                                 if (Regex.match_simple("\\.c$", fn) && vala_files.contains(without_ext + ".vala")) {
774                                         continue;
775                                 }
776                                 // Makefile (only allow am files at present.
777                                 if (without_ext == "Makefile") {
778                                         if (!Regex.match_simple("\\.am$", fn)) {
779                                                 continue;
780                                         }
781                                 }
782                                 if (without_ext == "configure") {
783                                         if (!Regex.match_simple("\\.ac$", fn)) {
784                                                 continue;
785                                         }
786                                 }
787                                 
788                                 
789                                 
790                                 
791                                 
792                                 //GLib.debug("Could have added %s/%s", dir, fn);
793                                 try {
794                                          var el = JsRender.JsRender.factory("PlainFile",this, dir + "/" + fn);
795                                          this.files.set( dir + "/" + fn, el);
796                                         jsDir.childfiles.append(el);
797                                 } catch (JsRender.Error e) {
798                                         GLib.warning("Project::scanDirs failed : " + e.message + "\n");
799                                 }
800                         }
801                         
802                         foreach (var sd in subs) {
803                                  this.loadSubDirectories(sd.substring(this.path.length+1), dp+1);
804                         }
805                         
806                 
807                 }
808                 
809                 // calle dfrom new file dialog
810                 // add files to dires (and main file list)
811                 // update 
812                         
813                  
814                 public void addFile(JsRender.JsRender pfile)
815                 { // add a single file, and trigger changed.
816                 
817                         if (pfile.xtype == "Gtk" || pfile.xtype == "Roo" ) {
818                                 this.files.set(pfile.path, pfile); // duplicate check
819                                 
820                                 if (pfile.xtype == "Gtk" && pfile.build_module != "") {
821                                 
822                                         var gfile = (JsRender.Gtk) pfile;
823                                         gfile.updateCompileGroup("", pfile.build_module);
824                                          
825                                 }
826                         }
827                         var sp = this.findDir(pfile.dir);
828                         sp.childfiles.append(pfile);    
829                         this.files.set(pfile.path, pfile);
830                         
831                         this.on_changed();
832                 }
833                 
834                 
835                 
836                 public void deleteFile(JsRender.JsRender file) 
837                 {
838                         if (file.xtype =="Dir") {
839                                 return;
840                         }
841                         var sp = this.findDir(file.dir);
842                         for(var i =0;i < sp.childfiles.n_items; i++) {
843                                 var jf = (JsRender.JsRender) sp.childfiles.get_item(i);
844                                 if (jf.path == file.path) {
845                                         sp.childfiles.remove(i);
846                                         break;
847                                 }
848                         }
849                         if (this.files.has_key(file.path)) {
850                                 this.files.unset(file.path);
851                         }
852
853                         
854                         file.remove();
855                         // remove it from 
856                         
857                         
858                 }
859                         
860                 // but do not add it to our list.!!!
861                 public void makeProjectSubdir(string name)
862                 {
863                         var dir = File.new_for_path(this.path + "/" + name);
864                         if (FileUtils.test(this.path + "/" + name, FileTest.EXISTS)) {
865                                 return;
866                         }
867                         try {
868                                  
869                                 dir.make_directory();   
870                         } catch (GLib.Error e) {
871                                 GLib.error("Failed to make directory %s", this.path + "/" + name);
872                         } 
873                 }
874                 
875                 public void createDir(string subdir)   // add a single dir, and trigger changed.
876                 {
877                         if (subdir.strip() == "" || this.subpathsContains(subdir)) {
878                                 return;
879                         }
880                         var dir= File.new_for_path(this.path + "/" + subdir);
881
882                         if (!dir.query_exists()) {
883                         
884                                 try {
885                                          
886                                         dir.make_directory();   
887                                 } catch (GLib.Error e) {
888                                         GLib.error("Failed to make directory %s", this.path + "/" + name);
889                                 }
890
891                         }
892                         this.sub_paths.add(new JsRender.Dir(this,this.path + "/" + subdir));
893                         this.on_changed();  // not sure if it's needed - adding a dir doesnt really change much.
894                 }
895                 
896                 // this store is used in the icon view ?? do we need to store and update it?
897                 public void loadFilesIntoStore(GLib.ListStore ls) 
898                 {
899                         ls.remove_all();
900                         //GLib.debug("Load files (into grid) %s", this.name);                   
901                         foreach(var f in this.files.values) {
902                         //      GLib.debug("Add file %s", f.name);
903                                 if (f.xtype == "PlainFile") {
904                                         continue;
905                                 }
906                                 ls.append(f);
907                         }
908                         
909                 }
910                 public void loadDirsIntoStore(GLib.ListStore  ls) 
911                 {
912                         ls.remove_all();
913                         foreach(var f in this.sub_paths) {
914                                 //GLib.debug("Add %s", f.name);
915                                 ls.append(f);
916                         }
917                          ;
918                 }
919                 
920                 public bool subpathsContains(string subpath) 
921                 {
922                         foreach(var sp in this.sub_paths) {
923
924                                 if (sp.path == this.path + "/" + subpath) {
925                                         return true;
926                                 }
927                         }
928                         return false;
929                         
930                 }
931                 
932                 public void loadDirsToStringList( global::Gtk.StringList sl, string prefix) 
933                 {
934                          
935                         while (sl.get_n_items() > 0) {
936                                 sl.remove(0);
937                         }
938                         
939                         foreach(var sp in this.sub_paths) {
940                                  var add = sp.path == this.path ? "/" : sp.path.substring(this.path.length);
941                                 if (prefix.length > 0 && !add.has_prefix(prefix)) {
942                                         continue;
943                                 }
944                                 sl.append(add);
945                         }
946                 
947                 }
948                 
949                 public JsRender.Dir? findDir(string path) {
950                         
951                         foreach(var jdir in this.sub_paths) { 
952                                 if (path == jdir.path) {
953                                         return (JsRender.Dir)jdir;
954                                 }
955                         }
956                         return null;
957                 }
958                 
959                 public string[] pathsMatching(string name, bool full_path)
960                 {
961                         string[] ret = {};
962                          
963                         foreach(var jdir in this.sub_paths) { 
964                                 
965
966                                 
967                                 if (Path.get_basename (jdir.path) == name) {
968                                         GLib.debug("pathsMatching %s\n", jdir.path);
969                                         ret += full_path ? jdir.path : jdir.relpath;
970                                 }
971                                 
972                         }
973                         return ret;
974                         
975                 }
976                 public Gee.ArrayList<string> readArray(Json.Array ar) 
977                 {
978                         var ret = new Gee.ArrayList<string>();
979                         for(var i =0; i< ar.get_length(); i++) {
980                                 var add = ar.get_string_element(i);
981                                 if (ret.contains(add)) {
982                                         continue;
983                                 }
984                         
985                                 ret.add(add);
986                         }
987                         return ret;
988                 }
989                 
990                 public abstract void onSave(); // write meson?
991                 public abstract void initDatabase();
992                 public abstract void initialize(); // for new projects (make dirs?);
993                 public abstract void loadJson(Json.Object obj); 
994                 public abstract void saveJson(Json.Object obj);
995                   
996         }
997 }
998