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