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