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... 
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
119                 var ret = Object.new(cls_gtype);
120                 ret.ref(); //??? problematic?
121                 this.wrapped_object = ret;
122                 
123                  
124                 switch(cls) {
125                         // fixme
126                         //case "GtkTreeStore": // top level.. - named and referenced
127                         case "GtkListStore": // top level.. - named and referenced
128                         //case "GtkTreeViewColumn": // part of liststore?!?!
129                         //case "GtkMenu": // top level..
130                         //case "GtkCellRendererText":
131                         case "GtkSourceBuffer":                         
132                         case "GtkClutterActor"://fixme..
133                         case "GtkClutterEmbed"://fixme.. -- we can not nest embedded.. need to solve..
134                                         
135                                 return null;
136                 }
137
138                 this.packParent();
139                 
140
141                 // pack paramenters
142
143                 
144                 if (parent != null && parent.get_type().is_a(typeof(global::Gtk.Container))) {
145                         this.packContainerParams();
146                 }
147                 
148                 var cls_gir =Palete.Gir.factoryFqn(this.node.fqn()); 
149                 if (cls_gir == null) {
150                         return null;
151                 }
152                 //var id = this.node.uid();
153                 //var ret = @"$pad<object class=\"$cls\" id=\"$id\">\n";
154                 // properties..
155                 var props = cls_gir.props;
156                 
157               
158                 var pviter = props.map_iterator();
159                 while (pviter.next()) {
160                         
161                                 // print("Check: " +cls + "::(" + pviter.get_value().propertyof + ")" + pviter.get_key() + " " );
162                         var k = pviter.get_key();
163                         // skip items we have already handled..
164                         if  (!this.node.has(k)) {
165                                 continue;
166                         }
167                         // find out the type of the property...
168                         var type = pviter.get_value().type;
169                         type = Palete.Gir.fqtypeLookup(type, ns);
170
171                         var val = this.toValue(this.node.get(k).strip(), type);
172                         if (val == null) {
173                                 print("skip (failed to transform value %s type = %s from %s\n", 
174                                         cls + "." + k, type,  this.node.get(k).strip());
175                                 continue;
176                         }
177                         print ("set_property ( %s , %s / %s)\n", k, this.node.get(k).strip(), val.strdup_contents());
178                         
179                         
180                         ret.set_property(k, val);  
181                         
182
183                 }
184                 // packing???
185                 // for now... - just try the builder style packing
186                 
187                 
188                  
189                 if (this.node.items.size < 1) {
190                         return this;
191                 }
192                 
193                 for (var i = 0; i < this.node.items.size; i++ ) {
194
195                          var ch = this.mungeChild(this.node.items.get(i));
196                          if (ch != null) {
197                                  this.children.add(ch);
198                          }
199                          
200                 }
201                 
202                 this.afterChildren();
203                 
204                 return this;
205                 
206
207                  
208
209         }
210         
211         public void  afterChildren()
212         {
213                 // things like GtkNotebook - we have to pack children after they have been created..
214                 var cls = this.node.fqn().replace(".", "");
215                 
216                 if (cls == "GtkNotebook") {
217                         this.afterChildrenGtkNotebook();
218                 }
219                 
220                 
221                 
222         }
223         
224         public void  afterChildrenGtkNotebook()
225         {
226                 // we have a number of children..
227                 // some are labels - this might need to be more complex...
228                 // perhaps labels should be a special property labels[] of the notebook..
229                 var labels = new Gee.ArrayList<NodeToGtk>();
230                 var bodies = new Gee.ArrayList<NodeToGtk>();
231                 for (var i = 0; i < this.children.size; i++) { 
232                         var cn = this.children.get(i).node.fqn().replace(".", "");
233                         if (cn != "GtkLabel") {
234                                 bodies.add(this.children.get(i));
235                                 continue;
236                         }
237                         labels.add(this.children.get(i));
238                 }
239                 for (var i = 0; i < bodies.size; i++) { 
240                         ((global::Gtk.Notebook)this.wrapped_object).append_page(
241                                  (global::Gtk.Notebook) bodies.get(i).wrapped_object,
242                                  (labels.size > i - 1) ? 
243                                         (global::Gtk.Notebook) labels.get(i).wrapped_object :
244                                         null
245                                         
246                         );
247                         
248                         
249                 }
250                 
251                 
252         }
253         
254         
255         /**
256          * called after the this.object  has been created
257          * and it needs to be packed onto parent.
258          */
259         public void packParent() 
260         {
261                 var cls = this.node.fqn().replace(".", "");
262                 
263                 var gtkbuilder = new global::Gtk.Builder();
264                 var cls_gtype = gtkbuilder.get_type_from_name(cls);
265
266                 if (this.parentObj == null) {
267                         return;
268                 }
269                                 
270                     
271                 var parent = this.parentObj.wrapped_object;
272                 
273                 var do_pack =true;
274
275                 if (parent == null) { // no parent.. can not pack.
276                         return; /// 
277                 }
278                 // -------------  handle various special parents .. -----------
279                 
280                 var par_type = this.parentObj.node.fqn().replace(".", "");
281                 
282                 if (par_type == "GtkNotebook") {
283                         // do not pack - it's done afterwards...
284                         return;
285                 }
286                 
287                 // -------------  handle various child types.. -----------
288                 // our overrides
289                 if (cls == "GtkMenu") {
290                         this.packMenu();
291                         return;
292                 }
293
294                 if (cls == "GtkTreeStore") { // other stores?
295                         // tree store is buildable??? --- 
296                         this.packTreeStore();
297                         return;
298                 }
299                 if (cls =="GtkTreeViewColumn") { // other stores?
300                         //?? treeview column is actually buildable -- but we do not use the builder???
301                         this.packTreeViewColumn();
302                         return;
303                 }
304                 if (cls_gtype.is_a(typeof(global::Gtk.CellRenderer))) { // other stores?
305                         this.packCellRenderer();
306                         return;
307                 }
308
309
310                 
311                 // -- handle buildable add_child..
312                 if (    cls_gtype.is_a(typeof(global::Gtk.Buildable))
313                      && 
314                         parent.get_type().is_a(typeof(global::Gtk.Buildable))
315                 )
316                 {
317                         ((global::Gtk.Buildable)parent).add_child(gtkbuilder, 
318                                                   this.wrapped_object, null);
319                         return;
320                 }
321                 // other packing?
322
323                 
324
325         }
326
327         public void packMenu()
328         {
329
330
331                 var parent = this.parentObj.wrapped_object;
332                 if (!parent.get_type().is_a(typeof(global::Gtk.Widget))) {
333                         print("skip menu pack - parent is not a widget");
334                         return;
335                 }
336                 
337                 var p = (global::Gtk.Menu)this.wrapped_object;
338                 ((global::Gtk.Widget)parent).button_press_event.connect((s, ev) => { 
339                         p.set_screen(Gdk.Screen.get_default());
340                         p.show_all();
341                         p.popup(null, null, null, ev.button, ev.time);
342                         return true;
343                 });
344         }
345
346         public void packTreeStore()
347         {
348                 var parent = this.parentObj.wrapped_object;
349                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
350                         print("skip treestore pack - parent is not a treeview");
351                         return;
352                 }
353                 ((global::Gtk.TreeView)parent).set_model((global::Gtk.TreeModel)this.wrapped_object);
354                 
355         }
356         public void packTreeViewColumn()
357         {
358                 var parent = this.parentObj.wrapped_object;
359                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
360                         print("skip packGtkViewColumn pack - parent is not a treeview");
361                         return;
362                 }
363                 ((global::Gtk.TreeView)parent).append_column((global::Gtk.TreeViewColumn)this.wrapped_object);
364                 // init contains the add_attribute for what to render...
365                 
366         }       
367
368
369         public void packCellRenderer()
370         {
371                 var parent = this.parentObj.wrapped_object;
372                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeViewColumn))) {
373                         print("skip packGtkViewColumn pack - parent is not a treeview");
374                         return;
375                 }
376                 ((global::Gtk.TreeViewColumn)parent).pack_start((global::Gtk.CellRenderer)this.wrapped_object, false);
377                 // init contains the add_attribute for what to render...
378                 
379         }       
380
381
382         public void packContainerParams()
383         {
384          
385                 if (this.parentObj == null) {
386                         return;
387                 }
388                 // child must be a widget..
389                 if (!this.wrapped_object.get_type().is_a(typeof(global::Gtk.Widget))) {
390                         return;
391                 }
392                 
393                 var parent_gir = Palete.Gir.factoryFqn(this.parentObj.node.fqn());
394
395                 var parent = this.parentObj.wrapped_object;
396                 
397                 if (parent_gir == null) {
398                         return;
399                 }
400                 
401                 // let's test just setting expand to false...
402                 var cls_methods = parent_gir.methods;
403                 if (cls_methods == null) {
404                         return;
405                 }
406         
407                 if (!this.node.props.has_key("* pack") || 
408                                 this.node.props.get("* pack").length < 1) {
409                         return;
410                 }
411                 
412                 var ns = this.parentObj.node.fqn().split(".")[0];
413                  
414                 var pack = this.node.props.get("* pack").split(",");
415
416         
417                 if (cls_methods.has_key(pack[0])) {
418                         var mparams = cls_methods.get(pack[0]).paramset.params;
419                         for (var i = 1; i < mparams.size; i++ ) {
420                                 if (i > (pack.length -1)) {
421                                         continue;
422                                 }
423                         
424                                 var k = mparams.get(i).name;
425
426                                 Value cur_val;
427                                  
428                                 var type = mparams.get(i).type;
429                                 type = Palete.Gir.fqtypeLookup(type, ns);
430
431                                 var val = this.toValue(pack[i].strip(), type);
432                                 if (val == null) {
433                                         print("skip (failed to transform value %s type = %s from %s\n", 
434                                                 this.parentObj.node.fqn()  + "." + k, type, pack[i].strip());
435                                         continue;
436                                 }
437                                 print ("pack:set_property ( %s , %s / %s)\n", k, pack[i].strip(), val.strdup_contents());
438         
439                                 ((global::Gtk.Container)parent).child_set_property(
440                                         (global::Gtk.Widget)this.wrapped_object , k, val);
441                                  
442                         }
443                 
444                 }
445         
446
447
448                         
449         }
450                    
451
452         public GLib.Value? toValue(string val, string type) {
453
454                 var gtkbuilder = new global::Gtk.Builder();
455
456                 if (type == "string") {
457                         var qret = GLib.Value(typeof(string));
458                         qret.set_string(val);
459                         return qret;
460                 }
461                 
462                 var prop_gtype = gtkbuilder.get_type_from_name(type);
463                 
464
465                 if (prop_gtype == GLib.Type.INVALID) {
466                          
467                         return null;
468                 }
469                 
470                 
471                 GLib.Value ret;
472
473
474                 switch(type) {
475                         case "bool":
476                                 ret = GLib.Value(typeof(bool));
477                                 ret.set_boolean(val.down() == "false" ? false : true);
478                                 return ret;
479                                 
480                         case "uint":
481                                 ret = GLib.Value(typeof(uint));
482                                 ret.set_uint(int.parse(val));
483                                 return ret;
484                                 
485                         case "int":
486                                 ret = GLib.Value(typeof(int));
487                                 ret.set_int(int.parse(val));
488                                 return ret;
489
490                         // uint64 ...??
491
492                         case "long":
493                                 ret = GLib.Value(typeof(long));
494                                 ret.set_long((long)int64.parse(val));
495                                 return ret;
496                         
497                         case "ulong":
498                                 ret = GLib.Value(typeof(ulong));
499                                 ret.set_ulong((ulong) uint64.parse(val));
500                                 return ret;
501
502                         case "float":
503                                 ret = GLib.Value(typeof(float));
504                                 ret.set_float((float)double.parse(val));
505                                 return ret;
506                                 
507                         case "string":
508                                 ret = GLib.Value(typeof(string));
509                                 ret.set_string(val);
510                                 return ret;
511
512                         default:
513
514                                 var sval =  GLib.Value(typeof(string));
515                                 sval.set_string(val);
516                         
517                                 if (!sval.transform(ref ret)) {
518                                 
519                                         return null;
520                                 }
521                                 return ret;
522                 }
523         }
524         
525          
526           
527                 
528 }