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