Fix #8011 - add resources
[roobuilder] / src / JsRender / Node.vala
index fc4c57a..cdc7cf3 100644 (file)
 
 
 
-public class JsRender.Node : Object {
+public class JsRender.Node : GLib.Object {
        
 
        public static int uid_count = 0;
        
+       public int oid { get; private set; }
        public Node parent;
-       public Gee.ArrayList<Node> items; // child items..
-       
-       public Gee.HashMap<string,NodeProp> props; // the properties..
-       public Gee.HashMap<string,NodeProp> listeners; // the listeners..
-       public string  xvala_cls;
-       public string xvala_xcls; // 'Xcls_' + id;
-       public string xvala_id; // item id or ""
+       private Gee.ArrayList<Node> items; // child items..
+       public GLib.ListStore  childstore; // must be kept in sync with items
+       public GLib.ListStore?  propstore; // must be kept in sync with items
+       public string  xvala_cls; // set by node to vala
+       public string xvala_xcls; // 'Xcls_' + id; // set by nodetoVala
+       public string xvala_id; // item id or "" // set by nodetovala
        
        // line markers..
        public int line_start;
@@ -111,12 +111,45 @@ public class JsRender.Node : Object {
        public Gee.ArrayList<int> node_lines; 
        public Gee.HashMap<int,Node> node_lines_map; // store of l:xxx or p:....
        
-
+       
+       public string node_pad = "";
+       
+       private int _updated_count = 0;
+       public int updated_count { 
+               get {
+                       return this._updated_count; 
+               }
+               set  {
+                       this.nodeTitleProp = ""; // ?? should trigger set?
+                       this.iconResourceName = "";
+                       this._updated_count = value;
+                               
+                       //GLib.debug("Update Node %d p%d - rev %d", this.oid, this.parent != null ? this.parent.oid : -1, value);
+                       if (this.parent != null) {
+                               this.parent.updated_count++;
+                       }  else {
+                               //GLib.debug("UNDO top node is %d", value);
+                               this.version_changed();
+                       }
+               }
+       } // changes to this trigger updates on the tree..
+       
+       public string as_source = "";
+       public int as_source_version = -1;
+       public int as_source_start_line = -1;
+        
+       
+       
+       public signal void  version_changed();
+       
        public Node()
        {
                this.items = new Gee.ArrayList<Node>();
-               this.props = new Gee.HashMap<string,NodeProp>();
-               this.listeners = new Gee.HashMap<string,NodeProp>(); // Nodeprop can include line numbers..
+               //this._props = new Gee.HashMap<string,NodeProp>();
+               //this._listeners = new Gee.HashMap<string,NodeProp>(); // Nodeprop can include line numbers..
+               this.propstore = new GLib.ListStore(typeof(NodeProp)); // Nodeprop can include line numbers..
                this.xvala_cls = "";
                this.xvala_xcls = "";
                this.xvala_id = "";
@@ -127,9 +160,30 @@ public class JsRender.Node : Object {
                this.line_map = new Gee.HashMap<int,string>();
                this.node_lines = new Gee.ArrayList<int>();
                this.node_lines_map = new Gee.HashMap<int,Node>();
+               this.childstore = new GLib.ListStore( typeof(Node));
+               this.oid = uid_count++;
                
        }
        
+       public bool has_parent(Node n) 
+       {
+               if (this.parent == null) {
+                       return false;
+               }
+               if (this.parent.oid == n.oid) {
+                       return true;
+               }
+               
+
+               return this.parent.has_parent(n);
+       }
+       
+       
+       public  Gee.ArrayList<Node> readItems()
+       {
+               return this.items; // note should not modify add/remove from this directly..
+               
+       }
        public void setNodeLine(int line, Node node) {
                //print("Add node @ %d\n", line);
                if (this.node_lines_map.has_key(line)) {
@@ -140,7 +194,9 @@ public class JsRender.Node : Object {
                
        }
        
-       public void setLine(int line, string type, string prop) {
+       public void setLine(int line, string type, string prop) 
+       {
+               //GLib.debug("set prop %s (%s) to line %d", prop, type, line);
                if (this.line_map.has_key(line)) {
                        if  (this.line_map.get(line) != "e:"  ) {
                                return;
@@ -149,7 +205,43 @@ public class JsRender.Node : Object {
                        this.lines.add(line);
                }
                this.line_map.set(line, type + ":" + prop);
-               GLib.debug("setLine %d, %s", line, type + ":" + prop);
+               if (type == "e" || type == "p" ) {
+               
+                       if (prop == "" || !this.props.has_key(prop)) {
+                               ///GLib.debug("cant find prop '%s'", prop);
+                               return;
+                       }
+                       
+                       var prope = this.props.get(prop);
+                       if (prope != null && type =="p") { 
+                               prope.start_line = line;
+                       }
+                       if (prope != null && type =="e") { 
+                               prope.end_line = line;
+                       }       
+                       
+               }
+               if (type == "l" || type =="x") {
+                       if (prop == "" || !this.listeners.has_key(prop)) {
+                               //GLib.debug("cant find listener '%s'", prop);
+                               return;
+                       }
+                       
+                       var prope = this.listeners.get(prop);
+                       if (prope != null && type =="l") { 
+                               prope.start_line = line;
+                       }
+                       if (prope != null && type =="x") { 
+                               prope.end_line = line;
+                       }       
+                       
+               
+               }
+               
+               
+               
+               
+               //GLib.debug("setLine %d, %s", line, type + ":" + prop);
        }
        public void sortLines() {
                //print("sortLines\n");
@@ -207,42 +299,24 @@ public class JsRender.Node : Object {
        }
        
        
-       public string lineToProp(int line)
+       public NodeProp? lineToProp(int line)
        {
-               // assume lineToNode called first...
-               var l = -1;
-               //foreach(int el in this.lines) {
-               //      //print("all lines %d\n", el);
-               //
-               
                
-               foreach(int el in this.lines) {
-                       //print("?match %d\n", el);
-                       if (el < line) {
-                               
-                               l = el;
-                               //print("LESS\n");
+               for(var i= 0; i < this.propstore.get_n_items();i++) {
+                       var p = (NodeProp) this.propstore.get_item(i);
+                       GLib.debug("prop %s lines %d -> %d", p.name, p.start_line, p.end_line);
+                       if (p.start_line > line) {
                                continue;
                        }
-                       if (el == line) {
-                               //print("SAME\n");
-                               l = el;
-                               break;
-                       }
-                       if (l > -1) {
-                               //print("RETURNING NODE ON LINE %d", l);
-                               return this.line_map.get(l);
+                       if (line > p.end_line) {
+                               continue;
                        }
-                       return "";
-                       
-               }
-               if (l > -1) {
-                       //print("RETURNING NODE ON LINE %d", l);
-                       return this.line_map.get(l);
+                       return p;
                }
-               return "";
-       
+               return null;
        }
+               
+                
        
        public bool getPropertyRange(string prop, out int start, out int end)
        {
@@ -280,8 +354,7 @@ public class JsRender.Node : Object {
        public string uid()
        {
                if (this.props.get("id") == null) {
-                       uid_count++;
-                       return "uid-%d".printf(uid_count);
+                       return "uid-%d".printf(this.oid);
                }
                return this.props.get("id").val;
        }
@@ -299,6 +372,15 @@ public class JsRender.Node : Object {
                }
                return false;
        }
+       
+       public string FQN { // for sorting
+               owned get { return this.fqn(); }
+               private set  {}
+       }
+       public string NS { // for sorting
+               owned get { return this.props.has_key("xns") ? this.props.get("xns").val  : ""; }
+               private set  {}
+       }
        public string fqn()
        {
                if (!this.hasXnsType ()) {
@@ -310,9 +392,22 @@ public class JsRender.Node : Object {
        public void setFqn(string name)
        {
                var ar = name.split(".");
-               this.props.set("xtype",new NodeProp.prop("xtype", "",  ar[ar.length-1]));
                var l = name.length - (ar[ar.length-1].length +1);
-               this.props.set("xns", new NodeProp.raw("xns", "", name.substring(0, l)));
+               
+
+               
+               if (this.props.has_key("xtype")) {
+                       this.props.get("xtype").val = ar[ar.length-1];
+               } else {
+                       this.add_prop(new NodeProp.prop("xtype", "",  ar[ar.length-1]));                
+               }       
+               if (this.props.has_key("xns")) {
+                       this.props.get("xns").val = name.substring(0, l);
+               } else {
+                       this.add_prop(new NodeProp.raw("xns", "", name.substring(0, l)));               
+               }       
+               
+               
                //print("setFQN %s to %s\n", name , this.fqn());
                               
 
@@ -325,57 +420,7 @@ public class JsRender.Node : Object {
                var v = this.props.get(key);
                return v == null ? "" : v.val;
        }       
-               /*
-               var k = this.props.get(key);
-               if (k != null) {
-                       return k;
-               }
-               
-               k = this.props.get("$ " + key);
-               if (k != null) {
-                       return k;
-               }
-               
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return iter.get_value();
-                       }
-               
-               
-               return "";
-               }
-               */
-        
-       /*
-       
-       SAMNE AS ABOVE
-       public string get_key(string key)
-       {
-               var k = this.props.get(key);
-               if (k != null) {
-                       return key;
-               }
-               
-               k = this.props.get("$ " + key);
-               if (k != null) {
-                       return "$ " + key;
-               }
-               
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return iter.get_key();
-                       }
-               }
-               
-               
-               return "";
-               
-       }
-       */
+                
        public  NodeProp? get_prop(string key)
        {
                
@@ -383,85 +428,21 @@ public class JsRender.Node : Object {
                
        }
        
-       public void set_prop(NodeProp prop)
-       {
-               
-                 this.props.set(prop.to_index_key(), prop);
-               
-       }
-       
-       /*
-       public void normalize_key(string key, out string kname, out string kflag, out string ktype)
-       {
-               // key formats : XXXX
-               // XXX - plain
-               // string XXX - with type
-               // $ XXX - with flag (no type)
-               // $ string XXX - with flag
-               kname = "";
-               ktype = ""; // these used to contain '-' ???
-               kflag = ""; // these used to contain '-' ???
-               var kkv = key.strip().split(" ");
-               string[] kk = {};
-               for (var i = 0; i < kkv.length; i++) {
-                       if (kkv[i].length > 0 ) {
-                               kk += kkv[i];
-                       }
-               }
-               //print("normalize %s => %s\n", key,string.joinv("=:=",kk));
-               
-               switch(kk.length) {
-                       case 1: 
-                               kname = kk[0];
-                               return;
-                       case 2: 
-                               kname = kk[1];
-                               if (kk[0].length > 1) {
-                                       ktype = kk[0];
-                               } else {
-                                       kflag = kk[0];
-                               }
-                               return;
-                       case 3:
-                               kname = kk[2];
-                               kflag = kk[0];
-                               ktype = kk[1];
-                               return;
-               }
-               // everything blank otherwise...
-       }
-       */
        
 
-       public new void set(string key, NodeProp val) {
-               this.props.set(key,val);
-       }
 
        public bool has(string key)
        {
                return this.props.has_key(key);
-               /*
-               var k = this.props.get(key);
-               if (k != null) {
-                       return true;
-               }
-               var iter = this.props.map_iterator();
-               while (iter.next()) {
-                       var kk = iter.get_key().strip().split(" ");
-                       if (kk[kk.length-1] == key) {
-                               return true;
-                       }
-               }
-               
-               return false;
-               */
+                
+        
        }
 
        public void  remove()
        {
                if (this.parent == null) {
-                       
-                       
+                       GLib.debug("remove - parent is null?");
                        return;
                }
                var nlist = new Gee.ArrayList<Node>();
@@ -471,6 +452,11 @@ public class JsRender.Node : Object {
                        }
                        nlist.add(this.parent.items.get(i));
                }
+               uint pos;
+               if ( this.parent.childstore.find(this, out pos)) {
+                       this.parent.childstore.remove(pos);
+               } 
+               this.parent.updated_count++;
                this.parent.items = nlist;
                this.parent = null;
 
@@ -508,6 +494,23 @@ public class JsRender.Node : Object {
                return  Node.gen.to_data (null);   
        }
 
+       public void loadFromJsonString(string str, int ver)
+       {
+               var pa = new Json.Parser();
+               try {
+                       pa.load_from_data(str);
+               } catch (GLib.Error e) {
+                       GLib.debug("Error loading string?");
+                       return;
+               }
+               var new_node = pa.get_root();
+               var obj = new_node.get_object ();
+                    
+               this.loadFromJson(obj, ver);
+       }
+       
+
        public void loadFromJson(Json.Object obj, int version) {
                 
                obj.foreach_member((o , key, value) => {
@@ -519,13 +522,15 @@ public class JsRender.Node : Object {
                                        node.parent = this;
                                        node.loadFromJson(el.get_object(), version);
                                        this.items.add(node);
+                                       this.childstore.append(node);
                                });
                                return;
                        }
                        if (key == "listeners") {
                                var li = value.get_object();
                                li.foreach_member((lio , li_key, li_value) => {
-                                       this.listeners.set(li_key,  new NodeProp.listener(li_key, this.jsonNodeAsString(li_value)));
+                                       this.add_prop(new NodeProp.listener(li_key, this.jsonNodeAsString(li_value)));
+                                       //this.listeners.set(li_key,  new NodeProp.listener(li_key, this.jsonNodeAsString(li_value)));
                                });
                                return;
                        }
@@ -539,7 +544,7 @@ public class JsRender.Node : Object {
                        }
                        var n =  new NodeProp.from_json(rkey, sval);
                                
-                       this.props.set(n.to_index_key(),  n );
+                       this.add_prop(n );
 
 
                });
@@ -718,6 +723,16 @@ public class JsRender.Node : Object {
                //o.set_string_member(key,val);
                
        }
+       
+       
+       public string nodeTipProp { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                        return  this.nodeTip();
+               } 
+       }
        // fixme this needs to better handle 'user defined types etc..
        public string nodeTip()
        {
@@ -745,35 +760,38 @@ public class JsRender.Node : Object {
                        var prop = this.props.get(pk);
                        var i = prop.name.strip();
                        
+                       var val = prop.val;
+                       val = val == null ? "" : val;
+                       
                        switch(prop.ptype) {
                                case PROP: 
                                case RAW: // should they be the same?
                                
-                                       props += "\n\t<b>" + 
-                                               GLib.Markup.escape_text(i) +"</b> : " + 
-                                               GLib.Markup.escape_text(prop.val.split("\n")[0]);
+                                       props += "\n\t" + GLib.Markup.escape_text(prop.rtype) +
+                                               " <b>" + GLib.Markup.escape_text(i) +"</b> : " + 
+                                               GLib.Markup.escape_text(val.split("\n")[0]);
                                                
                                        break;
                                        
                        
                                
                                case METHOD :
-                                       funcs += "\n\t<b>" + 
-                                               GLib.Markup.escape_text(i.substring(1)).strip() +"</b> : " + 
-                                               GLib.Markup.escape_text(prop.val.split("\n")[0]);
+                                       funcs += "\n\t" + GLib.Markup.escape_text(prop.rtype) +
+                                               " <b>" + GLib.Markup.escape_text(i) +"</b> : "  +
+                                               GLib.Markup.escape_text(val.split("\n")[0]);
                                        break;
                                        
                                 
                                case USER : // user defined.
                                        uprops += "\n\t<b>" + 
                                                GLib.Markup.escape_text(i) +"</b> : " + 
-                                               GLib.Markup.escape_text(prop.val.split("\n")[0]);
+                                               GLib.Markup.escape_text(val.split("\n")[0]);
                                        break;
                                        
                                case SPECIAL : // * prop| args | ctor | init
                                        spec += "\n\t<b>" + 
                                                GLib.Markup.escape_text(i) +"</b> : " + 
-                                               GLib.Markup.escape_text(prop.val.split("\n")[0]);
+                                               GLib.Markup.escape_text(val.split("\n")[0]);
                                        break;
                                        
                                case LISTENER : return  "";  // always raw...
@@ -832,6 +850,21 @@ public class JsRender.Node : Object {
                return ret;
 
        }
+       
+       public string nodeTitleProp { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                        return  this.nodeTitle();
+               } 
+       }
+       
+       
+       
+       
+       
+       
        public string nodeTitle(bool for_tip = false) 
        {
                string[] txt = {};
@@ -894,5 +927,197 @@ public class JsRender.Node : Object {
                //if (sr) txt.push('</s>');
                return (txt.length == 0) ? "Element" : string.joinv(" ", txt);
        }
+       // used by trees to display icons?
+       // needs more thought?!?
+       public string iconResourceName { 
+               set {
+                       // NOOp ??? should 
+               }
+               owned get {
+                       var clsname = this.fqn();
+    
+                       var clsb = clsname.split(".");
+                   var sub = clsb.length > 1 ? clsb[1].down()  : "";
+                       var fn = "/glade-icons/widget-gtk-" + sub + ".png";
+                       //if (FileUtils.test (fn, FileTest.IS_REGULAR)) {
+                               return fn;
+                       //}
+                       //return "/dev/null"; //???
+               } 
+       }
+       
+        
+       
+       public void insertAfter(Node child, Node after) 
+       {
+               this.insertChild(this.items.index_of(after) + 1, child);
+       }
+       public void insertBefore(Node child, Node before)       
+       {
+               this.insertChild(this.items.index_of(before), child);
+       }
+       
+       public void insertChild(int pos, Node child)
+       {
+               this.items.insert(pos, child);
+               this.childstore.insert(pos, child);
+               child.parent = this;
+       }
+       public void appendChild(Node child)
+       {
+               this.items.add( child);
+               this.childstore.append(child);
+               child.parent = this;
+       }
+       
+       
+       /**
+       
+       properties
+               previous we had listeners / and props
+               
+               we really need to store this as flat array - keep it simple!?
+               
+               getValue(key)
+               update(key, value)
+               
+               
+       
+       */
+       
 
+       
+       
+       public void loadProps(GLib.ListStore model, Project.Project project) 
+       {
+       
+               // fixme sorting?? - no need to loop twice .. just use sorting.!
+               var oldstore = this.propstore;
+               this.propstore = model;
+               for(var i =  0; i < oldstore.n_items; i++ ) {
+                       var it = (NodeProp) oldstore.get_item(i);
+                       it.update_is_valid_ptype(project);
+                   model.append(it);
+                       
+               }
+               this.sortProps();
+          
+   }
+   // used to replace propstore, so it does not get wiped by editing a node
+   public void dupeProps()
+   {
+               GLib.debug("dupeProps START");
+               var oldstore = this.propstore;
+               this.propstore = new GLib.ListStore(typeof(NodeProp));;
+               for(var i =  0; i < oldstore.n_items; i++ ) {
+                       var it = (NodeProp) oldstore.get_item(i);
+                       this.propstore.append(it);
+               }
+               GLib.debug("dupeProps END");
+       }
+       
+   
+   public void remove_prop(NodeProp prop)
+       {
+               uint pos;
+               if (!this.propstore.find(prop, out pos)) {
+                       return;
+               }
+               this.propstore.remove(pos);
+               this.updated_count++;
+               
+       }   
+   
+       public bool has_prop_key(NodeProp prop) 
+       {
+               for(var i =  0; i < this.propstore.n_items; i++ ) {
+                       var it = (NodeProp) this.propstore.get_item(i);
+                       if (it.ptype == prop.ptype && it.to_index_key() == prop.to_index_key()) {
+                               return true;
+                       }
+                       
+               }
+               return false;
+          
+       }
+       
+        
+       
+       
+       public void add_prop(NodeProp prop)
+       {
+               if (this.has_prop_key(prop) && !prop.to_index_key().has_suffix("[]")) {
+                       GLib.warning("duplicate key' %s'- can not add - call has_prop_key first", prop.to_index_key());
+                       return;
+               }
+               prop.parent = this;
+               this.propstore.append(prop);
+               this.sortProps();
+               
+               this.updated_count++;
+               
+               
+       }
+       
+       int props_updated_count = -1;
+       Gee.HashMap<string,NodeProp> props_cache;
+       
+       public Gee.HashMap<string,NodeProp> props {
+               owned get {
+                       if (this.updated_count == this.props_updated_count) {
+                               return this.props_cache;
+                       }
+                        this.props_cache = new Gee.HashMap<string,NodeProp>(); // the properties..
+
+                       for(var i =  0; i < this.propstore.n_items; i++ ) {
+                               var it = (NodeProp) this.propstore.get_item(i);
+                               if (it.ptype != NodePropType.LISTENER) {
+                               //      GLib.debug("props add key %s", it.to_index_key());
+                                       this.props_cache.set( it.to_index_key() , it);
+                               }
+                       }
+                       this.props_updated_count = this.updated_count;
+                       return this.props_cache;
+               }
+               private set {
+                       GLib.error("do not set listerners direclty");
+               }
+       }
+       
+       int listeners_updated_count = -1;
+       Gee.HashMap<string,NodeProp> listeners_cache;
+       
+       //private Gee.HashMap<string,NodeProp> _listeners; // the listeners..
+       public Gee.HashMap<string,NodeProp> listeners {
+               owned get {
+                       if (this.updated_count == this.listeners_updated_count) {
+                               return this.listeners_cache;
+                       }
+                       
+                       this.listeners_cache = new Gee.HashMap<string,NodeProp>(); // the properties..
+
+                       for(var i =  0; i < this.propstore.n_items; i++ ) {
+                               var it = (NodeProp) this.propstore.get_item(i);
+                               if (it.ptype == NodePropType.LISTENER) {
+                                       this.listeners_cache.set( it.to_index_key() , it);
+                               }
+                       }
+                       this.listeners_updated_count = this.updated_count;
+                       return this.listeners_cache;;
+               }
+               private set {
+                       GLib.error("do not set listerners direclty");
+               }
+       }
+       private void sortProps ()
+       {
+       
+               this.propstore.sort( (a, b) => {
+
+                       return Posix.strcmp( ((NodeProp)a).to_sort_key(),  ((NodeProp)b).to_sort_key());
+                       
+               });
+        
+       
+       }
 }