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