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