2 # Node class definition. The class methods simply wrap the
3 # low level exention functions which work on a "ghost" handle to a
4 # SGPropertyNode object stored in the _g field.
6 # Not all of the features of SGPropertyNode are supported. There is
7 # no support for ties, obviously, as that wouldn't make much sense
8 # from a Nasal context. The various get/set methods work only on the
9 # local node, there is no equivalent of the "relative path" variants
10 # available in C++; just use node.getNode(path).whatever() instead.
11 # There is no support for the "listener" interface yet. The aliasing
12 # feature isn't exposed, except that you can get an "ALIAS" return
13 # from getType to detect them (to avoid cycles while walking the
17 getType : func { wrap(_getType(me._g, arg)) },
18 getName : func { wrap(_getName(me._g, arg)) },
19 getIndex : func { wrap(_getIndex(me._g, arg)) },
20 getValue : func { wrap(_getValue(me._g, arg)) },
21 setValue : func { wrap(_setValue(me._g, arg)) },
22 setIntValue : func { wrap(_setIntValue(me._g, arg)) },
23 setBoolValue : func { wrap(_setBoolValue(me._g, arg)) },
24 setDoubleValue : func { wrap(_setDoubleValue(me._g, arg)) },
25 getParent : func { wrap(_getParent(me._g, arg)) },
26 getChild : func { wrap(_getChild(me._g, arg)) },
27 getChildren : func { wrap(_getChildren(me._g, arg)) },
28 removeChild : func { wrap(_removeChild(me._g, arg)) },
29 getNode : func { wrap(_getNode(me._g, arg)) },
34 # Static constructor for a Node object. Accepts a Nasal hash
35 # expression to initialize the object a-la setValues().
38 result = wrapNode(_new());
39 if(size(arg) >= 0 and typeof(arg[0]) == "hash") {
40 result.setValues(arg[0]);
46 # Useful utility. Sets a whole property tree from a Nasal hash
47 # object, such that scalars become leafs in the property tree, hashes
48 # become named subnodes, and vectors become indexed subnodes. This
49 # works recursively, so you can define whole property trees with
53 # name : "exit", width : 180, height : 100, modal : 0,
54 # text : { x : 10, y : 70, label : "Hello World!" } };
56 Node.setValues = func {
57 foreach(k; keys(arg[0])) { me._setChildren(k, arg[0][k]); }
61 # Private function to do the work of setValues().
62 # The first argument is a child name, the second a nasal scalar,
65 Node._setChildren = func {
66 name = arg[0]; val = arg[1];
67 subnode = me.getNode(name, 1);
68 if(typeof(val) == "scalar") { subnode.setValue(val); }
69 elsif(typeof(val) == "hash") { subnode.setValues(val); }
70 elsif(typeof(val) == "vector") {
71 for(i=0; i<size(val); i=i+1) {
72 iname = name ~ "[" ~ i ~ "]";
73 me._setChildren(iname, val[i]);
79 # Useful debugging utility. Recursively dumps the full state of a
80 # Node object to the console. Try binding "props.dump(props.globals)"
81 # to a key for a fun hack.
84 if(size(arg) == 1) { prefix = ""; node = arg[0]; }
85 else { prefix = arg[0]; node = arg[1]; }
87 index = node.getIndex();
88 type = node.getType();
89 name = node.getName();
90 val = node.getValue();
92 if(val == nil) { val = "nil"; }
94 if(index > 0) { name = name ~ "[" ~ index ~ "]"; }
95 print(name, " {", type, "} = ", val);
97 # Don't recurse into aliases, lest we get stuck in a loop
99 children = node.getChildren();
100 foreach(c; children) { dump(name ~ "/", c); }
105 # Utility. Turns any ghosts it finds (either solo, or in an
106 # array) into Node objects.
109 argtype = typeof(arg[0]);
110 if(argtype == "ghost") {
111 return wrapNode(arg[0]);
112 } elsif(argtype == "vector") {
115 for(i=0; i<n; i=i+1) { v[i] = wrapNode(v[i]); }
122 # Utility. Returns a new object with its superclass/parent set to the
123 # Node object and its _g (ghost) field set to the specified object.
124 # Nasal's literal syntax can be pleasingly terse. I like that. :)
126 wrapNode = func { { parents : [Node], _g : arg[0] } }
129 # Global property tree. Set once at initialization. Is that OK?
130 # Does anything ever call globals.set_props() from C++? May need to
131 # turn this into a function if so.
133 props.globals = wrapNode(_globals());
136 # Sets all indexed property children to a single value. arg[0]
137 # specifies a property name (e.g. /controls/engines/engine), arg[1] a
138 # path under each node of that name to set (e.g. "throttle"), arg[2]
142 node = props.globals.getNode(arg[0]);
143 if(node == nil) { return; }
144 name = node.getName();
145 node = node.getParent();
146 if(node == nil) { return; }
147 children = node.getChildren();
148 foreach(c; children) {
149 c.getNode(arg[1], 1).setValue(arg[2]);