Fix #7959 - new project flow - use void func to callback after each step, tidy up...
[roobuilder] / src / JsRender / JsRender.vala
1 //<Script type="text/javascript">
2
3 namespace JsRender {
4
5
6         public errordomain Error {
7                 INVALID_FORMAT,
8                 RENAME_FILE_EXISTS
9         }
10                 
11         public abstract class JsRender  : Object {
12                 /**
13                  * @cfg {Array} doubleStringProps list of properties that can be double quoted.
14                  */
15                 public Gee.ArrayList<string> doubleStringProps;
16                 
17                 public string id  = "";
18                 public string name { get; set; default = ""; }   // is the JS name of the file.
19                 public string fullname = "";
20                 public string path = "";  // is the full path to the file.
21                 
22                 public  string relpath {
23                         owned get { 
24                                 return  this.project.path  == this.path ? "" : this.path.substring(this.project.path.length+1);
25                         } 
26                         private set {}
27                 }
28                 public  string reldir {
29                         owned get { 
30                                 return  this.project.path == this.dir ? "" : this. dir.substring(this.project.path.length+1);
31                         } 
32                         private set {}
33                 }
34                 
35                 public  string  dir {
36                         owned get { 
37                                 return GLib.Path.get_dirname(this.path);
38                                  
39                         } 
40                         private set {}
41                 }
42                 
43                 public string file_namespace {
44                         public owned get {
45                                 if (!this.name.contains(".")) {
46                                         return "";
47                                 }
48                                 var bits = this.name.split(".");
49                                 return bits[0];
50                         }
51                         private set {}
52                 }
53                 public string file_without_namespace {
54                         public  owned get {
55                                 if (!this.name.contains(".")) {
56                                         return this.name;
57                                 }
58                                 var bits = this.name.split(".");
59                                 return this.name.substring(bits[0].length +1);
60                         }
61                         private set {}
62                 }
63                 
64                 public string file_ext {
65                         public owned get {
66                                 if (!this.path.contains(".")) {
67                                         return "";
68                                 }
69                                 var bits = this.name.split(".");
70                                 return bits[bits.length-1];
71                         }
72                         private set {}
73                 }
74                 public string parent = "";  // JS parent.
75                 public string region = "";  // RooJS - insert region.
76         
77                 public string title = "";  // a title.. ?? nickname.. ??? -
78
79                 
80
81                 public string permname;
82                 public string language;
83                 public string content_type;
84                 public string modOrder;
85                 public string xtype;
86                 public uint64 webkit_page_id; // set by webkit view - used to extract extension/etc..
87                 public bool gen_extended  = false; // nodetovala??
88
89                 public Project.Project project;
90
91                 // GTK Specifc
92  
93                 public string build_module; // module to build if we compile (or are running tests...)      
94
95                 //Project : false, // link to container project!
96                 
97                 public Node tree; // the tree of nodes.
98                 
99                 //public GLib.List<JsRender> cn; // child files.. (used by project ... should move code here..)
100
101                 public bool hasParent; 
102                 
103                 public bool loaded;
104                 
105                 public Gee.HashMap<string,string> transStrings; // map of md5 -> string.
106                 public  Gee.HashMap<string,string> namedStrings;
107
108                 public signal void changed (Node? node, string source); 
109                 
110                  
111                 public signal void compile_notice(string type, string file, int line, string message);
112                 /**
113                  * UI componenets
114                  * 
115                  */
116                 //public Xcls_Editor editor;
117                 public GLib.ListStore childfiles; // used by directories..
118                 
119                 
120                 //abstract JsRender(Project.Project project, string path); 
121                 
122                 public void aconstruct(Project.Project project, string path)
123                 {
124                     
125                         //this.cn = new GLib.List<JsRender>();
126                         this.path = path;
127                         this.project = project;
128                         this.hasParent = false;
129                         this.parent = "";
130                         this.tree = null; 
131                         this.title = "";
132                         this.region = "";
133                         this.permname = "";
134                         this.modOrder = "";
135                         this.language = "";
136                         this.content_type = "";
137                         this.build_module = "";
138                         //this.loaded = false;
139                         //print("JsRender.cto() - reset transStrings\n");
140                         this.transStrings = new Gee.HashMap<string,string>();
141                         this.namedStrings = new Gee.HashMap<string,string>();
142                         // should use basename reallly...
143                         
144                         var ar = this.path.split("/");
145                         // name is in theory filename without .bjs (or .js eventually...)
146                         try {
147                                 Regex regex = new Regex ("\\.(bjs)$");
148
149                                 this.name = ar.length > 0 ? regex.replace(ar[ar.length-1],ar[ar.length-1].length, 0 , "") : "";
150                         } catch (GLib.Error e) {
151                                 this.name = "???";
152                         }
153                         this.fullname = (this.parent.length > 0 ? (this.parent + ".") : "" ) + this.name;
154
155                         this.doubleStringProps = new Gee.ArrayList<string>();
156                         this.childfiles = new GLib.ListStore(typeof(JsRender));
157
158                 }
159                 
160                 public void renameTo(string name) throws  Error
161                 {
162                         if (this.xtype == "PlainFile") {
163                                 return;
164                         }
165                         var bjs = GLib.Path.get_dirname(this.path) +"/" +  name + ".bjs";
166                         if (FileUtils.test(bjs, FileTest.EXISTS)) {
167                                 throw new Error.RENAME_FILE_EXISTS("File exists %s\n",name);
168                         }
169                         GLib.FileUtils.remove(this.path);
170                         this.removeFiles();
171                         // remove other files?
172                         
173                         this.name = name;
174                         this.path = bjs;
175                         
176                 }
177                 
178
179                 
180                 // not sure why xt is needed... -> project contains xtype..
181                 
182                 public static JsRender factory(string xt, Project.Project project, string path) throws Error
183                 {
184          
185                         switch (xt) {
186                                 case "Gtk":
187                                         return new Gtk(project, path);
188                                         
189                                 case "Roo":
190                                         return new Roo((Project.Roo) project, path);
191 //                      case "Flutter":
192 //                                      return new Flutter(project, path);
193                                 case "PlainFile":
194                                         return new PlainFile(project, path);
195                         }
196                         throw new Error.INVALID_FORMAT("JsRender Factory called with xtype=%s", xt);
197                         //return null;    
198                 }
199                 
200         
201         
202                 public string nickType()
203                 {
204                         var ar = this.name.split(".");
205                         string[] ret = {};
206                         for (var i =0; i < ar.length -1; i++) {
207                                 ret += ar[i];
208                         }
209                         return string.joinv(".", ret);
210                         
211                 }
212                 public string nickName()
213                 {
214                         var ar = this.name.split(".");
215                         return ar[ar.length-1];
216                         
217                 }
218                 
219                 public string nickNameSplit()
220                 {
221                         var n = this.nickName();
222                         var ret = "";
223                         var len = 0;
224                         for (var i = 0; i < n.length; i++) {
225                                 if (i!=0 && n.get(i).isupper() && len > 10) {
226                                         ret +="\n";
227                                         len= 0;
228                                 }
229                                 ret += n.get(i).to_string();
230                                 len++;
231                         }
232                         
233
234                         
235                         return ret;
236                 
237                 }
238                 
239                 Gdk.Pixbuf screenshot = null;
240                 Gdk.Pixbuf screenshot92 = null;
241                 Gdk.Pixbuf screenshot368 = null;
242                 
243                 public Gdk.Pixbuf? getIcon(int size = 0) {
244                     var fname = this.getIconFileName( );                
245                     if (!FileUtils.test(fname, FileTest.EXISTS)) {
246                 GLib.debug("PIXBUF %s:  %s does not exist?", this.name, fname);
247                                 return null;
248                         }
249                         
250                         switch (size) {
251                                 case 0:
252                                         if (this.screenshot == null) {
253                                                 try { 
254                                                         this.screenshot = new Gdk.Pixbuf.from_file(fname);
255                                                 } catch (GLib.Error e) {}
256                                         }
257                                         return this.screenshot;
258                                 
259                                 case 92:
260                                         
261                                         if (this.screenshot == null) {
262                                                 this.getIcon(0);
263                                                 if (this.screenshot == null) {
264                                                         return null;
265                                                 }
266                                         }
267                                         
268                                         this.screenshot92 = this.screenshot.scale_simple(92, (int) (this.screenshot.height * 92.0 /this.screenshot.width * 1.0 )
269                                                 , Gdk.InterpType.NEAREST) ;
270                                     return this.screenshot92;
271                             
272                             case 368:
273                                         if (this.screenshot == null) {
274                                                 this.getIcon(0);
275                                                 if (this.screenshot == null) {
276                                                         return null;
277                                                 }
278                                         }
279                                         
280                                         this.screenshot368 = this.screenshot.scale_simple(368, (int) (this.screenshot.height * 368.0 /this.screenshot.width * 1.0 )
281                                     , Gdk.InterpType.NEAREST) ;
282                                     return this.screenshot368;
283                     }
284                     return null;
285                 }
286                 
287                 public void writeIcon(Gdk.Pixbuf pixbuf) {
288                         
289                         this.screenshot92 = null;
290                         this.screenshot368 = null;
291                         this.screenshot = null;
292                         try {
293                                 GLib.debug("Wirte %s", this.getIconFileName( ));
294                                 pixbuf.save(this.getIconFileName( ),"png");
295                                 this.screenshot = pixbuf;
296                         
297                         } catch (GLib.Error e) {
298                                 GLib.debug("failed to write pixbuf?");
299                         
300                         }
301                                 
302                          
303                         
304                 
305                 }
306                 public void widgetToIcon(global::Gtk.Widget widget) {
307                         
308                         this.screenshot92 = null;
309                         this.screenshot368 = null;
310                         this.screenshot = null;
311                         
312                         try {
313                         
314
315                         var filename = this.getIconFileName();
316                                 
317
318                                  var p = new global::Gtk.WidgetPaintable(widget);
319                                  var s = new global::Gtk.Snapshot();
320                                  GLib.debug("Width %d, Height %d", widget.get_width(), widget.get_height()); 
321                                  p.snapshot(s, widget.get_width(), widget.get_height());
322                                  var n = s.free_to_node();
323                                  var r = new  Gsk.CairoRenderer();
324                                  r.realize(null);
325                                  var t = r.render_texture(n,null);
326                                  GLib.debug("write to %s", filename);
327                                 t.save_to_png(filename);
328                                  r.unrealize();
329                                          
330                         
331                         } catch (GLib.Error e) {
332                                 GLib.debug("failed to write pixbuf?");
333                         
334                         }
335                                 
336                          
337                         
338                 
339                 }
340
341                 
342                 public string getIconFileName( )
343                 {
344                          
345                         var m5 = GLib.Checksum.compute_for_string(GLib.ChecksumType.MD5,this.path); 
346
347                         var dir = GLib.Environment.get_home_dir() + "/.Builder/icons";
348                         try {
349                                 if (!FileUtils.test(dir, FileTest.IS_DIR)) {
350                                          File.new_for_path(dir).make_directory();
351                                 }
352                         } catch (GLib.Error e) {
353                                 // eakk.. what to do here...
354                         }
355                         var fname = dir + "/" + m5 + ".png";
356                         
357                          
358                         return fname;
359                           
360                 }
361                 
362                 public string toJsonString()
363                 {
364                         if (this.xtype == "PlainFile") {
365                                 return "";
366                         }
367                         var node = new Json.Node(Json.NodeType.OBJECT);
368                         node.set_object(this.toJsonObject());                   
369                         var generator = new JsonGen(node);
370                     generator.indent = 1;
371                     generator.pretty = true;
372                     
373                         
374                         return generator.to_data();
375                 }
376                 
377                 public void saveBJS()
378                 {
379                    // if (!this.loaded) {
380                         ///     GLib.debug("saveBJS - skip - not loaded?");
381                 //          return;
382                     //}
383                     if (this.xtype == "PlainFile") {
384                             return;
385                     }
386                    
387                      
388                     
389                     GLib.debug("WRITE :%s\n " , this.path);// + "\n" + JSON.stringify(write));
390                     try {
391                                 this.writeFile(this.path, this.toJsonString());
392                          
393                     } catch(GLib.Error e) {
394                         print("Save failed");
395                     }
396                 }
397                  
398                  
399
400
401                  
402                  
403                  
404                   
405                 public string jsonHasOrEmpty(Json.Object obj, string key) {
406                         return obj.has_member(key) ? 
407                                                 obj.get_string_member(key) : "";
408                 }
409
410                 
411                 public Json.Object toJsonObject ()
412                 {
413                     
414                     
415                         var ret = new Json.Object();
416                         if (this.xtype == "PlainFile") {
417                                 return ret;
418                         }
419                         
420                         //ret.set_string_member("id", this.id); // not relivant..
421                         ret.set_string_member("name", this.name);
422                         
423                         if (this.project.xtype == "Roo") {
424                                 ret.set_string_member("parent", this.parent == null ? "" : this.parent);
425                                 ret.set_string_member("title", this.title == null ? "" : this.title);
426                                 //ret.set_string_member("path", this.path);
427                                 //ret.set_string_member("items", this.items);
428                                 ret.set_string_member("permname", this.permname  == null ? "" : this.permname);
429                                 ret.set_string_member("modOrder", this.modOrder  == null ? "" : this.modOrder);
430                         }
431                         if (this.project.xtype == "Gtk") {
432  
433                                 ret.set_string_member("build_module", this.build_module  );
434                         }
435                         ret.set_boolean_member("gen_extended", this.gen_extended);
436                         
437                         if (this.transStrings.size > 0) {
438                                 var tr =  new Json.Object();
439                                 var iter = this.transStrings.map_iterator();
440                                 while (iter.next()) {
441                                         tr.set_string_member(iter.get_value(), iter.get_key());
442                                 }
443                                 ret.set_object_member("strings", tr);
444             }
445
446             
447             
448                         if (this.namedStrings.size > 0) {
449                                 var tr =  new Json.Object();
450                                 var iter = this.namedStrings.map_iterator();
451                                 while (iter.next()) {
452                                         tr.set_string_member(iter.get_key(), iter.get_value());
453                                 }
454                                 ret.set_object_member("named_strings", tr);
455             }
456                         
457                         var ar = new Json.Array();
458                         // empty files do not have a tree.
459                         if (this.tree != null) {
460                                 ar.add_object_element(this.tree.toJsonObject());
461                         }
462                         ret.set_array_member("items", ar);
463                 
464                     return ret;
465                 }
466                 
467                 
468
469                 public string getTitle ()
470                 {
471                     if (this.title == null) { // not sure why this happens..
472                         return "";
473                 }
474                     if (this.title.length > 0) {
475                         return this.title;
476                     }
477                     var a = this.path.split("/");
478                     return a[a.length-1];
479                 }
480                 public string getTitleTip()
481                 {
482                     if (this.title.length > 0) {
483                         return "<b>" + this.title + "</b> " + this.path;
484                     }
485                     return this.path;
486                 }
487                 /*
488                     sortCn: function()
489                     {
490                         this.cn.sort(function(a,b) {
491                             return a.path > b.path;// ? 1 : -1;
492                         });
493                     },
494                 */
495                     // should be in palete provider really..
496
497
498                 public Palete.Palete palete()
499                 {
500                         // error on plainfile?
501                         return this.project.palete;
502
503                 }
504                 
505                 public string guessName(Node ar) // turns the object into full name.
506                 {
507                      // eg. xns: Roo, xtype: XXX -> Roo.xxx
508                     if (!ar.hasXnsType()) {
509                        return "";
510                     }
511                     
512                     return ar.get("xns") + "." + ar.get("xtype");
513                                       
514                                         
515                 }
516                 /**
517                  *  non-atomic write (replacement for put contents, as it creates temporary files.
518                  */
519                 public void writeFile(string path, string contents) throws GLib.IOError, GLib.Error
520                 {
521
522                                  
523                         var f = GLib.File.new_for_path(path);
524                         var data_out = new GLib.DataOutputStream(
525                                           f.replace(null, false, GLib.FileCreateFlags.NONE, null)
526                        );
527                         data_out.put_string(contents, null);
528                         data_out.close(null);
529                 }
530                  
531                 
532                 
533                 public  Node? lineToNode(int line)
534                 {
535                         if (this.tree == null) {
536                                 return null;
537                         }
538                         return this.tree.lineToNode(line);
539                         
540                         
541                 }
542                 
543                 public GLib.ListStore toListStore()
544                 {
545                         var ret = new GLib.ListStore(typeof(Node));
546                         ret.append(this.tree);
547                         return ret;
548                 }
549                  
550                 
551                 // used to handle list of files in project editor (really Gtk only)
552                 public bool compile_group_selected {
553                         get {
554                                 var gproj = (Project.Gtk) this.project;
555                                 
556                                 if (gproj.active_cg == null) {
557                                         return false;
558                                 }
559                                 if (this.xtype == "Dir") {
560                                         // show ticked if all ticked..
561                                         var ticked = true;
562                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
563                                                 var f = (JsRender) this.childfiles.get_item(i);
564                                                 if (!f.compile_group_selected) {
565                                                         ticked = false;
566                                                         break;
567                                                 }
568                                         }
569                                         return ticked;
570                                 
571                                 
572                                 }
573                                 if (gproj.active_cg.sources == null) {
574                                         GLib.debug("compile_group_selected - sources is null? ");
575                                         return false;
576                                 }
577
578                                 return gproj.active_cg.sources.contains(this.relpath);
579                                 
580                         }
581                         set {
582                                 
583                                 var gproj = (Project.Gtk) this.project;
584                                 
585                                 if (gproj.active_cg == null) {
586                                         return;
587                                 }
588                                 if (gproj.active_cg.loading_ui) {
589                                         return;
590                                 }
591                                 
592                                 if (this.xtype == "Dir") {
593                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
594                                                 var f = (JsRender) this.childfiles.get_item(i);
595                                                 f.compile_group_selected = value;
596  
597                                         }
598                                         return;
599                                  
600                                 }
601                                 
602                                 
603                                 
604                                 if (value == false) {
605                                         GLib.debug("REMOVE %s", this.relpath);
606                                         
607                                         gproj.active_cg.sources.remove(this.relpath);
608                                         return;
609                                 }
610                                 if (!gproj.active_cg.sources.contains(this.relpath)) { 
611                                         GLib.debug("ADD %s", this.relpath);
612                                         gproj.active_cg.sources.add(this.relpath);
613                                 }
614                         
615                         }
616                 }
617                 /*
618                 public bool compile_group_hidden {
619                         get {
620                                 var gproj = (Project.Gtk) this.project;
621                                 
622                                 
623                                 return gproj.hidden.contains(this.relpath);
624                                 
625                         }
626                         set {
627                                 
628                                 var gproj = (Project.Gtk) this.project;
629                                 
630                                 if (gproj.active_cg == null) {
631                                         return;
632                                 }
633                                 if (gproj.active_cg.loading_ui) {
634                                         return;
635                                 } 
636                                 if (value == false) {
637                                         GLib.debug("REMOVE %s", this.relpath);
638                                         
639                                         gproj.hidden.remove(this.relpath);
640                                         return;
641                                 }
642                                 if (!gproj.hidden.contains(this.relpath)) { 
643                                         gproj.hidden.add(this.relpath);
644                                         // hiding a project will auto clear it.
645                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
646                                                 var f = (JsRender) this.childfiles.get_item(i);
647                                                 f.compile_group_selected = false;
648                                         }
649                                         return;
650                                         
651                                 }
652                         
653                         }
654                 }
655                 */
656                 public void remove()
657                 {
658                         if (this.xtype == "Dir") {
659                                 return;
660                         }
661                         // cleans up build (should really be in children..
662                         this.removeFile(this.path);
663                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Roo") {
664                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".js");
665                                 return;
666                         }
667                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Gtk") {
668                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".vala");
669                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".c");
670                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".o");                             
671                         }
672                         if (this.path.has_suffix(".vala") && this.project.xtype == "Gtk") {
673                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".c");
674                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".o");                             
675                         }       
676                 
677                 
678                 }
679                 
680                 private void removeFile(string path)
681                 {
682
683                         if (GLib.FileUtils.test(path, GLib.FileTest.EXISTS)) {
684                                 GLib.FileUtils.unlink(path);
685                         }
686
687                 }
688                 
689                 
690                 public abstract void save();
691                 public abstract void saveHTML(string html);
692                 public abstract string toSource() ;
693                 public abstract string toSourceCode() ; // used by commandline tester..
694                 public abstract void setSource(string str);
695                 public abstract string toSourcePreview() ;
696                 public abstract void removeFiles() ;
697                 public abstract void  findTransStrings(Node? node );
698                 public abstract string toGlade();
699                 public abstract string targetName();
700                 public abstract void loadItems() throws GLib.Error;
701         } 
702
703 }
704  
705