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