]> git.mxchange.org Git - flightgear.git/blob - src/GUI/dialog.cxx
6d3c7c7cb2c5c7354faa75f456bb8d67bf1fa0ec
[flightgear.git] / src / GUI / dialog.cxx
1 // dialog.cxx: implementation of an XML-configurable dialog box.
2
3 #include <Input/input.hxx>
4
5 #include "dialog.hxx"
6 #include "new_gui.hxx"
7
8 #include "puList.hxx"
9 #include "AirportList.hxx"
10
11 int fgPopup::checkHit(int button, int updown, int x, int y)
12 {
13     int result = puPopup::checkHit(button, updown, x, y);
14
15     // This is annoying.  We would really want a true result from the
16     // superclass to indicate "handled by child object", but all it
17     // tells us is that the pointer is inside the dialog.  So do the
18     // intersection test (again) to make sure we don't start a drag
19     // when inside controls.  A further weirdness: plib inserts a
20     // "ghost" child which covers the whole control. (?)  Skip it.
21     if(!result) return result;
22     puObject* child = getFirstChild();
23     if(child) child = child->getNextObject();
24     while(child) {
25         int cx, cy, cw, ch;
26         child->getAbsolutePosition(&cx, &cy);
27         child->getSize(&cw, &ch);
28         if(x >= cx && x < cx + cw && y >= cy && y < cy + ch)
29             return result;
30         child = child->getNextObject();
31     }
32
33     // Finally, handle the mouse event
34     if(updown == PU_DOWN) {
35         int px, py;
36         getPosition(&px, &py);
37         _dragging = true;
38         _dX = px - x;
39         _dY = py - y;
40     } else if(updown == PU_DRAG && _dragging) {
41         setPosition(x + _dX, y + _dY);
42     } else {
43         _dragging = false;
44     }
45     return 1;
46 }
47
48 \f
49 ////////////////////////////////////////////////////////////////////////
50 // Callbacks.
51 ////////////////////////////////////////////////////////////////////////
52
53 /**
54  * User data for a GUI object.
55  */
56 struct GUIInfo
57 {
58     GUIInfo (FGDialog * d);
59     virtual ~GUIInfo ();
60
61     FGDialog * dialog;
62     vector <FGBinding *> bindings;
63 };
64
65
66 /**
67  * Action callback.
68  */
69 static void
70 action_callback (puObject * object)
71 {
72     GUIInfo * info = (GUIInfo *)object->getUserData();
73     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
74     gui->setActiveDialog(info->dialog);
75     int nBindings = info->bindings.size();
76     for (int i = 0; i < nBindings; i++) {
77         info->bindings[i]->fire();
78         if (gui->getActiveDialog() == 0)
79             break;
80     }
81     gui->setActiveDialog(0);
82 }
83
84
85 \f
86 ////////////////////////////////////////////////////////////////////////
87 // Static helper functions.
88 ////////////////////////////////////////////////////////////////////////
89
90 /**
91  * Copy a property value to a PUI object.
92  */
93 static void
94 copy_to_pui (SGPropertyNode * node, puObject * object)
95 {
96     switch (node->getType()) {
97     case SGPropertyNode::BOOL:
98     case SGPropertyNode::INT:
99     case SGPropertyNode::LONG:
100         object->setValue(node->getIntValue());
101         break;
102     case SGPropertyNode::FLOAT:
103     case SGPropertyNode::DOUBLE:
104         object->setValue(node->getFloatValue());
105         break;
106     default:
107         object->setValue(node->getStringValue());
108         break;
109     }
110 }
111
112
113 static void
114 copy_from_pui (puObject * object, SGPropertyNode * node)
115 {
116     switch (node->getType()) {
117     case SGPropertyNode::BOOL:
118     case SGPropertyNode::INT:
119     case SGPropertyNode::LONG:
120         node->setIntValue(object->getIntegerValue());
121         break;
122     case SGPropertyNode::FLOAT:
123     case SGPropertyNode::DOUBLE:
124         node->setFloatValue(object->getFloatValue());
125         break;
126     default:
127         node->setStringValue(object->getStringValue());
128         break;
129     }
130 }
131
132
133 \f
134 ////////////////////////////////////////////////////////////////////////
135 // Implementation of GUIInfo.
136 ////////////////////////////////////////////////////////////////////////
137
138 GUIInfo::GUIInfo (FGDialog * d)
139     : dialog(d)
140 {
141 }
142
143 GUIInfo::~GUIInfo ()
144 {
145     for (unsigned int i = 0; i < bindings.size(); i++) {
146         delete bindings[i];
147         bindings[i] = 0;
148     }
149 }
150
151
152 \f
153 ////////////////////////////////////////////////////////////////////////
154 // Implementation of FGDialog.
155 ////////////////////////////////////////////////////////////////////////
156
157 FGDialog::FGDialog (SGPropertyNode * props)
158     : _object(0)
159 {
160     display(props);
161 }
162
163 FGDialog::~FGDialog ()
164 {
165     puDeleteObject(_object);
166
167     unsigned int i;
168
169                                 // Delete all the arrays we made
170                                 // and were forced to keep around
171                                 // because PUI won't do its own
172                                 // memory management.
173     for (i = 0; i < _char_arrays.size(); i++) {
174         for (int j = 0; _char_arrays[i][j] != 0; j++)
175             free(_char_arrays[i][j]); // added with strdup
176         delete[] _char_arrays[i];
177     }
178
179                                 // Delete all the info objects we
180                                 // were forced to keep around because
181                                 // PUI cannot delete its own user data.
182     for (i = 0; i < _info.size(); i++) {
183         delete (GUIInfo *)_info[i];
184         _info[i] = 0;
185     }
186
187                                 // Finally, delete the property links.
188     for (i = 0; i < _propertyObjects.size(); i++) {
189         delete _propertyObjects[i];
190         _propertyObjects[i] = 0;
191     }
192 }
193
194 void
195 FGDialog::updateValue (const char * objectName)
196 {
197     for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
198         const string &name = _propertyObjects[i]->name;
199         if (name == objectName)
200             copy_to_pui(_propertyObjects[i]->node,
201                         _propertyObjects[i]->object);
202     }
203 }
204
205 void
206 FGDialog::applyValue (const char * objectName)
207 {
208     for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
209         if (_propertyObjects[i]->name == objectName)
210             copy_from_pui(_propertyObjects[i]->object,
211                           _propertyObjects[i]->node);
212     }
213 }
214
215 void
216 FGDialog::updateValues ()
217 {
218     for (unsigned int i = 0; i < _propertyObjects.size(); i++)
219         copy_to_pui(_propertyObjects[i]->node, _propertyObjects[i]->object);
220 }
221
222 void
223 FGDialog::applyValues ()
224 {
225     for (unsigned int i = 0; i < _propertyObjects.size(); i++)
226         copy_from_pui(_propertyObjects[i]->object,
227                       _propertyObjects[i]->node);
228 }
229
230 void
231 FGDialog::display (SGPropertyNode * props)
232 {
233     if (_object != 0) {
234         SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active");
235         return;
236     }
237
238     _object = makeObject(props,
239                       globals->get_props()->getIntValue("/sim/startup/xsize"),
240                       globals->get_props()->getIntValue("/sim/startup/ysize"));
241
242     if (_object != 0) {
243         _object->reveal();
244     } else {
245         SG_LOG(SG_GENERAL, SG_ALERT, "Widget "
246                << props->getStringValue("name", "[unnamed]")
247                << " does not contain a proper GUI definition");
248     }
249 }
250
251 puObject *
252 FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight)
253 {
254     int width = props->getIntValue("width", parentWidth);
255     int height = props->getIntValue("height", parentHeight);
256
257     int x = props->getIntValue("x", (parentWidth - width) / 2);
258     int y = props->getIntValue("y", (parentHeight - height) / 2);
259
260     string type = props->getName();
261     if (type == "")
262         type = "dialog";
263
264     if (type == "dialog") {
265         puPopup * dialog;
266         if (props->getBoolValue("modal", false))
267             dialog = new puDialogBox(x, y);
268         else
269             dialog = new fgPopup(x, y);
270         setupGroup(dialog, props, width, height, true);
271         return dialog;
272     } else if (type == "group") {
273         puGroup * group = new puGroup(x, y);
274         setupGroup(group, props, width, height, false);
275         return group;
276     } else if (type == "list") {
277         puList * list = new puList(x, y, x + width, y + height);
278         setupObject(list, props);
279         return list;
280     } else if (type == "airport-list") {
281         AirportList * list = new AirportList(x, y, x + width, y + height);
282         setupObject(list, props);
283         return list;
284     } else if (type == "input") {
285         puInput * input = new puInput(x, y, x + width, y + height);
286         setupObject(input, props);
287         return input;
288     } else if (type == "text") {
289         puText * text = new puText(x, y);
290         setupObject(text, props);
291         return text;
292     } else if (type == "checkbox") {
293         puButton * b;
294         b = new puButton(x, y, x + width, y + height, PUBUTTON_CIRCLE);
295         setupObject(b, props);
296         return b;
297     } else if (type == "button") {
298         puButton * b;
299         const char * legend = props->getStringValue("legend", "[none]");
300         if (props->getBoolValue("one-shot", true))
301             b = new puOneShot(x, y, legend);
302         else
303             b = new puButton(x, y, legend);
304         setupObject(b, props);
305         return b;
306     } else if (type == "combo") {
307         vector<SGPropertyNode_ptr> value_nodes = props->getChildren("value");
308         char ** entries = make_char_array(value_nodes.size());
309         for (unsigned int i = 0, j = value_nodes.size() - 1;
310              i < value_nodes.size();
311              i++, j--)
312             entries[i] = strdup((char *)value_nodes[i]->getStringValue());
313         puComboBox * combo =
314             new puComboBox(x, y, x + width, y + height, entries,
315                            props->getBoolValue("editable", false));
316         setupObject(combo, props);
317         return combo;
318     } else if (type == "slider") {
319         bool vertical = props->getBoolValue("vertical", false);
320         puSlider * slider = new puSlider(x, y, (vertical ? height : width));
321         slider->setMinValue(props->getFloatValue("min", 0.0));
322         slider->setMaxValue(props->getFloatValue("max", 1.0));
323         setupObject(slider, props);
324         return slider;
325     } else if (type == "dial") {
326         puDial * dial = new puDial(x, y, width);
327         dial->setMinValue(props->getFloatValue("min", 0.0));
328         dial->setMaxValue(props->getFloatValue("max", 1.0));
329         dial->setWrap(props->getBoolValue("wrap", true));
330         setupObject(dial, props);
331         return dial;
332     } else if (type == "select") {
333         vector<SGPropertyNode_ptr> value_nodes;
334         SGPropertyNode * selection_node =
335                 fgGetNode(props->getChild("selection")->getStringValue(), true);
336
337         for (int q = 0; q < selection_node->nChildren(); q++)
338             value_nodes.push_back(selection_node->getChild(q));
339
340         char ** entries = make_char_array(value_nodes.size());
341         for (unsigned int i = 0, j = value_nodes.size() - 1;
342              i < value_nodes.size();
343              i++, j--)
344             entries[i] = strdup((char *)value_nodes[i]->getName());
345         puSelectBox * select =
346             new puSelectBox(x, y, x + width, y + height, entries);
347         setupObject(select, props);
348         return select;
349     } else {
350         return 0;
351     }
352 }
353
354 void
355 FGDialog::setupObject (puObject * object, SGPropertyNode * props)
356 {
357     if (props->hasValue("legend"))
358         object->setLegend(props->getStringValue("legend"));
359
360     if (props->hasValue("label"))
361         object->setLabel(props->getStringValue("label"));
362
363     if (props->hasValue("property")) {
364         const char * name = props->getStringValue("name");
365         if (name == 0)
366             name = "";
367         const char * propname = props->getStringValue("property");
368         SGPropertyNode_ptr node = fgGetNode(propname, true);
369         copy_to_pui(node, object);
370         if (name != 0)
371             _propertyObjects.push_back(new PropertyObject(name, object, node));
372     }
373
374     vector<SGPropertyNode_ptr> nodes = props->getChildren("binding");
375     if (nodes.size() > 0) {
376         GUIInfo * info = new GUIInfo(this);
377
378         for (unsigned int i = 0; i < nodes.size(); i++)
379             info->bindings.push_back(new FGBinding(nodes[i]));
380         object->setCallback(action_callback);
381         object->setUserData(info);
382         _info.push_back(info);
383     }
384
385     object->makeReturnDefault(props->getBoolValue("default"));
386 }
387
388 void
389 FGDialog::setupGroup (puGroup * group, SGPropertyNode * props,
390                     int width, int height, bool makeFrame)
391 {
392     setupObject(group, props);
393
394     if (makeFrame)
395         new puFrame(0, 0, width, height);
396
397     int nChildren = props->nChildren();
398     for (int i = 0; i < nChildren; i++)
399         makeObject(props->getChild(i), width, height);
400     group->close();
401 }
402
403 char **
404 FGDialog::make_char_array (int size)
405 {
406     char ** list = new char*[size+1];
407     for (int i = 0; i <= size; i++)
408         list[i] = 0;
409     _char_arrays.push_back(list);
410     return list;
411 }
412
413
414 \f
415 ////////////////////////////////////////////////////////////////////////
416 // Implementation of FGDialog::PropertyObject.
417 ////////////////////////////////////////////////////////////////////////
418
419 FGDialog::PropertyObject::PropertyObject (const char * n,
420                                            puObject * o,
421                                            SGPropertyNode_ptr p)
422     : name(n),
423       object(o),
424       node(p)
425 {
426 }
427
428
429 // end of dialog.cxx