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>
33 typedef string stdString; // puObject has a "string" member
35 #include <Main/fg_os.hxx> // fgGetKeyModifiers()
36 #include <Main/fg_props.hxx>
38 #include "property_list.hxx"
41 static string getValueTypeString(const SGPropertyNode *node)
46 SGPropertyNode::Type type = node->getType();
47 if (type == SGPropertyNode::UNSPECIFIED)
48 result = "unspecified";
49 else if (type == SGPropertyNode::NONE)
51 else if (type == SGPropertyNode::BOOL)
53 else if (type == SGPropertyNode::INT)
55 else if (type == SGPropertyNode::LONG)
57 else if (type == SGPropertyNode::FLOAT)
59 else if (type == SGPropertyNode::DOUBLE)
61 else if (type == SGPropertyNode::STRING)
68 static void dumpProperties(const SGPropertyNode *node)
70 cout << node->getPath() << "/" << endl;
71 for (int i = 0; i < node->nChildren(); i++) {
72 const SGPropertyNode *c = node->getChild(i);
73 SGPropertyNode::Type type = c->getType();
74 if (type == SGPropertyNode::ALIAS || c->nChildren())
77 int index = c->getIndex();
78 cout << std::setw(11) << getValueTypeString(c) << " " << c->getName();
80 cout << '[' << index << ']';
83 switch (c->getType()) {
84 case SGPropertyNode::DOUBLE:
85 case SGPropertyNode::FLOAT:
86 cout << std::setprecision(15) << c->getDoubleValue();
88 case SGPropertyNode::LONG:
89 case SGPropertyNode::INT:
90 cout << c->getLongValue();
92 case SGPropertyNode::BOOL:
93 cout << (c->getBoolValue() ? "true" : "false");
95 case SGPropertyNode::STRING:
96 cout << '"' << c->getStringValue() << '"';
98 case SGPropertyNode::NONE:
101 cout << '\'' << c->getStringValue() << '\'';
109 static void sanitize(stdString& s)
113 for (unsigned i = 0; i < r.size(); i++) {
116 else if (r[i] == '\b')
118 else if (r[i] == '\t')
120 else if (r[i] == '\n')
122 else if (r[i] == '\v')
124 else if (r[i] == '\f')
126 else if (r[i] == '\r')
128 else if (r[i] == '\'')
130 else if (r[i] == '\\')
132 else if (isprint(r[i]))
135 const char *hex = "0123456789abcdef";
137 s += stdString("\\x") + hex[c / 16] + hex[c % 16];
145 PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNode *start) :
146 puList(minx, miny, maxx, maxy, short(0), 20),
147 GUI_ID(FGCLASS_PROPERTYLIST),
155 _list_box->setUserData(this);
156 _list_box->setCallback(handle_select);
157 _list_box->setValue(0);
162 PropertyList::~PropertyList()
168 void PropertyList::delete_arrays()
173 for (int i = 0; i < _num_entries; i++)
174 delete[] _entries[i];
183 void PropertyList::handle_select(puObject *list_box)
185 PropertyList *prop_list = (PropertyList *)list_box->getUserData();
186 int selected = list_box->getIntegerValue();
187 int mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
188 int mod_shift = fgGetKeyModifiers() & KEYMOD_SHIFT;
190 if (selected >= 0 && selected < prop_list->_num_entries) {
191 const char *src = prop_list->_entries[selected];
193 if (prop_list->_dot_files && (selected < 2)) {
194 if (!strcmp(src, ".")) {
196 prop_list->toggleFlags();
198 dumpProperties(prop_list->_curr);
203 } else if (!strcmp(src, "..")) {
204 SGPropertyNode *parent = prop_list->getCurrent()->getParent();
207 for (; parent->getParent(); parent = parent->getParent())
209 prop_list->setCurrent(parent);
215 // we know we're dealing with a regular entry, so convert
216 // it to an index into children[]
217 if (prop_list->_dot_files)
220 SGPropertyNode_ptr child = prop_list->_children[selected].node;
223 // check if it's a directory
224 if (child->nChildren()) {
225 prop_list->setTopItem(0);
226 prop_list->setCurrent(child);
230 // it is a regular property
231 if (child->getType() == SGPropertyNode::BOOL && mod_ctrl) {
232 child->setBoolValue(!child->getBoolValue());
233 prop_list->update(true);
235 prop_list->publish(child);
238 // the user clicked on blank screen
239 prop_list->update(true);
244 void PropertyList::update(bool restore_pos)
249 _num_entries = (int)_curr->nChildren();
251 // instantiate string objects and add [.] and [..] for subdirs
252 if (!_curr->getParent()) {
253 _entries = new char*[_num_entries + 1];
258 _num_entries += 2; // for . and ..
259 _entries = new char*[_num_entries + 1];
261 _entries[0] = new char[2];
262 strcpy(_entries[0], ".");
264 _entries[1] = new char[3];
265 strcpy(_entries[1], "..");
272 _num_children = _curr->nChildren();
273 _children = new NodeData[_num_children];
274 for (i = 0; i < _num_children; i++)
275 _children[i].node = _curr->getChild(i);
277 qsort(_children, _num_children, sizeof(_children[0]), nodeNameCompare);
279 // Make lists of the children's names, values, etc.
280 for (i = 0; i < _num_children; i++, pi++) {
281 SGPropertyNode *child = _children[i].node;
283 if (child->nChildren() > 0) {
284 stdString name = stdString(child->getDisplayName(true)) + '/';
285 _entries[pi] = new char[name.size() + 1];
286 strcpy(_entries[pi], name.c_str());
289 _entries[pi] = 0; // make it delete-able
290 updateTextForEntry(i);
291 _children[i].setListener(this);
295 _entries[_num_entries] = 0;
297 int top = getTopItem();
304 void PropertyList::updateTextForEntry(int index)
306 assert((index >= 0) && (index < _num_children));
307 SGPropertyNode_ptr node = _children[index].node;
309 stdString name = node->getDisplayName(true);
310 stdString type = getValueTypeString(node);
311 stdString value = node->getStringValue();
313 if (node->getType() == SGPropertyNode::STRING
314 || node->getType() == SGPropertyNode::UNSPECIFIED)
317 stdString line = name + " = '" + value + "' (" + type;
321 if (!node->getAttribute(SGPropertyNode::READ))
323 if (!node->getAttribute(SGPropertyNode::WRITE))
325 if (node->getAttribute(SGPropertyNode::TRACE_READ))
327 if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
329 if (node->getAttribute(SGPropertyNode::ARCHIVE))
331 if (node->getAttribute(SGPropertyNode::USERARCHIVE))
341 if (line.size() >= PUSTRING_MAX)
342 line.resize(PUSTRING_MAX - 1);
347 delete[] _entries[index];
348 _entries[index] = new char[line.size() + 1];
349 strcpy(_entries[index], line.c_str());
353 void PropertyList::valueChanged(SGPropertyNode *nd)
355 for (int i = 0; i < _num_children; i++)
356 if (_children[i].node == nd) {
357 updateTextForEntry(i);
363 int PropertyList::nodeNameCompare(const void *p1, const void *p2)
365 const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
366 const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
368 int diff = strcmp(n1->getName(), n2->getName());
369 return diff ? diff : n1->getIndex() - n2->getIndex();
373 void PropertyList::setValue(const char *s)
376 SGPropertyNode *p = fgGetNode(s, false);
380 throw stdString("node doesn't exist");
381 } catch (const stdString& m) {
382 SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
387 void PropertyList::setCurrent(SGPropertyNode *p)
389 bool same = (_curr == p);