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