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>
39 typedef string stdString; // puObject has a "string" member
41 #include <Main/fg_os.hxx> // fgGetKeyModifiers()
42 #include <Main/fg_props.hxx>
44 #include "property_list.hxx"
47 static string getValueTypeString(const SGPropertyNode *node)
49 using namespace simgear;
52 props::Type type = node->getType();
53 if (type == props::UNSPECIFIED)
54 result = "unspecified";
55 else if (type == props::NONE)
57 else if (type == props::BOOL)
59 else if (type == props::INT)
61 else if (type == props::LONG)
63 else if (type == props::FLOAT)
65 else if (type == props::DOUBLE)
67 else if (type == props::STRING)
69 else if (type == props::VEC3D)
71 else if (type == props::VEC4D)
78 static void dumpProperties(const SGPropertyNode *node)
80 using namespace simgear;
81 cout << node->getPath() << '/' << endl;
82 for (int i = 0; i < node->nChildren(); i++) {
83 const SGPropertyNode *c = node->getChild(i);
84 props::Type type = c->getType();
85 if (type == props::ALIAS || c->nChildren())
88 int index = c->getIndex();
89 cout << std::setw(11) << getValueTypeString(c) << " " << c->getName();
91 cout << '[' << index << ']';
94 switch (c->getType()) {
100 std::streamsize precision = cout.precision(15);
102 cout.precision(precision);
111 cout << '"' << c->getStringValue() << '"';
116 cout << '\'' << c->getStringValue() << '\'';
124 static void sanitize(stdString& s)
128 for (unsigned i = 0; i < r.size(); i++) {
131 else if (r[i] == '\b')
133 else if (r[i] == '\t')
135 else if (r[i] == '\n')
137 else if (r[i] == '\v')
139 else if (r[i] == '\f')
141 else if (r[i] == '\r')
143 else if (r[i] == '\'')
145 else if (r[i] == '\\')
147 else if (isprint(r[i]))
150 const char *hex = "0123456789abcdef";
152 s += stdString("\\x") + hex[c / 16] + hex[c % 16];
160 PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNode *start) :
161 puaList(minx, miny, maxx, maxy, short(0), 20),
162 GUI_ID(FGCLASS_PROPERTYLIST),
170 _list_box->setUserData(this);
171 _list_box->setCallback(handle_select);
172 _list_box->setValue(0);
177 PropertyList::~PropertyList()
183 void PropertyList::delete_arrays()
188 for (int i = 0; i < _num_entries; i++)
189 delete[] _entries[i];
198 void PropertyList::handle_select(puObject *list_box)
200 PropertyList *prop_list = (PropertyList *)list_box->getUserData();
201 int selected = list_box->getIntegerValue();
202 int mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
203 int mod_shift = fgGetKeyModifiers() & KEYMOD_SHIFT;
205 if (selected >= 0 && selected < prop_list->_num_entries) {
206 const char *src = prop_list->_entries[selected];
208 if (prop_list->_dot_files && (selected < 2)) {
209 if (src[0] == '.' && (src[1] == '\0' || src[1] == ' ')) {
210 if (mod_ctrl && mod_shift)
211 prop_list->_curr->fireValueChanged();
213 prop_list->toggleVerbosity();
215 dumpProperties(prop_list->_curr);
220 } else if (!strcmp(src, "..")) {
221 SGPropertyNode *parent = prop_list->getCurrent()->getParent();
224 for (; parent->getParent(); parent = parent->getParent())
226 prop_list->setCurrent(parent);
232 // we know we're dealing with a regular entry, so convert
233 // it to an index into children[]
234 if (prop_list->_dot_files)
237 SGPropertyNode_ptr child = prop_list->_children[selected].node;
239 // check if it's a directory
240 if (child->nChildren()) {
241 prop_list->setTopItem(0);
242 prop_list->setCurrent(child);
246 // it is a regular property
247 if (child->getType() == simgear::props::BOOL && mod_ctrl) {
248 child->setBoolValue(!child->getBoolValue());
249 prop_list->update(true);
251 prop_list->publish(child);
254 // the user clicked on blank screen
255 prop_list->update(true);
260 void PropertyList::update(bool restore_pos)
265 _num_entries = (int)_curr->nChildren();
267 // instantiate string objects and add [.] and [..] for subdirs
268 if (!_curr->getParent()) {
269 _entries = new char*[_num_entries + 1];
274 _num_entries += 2; // for . and ..
275 _entries = new char*[_num_entries + 1];
277 _entries[0] = new char[16];
278 strcpy(_entries[0], _verbose ? ". [verbose]" : ".");
280 _entries[1] = new char[3];
281 strcpy(_entries[1], "..");
288 _num_children = _curr->nChildren();
289 _children = new NodeData[_num_children];
290 for (i = 0; i < _num_children; i++)
291 _children[i].node = _curr->getChild(i);
293 qsort(_children, _num_children, sizeof(_children[0]), nodeNameCompare);
295 // Make lists of the children's names, values, etc.
296 for (i = 0; i < _num_children; i++, pi++) {
297 _children[i].text = &_entries[pi];
298 _entries[pi] = 0; // make it deletable
299 updateTextForEntry(_children[i]);
300 _children[i].setListener(this);
303 _entries[_num_entries] = 0;
305 int top = getTopItem();
312 void PropertyList::updateTextForEntry(NodeData& data)
314 using namespace simgear;
315 SGPropertyNode *node = data.node;
316 stdString name = node->getDisplayName(true);
317 stdString type = getValueTypeString(node);
318 stdString value = node->getStringValue();
320 std::ostringstream line;
323 int children = node->nChildren();
327 if (!children || (_verbose && node->hasValue())) {
328 if (node->getType() == props::STRING
329 || node->getType() == props::UNSPECIFIED)
332 line << " = '" << value << "' (" << type;
336 if (!node->getAttribute(SGPropertyNode::READ))
338 if (!node->getAttribute(SGPropertyNode::WRITE))
340 if (node->getAttribute(SGPropertyNode::TRACE_READ))
342 if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
344 if (node->getAttribute(SGPropertyNode::ARCHIVE))
346 if (node->getAttribute(SGPropertyNode::USERARCHIVE))
348 if (node->getAttribute(SGPropertyNode::PRESERVE))
356 int num = node->nListeners();
360 line << ", L" << num;
365 if ((_verbose)&&(node->getAttribute(SGPropertyNode::PRESERVE)))
367 // only preserve/protection flag matters for nodes without values
371 stdString out = line.str();
372 if (out.size() >= PUSTRING_MAX)
373 out.resize(PUSTRING_MAX - 1);
376 *data.text = new char[out.size() + 1];
377 strcpy(*data.text, out.c_str());
381 void PropertyList::valueChanged(SGPropertyNode *nd)
383 for (int i = 0; i < _num_children; i++)
384 if (_children[i].node == nd) {
385 updateTextForEntry(_children[i]);
391 int PropertyList::nodeNameCompare(const void *p1, const void *p2)
393 const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
394 const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
396 int diff = strcmp(n1->getName(), n2->getName());
397 return diff ? diff : n1->getIndex() - n2->getIndex();
401 void PropertyList::setValue(const char *s)
404 SGPropertyNode *p = fgGetNode(s, false);
408 throw stdString("node doesn't exist");
409 } catch (const stdString& m) {
410 SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
415 void PropertyList::setCurrent(SGPropertyNode *p)
417 bool same = (_curr == p);