]> git.mxchange.org Git - flightgear.git/blob - src/GUI/property_list.cxx
write a more useful list on shift-"."-click, not the whole subtree
[flightgear.git] / src / GUI / property_list.cxx
1 // Implementation of the <property-list> widget.
2 //
3 // Copyright (C) 2001  Steve BAKER
4 // Copyright (C) 2001  Jim WILSON
5 // Copyright (C) 2006  Melchior FRANZ
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <iomanip>
29 #include <simgear/compiler.h>
30
31 #include STL_STRING
32 SG_USING_STD(string);
33 typedef string stdString;      // puObject has a "string" member
34
35 #include <Main/fg_os.hxx>      // fgGetKeyModifiers()
36 #include <Main/fg_props.hxx>
37
38 #include "property_list.hxx"
39
40
41 static string getValueTypeString(const SGPropertyNode *node)
42 {
43     string result;
44     assert(node);
45
46     SGPropertyNode::Type type = node->getType();
47     if (type == SGPropertyNode::UNSPECIFIED)
48         result = "unspecified";
49     else if (type == SGPropertyNode::NONE)
50         result = "none";
51     else if (type == SGPropertyNode::BOOL)
52         result = "bool";
53     else if (type == SGPropertyNode::INT)
54         result = "int";
55     else if (type == SGPropertyNode::LONG)
56         result = "long";
57     else if (type == SGPropertyNode::FLOAT)
58         result = "float";
59     else if (type == SGPropertyNode::DOUBLE)
60         result = "double";
61     else if (type == SGPropertyNode::STRING)
62         result = "string";
63
64     return result;
65 }
66
67
68 static void dumpProperties(const SGPropertyNode *node)
69 {
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())
75             continue;
76
77         int index = c->getIndex();
78         cout << std::setw(11) << getValueTypeString(c) << "  " << c->getName();
79         if (index > 0)
80             cout << '[' << index << ']';
81         cout << " = ";
82
83         switch (c->getType()) {
84         case SGPropertyNode::DOUBLE:
85         case SGPropertyNode::FLOAT:
86             cout << std::setprecision(15) << c->getDoubleValue();
87             break;
88         case SGPropertyNode::LONG:
89         case SGPropertyNode::INT:
90             cout << c->getLongValue();
91             break;
92         case SGPropertyNode::BOOL:
93             cout << (c->getBoolValue() ? "true" : "false");
94             break;
95         case SGPropertyNode::STRING:
96             cout << '"' << c->getStringValue() << '"';
97             break;
98         case SGPropertyNode::NONE:
99             break;
100         default:
101             cout << '\'' << c->getStringValue() << '\'';
102         }
103         cout << endl;
104     }
105     cout << endl;
106 }
107
108
109 static void sanitize(stdString& s)
110 {
111     stdString r = s;
112     s = "";
113     for (unsigned i = 0; i < r.size(); i++) {
114         if (r[i] == '\a')
115             s += "\\a";
116         else if (r[i] == '\b')
117             s += "\\b";
118         else if (r[i] == '\t')
119             s += "\\t";
120         else if (r[i] == '\n')
121             s += "\\n";
122         else if (r[i] == '\v')
123             s += "\\v";
124         else if (r[i] == '\f')
125             s += "\\f";
126         else if (r[i] == '\r')
127             s += "\\r";
128         else if (r[i] == '\'')
129             s += "\'";
130         else if (r[i] == '\\')
131             s += "\\";
132         else if (isprint(r[i]))
133             s += r[i];
134         else {
135             const char *hex = "0123456789abcdef";
136             int c = r[i] & 255;
137             s += stdString("\\x") + hex[c / 16] + hex[c % 16];
138         }
139     }
140 }
141
142
143
144
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),
148     _curr(start),
149     _return(0),
150     _entries(0),
151     _num_entries(0),
152     _flags(false)
153
154 {
155     _list_box->setUserData(this);
156     _list_box->setCallback(handle_select);
157     _list_box->setValue(0);
158     update();
159 }
160
161
162 PropertyList::~PropertyList()
163 {
164     delete_arrays();
165 }
166
167
168 void PropertyList::delete_arrays()
169 {
170     if (!_entries)
171         return;
172
173     for (int i = 0; i < _num_entries; i++)
174         delete[] _entries[i];
175
176     delete[] _entries;
177     delete[] _children;
178     _entries = 0;
179     _children = 0;
180 }
181
182
183 void PropertyList::handle_select(puObject *list_box)
184 {
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;
189
190     if (selected >= 0 && selected < prop_list->_num_entries) {
191         const char *src = prop_list->_entries[selected];
192
193         if (prop_list->_dot_files && (selected < 2)) {
194             if (!strcmp(src, ".")) {
195                 if (mod_ctrl)
196                     prop_list->toggleFlags();
197                 else if (mod_shift)
198                     dumpProperties(prop_list->_curr);
199
200                 prop_list->update();
201                 return;
202
203             } else if (!strcmp(src, "..")) {
204                 SGPropertyNode *parent = prop_list->getCurrent()->getParent();
205                 if (parent) {
206                     if (mod_ctrl)
207                         for (; parent->getParent(); parent = parent->getParent())
208                             ;
209                     prop_list->setCurrent(parent);
210                 }
211                 return;
212             }
213         }
214
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)
218             selected -= 2;
219
220         SGPropertyNode_ptr child = prop_list->_children[selected].node;
221         assert(child);
222
223         // check if it's a directory
224         if (child->nChildren()) {
225             prop_list->setTopItem(0);
226             prop_list->setCurrent(child);
227             return;
228         }
229
230         // it is a regular property
231         if (child->getType() == SGPropertyNode::BOOL && mod_ctrl) {
232             child->setBoolValue(!child->getBoolValue());
233             prop_list->update(true);
234         } else
235             prop_list->publish(child);
236
237     } else {
238         // the user clicked on blank screen
239         prop_list->update(true);
240     }
241 }
242
243
244 void PropertyList::update(bool restore_pos)
245 {
246     int pi;
247
248     delete_arrays();
249     _num_entries = (int)_curr->nChildren();
250
251     // instantiate string objects and add [.] and [..] for subdirs
252     if (!_curr->getParent()) {
253         _entries = new char*[_num_entries + 1];
254         pi = 0;
255         _dot_files = false;
256
257     } else {
258         _num_entries += 2;    // for . and ..
259         _entries = new char*[_num_entries + 1];
260
261         _entries[0] = new char[2];
262         strcpy(_entries[0], ".");
263
264         _entries[1] = new char[3];
265         strcpy(_entries[1], "..");
266
267         pi = 2;
268         _dot_files = true;
269     }
270
271     int i;
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);
276
277     qsort(_children, _num_children, sizeof(_children[0]), nodeNameCompare);
278
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;
282
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());
287
288         } else {
289             _entries[pi] = 0;       // make it delete-able
290             updateTextForEntry(i);
291             _children[i].setListener(this);
292         }
293     }
294
295     _entries[_num_entries] = 0;
296
297     int top = getTopItem();
298     newList(_entries);
299     if (restore_pos)
300         setTopItem(top);
301 }
302
303
304 void PropertyList::updateTextForEntry(int index)
305 {
306     assert((index >= 0) && (index < _num_children));
307     SGPropertyNode_ptr node = _children[index].node;
308
309     stdString name = node->getDisplayName(true);
310     stdString type = getValueTypeString(node);
311     stdString value = node->getStringValue();
312
313     if (node->getType() == SGPropertyNode::STRING
314             || node->getType() == SGPropertyNode::UNSPECIFIED)
315         sanitize(value);
316
317     stdString line = name + " = '" + value + "' (" + type;
318
319     if (_flags) {
320         stdString ext;
321         if (!node->getAttribute(SGPropertyNode::READ))
322             ext += 'r';
323         if (!node->getAttribute(SGPropertyNode::WRITE))
324             ext += 'w';
325         if (node->getAttribute(SGPropertyNode::TRACE_READ))
326             ext += 'R';
327         if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
328             ext += 'W';
329         if (node->getAttribute(SGPropertyNode::ARCHIVE))
330             ext += 'A';
331         if (node->getAttribute(SGPropertyNode::USERARCHIVE))
332             ext += 'U';
333         if (node->isTied())
334             ext += 'T';
335         if (ext.size())
336             line += ", " + ext;
337     }
338
339     line += ')';
340
341     if (line.size() >= PUSTRING_MAX)
342         line.resize(PUSTRING_MAX - 1);
343
344     if (_dot_files)
345         index += 2;
346
347     delete[] _entries[index];
348     _entries[index] = new char[line.size() + 1];
349     strcpy(_entries[index], line.c_str());
350 }
351
352
353 void PropertyList::valueChanged(SGPropertyNode *nd)
354 {
355     for (int i = 0; i < _num_children; i++)
356         if (_children[i].node == nd) {
357             updateTextForEntry(i);
358             return;
359         }
360 }
361
362
363 int PropertyList::nodeNameCompare(const void *p1, const void *p2)
364 {
365     const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
366     const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
367
368     int diff = strcmp(n1->getName(), n2->getName());
369     return diff ? diff : n1->getIndex() - n2->getIndex();
370 }
371
372
373 void PropertyList::setValue(const char *s)
374 {
375     try {
376         SGPropertyNode *p = fgGetNode(s, false);
377         if (p)
378             setCurrent(p);
379         else
380             throw stdString("node doesn't exist");
381     } catch (const stdString& m) {
382         SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
383     }
384 }
385
386
387 void PropertyList::setCurrent(SGPropertyNode *p)
388 {
389     bool same = (_curr == p);
390     _return = _curr = p;
391     update(same);
392     if (!same)
393         publish(p);
394 }
395