New Nasal code, with lots of input binding handlers in controls.nas.
[fg:toms-fgdata.git] / Nasal / props.nas
1 ##
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.
5 #
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
14 # tree).
15 #
16 Node = {
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)) },
30
31 };
32
33 ##
34 # Static constructor for a Node object.  Accepts a Nasal hash
35 # expression to initialize the object a-la setValues().
36 #
37 Node.new = func {
38     result = wrapNode(_new());
39     if(size(arg) >= 0 and typeof(arg[0]) == "hash") {
40         result.setValues(arg[0]);
41     }
42     return result;
43 }
44
45 ##
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
50 # syntax like:
51 #
52 # dialog = {
53 #   name : "exit", width : 180, height : 100, modal : 0,
54 #   text : { x : 10, y : 70, label : "Hello World!" } };
55 #
56 Node.setValues = func {
57     foreach(k; keys(arg[0])) { me._setChildren(k, arg[0][k]); }
58 }
59
60 ##
61 # Private function to do the work of setValues().
62 # The first argument is a child name, the second a nasal scalar,
63 # vector, or hash.
64 #
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]);
74         }
75     }
76 }
77
78 ##
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.
82 #
83 dump = func {
84     if(size(arg) == 1) { prefix = "";     node = arg[0]; }
85     else               { prefix = arg[0]; node = arg[1]; }
86
87     index = node.getIndex();
88     type = node.getType();
89     name = node.getName();
90     val = node.getValue();
91
92     if(val == nil) { val = "nil"; }
93     name = prefix ~ name;
94     if(index > 0) { name = name ~ "[" ~ index ~ "]"; }
95     print(name, " {", type, "} = ", val);
96
97     # Don't recurse into aliases, lest we get stuck in a loop
98     if(type != "ALIAS") {
99         children = node.getChildren();
100         foreach(c; children) { dump(name ~ "/", c); }
101     }
102 }
103
104 ##
105 # Utility.  Turns any ghosts it finds (either solo, or in an
106 # array) into Node objects.
107 #
108 wrap = func {
109     argtype = typeof(arg[0]);
110     if(argtype == "ghost") {
111         return wrapNode(arg[0]);
112     } elsif(argtype == "vector") {
113         v = arg[0];
114         n = size(v);
115         for(i=0; i<n; i=i+1) { v[i] = wrapNode(v[i]); }
116         return v;
117     }
118     return arg[0];
119 }
120
121 ##
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. :)
125 #
126 wrapNode = func { { parents : [Node], _g : arg[0] } }
127
128 ##
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.
132 #
133 props.globals = wrapNode(_globals());
134
135 ##
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]
139 # is the value.
140 #
141 setAll = func {
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]);
150     }
151 }