1 // Implementation of the <property-list> widget.
3 // Copyright (C) 2001 Steve BAKER
4 // Copyright (C) 2001 Jim WILSON
5 // Copyright (C) 2006 Melchior FRANZ
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <simgear/compiler.h>
37 typedef string stdString; // puObject has a "string" member
39 #include <Main/fg_os.hxx> // fgGetKeyModifiers()
40 #include <Main/fg_props.hxx>
42 #include "property_list.hxx"
45 static string getValueTypeString(const SGPropertyNode *node)
49 SGPropertyNode::Type type = node->getType();
50 if (type == SGPropertyNode::UNSPECIFIED)
51 result = "unspecified";
52 else if (type == SGPropertyNode::NONE)
54 else if (type == SGPropertyNode::BOOL)
56 else if (type == SGPropertyNode::INT)
58 else if (type == SGPropertyNode::LONG)
60 else if (type == SGPropertyNode::FLOAT)
62 else if (type == SGPropertyNode::DOUBLE)
64 else if (type == SGPropertyNode::STRING)
71 static void dumpProperties(const SGPropertyNode *node)
73 cout << node->getPath() << '/' << endl;
74 for (int i = 0; i < node->nChildren(); i++) {
75 const SGPropertyNode *c = node->getChild(i);
76 SGPropertyNode::Type type = c->getType();
77 if (type == SGPropertyNode::ALIAS || c->nChildren())
80 int index = c->getIndex();
81 cout << std::setw(11) << getValueTypeString(c) << " " << c->getName();
83 cout << '[' << index << ']';
86 switch (c->getType()) {
87 case SGPropertyNode::DOUBLE:
88 case SGPropertyNode::FLOAT:
89 cout << std::setprecision(15) << c->getDoubleValue();
91 case SGPropertyNode::LONG:
92 case SGPropertyNode::INT:
93 cout << c->getLongValue();
95 case SGPropertyNode::BOOL:
96 cout << (c->getBoolValue() ? "true" : "false");
98 case SGPropertyNode::STRING:
99 cout << '"' << c->getStringValue() << '"';
101 case SGPropertyNode::NONE:
104 cout << '\'' << c->getStringValue() << '\'';
112 static void sanitize(stdString& s)
116 for (unsigned i = 0; i < r.size(); i++) {
119 else if (r[i] == '\b')
121 else if (r[i] == '\t')
123 else if (r[i] == '\n')
125 else if (r[i] == '\v')
127 else if (r[i] == '\f')
129 else if (r[i] == '\r')
131 else if (r[i] == '\'')
133 else if (r[i] == '\\')
135 else if (isprint(r[i]))
138 const char *hex = "0123456789abcdef";
140 s += stdString("\\x") + hex[c / 16] + hex[c % 16];
148 PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNode *start) :
149 puaList(minx, miny, maxx, maxy, short(0), 20),
150 GUI_ID(FGCLASS_PROPERTYLIST),
158 _list_box->setUserData(this);
159 _list_box->setCallback(handle_select);
160 _list_box->setValue(0);
165 PropertyList::~PropertyList()
171 void PropertyList::delete_arrays()
176 for (int i = 0; i < _num_entries; i++)
177 delete[] _entries[i];
186 void PropertyList::handle_select(puObject *list_box)
188 PropertyList *prop_list = (PropertyList *)list_box->getUserData();
189 int selected = list_box->getIntegerValue();
190 int mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
191 int mod_shift = fgGetKeyModifiers() & KEYMOD_SHIFT;
193 if (selected >= 0 && selected < prop_list->_num_entries) {
194 const char *src = prop_list->_entries[selected];
196 if (prop_list->_dot_files && (selected < 2)) {
197 if (src[0] == '.' && (src[1] == '\0' || src[1] == ' ')) {
198 if (mod_ctrl && mod_shift)
199 prop_list->_curr->fireValueChanged();
201 prop_list->toggleVerbosity();
203 dumpProperties(prop_list->_curr);
208 } else if (!strcmp(src, "..")) {
209 SGPropertyNode *parent = prop_list->getCurrent()->getParent();
212 for (; parent->getParent(); parent = parent->getParent())
214 prop_list->setCurrent(parent);
220 // we know we're dealing with a regular entry, so convert
221 // it to an index into children[]
222 if (prop_list->_dot_files)
225 SGPropertyNode_ptr child = prop_list->_children[selected].node;
227 // check if it's a directory
228 if (child->nChildren()) {
229 prop_list->setTopItem(0);
230 prop_list->setCurrent(child);
234 // it is a regular property
235 if (child->getType() == SGPropertyNode::BOOL && mod_ctrl) {
236 child->setBoolValue(!child->getBoolValue());
237 prop_list->update(true);
239 prop_list->publish(child);
242 // the user clicked on blank screen
243 prop_list->update(true);
248 void PropertyList::update(bool restore_pos)
253 _num_entries = (int)_curr->nChildren();
255 // instantiate string objects and add [.] and [..] for subdirs
256 if (!_curr->getParent()) {
257 _entries = new char*[_num_entries + 1];
262 _num_entries += 2; // for . and ..
263 _entries = new char*[_num_entries + 1];
265 _entries[0] = new char[16];
266 strcpy(_entries[0], _verbose ? ". [verbose]" : ".");
268 _entries[1] = new char[3];
269 strcpy(_entries[1], "..");
276 _num_children = _curr->nChildren();
277 _children = new NodeData[_num_children];
278 for (i = 0; i < _num_children; i++)
279 _children[i].node = _curr->getChild(i);
281 qsort(_children, _num_children, sizeof(_children[0]), nodeNameCompare);
283 // Make lists of the children's names, values, etc.
284 for (i = 0; i < _num_children; i++, pi++) {
285 _children[i].text = &_entries[pi];
286 _entries[pi] = 0; // make it deletable
287 updateTextForEntry(_children[i]);
288 _children[i].setListener(this);
291 _entries[_num_entries] = 0;
293 int top = getTopItem();
300 void PropertyList::updateTextForEntry(NodeData& data)
302 SGPropertyNode *node = data.node;
303 stdString name = node->getDisplayName(true);
304 stdString type = getValueTypeString(node);
305 stdString value = node->getStringValue();
307 std::ostringstream line;
310 int children = node->nChildren();
314 if (!children || (_verbose && node->hasValue())) {
315 if (node->getType() == SGPropertyNode::STRING
316 || node->getType() == SGPropertyNode::UNSPECIFIED)
319 line << " = '" << value << "' (" << type;
323 if (!node->getAttribute(SGPropertyNode::READ))
325 if (!node->getAttribute(SGPropertyNode::WRITE))
327 if (node->getAttribute(SGPropertyNode::TRACE_READ))
329 if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
331 if (node->getAttribute(SGPropertyNode::ARCHIVE))
333 if (node->getAttribute(SGPropertyNode::USERARCHIVE))
341 int num = node->nListeners();
345 line << ", L" << num;
350 stdString out = line.str();
351 if (out.size() >= PUSTRING_MAX)
352 out.resize(PUSTRING_MAX - 1);
355 *data.text = new char[out.size() + 1];
356 strcpy(*data.text, out.c_str());
360 void PropertyList::valueChanged(SGPropertyNode *nd)
362 for (int i = 0; i < _num_children; i++)
363 if (_children[i].node == nd) {
364 updateTextForEntry(_children[i]);
370 int PropertyList::nodeNameCompare(const void *p1, const void *p2)
372 const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
373 const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
375 int diff = strcmp(n1->getName(), n2->getName());
376 return diff ? diff : n1->getIndex() - n2->getIndex();
380 void PropertyList::setValue(const char *s)
383 SGPropertyNode *p = fgGetNode(s, false);
387 throw stdString("node doesn't exist");
388 } catch (const stdString& m) {
389 SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
394 void PropertyList::setCurrent(SGPropertyNode *p)
396 bool same = (_curr == p);