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