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