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