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