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