-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,string> props; // the properties..
- public Gee.HashMap<string,string> listeners; // the listeners..
+ 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;
public string xvala_xcls; // 'Xcls_' + id;
public string xvala_id; // item id or ""
public int line_start;
public int line_end;
public Gee.ArrayList<int> lines;
- public Gee.HashMap<int,string> line_map; // store of l:xxx or p:....
- public Gee.ArrayList<int> node_lines;
+ public Gee.HashMap<int,string> line_map; // store of l:xxx or p:.... // fixme - not needed as we can store line numbers in props now.
+ public Gee.ArrayList<int> node_lines;
public Gee.HashMap<int,Node> node_lines_map; // store of l:xxx or p:....
+ private int _updated_count = 0;
+ public int updated_count {
+ get {
+ return this._updated_count;
+ }
+ set {
+ this.nodeTitleProp = ""; // ?? should trigger set?
+ this.iconFilename = "";
+ this._updated_count = value;
+ if (this.parent != null) {
+ this.parent.updated_count++;
+ }
+ }
+
+ } // changes to this trigger updates on the tree..
public Node()
{
this.items = new Gee.ArrayList<Node>();
- this.props = new Gee.HashMap<string,string>();
- this.listeners = new Gee.HashMap<string,string>();
+ //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 = "";
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 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)) {
}
- 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;
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");
return null;
}
- 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 null;
-
- }
- if (l > -1) {
- //print("RETURNING NODE ON LINE %d", l);
- return this.line_map.get(l);
+ return p;
}
return null;
-
}
+
+
public bool getPropertyRange(string prop, out int start, out int end)
{
+ end = 0;
start = -1;
foreach(int el in this.lines) {
if (start < 0) {
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");
+ return this.props.get("id").val;
}
}
public bool hasXnsType()
{
- if (this.props.get("$ xns") != null && this.props.get("xtype") != null) {
+ if (this.props.get("xns") != null && this.props.get("xtype") != null) {
return true;
}
return false;
}
+
+ public string FQN { // for sorting
+ owned get { return this.fqn(); }
+ private set {}
+ }
+
public string fqn()
{
if (!this.hasXnsType ()) {
return "";
}
- return this.props.get("$ xns") + "." + this.props.get("xtype");
+ return this.props.get("xns").val + "." + this.props.get("xtype").val;
}
public void setFqn(string name)
{
var ar = name.split(".");
- this.props.set("xtype", ar[ar.length-1]);
var l = name.length - (ar[ar.length-1].length +1);
- this.props.set("$ 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());
}
// wrapper around get props that returns empty string if not found.
- public string get(string key)
+ //overrides Glib.object.get (hence new)
+ public new string get(string key)
{
- 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 "";
-
- }
-
- public string get_key(string key)
+ var v = this.props.get(key);
+ return v == null ? "" : v.val;
+ }
+
+ public NodeProp? get_prop(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 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));
+ return this.props.get(key);
- 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 void set(string key, string value) {
- this.props.set(key,value);
- }
- public bool has(string key)
+
+
+
+
+
+ public bool has(string 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;
-
+ return this.props.has_key(key);
+
+
}
public void remove()
{
if (this.parent == null) {
-
-
+ GLib.debug("remove - parent is null?");
return;
}
var nlist = new Gee.ArrayList<Node>();
}
nlist.add(this.parent.items.get(i));
}
+ uint pos;
+ if ( this.parent.childstore.find(this, out pos)) {
+ this.parent.childstore.remove(pos);
+ }
+
this.parent.items = nlist;
this.parent = null;
public Node? findProp(string n) {
for(var i=0;i< this.items.size;i++) {
var p = this.items.get(i).get("* prop");
- if (this.items.get(i).get("* prop").length < 1) {
+ if (p == null) {
continue;
}
if (p == n) {
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) => {
//print(key+"\n");
if (key == "items") {
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, li_value.get_string());
-
+ 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;
}
- var v = value.get_value();
- var sv = Value (typeof (string));
- v.transform(ref sv);
+
var rkey = key;
+ var sval = this.jsonNodeAsString(value);
+
if (version == 1) {
- rkey = this.upgradeKey(key, (string)sv);
+ rkey = this.upgradeKey(key, sval);
}
+ var n = new NodeProp.from_json(rkey, sval);
+
+ this.add_prop(n );
+
-
- this.props.set(rkey, (string)sv);
});
+
+
+
+ }
+
+ // converts the array into a string with line breaks.
+ public string jsonNodeAsString(Json.Node node)
+ {
+
+ if (node.get_node_type() == Json.NodeType.ARRAY) {
+ var buffer = new GLib.StringBuilder();
+ var ar = node.get_array();
+ for (var i = 0; i < ar.get_length(); i++) {
+ if (i >0 ) {
+ buffer.append_c('\n');
+ }
+ buffer.append(ar.get_string_element(i));
+ }
+ return buffer.str;
+ }
+ // hopeflyu only type value..
+ var sv = Value (typeof (string));
+ var v = node.get_value();
+ v.transform(ref sv);
+ return (string)sv;
}
+
+ // really old files...
public string upgradeKey(string key, string val)
{
return Node.gen.to_data (null);
}
+ public void jsonObjectAddStringValue(Json.Object obj, string key, string v)
+ {
+ if (v.index_of_char('\n',0) < 0) {
+ obj.set_string_member(key,v);
+ return;
+ }
+ var aro = new Json.Array();
+ var ar = v.split("\n");
+ for(var i =0;i < ar.length;i++) {
+ aro.add_string_element(ar[i]);
+ }
+ obj.set_array_member(key,aro);
+ }
+
public Json.Object toJsonObject()
{
var ret = new Json.Object();
ret.set_object_member("listeners", li);
var liter = this.listeners.map_iterator();
while (liter.next()) {
- li.set_string_member(liter.get_key(), liter.get_value());
+ this.jsonObjectAddStringValue(li, liter.get_value().to_json_key(), liter.get_value().val);
}
}
//props
if (this.props.size > 0 ) {
var iter = this.props.map_iterator();
while (iter.next()) {
- this.jsonObjectsetMember(ret, iter.get_key(), iter.get_value());
+ this.jsonObjectsetMember(ret, iter.get_value().to_json_key(), iter.get_value().val);
}
}
if (this.items.size > 0) {
return;
}
///print( "ADD " + key + "=" + val + " as a string?\n");
- o.set_string_member(key,val);
+ this.jsonObjectAddStringValue(o,key,val);
+ //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()
{
var ret = this.nodeTitle(true);
+ var spec = "";
var funcs = "";
var props = "";
var listen = "";
- var iter = this.props.map_iterator();
- while (iter.next()) {
- var i = iter.get_key().strip();
- var val = iter.get_value().strip();
- if (val == null || val.length < 1) {
- continue;
- }
- if ( i[0] != '|') {
- props += "\n\t<b>" +
- GLib.Markup.escape_text(i) +"</b> : " +
- GLib.Markup.escape_text(val.split("\n")[0]);
-
- continue;
- }
+
+ var uprops = "";
+ // sort?
+
+ var keys = new Gee.ArrayList<string>();
+ foreach(var k in this.props.keys) {
+ keys.add(k);
+ }
+ keys.sort((a,b) => {
+ return Posix.strcmp(a, b);
+
+ });
- //if (i == "* init") {
- // continue;
- //}
+
+ foreach(var pk in keys) {
+
+ 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" + GLib.Markup.escape_text(prop.rtype) +
+ " <b>" + GLib.Markup.escape_text(i) +"</b> : " +
+ GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+
+ break;
+
+
+
+ case METHOD :
+
+ funcs += "\n\t" + GLib.Markup.escape_text(prop.rtype) +
+ " <b>" + GLib.Markup.escape_text(i) +"</b> : " +
+ GLib.Markup.escape_text(val == "" ? "" : val.split("\n")[0]);
+ break;
+
+
+ case USER : // user defined.
+
+ uprops += "\n\t<b>" +
+ GLib.Markup.escape_text(i) +"</b> : " +
+ GLib.Markup.escape_text(val == "" ? "" : 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(val == "" ? "" : val.split("\n")[0]);
+ break;
+
+ case LISTENER : return ""; // always raw...
+ // not used
+ default:
+ break;;
- if (Regex.match_simple("^\\s*function", val)) {
- funcs += "\n\t<b>" +
- GLib.Markup.escape_text(i.substring(1)).strip() +"</b> : " +
- GLib.Markup.escape_text(val.split("\n")[0]);
- continue;
- }
- if (Regex.match_simple("^\\s*\\(", val)) {
- funcs += "\n\t<b>" + GLib.Markup.escape_text(i.substring(1)).strip() +
- "</b> : " +
- GLib.Markup.escape_text(val.split("\n")[0]);
- continue;
}
+
}
- iter = this.listeners.map_iterator();
- while (iter.next()) {
- var i = iter.get_key().strip();
- var val = iter.get_value().strip();
+
+ keys = new Gee.ArrayList<string>();
+ foreach(var k in this.listeners.keys) {
+ keys.add(k);
+ }
+ keys.sort((a,b) => {
+ return Posix.strcmp(a, b);
+
+ });
+
+ foreach(var pk in keys) {
+
+ var prop = this.listeners.get(pk);
+ var i = prop.name.strip();
+
+ var val = prop.val.strip();
if (val == null || val.length < 1) {
continue;
}
if (props.length > 0) {
ret+="\n\nProperties:" + props;
+ }
+ if (uprops.length > 0) {
+ ret+="\n\nUser defined Properties:" + uprops;
}
+
+
if (funcs.length > 0) {
ret+="\n\nMethods:" + funcs;
}
if (listen.length > 0) {
ret+="\n\nListeners:" + listen;
}
+ if (spec.length > 0) {
+ ret+="\n\nSpecial:" + spec;
+ }
+
return ret;
}
- public string nodeTitle(bool for_tip = false) {
+
+ public string nodeTitleProp {
+ set {
+ // NOOp ??? should
+ }
+ owned get {
+ return this.nodeTitle();
+ }
+ }
+
+
+
+
+
+
+ public string nodeTitle(bool for_tip = false)
+ {
string[] txt = {};
//var sr = (typeof(c['+buildershow']) != 'undefined') && !c['+buildershow'] ? true : false;
//if (sr) txt.push('</s>');
return (txt.length == 0) ? "Element" : string.joinv(" ", txt);
}
+ // used by trees to display icons?
+ // needs more thought?!?
+ public string iconFilename {
+ set {
+ // NOOp ??? should
+ }
+ owned get {
+ var clsname = this.fqn();
+
+ var clsb = clsname.split(".");
+ var sub = clsb.length > 1 ? clsb[1].down() : "";
+ var fn = "/usr/share/glade/pixmaps/hicolor/16x16/actions/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)
+ {
+
+ // 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);
+ 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());
+
+ });
+
+
+ }
}