Fix #7982 - roo javascript completion
[roobuilder] / src / JsRender / NodeToVala.vala
1 /**
2  * 
3  * Code to convert node tree to Vala...
4  * 
5  * usage : x = (new JsRender.NodeToVala(node)).munge();
6  * 
7  * Fixmes?
8  *
9  *  pack - can we come up with a replacement?
10      - parent.child == child_widget -- actually uses getters and effectively does 'add'?
11        (works on most)?
12     
13      
14  * args  -- vala constructor args (should really only be used at top level - we did use it for clutter originally(
15  * ctor  -- different ctor argument
16  
17  * 
18  
19  * 
20  * 
21 */
22
23  
24 public class JsRender.NodeToVala : Object {
25
26         Node node;
27
28         int depth;
29         string inpad;
30         string pad;
31         string ipad;
32         string cls;  // node fqn()
33         string xcls;
34         
35         string ret;
36         
37         int cur_line;
38
39         Gee.ArrayList<string> ignoreList;
40         Gee.ArrayList<string> ignoreWrappedList; 
41         Gee.ArrayList<string> myvars;
42         Gee.ArrayList<Node> vitems; // top level items
43         NodeToVala top;
44         JsRender file;
45         int pane_number = 0;
46         
47         /* 
48          * ctor - just initializes things
49          * - wraps a render node 
50          */
51         public NodeToVala( JsRender file,  Node node,  int depth, NodeToVala? parent) 
52         {
53
54                 
55                 this.node = node;
56                 this.depth = depth;
57                 if (file.name.contains(".")) { // namespaced..
58                         this.inpad = string.nfill(depth > 0 ? 2 : 1, '\t');
59                 } else {
60                         this.inpad = string.nfill(depth > 0 ? 1 : 0, '\t');
61                 }
62                 this.pad = this.inpad + "\t";
63                 
64                 this.node.node_pad = this.inpad;
65                 this.ipad = this.inpad + "\t\t";
66                 this.cls = node.xvala_cls;
67                 this.xcls = node.xvala_xcls;
68                 if (depth == 0 && this.xcls.contains(".")) {
69                         var ar = this.xcls.split(".");
70                         this.xcls = ar[ar.length-1];
71                 }
72                 
73                 
74                 this.ret = "";
75                 this.cur_line = parent == null ? 0 : parent.cur_line;
76                 
77                 
78                 this.top = parent == null ? this : parent.top;
79                 this.ignoreList = new Gee.ArrayList<string>();
80                 this.ignoreWrappedList  = new Gee.ArrayList<string>();
81                 this.myvars = new Gee.ArrayList<string>();
82                 this.vitems = new Gee.ArrayList<Node>();
83                 this.file = file;
84                 
85                 // initialize line data..
86                 node.line_start = this.cur_line;
87                 node.line_end  = this.cur_line;
88                 node.lines = new Gee.ArrayList<int>();
89                 node.line_map = new Gee.HashMap<int,string>();
90                 if (parent == null) {
91                         node.node_lines = new Gee.ArrayList<int>();
92                         node.node_lines_map = new Gee.HashMap<int,Node>();
93                  }
94                 
95         }
96
97         public int vcnt = 0;
98         string toValaNS(Node item)
99         {
100                 var ns = item.get("xns") ;
101                 //if (ns == "GtkSource") {  technically on Gtk3?
102                 //      return "Gtk.Source";
103                 //}
104                 return ns + ".";
105         }
106         public void  toValaName(Node item, int depth =0) 
107         {
108                 this.vcnt++;
109
110                 var ns =  this.toValaNS(item) ;
111                 var cls = ns + item.get("xtype");
112                 
113                 
114                 item.xvala_cls = cls;
115                 
116                 
117                 string id = item.get("id").length > 0 ?
118                         item.get("id") :  "%s%d".printf(item.get("xtype"), this.vcnt);
119
120                 
121                 
122                 
123                 if (id[0] == '*' || id[0] == '+') {
124                         item.xvala_xcls = "Xcls_" + id.substring(1);
125                 } else {
126                         item.xvala_xcls = "Xcls_" + id;
127                 }
128                         
129                 
130                 item.xvala_id =  id;
131                 if (depth > 0) {                        
132                         this.vitems.add(item);
133                         
134                 // setting id on top level class changes it classname..                 
135                 // oddly enough we havent really thought about namespacing here.
136                 
137                 } else if (!item.props.has_key("id")) { 
138                         // use the file name..
139                         item.xvala_xcls =  this.file.file_without_namespace;
140                         // is id used?
141                         item.xvala_id = this.file.file_without_namespace;
142
143                 }
144                 // loop children..
145                                                                                                                            
146                 if (item.readItems().size < 1) {
147                         return;
148                 }
149                 for(var i =0;i<item.readItems().size;i++) {
150                         this.toValaName(item.readItems().get(i), depth+1);
151                 }
152                                           
153         }
154         /**
155          *  Main entry point to convert a file into a string..
156          */
157         public static string mungeFile(JsRender file) 
158         {
159                 if (file.tree == null) {
160                         return "";
161                 }
162
163                 var n = new NodeToVala(file, file.tree, 0, null);
164                 n.file = file;
165                 n.vcnt = 0;
166                 
167                 n.toValaName(file.tree);
168                 
169                 
170                 GLib.debug("top cls %s / xlcs %s\n ",file.tree.xvala_cls,file.tree.xvala_cls); 
171                 n.cls = file.tree.xvala_cls;
172                 n.xcls = file.tree.xvala_xcls;
173                 return n.munge();
174                 
175
176         }
177         int child_count = 1; // used to number the children.
178         public string munge ( )
179         {
180                 //return this.mungeToString(this.node);
181                 this.child_count = 1;
182                 this.ignore("pack");
183                 this.ignore("init");
184                 this.ignore("xns");
185                 this.ignore("xtype");
186                 this.ignore("id");
187                 
188                 this.namespaceHeader();
189                 this.globalVars();
190                 this.classHeader();
191                 this.addSingleton();
192                 this.addTopProperties();
193                 this.addMyVars();
194                 this.addPlusProperties();
195                 this.addValaCtor();
196                 this.addUnderThis();
197                 this.addWrappedCtor();  // var this.el = new XXXXX()
198
199                 this.addInitMyVars();
200                 this.addWrappedProperties();
201                 this.addChildren();
202                 this.addAutoShow(); // autoshow menuitems
203                 
204                 this.addInit();
205                 this.addListeners();
206                 this.addEndCtor();
207                 this.addUserMethods();
208                 this.iterChildren();
209                 this.namespaceFooter();
210                 
211                 return this.ret;
212                  
213                          
214         } 
215         public string mungeChild(  Node cnode)
216         {
217                 var x = new  NodeToVala(this.file, cnode,  this.depth+1, this);
218                 return x.munge();
219         }
220         public void addLine(string str= "")
221         {
222                 
223                 if (str.contains("\n")) {
224                         this.addMultiLine(str);
225                         return;
226                 }
227                 this.cur_line++;
228                 if (BuilderApplication.opt_bjs_compile != null) {
229                         this.ret += "/*%d*/ ".printf(this.cur_line) + str + "\n";
230                 } else {
231                         this.ret += str + "\n";
232                 }
233         }
234         public void addMultiLine(string str= "")
235         {
236                  
237                 this.cur_line += str.split("\n").length;
238                 //this.ret +=  "/*%d*/ ".printf(l) + str + "\n";
239                 this.ret +=   str + "\n";
240         }
241          
242         public void namespaceHeader()
243         {
244                 if (this.depth > 0 || this.file.file_namespace == "") {
245                         return;
246                 }
247                 this.addLine("namespace " + this.file.file_namespace);
248                 this.addLine("{");
249         
250         }
251         public void namespaceFooter()
252         {
253                 if (this.depth > 0 || this.file.file_namespace == "") {
254                         return;
255                 }
256                 this.addLine("}");
257         
258         }
259         public void globalVars()
260         {
261                 if (this.depth > 0) {
262                         return;
263                 }
264                 // Global Vars..??? when did this get removed..?
265                 //this.ret += this.inpad + "public static " + this.xcls + "  " + this.node.xvala_id+ ";\n\n";
266
267                 this.addLine(this.inpad + "static " + this.xcls + "  _" + this.node.xvala_id+ ";");
268                 this.addLine();
269                    
270         }
271
272         void classHeader()
273         {
274                            
275                 // class header..
276                 // class xxx {   WrappedGtk  el; }
277                 this.node.line_start = this.cur_line;
278                 
279                 this.top.node.setNodeLine(this.cur_line, this.node);
280                 
281                 this.addLine(this.inpad + "public class " + this.xcls + " : Object");
282                 this.addLine(this.inpad + "{");
283                 
284                  
285                 this.addLine(this.pad + "public " + this.cls + " el;");
286  
287                 this.addLine(this.pad + "private " + this.top.xcls + "  _this;");
288                 this.addLine();
289                         
290                         
291                         
292                         // singleton
293         }
294         void addSingleton() 
295         {
296                 if (depth > 0) {
297                         return;
298                 }
299                 this.addLine(pad + "public static " + xcls + " singleton()");
300                 this.addLine(this.pad + "{");
301                 this.addLine(this.ipad +    "if (_" + this.node.xvala_id  + " == null) {");
302                 this.addLine(this.ipad +    "    _" + this.node.xvala_id + "= new "+ this.xcls + "();");  // what about args?
303                 this.addLine(this.ipad +    "}");
304                 this.addLine(this.ipad +    "return _" + this.node.xvala_id +";");
305                 this.addLine(this.pad + "}");
306         }
307                         
308         /**
309          * when ID is used... on an element, it registeres a property on the top level...
310          * so that _this.ID always works..
311          * 
312          */
313         void addTopProperties()
314         {
315                 if (this.depth > 0) {
316                         return;
317                 }
318                 // properties - global..??
319
320                 var iter = this.vitems.list_iterator();
321                 while(iter.next()) {
322                         var n = iter.get();
323
324                          
325                         if (!n.props.has_key("id") || n.xvala_id.length < 0) {
326                                 continue;
327                                 
328                         }
329                         if (n.xvala_id[0] == '*') {
330                                 continue;
331                         }
332                         if (n.xvala_id[0] == '+') {
333                                 continue;
334                         }
335                         this.addLine(this.pad + "public " + n.xvala_xcls + " " + n.xvala_id + ";");
336                         
337                 }
338                                 
339         }
340         /**
341          * create properties that are not 'part of the wrapped element.
342          * 
343          * 
344          */
345  
346         void addMyVars()
347         {
348                 GLib.debug("callinged addMhyVars");
349                 
350                 this.addLine();
351                 this.addLine(this.ipad + "// my vars (def)");
352                         
353
354  
355                 var cls = Palete.Gir.factoryFqn((Project.Gtk) this.file.project, this.node.fqn());
356                    
357                 if (cls == null) {
358                         GLib.debug("Gir factory failed to find class %s", this.node.fqn());
359                         
360                         //return;
361                 }
362           
363                 
364                         // Key = TYPE:name
365                 var iter = this.node.props.map_iterator();
366                 while (iter.next()) {
367                          
368                         var prop = iter.get_value();
369                         
370                         if (this.shouldIgnore(prop.name)) {
371                                 continue;
372                         }
373
374                         // user defined method
375                         if (prop.ptype == NodePropType.METHOD) {
376                                 continue;
377                         }
378                         if (prop.ptype == NodePropType.SPECIAL) {
379                                 continue;
380                         }
381                                 
382                         if (prop.ptype == NodePropType.SIGNAL) {
383                                 this.node.setLine(this.cur_line, "p", prop.name);
384                                 this.addLine(this.pad + "public signal " + prop.rtype + " " + prop.name  + " "  + prop.val + ";");
385                                 
386                                 this.ignore(prop.name);
387                                 continue;
388                         }
389                         
390                         GLib.debug("Got myvars: %s", prop.name.strip());
391                         
392                         if (prop.rtype.strip().length < 1) {
393                                 continue;
394                         }
395                         
396                         // is it a class property...
397                         if (cls != null && cls.props.has_key(prop.name) && prop.ptype != NodePropType.USER) {
398                                 continue;
399                         }
400                         
401                         this.myvars.add(prop.name);
402                         prop.start_line = this.cur_line;
403                         
404                         this.node.setLine(this.cur_line, "p", prop.name);
405                         
406                         this.addLine(this.pad + "public " + prop.rtype + " " + prop.name + ";"); // definer - does not include value.
407
408
409                         prop.end_line = this.cur_line;                          
410                         this.ignore(prop.name);
411                         
412                                 
413                 }
414         }
415         
416         // if id of child is '+' then it's a property of this..
417         void addPlusProperties()
418         {
419                 if (this.node.readItems().size < 1) {
420                         return;
421                 }
422                 var iter = this.node.readItems().list_iterator();
423                 while (iter.next()) {
424                         var ci = iter.get();
425                                 
426                         if (ci.xvala_id[0] != '+') {
427                                 continue; // skip generation of children?
428                                 
429                         }
430                          
431                         this.addLine(this.pad + "public " + ci.xvala_xcls + " " + ci.xvala_id.substring(1) + ";");
432                                            
433                         
434                 }
435         }
436         /**
437          * add the constructor definition..
438          */
439         void addValaCtor()
440         {
441                         
442                 
443                 // .vala props.. 
444                 
445  
446                 var cargs_str = "";
447                 // ctor..
448                 this.addLine();
449                 this.addLine(this.pad + "// ctor");
450                 
451                 if (this.node.has("* args")) {
452                         // not sure what this is supposed to be ding..
453                 
454                         cargs_str =  this.node.get("* args");
455                         //var ar = this.node.get("* args");.split(",");
456                         //for (var ari =0; ari < ar.length; ari++) {
457                                 //      cargs +=  (ar[ari].trim().split(" ").pop();
458                                   // }
459                         }
460         
461                 if (this.depth < 1) {
462                  
463                         // top level - does not pass the top level element..
464                         this.addLine(this.pad + "public " + this.xcls + "(" +  cargs_str +")");
465                         this.addLine(this.pad + "{");
466                 } else {
467                         if (cargs_str.length > 0) {
468                                 cargs_str = ", " + cargs_str;
469                         }
470                         // for sub classes = we passs the top level as _owner
471                         this.addLine(this.pad + "public " + this.xcls + "(" +  this.top.xcls + " _owner " + cargs_str + ")");
472                         this.addLine(this.pad + "{");
473                 }
474                 
475
476         }
477         /**
478          *  make sure _this is defined..
479          */
480         void addUnderThis() 
481         {
482                 // public static?
483                 if (depth < 1) {
484                         this.addLine( this.ipad + "_this = this;");
485                         return;
486                 }
487                 // for non top level = _this point to owner, and _this.ID is set
488                 
489                 this.addLine( this.ipad + "_this = _owner;");
490
491                 if (this.node.props.has_key("id")
492                         &&
493                         this.node.xvala_id != "" 
494                         && 
495                         this.node.xvala_id[0] != '*' 
496                         && 
497                         this.node.xvala_id[0] != '+' 
498                         ) {
499                                 this.addLine( this.ipad + "_this." + node.xvala_id  + " = this;");
500                    
501                 }
502                          
503         }
504          
505         /**
506          * Initialize this.el to point to the wrapped element.
507          * 
508          * 
509          */
510
511         void addWrappedCtor()
512         {
513                 // wrapped ctor..
514                 // this may need to look up properties to fill in the arguments..
515                 // introspection does not workk..... - as things like gtkmessagedialog
516                 /*
517                 if (cls == 'Gtk.Table') {
518
519                 var methods = this.palete.getPropertiesFor(cls, 'methods');
520
521                 print(JSON.stringify(this.palete.proplist[cls], null,4));
522                 Seed.quit();
523                 }
524                 */
525                 
526                 // ctor can still override.
527                 if (this.node.has("* ctor")) {
528                         this.node.setLine(this.cur_line, "p", "* ctor");
529                         this.addLine(this.ipad + "this.el = " + this.node.get("* ctor")+ ";");
530                         return;
531                 }
532                 
533                 this.node.setLine(this.cur_line, "p", "* xtype");;
534                 
535                 // is the wrapped element a struct?
536                 
537                 var ncls = Palete.Gir.factoryFqn((Project.Gtk) this.file.project, this.node.fqn());
538                 if (ncls != null && ncls.nodetype == "Struct") {
539                         // we can use regular setters to apply the values.
540                         this.addLine(this.ipad + "this.el = " + this.node.fqn() + "();");
541                         return;
542                 
543                 
544                 }
545
546                 var ctor = ".new";
547                 var args_str = "";
548                 switch(this.node.fqn()) {
549                 
550                 // FIXME -- these are all GTK3 - can be removed when I get rid of them..
551                         case "Gtk.ComboBox":
552                                 var is_entry = this.node.has("has_entry") && this.node.get_prop("has_entry").val.down() == "true";
553                                 if (!is_entry) { 
554                                         break; // regular ctor.
555                                 }
556                                 this.ignoreWrapped("has_entry");
557                                 ctor = ".with_entry";
558                                 break;
559                                 
560                 
561                         case "Gtk.ListStore":
562                         case "Gtk.TreeStore":
563
564                                 // not sure if this works.. otherwise we have to go with varargs and count + vals...
565                                 if (this.node.has("* types")) {
566                                         args_str = this.node.get_prop("* types").val;
567                                 }
568                                 if (this.node.has("n_columns") && this.node.has("columns")) { // old value?
569                                         args_str = " { " + this.node.get_prop("columns").val + " } ";
570                                         this.ignoreWrapped("columns");
571                                         this.ignoreWrapped("n_columns");
572                                 }
573                                 
574                                 this.addLine(this.ipad + "this.el = new " + this.node.fqn() + ".newv( " + args_str + " );");
575                                 return;
576  
577                                 
578                         case "Gtk.LinkButton": // args filled with values.
579                                 if (this.node.has("label")) {
580                                         ctor = ".with_label";    
581                                 }
582                                 break;
583                                 
584                         default:
585                                 break;
586                 }
587                 var default_ctor = Palete.Gir.factoryFqn((Project.Gtk) this.file.project, this.node.fqn() + ctor);              
588                  
589                 
590                 // use the default ctor - with arguments (from properties)
591                 
592                 if (default_ctor != null && default_ctor.paramset != null && default_ctor.paramset.params.size > 0) {
593                         string[] args  = {};
594                         foreach(var param in default_ctor.paramset.params) {
595                                  
596                                 var n = param.name;
597                             GLib.debug("building CTOR ARGS: %s, %s", n, param.is_varargs ? "VARARGS": "");
598                                 if (n == "___") { // for some reason our varargs are converted to '___' ...
599                                         continue;
600                                 }
601                                 
602                                 if (this.node.has(n)) {  // node does not have a value
603                                         
604                                         this.ignoreWrapped(n);
605                                         this.ignore(n);
606                                         
607                                         var v = this.node.get(n);
608
609                                         if (param.type == "string") {
610                                                 v = "\"" +  v.escape("") + "\"";
611                                         }
612                                         if (v == "TRUE" || v == "FALSE") {
613                                                 v = v.down();
614                                         }
615
616                                         
617                                         args += v;
618                                         continue;
619                                 }
620                                 var propnode = this.node.findProp(n);
621                                 if (propnode != null) {
622                                         // assume it's ok..
623                                         
624                                         var pname = this.addPropSet(propnode, propnode.has("id") ? propnode.get_prop("id").val : "");
625                                         args += (pname + ".el") ;
626                                         if (!propnode.has("id")) {
627                                                 this.addLine(this.ipad + pname +".ref();"); 
628                                         }
629                                         
630                                         
631                                         
632                                         this.ignoreWrapped(n);
633                                         
634                                         continue;
635                                 }
636                                         
637                                          
638                                         
639                                         
640                                  
641                                 if (param.type.contains("int")) {
642                                         args += "0";
643                                         continue;
644                                 }
645                                 if (param.type.contains("float")) {
646                                         args += "0f";
647                                         continue;
648                                 }
649                                 if (param.type.contains("bool")) {
650                                         args += "true"; // always default to true?
651                                         continue;
652                                 }
653                                 // any other types???
654                                 
655                                 
656                                 
657                                 
658                                 args += "null";
659                                  
660                                 
661
662                         }
663                         this.node.setLine(this.cur_line, "p", "* xtype");
664                         this.addLine(this.ipad + "this.el = new " + this.node.fqn() + "( "+ string.joinv(", ",args) + " );") ;
665                         return;
666                         
667                 }
668                 // default ctor with no params..
669                  if (default_ctor != null && ctor != ".new" ) {
670                         this.node.setLine(this.cur_line, "p", "* xtype");
671                         
672                         this.addLine(this.ipad + "this.el = new " + this.node.fqn() + ctor + "(  );") ;
673                         return;
674                  }
675                 
676                 
677                 this.addLine(this.ipad + "this.el = new " + this.node.fqn() + "(" + args_str + ");");
678                 
679                 
680
681                         
682         }
683         public static Gee.ArrayList<string> menuitem_children = null;
684         
685         void addAutoShow()
686         {
687                 if (menuitem_children == null) {
688                         menuitem_children = new Gee.ArrayList<string>();
689                         menuitem_children.add("Gtk.MenuItem");
690                         var gir = this.file.project.palete.getClass("Gtk.MenuItem");
691                         if (gir != null) {
692                             foreach(var impl in gir.implementations) {
693                                     menuitem_children.add(impl);
694                             }
695                     }
696                 }
697
698                 if (menuitem_children.contains(this.node.fqn())) {
699                         this.addLine(this.ipad + "this.el.show();");
700                 
701                 }
702         }
703
704         void addInitMyVars()
705         {
706                         //var meths = this.palete.getPropertiesFor(item['|xns'] + '.' + item.xtype, 'methods');
707                         //print(JSON.stringify(meths,null,4));Seed.quit();
708                         
709                         
710                         
711                         // initialize.. my vars..
712                 this.addLine();
713                 this.addLine( this.ipad + "// my vars (dec)");
714                 
715                 var iter = this.myvars.list_iterator();
716                 while(iter.next()) {
717                         
718                         var k = iter.get();
719                         
720                          
721                         var prop = this.node.props.get(k);
722                         
723                         var v = prop.val.strip();                       
724                         
725                         if (v.length < 1) {
726                                 continue; 
727                         }
728                         // at this point start using 
729
730                         if (v == "FALSE" || v == "TRUE") {
731                                 v= v.down();
732                         }
733                         //FIXME -- check for raw string.. "string XXXX"
734                         
735                         // if it's a string...
736                         
737                         prop.start_line = this.cur_line;
738                         this.addLine(this.ipad + "this." + prop.name + " = " +   v +";");
739                         prop.end_line = this.cur_line;
740                 }
741         }
742
743         
744
745
746         
747         void addWrappedProperties()
748         {
749                 var cls = Palete.Gir.factoryFqn((Project.Gtk) this.file.project, this.node.fqn());
750                 if (cls == null) {
751                         GLib.debug("Skipping wrapped properties - could not find class  %s" , this.node.fqn());
752                         return;
753                 }
754                         // what are the properties of this class???
755                 this.addLine();
756                 this.addLine(this.ipad + "// set gobject values");
757                 
758
759                 var iter = cls.props.map_iterator();
760                 while (iter.next()) {
761                         var p = iter.get_key();
762                         //print("Check Write %s\n", p);
763                         if (!this.node.has(p)) {
764                                 continue;
765                         }
766                         if (this.shouldIgnoreWrapped(p)) {
767                                 continue;
768                         }
769                         
770                         this.ignore(p);
771
772
773                         var prop = this.node.get_prop(p);
774                         var v = prop.val;
775                         
776                         // user defined properties.
777                         if (prop.ptype == NodePropType.USER) {
778                                 continue;
779                         }
780                                 
781
782                         
783                         var is_raw = prop.ptype == NodePropType.RAW;
784                         
785                         // what's the type.. - if it's a string.. then we quote it..
786                         if (iter.get_value().type == "string" && !is_raw) {
787                                  v = "\"" +  v.escape("") + "\"";
788                         }
789                         if (v == "TRUE" || v == "FALSE") {
790                                 v = v.down();
791                         }
792                         if (iter.get_value().type == "float" && v[v.length-1] != 'f') {
793                                 v += "f";
794                         }
795                         
796                         prop.start_line = this.cur_line;
797                         this.addLine("%sthis.el.%s = %s;".printf(ipad,p,v)); // // %s,  iter.get_value().type);
798                         prop.end_line = this.cur_line;          
799                            // got a property..
800                            
801
802                 }
803                 
804         }
805         /**
806          *  pack the children into the parent.
807          * 
808          * if the child's id starts with '*' then it is not packed...
809          * - this allows you to define children and add them manually..
810          */
811
812         void addChildren()
813         {
814                                 //code
815                 if (this.node.readItems().size < 1) {
816                         return;
817                 }
818                 this.pane_number = 0;
819                 var cols = this.node.has("* columns") ? int.max(1, int.parse(this.node.get_prop("* columns").val)) : 1;
820                 var colpos = 0;
821                 
822  
823                  
824                 foreach(var child in this.node.readItems()) {
825                         
826                         
827                          
828
829                         if (child.xvala_id[0] == '*') {
830                                 continue; // skip generation of children?
831                         }
832
833                         // probably added in ctor..                             
834                         if (child.has("* prop") && this.shouldIgnoreWrapped(child.get_prop("* prop").val)) {
835                                 continue;
836                         }
837                         // create the element..
838                         
839                         // this is only needed if it does not have an ID???
840                         var childname = this.addPropSet(child, child.has("id") ? child.get_prop("id").val : "") ; 
841                         
842                         if (child.has("* prop")) {
843                          
844                         
845                                 // fixme special packing!??!?!
846                                 if (child.get_prop("* prop").val.contains("[]")) {
847                                         // currently these 'child props
848                                         // used for label[]  on Notebook
849                                         // used for button[]  on Dialog?
850                                         // columns[] ?
851                                          
852                                         this.packChild(child, childname, 0, 0, child.get_prop("* prop").val);  /// fixme - this is a bit speciall...
853                                         continue;
854                                 }
855                                 
856         
857                                 
858                                 this.ignoreWrapped(child.get_prop("* prop").val);
859                                 
860                                 this.addLine(ipad + "this.el." + child.get_prop("* prop").val + " = " + childname + ".el;");
861                                 continue;
862                         } 
863                          if (!child.has("id")) {
864                                 this.addLine(this.ipad + childname +".ref();"); 
865                          } 
866                         this.packChild(child, childname, cols, colpos);
867                         
868                         if (child.has("colspan")) {
869                                 colpos += int.parse(child.get_prop("colspan").val);
870                         } else {
871                                 colpos += 1;
872                         }
873                                           
874                         
875                         // this.{id - without the '+'} = the element...
876                          
877                                   
878                 }
879         }
880         
881         string addPropSet(Node child, string child_name) 
882         {
883          
884                 
885                 var xargs = "";
886                 if (child.has("* args")) {
887                         
888                         var ar = child.get_prop("* args").val.split(",");
889                         for (var ari = 0 ; ari < ar.length; ari++ ) {
890                                 var arg = ar[ari].split(" ");
891                                 xargs += "," + arg[arg.length -1];
892                         }
893                 }
894                 
895                 var childname = "child_" + "%d".printf(this.child_count++);     
896                 var prefix = "";
897                 if (child_name == "") {
898                         prefix = "var " + childname + " = ";
899                 }
900                 
901                 this.addLine(this.ipad +  prefix + "new " + child.xvala_xcls + "( _this " + xargs + ");" );
902                  
903                 // add a ref... (if 'id' is not set... to a '+' ?? what does that mean? - fake ids?
904                 // remove '+' support as I cant remember what it does!!!
905                 //if (child.xvala_id.length < 1 ) {
906                 //      this.addLine(this.ipad + childname +".ref();"); // we need to reference increase unnamed children...
907                 //}                     
908             //if (child.xvala_id[0] == '+') {
909                 //      this.addLine(this.ipad + "this." + child.xvala_id.substring(1) + " = " + childname+  ";");
910                                         
911                 //}
912                 
913
914                 return child_name == "" ? childname : ("_this." + child_name);  
915         }               
916                         
917         
918
919         
920         void packChild(Node child, string childname, int cols, int colpos, string propname= "")
921         {
922                 
923                 GLib.debug("packChild %s=>%s", this.node.fqn(), child.fqn());
924                 // forcing no packing? - true or false? -should we just accept false?
925                 if (child.has("* pack") && child.get("* pack").down() == "false") {
926                         return; // force no packing
927                 }
928                 if (child.has("* pack") && child.get("* pack").down() == "true") {
929                         return; // force no packing
930                 }
931                 
932                 // BC really - don't want to support this anymore.
933                 if (child.has("* pack")) {
934                         
935                         string[]  packing =  { "add" };
936                         if (child.has("* pack")) {
937                                 packing = child.get("* pack").split(",");
938                         }
939                         
940                         var pack = packing[0];
941                         this.addLine(this.ipad + "this.el." + pack.strip() + " ( " + childname + ".el " +
942                                    (packing.length > 1 ? 
943                                                 (", " + string.joinv(",", packing).substring(pack.length+1))
944                                         :
945                                                         ""
946                                                 ) + " );");
947                         return;  
948                 }
949                 var childcls =  this.file.project.palete.getClass(child.fqn()); // very trusting..
950                 if (childcls == null) {
951                   return;
952                 }
953                 // GTK4
954                 var is_event = childcls.inherits.contains("Gtk.EventController") || childcls.implements.contains("Gtk.EventController");
955                 if (is_event) {
956                     this.addLine(this.ipad + "this.el.add_controller(  %s.el );".printf(childname) );
957                     return;
958                 }
959                 
960                 
961                 switch (this.node.fqn()) {
962                         
963                                 
964                 
965                         case "Gtk.Fixed":
966                         case "Gtk.Layout":
967                                 var x = child.has("x") ?  child.get_prop("x").val  : "0";
968                                 var y = child.has("y") ?  child.get_prop("y").val  : "0";
969                                 this.addLine(this.ipad + "this.el.put( %s.el, %s, %s );".printf(childname,x,y) );
970                                 return;
971                                 
972                         
973
974                         case "Gtk.Stack":
975                                 var named = child.has("stack_name") ?  child.get_prop("stack_name").val.escape() : "";
976                                 var title = child.has("stack_title") ?  child.get_prop("stack_title").val.escape()  : "";
977                                 if (title.length > 0) {
978                                         this.addLine(this.ipad + "this.el.add_titled( %s.el, \"%s\", \"%s\" );".printf(childname,named,title)); 
979                                 } else {
980                                         this.addLine(this.ipad + "this.el.add_named( %s.el, \"%s\" );".printf(childname,named));
981                                 }
982                                 return;
983                                 
984                         case "Gtk.Notebook": // use label
985                                 var label = child.has("notebook_label") ?  child.get_prop("notebook_label").val.escape() : "";
986                                 this.addLine(this.ipad + "this.el.append_page( %s.el, new Gtk.Label(\"%s\"));".printf(childname, label));       
987                                 return;
988                                 
989                          
990                         case "Gtk.TreeView": // adding TreeViewColumns
991                                 this.addLine(this.ipad + "this.el.append_column( " + childname + ".el );");
992                                 return;
993                         
994                         case "Gtk.TreeViewColumn": //adding Renderers - I think these are all proprerties of the renderer used...
995                                 if (child.has("markup_column") && int.parse(child.get_prop("markup_column").val) > -1) {
996                                         this.addLine(this.ipad + "this.el.add_attribute( %s.el, \"markup\", %s );".printf(childname, child.get_prop("markup_column").val));
997                                 }
998                                 if (child.has("text_column") && int.parse(child.get_prop("text_column").val) > -1) {
999                                         this.addLine(this.ipad + "this.el.add_attribute(  %s.el, \"text\", %s );".printf(childname, child.get_prop("text_column").val));
1000                                 }
1001                                 if (child.has("pixbuf_column") && int.parse(child.get_prop("pixbuf_column").val) > -1) {
1002                                         this.addLine(this.ipad + "this.el.add_attribute(  %s.el, \"pixbuf\", %s );".printf(childname, child.get_prop("pixbuf_column").val));
1003                                 }
1004                                 if (child.has("pixbuf_column") && int.parse(child.get_prop("active_column").val) > -1) {
1005                                         this.addLine(this.ipad + "this.el.add_attribute(  %s.el, \"active\", %s );".printf(childname, child.get_prop("active_column").val));
1006                                 }
1007                                 if (child.has("background_column") && int.parse(child.get_prop("background_column").val) > -1) {
1008                                         this.addLine(this.ipad + "this.el.add_attribute(  %s.el, \"background-rgba\", %s );".printf(childname, child.get_prop("background_column").val));
1009                                 }
1010                                 this.addLine(this.ipad + "this.el.add( " + childname + ".el );");
1011                                 // any more!?
1012                                 return;
1013                         
1014                         case "Gtk.Dialog":
1015                                 if (propname == "buttons[]") {
1016                                         var resp_id = int.parse(childname.replace("child_", ""));
1017                                         if (child.has("* response_id")) { 
1018                                                 resp_id = int.parse(child.get_prop("* response_id").val);
1019                                         }
1020                                         this.addLine(this.ipad + "this.el.add_action_widget( %s.el, %d);".printf(childname,resp_id) );
1021                                         return;
1022                                 }
1023                         
1024                                 
1025                                 this.addLine(this.ipad + "this.el.get_content_area().add( " + childname + ".el );");
1026                                 return;
1027
1028                 
1029                                 
1030                         
1031         
1032         
1033         // known working with GTK4 !
1034                         case "Gtk.HeaderBar": // it could be end... - not sure how to hanle that other than overriding the pack method?
1035                                 this.addLine(this.ipad + "this.el.pack_start( "+ childname + ".el );");
1036                                 return;
1037                         
1038                         case "GLib.Menu":
1039                                 this.addLine(this.ipad + "this.el.append_item( "+ childname + ".el );");
1040                                 return; 
1041                         
1042                         case "Gtk.Paned":
1043                                 this.pane_number++;
1044                                 switch(this.pane_number) {
1045                                         case 1:
1046                                                 this.addLine(this.ipad + "this.el.pack_start( %s.el );".printf(childname));
1047                                                 return;
1048                                         case 2:                                 
1049                                                 this.addLine(this.ipad + "this.el.pack_end( %s.el );".printf(childname));
1050                                                 return;
1051                                         default:
1052                                                 // do nothing
1053                                                 break;
1054                                 }
1055                                 return;
1056                         
1057                         case "Gtk.ColumnView":
1058                                 this.addLine(this.ipad + "this.el.append_column( "+ childname + ".el );");
1059                                 return;
1060                         
1061                         case "Gtk.Grid":
1062                                 var x = "%d".printf(colpos % cols);
1063                                 var y = "%d".printf(( colpos - (colpos % cols) ) / cols);
1064                                 var w = child.has("colspan") ? child.get_prop("colspan").val : "1";
1065                                 var h = "1";
1066                                 this.addLine(this.ipad + "this.el.attach( %s.el, %s, %s, %s, %s );".printf(childname ,x,y, w, h) );
1067                                 return;
1068                         
1069                         default:
1070                             // gtk4 uses append!!!! - gtk3 - uses add..
1071                                 this.addLine(this.ipad + "this.el.append( "+ childname + ".el );");
1072                                 return;
1073                 
1074                 
1075                 }
1076                 
1077                 
1078         }
1079         
1080         // fixme GtkDialog?!? buttons[]
1081         
1082         // fixme ... add case "Gtk.RadioButton":  // group_id ??
1083
1084                         
1085
1086         void addInit()
1087         {
1088
1089                 
1090                 if (!this.node.has("* init")) {
1091                                 return;
1092                 }
1093                 this.addLine();
1094                 this.addLine(ipad + "// init method");
1095                 this.addLine();
1096                 this.node.setLine(this.cur_line, "p", "init");
1097                 
1098                 var init =  this.node.get_prop("* init");
1099                 init.start_line = this.cur_line;
1100                 this.addMultiLine(ipad + this.padMultiline(ipad, init.val) );
1101                 init.end_line = this.cur_line;
1102          }
1103          void addListeners()
1104          {
1105                 if (this.node.listeners.size < 1) {
1106                         return;
1107                 }
1108                                 
1109                 this.addLine();
1110                 this.addLine(ipad + "//listeners");
1111                         
1112                          
1113
1114                 var iter = this.node.listeners.map_iterator();
1115                 while (iter.next()) {
1116                         var k = iter.get_key();
1117                         var prop = iter.get_value();
1118                         var v = prop.val;
1119                         
1120                         prop.start_line = this.cur_line;
1121                         this.node.setLine(this.cur_line, "l", k);
1122                         this.addMultiLine(this.ipad + "this.el." + k + ".connect( " + 
1123                                         this.padMultiline(this.ipad,v) +");"); 
1124                         prop.end_line = this.cur_line;
1125                 }
1126         }    
1127         void addEndCtor()
1128         {
1129                          
1130                         // end ctor..
1131                         this.addLine(this.pad + "}");
1132         }
1133
1134
1135         /*
1136  * Standardize this crap...
1137  * 
1138  * standard properties (use to set)
1139  *          If they are long values show the dialog..
1140  *
1141  * someprop : ....
1142  * bool is_xxx  :: can show a pulldown.. (true/false)
1143  * string html  
1144  * $ string html  = string with value interpolated eg. baseURL + ".." 
1145  *  Clutter.ActorAlign x_align  (typed)  -- shows pulldowns if type is ENUM? 
1146  * $ untypedvalue = javascript untyped value...  
1147  * _ string html ... = translatable..
1148
1149  * 
1150  * object properties (not part of the GOjbect being wrapped?
1151  * # Gee.ArrayList<Xcls_fileitem> fileitems
1152  * 
1153  * signals
1154  * @ void open 
1155  * 
1156  * methods -- always text editor..
1157  * | void clearFiles
1158  * | someJSmethod
1159  * 
1160  * specials
1161  * * prop -- string
1162  * * args  -- string
1163  * * ctor -- string
1164  * * init -- big string?
1165  * 
1166  * event handlers (listeners)
1167  *   just shown 
1168  * 
1169  * -----------------
1170  * special ID values
1171  *  +XXXX -- indicates it's a instance property / not glob...
1172  *  *XXXX -- skip writing glob property (used as classes that can be created...)
1173  * 
1174  * 
1175  */
1176          
1177         void addUserMethods()
1178         {
1179                 this.addLine();
1180                 this.addLine(this.pad + "// user defined functions");
1181                         
1182                         // user defined functions...
1183                 var iter = this.node.props.map_iterator();
1184                 while(iter.next()) {
1185                         var prop = iter.get_value();
1186                         if (this.shouldIgnore(prop.name)) {
1187                                 continue;
1188                         }
1189                         // HOW TO DETERIME if its a method?            
1190                         if (prop.ptype != NodePropType.METHOD) {
1191                                         //strbuilder("\n" + pad + "// skip " + k + " - not pipe \n"); 
1192                                         continue;
1193                         }
1194                         
1195                         // function in the format of {type} (args) { .... }
1196
1197
1198
1199                         prop.start_line = this.cur_line;
1200                         this.node.setLine(this.cur_line, "p", prop.name);
1201                         this.addMultiLine(this.pad + "public " + prop.rtype + " " +  prop.name + " " + this.padMultiline(this.pad, prop.val));;
1202                         prop.end_line = this.cur_line;
1203                                 
1204                 }
1205         }
1206
1207         void iterChildren()
1208         {
1209                 this.node.line_end = this.cur_line;
1210                 this.node.sortLines();
1211                 
1212                         
1213                 if (this.depth > 0) {
1214                         this.addLine(this.inpad + "}");
1215                 }
1216                 
1217                 var iter = this.node.readItems().list_iterator();
1218                  
1219                 while (iter.next()) {
1220                         this.addMultiLine(this.mungeChild(iter.get()));
1221                 }
1222                          
1223                 if (this.depth < 1) {
1224                         this.addLine(this.inpad + "}");
1225                 }
1226                         
1227         }
1228
1229         string padMultiline(string pad, string str)
1230         {
1231                 var ar = str.strip().split("\n");
1232                 return string.joinv("\n" + pad , ar);
1233         }
1234         
1235         void ignore(string i) {
1236                 this.ignoreList.add(i);
1237                 
1238         }
1239         void ignoreWrapped(string i) {
1240                 this.ignoreWrappedList.add(i);
1241                 
1242         }
1243         bool shouldIgnore(string i)
1244         {
1245                 return ignoreList.contains(i);
1246         }
1247         bool shouldIgnoreWrapped(string i)
1248         {
1249                 return ignoreWrappedList.contains(i);
1250         }
1251         
1252 }
1253         
1254          
1255         
1256