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