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 * -- 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 val = this.toValue(this.node.get(k).strip(), type);
179                         if (val == null) {
180                                 print("skip (failed to transform value %s type = %s from %s\n", 
181                                         cls + "." + k, type,  this.node.get(k).strip());
182                                 continue;
183                         }
184                         print ("set_property ( %s , %s / %s)\n", k, this.node.get(k).strip(), val.strdup_contents());
185                         
186                         
187                         ret.set_property(k, val);  
188                         
189
190                 }
191                 // packing???
192                 // for now... - just try the builder style packing
193                 
194                 
195                  
196                 if (this.node.items.size < 1) {
197                         return this;
198                 }
199                 
200                 for (var i = 0; i < this.node.items.size; i++ ) {
201
202                          var ch = this.mungeChild(this.node.items.get(i));
203                          if (ch != null) {
204                                  this.children.add(ch);
205                          }
206                          
207                 }
208                 
209                 this.afterChildren();
210                 
211                 return this;
212                 
213
214                  
215
216         }
217         
218         public void  afterChildren()
219         {
220                 // things like GtkNotebook - we have to pack children after they have been created..
221                 var cls = this.node.fqn().replace(".", "");
222                 
223                 if (cls == "GtkNotebook") {
224                         this.afterChildrenGtkNotebook();
225                 }
226                 
227                 
228                 
229         }
230         
231         public void  afterChildrenGtkNotebook()
232         {
233                 // we have a number of children..
234                 // some are labels - this might need to be more complex...
235                 // perhaps labels should be a special property labels[] of the notebook..
236                 var labels = new Gee.ArrayList<NodeToGtk>();
237                 var bodies = new Gee.ArrayList<NodeToGtk>();
238                 for (var i = 0; i < this.children.size; i++) { 
239                         var cn = this.children.get(i).node.fqn().replace(".", "");
240                         if (cn != "GtkLabel") {
241                                 bodies.add(this.children.get(i));
242                                 continue;
243                         }
244                         labels.add(this.children.get(i));
245                 }
246                 for (var i = 0; i < bodies.size; i++) { 
247                         ((global::Gtk.Notebook)this.wrapped_object).append_page(
248                                  (global::Gtk.Notebook) bodies.get(i).wrapped_object,
249                                  (labels.size > i - 1) ? 
250                                         (global::Gtk.Notebook) labels.get(i).wrapped_object :
251                                         null
252                                         
253                         );
254                         
255                         
256                 }
257                 
258                 
259         }
260         
261         
262         /**
263          * called after the this.object  has been created
264          * and it needs to be packed onto parent.
265          */
266         public void packParent() 
267         {
268                 var cls = this.node.fqn().replace(".", "");
269                 
270                 var gtkbuilder = new global::Gtk.Builder();
271                 var cls_gtype = gtkbuilder.get_type_from_name(cls);
272
273                 if (this.parentObj == null) {
274                         return;
275                 }
276                                 
277                     
278                 var parent = this.parentObj.wrapped_object;
279                 
280                 var do_pack =true;
281
282                 if (parent == null) { // no parent.. can not pack.
283                         return; /// 
284                 }
285                 // -------------  handle various special parents .. -----------
286                 
287                 var par_type = this.parentObj.node.fqn().replace(".", "");
288                 
289                 if (par_type == "GtkNotebook") {
290                         // do not pack - it's done afterwards...
291                         return;
292                 }
293                 
294                 // -------------  handle various child types.. -----------
295                 // our overrides
296                 if (cls == "GtkMenu") {
297                         this.packMenu();
298                         return;
299                 }
300
301                 if (cls == "GtkTreeStore") { // other stores?
302                         // tree store is buildable??? --- 
303                         this.packTreeStore();
304                         return;
305                 }
306                 if (cls =="GtkTreeViewColumn") { // other stores?
307                         //?? treeview column is actually buildable -- but we do not use the builder???
308                         this.packTreeViewColumn();
309                         return;
310                 }
311                 if (cls_gtype.is_a(typeof(global::Gtk.CellRenderer))) { // other stores?
312                         this.packCellRenderer();
313                         return;
314                 }
315
316
317                 
318                 // -- handle buildable add_child..
319                 if (    cls_gtype.is_a(typeof(global::Gtk.Buildable))
320                      && 
321                         parent.get_type().is_a(typeof(global::Gtk.Buildable))
322                 )
323                 {
324                         ((global::Gtk.Buildable)parent).add_child(gtkbuilder, 
325                                                   this.wrapped_object, null);
326                         return;
327                 }
328                 // other packing?
329
330                 
331
332         }
333
334         public void packMenu()
335         {
336
337
338                 var parent = this.parentObj.wrapped_object;
339                 if (!parent.get_type().is_a(typeof(global::Gtk.Widget))) {
340                         print("skip menu pack - parent is not a widget");
341                         return;
342                 }
343                 
344                 var p = (global::Gtk.Menu)this.wrapped_object;
345                 ((global::Gtk.Widget)parent).button_press_event.connect((s, ev) => { 
346                         p.set_screen(Gdk.Screen.get_default());
347                         p.show_all();
348                         p.popup(null, null, null, ev.button, ev.time);
349                         return true;
350                 });
351         }
352
353         public void packTreeStore()
354         {
355                 var parent = this.parentObj.wrapped_object;
356                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
357                         print("skip treestore pack - parent is not a treeview");
358                         return;
359                 }
360                 ((global::Gtk.TreeView)parent).set_model((global::Gtk.TreeModel)this.wrapped_object);
361                 
362         }
363         public void packTreeViewColumn()
364         {
365                 var parent = this.parentObj.wrapped_object;
366                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeView))) {
367                         print("skip packGtkViewColumn pack - parent is not a treeview");
368                         return;
369                 }
370                 ((global::Gtk.TreeView)parent).append_column((global::Gtk.TreeViewColumn)this.wrapped_object);
371                 // init contains the add_attribute for what to render...
372                 
373         }       
374
375
376         public void packCellRenderer()
377         {
378                 var parent = this.parentObj.wrapped_object;
379                 if (!parent.get_type().is_a(typeof(global::Gtk.TreeViewColumn))) {
380                         print("skip packGtkViewColumn pack - parent is not a treeview");
381                         return;
382                 }
383                 ((global::Gtk.TreeViewColumn)parent).pack_start((global::Gtk.CellRenderer)this.wrapped_object, false);
384                 // init contains the add_attribute for what to render...
385                 
386         }       
387
388
389         public void packContainerParams()
390         {
391          
392                 if (this.parentObj == null) {
393                         return;
394                 }
395                 // child must be a widget..
396                 if (!this.wrapped_object.get_type().is_a(typeof(global::Gtk.Widget))) {
397                         return;
398                 }
399                 
400                 var parent_gir = Palete.Gir.factoryFqn(this.parentObj.node.fqn());
401
402                 var parent = this.parentObj.wrapped_object;
403                 
404                 if (parent_gir == null) {
405                         return;
406                 }
407                 
408                 // let's test just setting expand to false...
409                 var cls_methods = parent_gir.methods;
410                 if (cls_methods == null) {
411                         return;
412                 }
413         
414                 if (!this.node.props.has_key("* pack") || 
415                                 this.node.props.get("* pack").length < 1) {
416                         return;
417                 }
418                 
419                 var ns = this.parentObj.node.fqn().split(".")[0];
420                  
421                 var pack = this.node.props.get("* pack").split(",");
422
423         
424                 if (cls_methods.has_key(pack[0])) {
425                         var mparams = cls_methods.get(pack[0]).paramset.params;
426                         for (var i = 1; i < mparams.size; i++ ) {
427                                 if (i > (pack.length -1)) {
428                                         continue;
429                                 }
430                         
431                                 var k = mparams.get(i).name;
432
433                                 Value cur_val;
434                                  
435                                 var type = mparams.get(i).type;
436                                 type = Palete.Gir.fqtypeLookup(type, ns);
437
438                                 var val = this.toValue(pack[i].strip(), type);
439                                 if (val == null) {
440                                         print("skip (failed to transform value %s type = %s from %s\n", 
441                                                 this.parentObj.node.fqn()  + "." + k, type, pack[i].strip());
442                                         continue;
443                                 }
444                                 print ("pack:set_property ( %s , %s / %s)\n", k, pack[i].strip(), val.strdup_contents());
445         
446                                 ((global::Gtk.Container)parent).child_set_property(
447                                         (global::Gtk.Widget)this.wrapped_object , k, val);
448                                  
449                         }
450                 
451                 }
452         
453
454
455                         
456         }
457                    
458
459         public GLib.Value? toValue(string val, string type) {
460
461                 
462                 /*
463                 if (type == "string") {
464                         var qret = GLib.Value(typeof(string));
465                         qret.set_string(val);
466                         return qret;
467                 }
468                 * */
469                 /*
470                  * 
471            * var gtkbuilder = new global::Gtk.Builder();
472                 var prop_gtype = gtkbuilder.get_type_from_name(type);
473                 
474
475                 if (prop_gtype == GLib.Type.INVALID) {
476                          
477                         return null;
478                 }
479                 */
480                 
481                  
482
483
484                 switch(type) {
485                         case "bool":
486                                 var ret = GLib.Value(typeof(bool));
487                                 ret.set_boolean(val.down() == "false" ? false : true);
488                                 return ret;
489                                 
490                         case "uint":
491                                 var ret = GLib.Value(typeof(uint));
492                                 ret.set_uint(int.parse(val));
493                                 return ret;
494                                 
495                         case "int":
496                                 var ret = GLib.Value(typeof(int));
497                                 ret.set_int(int.parse(val));
498                                 return ret;
499
500                         // uint64 ...??
501
502                         case "long":
503                                 var ret = GLib.Value(typeof(long));
504                                 ret.set_long((long)int64.parse(val));
505                                 return ret;
506                         
507                         case "ulong":
508                                 var ret = GLib.Value(typeof(ulong));
509                                 ret.set_ulong((ulong) uint64.parse(val));
510                                 return ret;
511
512                         case "float":
513                                 var ret = GLib.Value(typeof(float));
514                                 ret.set_float((float)double.parse(val));
515                                 return ret;
516                                 
517                         case "string":
518                                 var ret = GLib.Value(typeof(string));
519                                 ret.set_string(val);
520                                 return ret;
521
522                         default:
523                                 return null;
524                                 /*
525                                 var sval =  GLib.Value(typeof(string));
526                                 sval.set_string(val);
527                         
528                                 if (!sval.transform(ref ret)) {
529                                 
530                                         return null;
531                                 }
532                                 return ret;
533                                 */
534                 }
535                 // should not get here..
536                 return null;
537         }
538         
539          
540           
541                 
542 }