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