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