src/JsRender/NodeToGtk.vala
[app.Builder.js] / src / JsRender / NodeToGtk.vala
1 /*
2
3 * This code renders the Gtk tree into a set of Gtk elements.
4 * principle = one NodeToGtk wraps around the original 'node'
5 *  
6 * it's called by the view element with
7 *       var x = new JsRender.NodeToGtk(file.tree);
8      var obj = x.munge() as Gtk.Widget;
9        
10 *
11
12 * The idea behind the Javascript tools stuff is that we can 
13 * transform what is actually being requested to be rendered
14 * -- eg. an AboutBox, and turn that into load of real widgets..
15 * that could be displayed..
16
17 * we could go on the theory that we send the whole tree to the 'plugin'
18 * and that would do all the transformations before rendering..
19 * -- this would make more sense...
20 * -- otherwise we would call it on each element, and might get really confused
21 * about scope etc..
22
23
24
25 */
26 public class JsRender.NodeToGtk : Object {
27
28         Node node;
29         Object wrapped_object; 
30         NodeToGtk parentObj;
31         
32         Gee.ArrayList<NodeToGtk> children;
33         
34         Gee.ArrayList<string> els;
35         //Gee.ArrayList<string> skip;
36         Gee.HashMap<string,string> ar_props;
37         public static int vcnt = 0; 
38
39         public NodeToGtk( Node node , NodeToGtk? parent_obj = null) 
40         {
41                 this.node = node;
42                 this.els = new Gee.ArrayList<string>(); 
43                 this.children = new Gee.ArrayList<NodeToGtk>(); 
44                 //this.skip = new Gee.ArrayList<string>();
45                 this.ar_props = new Gee.HashMap<string,string>();
46                 this.parentObj = parent_obj;
47                 
48                 if (parent_obj == null) {
49                         // then serialize up the node,
50                         // send it to javascript for processsing,
51                         // then rebuild node from return value..
52                         try {
53                                 var ret = Palete.Javascript.singleton().executeFile(
54                                                 BuilderApplication.configDirectory() + "/resources/node_to_gtk.js",
55                                                 "node_to_gtk",
56                                                 node.toJsonString()
57                                 );
58                                 var new_node = new Node();
59                                 var pa = new Json.Parser();
60                                 pa.load_from_data(ret);
61                                 var rnode = pa.get_root();
62                            
63                                 
64                                 new_node.loadFromJson(rnode.get_object(), 2);
65                                 this.node = new_node;
66                                 
67                         } catch (Palete.JavascriptError e) {
68                                 print("Error: %s\n", e.message);
69                         }
70                         
71                         
72                 }
73                 
74         }
75         
76         public Object? munge ( )
77         {
78                 var ret = this.mungeNode();
79                 if (ret == null) {
80                         return null;
81                 }
82                         
83                 return ret.wrapped_object;
84                       
85         }
86         public NodeToGtk? mungeChild(  Node cnode)
87         {
88                 var x = new  NodeToGtk(cnode, this);
89                 
90                 return x.mungeNode();
91                 
92         }
93         
94         public NodeToGtk? mungeNode()
95         {
96                 
97                 var parent = this.parentObj != null ? this.parentObj.wrapped_object : null;
98                 var cls = this.node.fqn().replace(".", "");
99                 var ns = this.node.fqn().split(".")[0];
100                 var gtkbuilder = new global::Gtk.Builder();
101
102                 var cls_gtype = gtkbuilder.get_type_from_name(cls);
103                 print("Type: %s ?= %s\n", this.node.fqn(), cls_gtype.name());
104
105                 if (cls_gtype == GLib.Type.INVALID) {
106                         print("SKIP - gtype is invalid\n");
107                         return null;
108                 }
109                 // if it's a window...  -- things we can not render....
110
111                 if (cls_gtype.is_a(typeof(global::Gtk.Window))) {
112                         // what if it has none...
113                         if (this.node.items.size < 1) {
114                                 return null;
115                         }
116                         return this.mungeChild(this.node.items.get(0));
117                 }
118                 if (cls_gtype.is_a(typeof(global::Gtk.Popover))) {
119                         // what if it has none...
120                         if (this.node.items.size < 1) {
121                                 return null;
122                         }
123                         return this.mungeChild(this.node.items.get(0));
124                 }
125
126                 var ret = Object.new(cls_gtype);
127                 ret.ref(); //??? problematic?
128                 this.wrapped_object = ret;
129                 
130                  
131                 switch(cls) {
132                         // fixme
133                         //case "GtkTreeStore": // top level.. - named and referenced
134                         case "GtkListStore": // top level.. - named and referenced
135                         //case "GtkTreeViewColumn": // part of liststore?!?!
136                         //case "GtkMenu": // top level..
137                         //case "GtkCellRendererText":
138                         case "GtkSourceBuffer":                         
139                         case "GtkClutterActor"://fixme..
140                         case "GtkClutterEmbed"://fixme.. -- we can not nest embedded.. need to solve..
141                                         
142                                 return null;
143                 }
144
145                 this.packParent();
146                 
147
148                 // pack paramenters
149
150                 
151                 if (parent != null && parent.get_type().is_a(typeof(global::Gtk.Container))) {
152                         this.packContainerParams();
153                 }
154                 
155                 var cls_gir =Palete.Gir.factoryFqn(this.node.fqn()); 
156                 if (cls_gir == null) {
157                         return null;
158                 }
159                 //var id = this.node.uid();
160                 //var ret = @"$pad<object class=\"$cls\" id=\"$id\">\n";
161                 // properties..
162                 var props = cls_gir.props;
163                 
164               
165                 var pviter = props.map_iterator();
166                 while (pviter.next()) {
167                         
168                                 // print("Check: " +cls + "::(" + pviter.get_value().propertyof + ")" + pviter.get_key() + " " );
169                         var k = pviter.get_key();
170                         // skip items we have already handled..
171                         if  (!this.node.has(k)) {
172                                 continue;
173                         }
174                         // find out the type of the property...
175                         var type = pviter.get_value().type;
176                         type = Palete.Gir.fqtypeLookup(type, ns);
177
178                         var  ocl = (ObjectClass) cls_gtype.class_ref ();
179                         var ps = ocl.find_property(k);
180                         
181                         // attempt to read property type and enum...
182                         
183                         if (ps != null) {
184                                 var vt = ps.value_type;
185                                 if (vt.is_enum()) {
186                                         
187                                         var raw_val = this.node.get(k).strip();
188                                         var rv_s = raw_val.split(".");
189                                         if (rv_s.length > 0) {
190                                                 raw_val = rv_s[rv_s.length-1];                                  
191                                                 EnumClass ec = (EnumClass) vt.class_ref ();
192                                                 var foundit = false;
193                                                 for (var i =0;i< ec.n_values; i++) {
194                                                         var ev = ec.values[i].value_name;
195                                                         var ev_s= ev.split("_");
196                                                         if (raw_val == ev_s[ev_s.length-1]) {
197                                                                 var sval = GLib.Value(typeof(int));
198                                                                 sval.set_int(ec.values[i].value);
199                                                                 ret.set_property(k, sval);
200                                                                 foundit = true;
201                                                                 break;
202                                                         }
203                                                 }
204                                                 if (foundit) {
205                                                         continue;
206                                                 }
207                                                         
208                                         }
209                                 }
210                         }
211
212
213                         
214
215                         var val = this.toValue(this.node.get(k).strip(), type);
216                         if (val == null) {
217                                 print("skip (failed to transform value %s type = %s from %s\n", 
218                                         cls + "." + k, type,  this.node.get(k).strip());
219                                 continue;
220                         }
221                         print ("set_property ( %s , %s / %s)\n", k, this.node.get(k).strip(), val.strdup_contents());
222                         
223                         
224                         ret.set_property(k, val);  
225                         
226
227                 }
228                 // packing???
229                 // for now... - just try the builder style packing
230                 
231                 
232                  
233                 if (this.node.items.size < 1) {
234                         return this;
235                 }
236                 
237                 for (var i = 0; i < this.node.items.size; i++ ) {
238
239                          var ch = this.mungeChild(this.node.items.get(i));
240                          if (ch != null) {
241                                  this.children.add(ch);
242                          }
243                          
244                 }
245                 
246                 this.afterChildren();
247                 
248                 return this;
249                 
250
251                  
252
253         }
254         
255         public void  afterChildren()
256         {
257                 // things like GtkNotebook - we have to pack children after they have been created..
258                 var cls = this.node.fqn().replace(".", "");
259                 
260                 if (cls == "GtkNotebook") {
261                         this.afterChildrenGtkNotebook();
262                 }
263                 
264                 
265                 
266         }
267         
268         public void  afterChildrenGtkNotebook()
269         {
270                 // we have a number of children..
271                 // some are labels - this might need to be more complex...
272                 // perhaps labels should be a special property labels[] of the notebook..
273                 var labels = new Gee.ArrayList<NodeToGtk>();
274                 var bodies = new Gee.ArrayList<NodeToGtk>();
275                 for (var i = 0; i < this.children.size; i++) { 
276                         var cn = this.children.get(i).node.fqn().replace(".", "");
277                         if (cn != "GtkLabel") {
278                                 bodies.add(this.children.get(i));
279                                 continue;
280                         }
281                         labels.add(this.children.get(i));
282                 }
283                 for (var i = 0; i < bodies.size; i++) {
284                         if (i => labels.size) {
285                                 // more bodies than labels..
286                                 continue;
287                         }
288                         ((global::Gtk.Notebook)this.wrapped_object).append_page(
289                                  (global::Gtk.Notebook) bodies.get(i).wrapped_object,
290                                  (labels.size > i - 1) ? 
291                                         (global::Gtk.Notebook) labels.get(i).wrapped_object :
292                                         null
293                                         
294                         );
295                         
296                         
297                 }
298                 
299                 
300         }
301         
302         
303         /**
304          * called after the this.object  has been created
305          * and it needs to be packed onto parent.
306          */
307         public void packParent() 
308         {
309                 var cls = this.node.fqn().replace(".", "");
310                 
311                 var gtkbuilder = new global::Gtk.Builder();
312                 var cls_gtype = gtkbuilder.get_type_from_name(cls);
313
314                 if (this.parentObj == null) {
315                         return;
316                 }
317                                 
318                     
319                 var parent = this.parentObj.wrapped_object;
320                 
321                 var do_pack =true;
322
323                 if (parent == null) { // no parent.. can not pack.
324                         return; /// 
325                 }
326                 // -------------  handle various special parents .. -----------
327                 
328                 var par_type = this.parentObj.node.fqn().replace(".", "");
329                 
330                 if (par_type == "GtkNotebook") {
331                         // do not pack - it's done afterwards...
332                         return;
333                 }
334                 
335                 // -------------  handle various child types.. -----------
336                 // our overrides
337                 if (cls == "GtkMenu") {
338                         this.packMenu();
339                         return;
340                 }
341
342                 if (cls == "GtkTreeStore") { // other stores?
343                         // tree store is buildable??? --- 
344                         this.packTreeStore();
345                         return;
346                 }
347                 if (cls =="GtkTreeViewColumn") { // other stores?
348                         //?? treeview column is actually buildable -- but we do not use the builder???
349                         this.packTreeViewColumn();
350                         return;
351                 }
352                 if (cls_gtype.is_a(typeof(global::Gtk.CellRenderer))) { // other stores?
353                         this.packCellRenderer();
354                         return;
355                 }
356
357
358                 
359                 // -- handle buildable add_child..
360                 if (    cls_gtype.is_a(typeof(global::Gtk.Buildable))
361                      && 
362                         parent.get_type().is_a(typeof(global::Gtk.Buildable))
363                 )
364                 {
365                         ((global::Gtk.Buildable)parent).add_child(gtkbuilder, 
366                                                   this.wrapped_object, null);
367                         return;
368                 }
369                 // other packing?
370
371                 
372
373         }
374
375         public void packMenu()
376         {
377
378
379                 var parent = this.parentObj.wrapped_object;
380                 if (!parent.get_type().is_a(typeof(global::Gtk.Widget))) {
381                         print("skip menu pack - parent is not a widget");
382                         return;
383                 }
384                 
385                 var p = (global::Gtk.Menu)this.wrapped_object;
386                 ((global::Gtk.Widget)parent).button_press_event.connect((s, ev) => { 
387                         p.set_screen(Gdk.Screen.get_default());
388                         p.show_all();
389                         p.popup(null, null, null, ev.button, ev.time);
390                         return true;
391                 });
392         }
393
394         public void packTreeStore()
395         {
396                 var parent = this.parentObj.wrapped_object;
397                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
398                         print("skip treestore pack - parent is not a treeview");
399                         return;
400                 }
401                 ((global::Gtk.TreeView)parent).set_model((global::Gtk.TreeModel)this.wrapped_object);
402                 
403         }
404         public void packTreeViewColumn()
405         {
406                 var parent = this.parentObj.wrapped_object;
407                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
408                         print("skip packGtkViewColumn pack - parent is not a treeview");
409                         return;
410                 }
411                 ((global::Gtk.TreeView)parent).append_column((global::Gtk.TreeViewColumn)this.wrapped_object);
412                 // init contains the add_attribute for what to render...
413                 
414         }       
415
416
417         public void packCellRenderer()
418         {
419                 var parent = this.parentObj.wrapped_object;
420                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeViewColumn))) {
421                         print("skip packGtkViewColumn pack - parent is not a treeview");
422                         return;
423                 }
424                 ((global::Gtk.TreeViewColumn)parent).pack_start((global::Gtk.CellRenderer)this.wrapped_object, false);
425                 // init contains the add_attribute for what to render...
426                 
427         }       
428
429
430         public void packContainerParams()
431         {
432          
433                 if (this.parentObj == null) {
434                         return;
435                 }
436                 // child must be a widget..
437                 if (!this.wrapped_object.get_type().is_a(typeof(global::Gtk.Widget))) {
438                         return;
439                 }
440                 
441                 var parent_gir = Palete.Gir.factoryFqn(this.parentObj.node.fqn());
442
443                 var parent = this.parentObj.wrapped_object;
444                 
445                 if (parent_gir == null) {
446                         return;
447                 }
448                 
449                 // let's test just setting expand to false...
450                 var cls_methods = parent_gir.methods;
451                 if (cls_methods == null) {
452                         return;
453                 }
454         
455                 if (!this.node.props.has_key("* pack") || 
456                                 this.node.props.get("* pack").length < 1) {
457                         return;
458                 }
459                 
460                 var ns = this.parentObj.node.fqn().split(".")[0];
461                  
462                 var pack = this.node.props.get("* pack").split(",");
463
464         
465                 if (cls_methods.has_key(pack[0])) {
466                         var mparams = cls_methods.get(pack[0]).paramset.params;
467                         for (var i = 1; i < mparams.size; i++ ) {
468                                 if (i > (pack.length -1)) {
469                                         continue;
470                                 }
471                         
472                                 var k = mparams.get(i).name;
473
474                                 Value cur_val;
475                                  
476                                 var type = mparams.get(i).type;
477                                 type = Palete.Gir.fqtypeLookup(type, ns);
478
479                                 var val = this.toValue(pack[i].strip(), type);
480                                 if (val == null) {
481                                         print("skip (failed to transform value %s type = %s from %s\n", 
482                                                 this.parentObj.node.fqn()  + "." + k, type, pack[i].strip());
483                                         continue;
484                                 }
485                                 print ("pack:set_property ( %s , %s / %s)\n", k, pack[i].strip(), val.strdup_contents());
486         
487                                 ((global::Gtk.Container)parent).child_set_property(
488                                         (global::Gtk.Widget)this.wrapped_object , k, val);
489                                  
490                         }
491                 
492                 }
493         
494
495
496                         
497         }
498                    
499
500         public GLib.Value? toValue(string val, string type) {
501
502                 
503                 /*
504                 if (type == "string") {
505                         var qret = GLib.Value(typeof(string));
506                         qret.set_string(val);
507                         return qret;
508                 }
509                 * */
510                 /*
511                  * 
512            * var gtkbuilder = new global::Gtk.Builder();
513                 var prop_gtype = gtkbuilder.get_type_from_name(type);
514                 
515
516                 if (prop_gtype == GLib.Type.INVALID) {
517                          
518                         return null;
519                 }
520                 */
521                 
522                  
523
524
525                 switch(type) {
526                         case "bool":
527                                 var ret = GLib.Value(typeof(bool));
528                                 ret.set_boolean(val.down() == "false" ? false : true);
529                                 return ret;
530                                 
531                         case "uint":
532                                 var ret = GLib.Value(typeof(uint));
533                                 ret.set_uint(int.parse(val));
534                                 return ret;
535                                 
536                         case "int":
537                                 var ret = GLib.Value(typeof(int));
538                                 ret.set_int(int.parse(val));
539                                 return ret;
540
541                         // uint64 ...??
542
543                         case "long":
544                                 var ret = GLib.Value(typeof(long));
545                                 ret.set_long((long)int64.parse(val));
546                                 return ret;
547                         
548                         case "ulong":
549                                 var ret = GLib.Value(typeof(ulong));
550                                 ret.set_ulong((ulong) uint64.parse(val));
551                                 return ret;
552
553                         case "float":
554                                 var ret = GLib.Value(typeof(float));
555                                 ret.set_float((float)double.parse(val));
556                                 return ret;
557                                 
558                         case "string":
559                                 var ret = GLib.Value(typeof(string));
560                                 ret.set_string(val);
561                                 return ret;
562
563                         default:
564                                 return null;
565                                 /*
566                                 var sval =  GLib.Value(typeof(string));
567                                 sval.set_string(val);
568                         
569                                 if (!sval.transform(ref ret)) {
570                                 
571                                         return null;
572                                 }
573                                 return ret;
574                                 */
575                 }
576                 // should not get here..
577                 return null;
578         }
579         
580          
581           
582                 
583 }