]> git.mxchange.org Git - flightgear.git/blob - src/GUI/new_gui.cxx
Separated out GUIWidget class to manage a top-level widget.
[flightgear.git] / src / GUI / new_gui.cxx
1 // new_gui.cxx: implementation of XML-configurable GUI support.
2
3 #include "new_gui.hxx"
4
5 #include <plib/pu.h>
6 #include <plib/ul.h>
7
8 #include <vector>
9 SG_USING_STD(vector);
10
11 #include <simgear/misc/exception.hxx>
12 #include <Main/fg_props.hxx>
13
14
15 \f
16 ////////////////////////////////////////////////////////////////////////
17 // Callbacks.
18 ////////////////////////////////////////////////////////////////////////
19
20
21 /**
22  * Callback to update all property values.
23  */
24 static void
25 update_callback (puObject * object)
26 {
27     ((GUIWidget *)object->getUserData())->updateProperties();
28 }
29
30
31 /**
32  * Callback to close the dialog.
33  */
34 static void
35 close_callback (puObject * object)
36 {
37     delete ((GUIWidget *)object->getUserData());
38 }
39
40
41 /**
42  * Callback to apply the property value for every field.
43  */
44 static void
45 apply_callback (puObject * object)
46 {
47     ((GUIWidget *)object->getUserData())->applyProperties();
48     update_callback(object);
49 }
50
51
52 /**
53  * Callback to apply the property values and close the dialog.
54  */
55 static void
56 close_apply_callback (puObject * object)
57 {
58     apply_callback(object);
59     close_callback(object);
60 }
61
62
63 \f
64 ////////////////////////////////////////////////////////////////////////
65 // Implementation of GUIWidget.
66 ////////////////////////////////////////////////////////////////////////
67
68 GUIWidget::GUIWidget (SGPropertyNode_ptr props)
69     : _object(0)
70 {
71     display(props);
72 }
73
74 GUIWidget::~GUIWidget ()
75 {
76     delete _object;
77 }
78
79 void
80 GUIWidget::display (SGPropertyNode_ptr props)
81 {
82     if (_object != 0) {
83         SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active");
84         return;
85     }
86
87     _object = makeObject(props, 1024, 768);
88
89     if (_object != 0) {
90         _object->reveal();
91     } else {
92         SG_LOG(SG_GENERAL, SG_ALERT, "Widget "
93                << props->getStringValue("name", "[unnamed]")
94                << " does not contain a proper GUI definition");
95     }
96 }
97
98
99 void
100 GUIWidget::applyProperties ()
101 {
102     for (int i = 0; i < _propertyObjects.size(); i++) {
103         puObject * object = _propertyObjects[i].object;
104         SGPropertyNode_ptr node = _propertyObjects[i].node;
105         node->setStringValue(object->getStringValue());
106     }
107 }
108
109 void
110 GUIWidget::updateProperties ()
111 {
112     for (int i = 0; i < _propertyObjects.size(); i++) {
113         puObject * object = _propertyObjects[i].object;
114         SGPropertyNode_ptr node = _propertyObjects[i].node;
115         object->setValue(node->getStringValue());
116     }
117 }
118
119 puObject *
120 GUIWidget::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight)
121 {
122     int width = props->getIntValue("width", parentWidth);
123     int height = props->getIntValue("height", parentHeight);
124
125     int x = props->getIntValue("x", (parentWidth - width) / 2);
126     int y = props->getIntValue("y", (parentHeight - height) / 2);
127
128     string type = props->getName();
129     if (type == "")
130         type = props->getStringValue("type");
131     if (type == "") {
132         SG_LOG(SG_GENERAL, SG_ALERT, "No type specified for GUI object");
133         return 0;
134     }
135
136     if (type == "dialog") {
137         puPopup * dialog;
138         if (props->getBoolValue("modal", false))
139             dialog = new puDialogBox(x, y);
140         else
141             dialog = new puPopup(x, y);
142         setupGroup(dialog, props, width, height, true);
143         return dialog;
144     } else if (type == "group") {
145         puGroup * group = new puGroup(x, y);
146         setupGroup(group, props, width, height, false);
147         return group;
148     } else if (type == "input") {
149         puInput * input = new puInput(x, y, x + width, y + height);
150         setupObject(input, props);
151         return input;
152     } else if (type == "text") {
153         puText * text = new puText(x, y);
154         setupObject(text, props);
155         return text;
156     } else if (type == "button") {
157         puButton * b;
158         const char * legend = props->getStringValue("legend", "[none]");
159         if (props->getBoolValue("one-shot", true))
160             b = new puOneShot(x, y, legend);
161         else
162             b = new puButton(x, y, legend);
163         setupObject(b, props);
164         return b;
165     } else {
166         return 0;
167     }
168 }
169
170 void
171 GUIWidget::setupObject (puObject * object, SGPropertyNode * props)
172 {
173     object->setUserData(this);
174
175     if (props->hasValue("legend"))
176         object->setLegend(props->getStringValue("legend"));
177
178     if (props->hasValue("label"))
179         object->setLabel(props->getStringValue("label"));
180
181     if (props->hasValue("default-value-prop")) {
182         const char * name = props->getStringValue("default-value-prop");
183         SGPropertyNode_ptr node = fgGetNode(name, true);
184         object->setValue(node->getStringValue());
185         _propertyObjects.push_back(PropertyObject(object, node));
186     }
187
188     if (props->hasValue("action")) {
189         string action = props->getStringValue("action");
190         if (action == "update")
191             object->setCallback(update_callback);
192         else if (action == "close")
193             object->setCallback(close_callback);
194         else if (action == "apply")
195             object->setCallback(apply_callback);
196         else if (action == "close-apply")
197             object->setCallback(close_apply_callback);
198         else
199             SG_LOG(SG_GENERAL, SG_ALERT, "Unknown GUI action " + action);
200     }
201
202     object->makeReturnDefault(props->getBoolValue("default"));
203 }
204
205 void
206 GUIWidget::setupGroup (puGroup * group, SGPropertyNode * props,
207                     int width, int height, bool makeFrame)
208 {
209     setupObject(group, props);
210
211     if (makeFrame)
212         new puFrame(0, 0, width, height);
213
214     int nChildren = props->nChildren();
215     for (int i = 0; i < nChildren; i++)
216         makeObject(props->getChild(i), width, height);
217     group->close();
218 }
219
220 GUIWidget::PropertyObject::PropertyObject (puObject * o, SGPropertyNode_ptr n)
221     : object(o),
222       node(n)
223 {
224 }
225
226
227 \f
228 ////////////////////////////////////////////////////////////////////////
229 // Implementation of NewGUI.
230 ////////////////////////////////////////////////////////////////////////
231
232
233 NewGUI::NewGUI ()
234 {
235 }
236
237 NewGUI::~NewGUI ()
238 {
239 }
240
241 void
242 NewGUI::init ()
243 {
244     char path[1024];
245     ulMakePath(path, getenv("FG_ROOT"), "gui");
246     readDir(path);
247 }
248
249 void
250 NewGUI::update (double delta_time_sec)
251 {
252     // NO OP
253 }
254
255 void
256 NewGUI::display (const string &name)
257 {
258     if (_widgets.find(name) == _widgets.end())
259         SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined");
260     else
261         new GUIWidget(_widgets[name]); // PUI will delete it
262 }
263
264 void
265 NewGUI::readDir (const char * path)
266 {
267     ulDir * dir = ulOpenDir(path);
268
269     if (dir == 0) {
270         SG_LOG(SG_GENERAL, SG_ALERT, "Failed to read GUI files from "
271                << path);
272         return;
273     }
274
275     ulDirEnt * dirEnt = ulReadDir(dir);
276     while (dirEnt != 0) {
277         char subpath[1024];
278
279         ulMakePath(subpath, path, dirEnt->d_name);
280
281         if (dirEnt->d_isdir && dirEnt->d_name[0] != '.') {
282             readDir(subpath);
283         } else {
284             SGPropertyNode_ptr props = new SGPropertyNode;
285             try {
286                 readProperties(subpath, props);
287             } catch (const sg_exception &ex) {
288                 SG_LOG(SG_INPUT, SG_ALERT, "Error parsing GUI file "
289                        << subpath);
290             }
291             if (!props->hasValue("name")) {
292                 SG_LOG(SG_INPUT, SG_WARN, "GUI file " << subpath
293                    << " has no name; skipping.");
294             } else {
295                 string name = props->getStringValue("name");
296                 SG_LOG(SG_INPUT, SG_BULK, "Saving GUI node " << name);
297                 _widgets[name] = props;
298             }
299         }
300         dirEnt = ulReadDir(dir);
301     }
302     ulCloseDir(dir);
303 }
304
305 // end of new_gui.cxx