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