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