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