]> git.mxchange.org Git - flightgear.git/blob - src/GUI/dialog.cxx
Fix the slider to request a non-zero length, and make its width a
[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 #include "layout.hxx"
11
12 int fgPopup::checkHit(int button, int updown, int x, int y)
13 {
14     int result = puPopup::checkHit(button, updown, x, y);
15
16     // This is annoying.  We would really want a true result from the
17     // superclass to indicate "handled by child object", but all it
18     // tells us is that the pointer is inside the dialog.  So do the
19     // intersection test (again) to make sure we don't start a drag
20     // when inside controls.
21     if(!result) return result;
22     puObject* child = getFirstChild();
23     if(child) child = child->getNextObject(); // Skip the puFrame
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     // Treat puText objects specially, so their "values" can be set
112     // from properties.
113     if(object->getType() & PUCLASS_TEXT)
114         object->setLabel(node->getStringValue());
115 }
116
117
118 static void
119 copy_from_pui (puObject * object, SGPropertyNode * node)
120 {
121     switch (node->getType()) {
122     case SGPropertyNode::BOOL:
123     case SGPropertyNode::INT:
124     case SGPropertyNode::LONG:
125         node->setIntValue(object->getIntegerValue());
126         break;
127     case SGPropertyNode::FLOAT:
128     case SGPropertyNode::DOUBLE:
129         node->setFloatValue(object->getFloatValue());
130         break;
131     default:
132         node->setStringValue(object->getStringValue());
133         break;
134     }
135 }
136
137
138 \f
139 ////////////////////////////////////////////////////////////////////////
140 // Implementation of GUIInfo.
141 ////////////////////////////////////////////////////////////////////////
142
143 GUIInfo::GUIInfo (FGDialog * d)
144     : dialog(d)
145 {
146 }
147
148 GUIInfo::~GUIInfo ()
149 {
150     for (unsigned int i = 0; i < bindings.size(); i++) {
151         delete bindings[i];
152         bindings[i] = 0;
153     }
154 }
155
156
157 \f
158 ////////////////////////////////////////////////////////////////////////
159 // Implementation of FGDialog.
160 ////////////////////////////////////////////////////////////////////////
161
162 FGDialog::FGDialog (SGPropertyNode * props)
163     : _object(0)
164 {
165     display(props);
166 }
167
168 FGDialog::~FGDialog ()
169 {
170     puDeleteObject(_object);
171
172     unsigned int i;
173
174                                 // Delete all the arrays we made
175                                 // and were forced to keep around
176                                 // because PUI won't do its own
177                                 // memory management.
178     for (i = 0; i < _char_arrays.size(); i++) {
179         for (int j = 0; _char_arrays[i][j] != 0; j++)
180             free(_char_arrays[i][j]); // added with strdup
181         delete[] _char_arrays[i];
182     }
183
184                                 // Delete all the info objects we
185                                 // were forced to keep around because
186                                 // PUI cannot delete its own user data.
187     for (i = 0; i < _info.size(); i++) {
188         delete (GUIInfo *)_info[i];
189         _info[i] = 0;
190     }
191
192                                 // Finally, delete the property links.
193     for (i = 0; i < _propertyObjects.size(); i++) {
194         delete _propertyObjects[i];
195         _propertyObjects[i] = 0;
196     }
197 }
198
199 void
200 FGDialog::updateValue (const char * objectName)
201 {
202     for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
203         const string &name = _propertyObjects[i]->name;
204         if (name == objectName)
205             copy_to_pui(_propertyObjects[i]->node,
206                         _propertyObjects[i]->object);
207     }
208 }
209
210 void
211 FGDialog::applyValue (const char * objectName)
212 {
213     for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
214         if (_propertyObjects[i]->name == objectName)
215             copy_from_pui(_propertyObjects[i]->object,
216                           _propertyObjects[i]->node);
217     }
218 }
219
220 void
221 FGDialog::updateValues ()
222 {
223     for (unsigned int i = 0; i < _propertyObjects.size(); i++)
224         copy_to_pui(_propertyObjects[i]->node, _propertyObjects[i]->object);
225 }
226
227 void
228 FGDialog::applyValues ()
229 {
230     for (unsigned int i = 0; i < _propertyObjects.size(); i++)
231         copy_from_pui(_propertyObjects[i]->object,
232                       _propertyObjects[i]->node);
233 }
234
235 void
236 FGDialog::update ()
237 {
238     for (unsigned int i = 0; i < _liveObjects.size(); i++)
239         copy_to_pui(_liveObjects[i]->node, _liveObjects[i]->object);
240 }
241
242 void
243 FGDialog::display (SGPropertyNode * props)
244 {
245     if (_object != 0) {
246         SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active");
247         return;
248     }
249
250     int screenw = globals->get_props()->getIntValue("/sim/startup/xsize");
251     int screenh = globals->get_props()->getIntValue("/sim/startup/ysize");
252
253     bool userx = props->hasValue("x");
254     bool usery = props->hasValue("y");
255     bool userw = props->hasValue("width");
256     bool userh = props->hasValue("height");
257
258     LayoutWidget wid(props);
259     int pw=0, ph=0;
260     if(!userw || !userh)
261         wid.calcPrefSize(&pw, &ph);
262     pw = props->getIntValue("width", pw);
263     ph = props->getIntValue("height", ph);
264     int px = props->getIntValue("x", (screenw - pw) / 2);
265     int py = props->getIntValue("y", (screenh - ph) / 2);
266     wid.layout(px, py, pw, ph);
267
268     _object = makeObject(props, screenw, screenh);
269
270     // Remove automatically generated properties, so the layout looks
271     // the same next time around.
272     if(!userx) props->removeChild("x");
273     if(!usery) props->removeChild("y");
274     if(!userw) props->removeChild("width");
275     if(!userh) props->removeChild("height");
276
277     if (_object != 0) {
278         _object->reveal();
279     } else {
280         SG_LOG(SG_GENERAL, SG_ALERT, "Widget "
281                << props->getStringValue("name", "[unnamed]")
282                << " does not contain a proper GUI definition");
283     }
284 }
285
286 puObject *
287 FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight)
288 {
289     bool presetSize = props->hasValue("width") && props->hasValue("height");
290     int width = props->getIntValue("width", parentWidth);
291     int height = props->getIntValue("height", parentHeight);
292     int x = props->getIntValue("x", (parentWidth - width) / 2);
293     int y = props->getIntValue("y", (parentHeight - height) / 2);
294
295     string type = props->getName();
296     if (type == "")
297         type = "dialog";
298
299     if (type == "dialog") {
300         puPopup * dialog;
301         if (props->getBoolValue("modal", false))
302             dialog = new puDialogBox(x, y);
303         else
304             dialog = new fgPopup(x, y);
305         setupGroup(dialog, props, width, height, true);
306         return dialog;
307     } else if (type == "group") {
308         puGroup * group = new puGroup(x, y);
309         setupGroup(group, props, width, height, false);
310         return group;
311     } else if (type == "list") {
312         puList * list = new puList(x, y, x + width, y + height);
313         setupObject(list, props);
314         return list;
315     } else if (type == "airport-list") {
316         AirportList * list = new AirportList(x, y, x + width, y + height);
317         setupObject(list, props);
318         return list;
319     } else if (type == "input") {
320         puInput * input = new puInput(x, y, x + width, y + height);
321         setupObject(input, props);
322         return input;
323     } else if (type == "text") {
324         puText * text = new puText(x, y);
325         setupObject(text, props);
326         // Layed-out objects need their size set, and non-layout ones
327         // get a different placement.
328         if(presetSize) text->setSize(width, height);
329         else text->setLabelPlace(PUPLACE_LABEL_DEFAULT);
330         return text;
331     } else if (type == "checkbox") {
332         puButton * b;
333         b = new puButton(x, y, x + width, y + height, PUBUTTON_XCHECK);
334         b->setColourScheme(.8, .7, .7); // matches "PUI input pink"
335         setupObject(b, props);
336         return b;
337     } else if (type == "radio") {
338         puButton * b;
339         b = new puButton(x, y, x + width, y + height, PUBUTTON_CIRCLE);
340         b->setColourScheme(.8, .7, .7); // matches "PUI input pink"
341         setupObject(b, props);
342         return b;
343     } else if (type == "button") {
344         puButton * b;
345         const char * legend = props->getStringValue("legend", "[none]");
346         if (props->getBoolValue("one-shot", true))
347             b = new puOneShot(x, y, legend);
348         else
349             b = new puButton(x, y, legend);
350         if(presetSize)
351             b->setSize(width, height);
352         setupObject(b, props);
353         return b;
354     } else if (type == "combo") {
355         vector<SGPropertyNode_ptr> value_nodes = props->getChildren("value");
356         char ** entries = make_char_array(value_nodes.size());
357         for (unsigned int i = 0, j = value_nodes.size() - 1;
358              i < value_nodes.size();
359              i++, j--)
360             entries[i] = strdup((char *)value_nodes[i]->getStringValue());
361         puComboBox * combo =
362             new puComboBox(x, y, x + width, y + height, entries,
363                            props->getBoolValue("editable", false));
364         setupObject(combo, props);
365         return combo;
366     } else if (type == "slider") {
367         bool vertical = props->getBoolValue("vertical", false);
368         puSlider * slider = new puSlider(x, y, (vertical ? height : width));
369         slider->setMinValue(props->getFloatValue("min", 0.0));
370         slider->setMaxValue(props->getFloatValue("max", 1.0));
371         setupObject(slider, props);
372         if(presetSize)
373             slider->setSize(width, height);
374         return slider;
375     } else if (type == "dial") {
376         puDial * dial = new puDial(x, y, width);
377         dial->setMinValue(props->getFloatValue("min", 0.0));
378         dial->setMaxValue(props->getFloatValue("max", 1.0));
379         dial->setWrap(props->getBoolValue("wrap", true));
380         setupObject(dial, props);
381         return dial;
382     } else if (type == "select") {
383         vector<SGPropertyNode_ptr> value_nodes;
384         SGPropertyNode * selection_node =
385                 fgGetNode(props->getChild("selection")->getStringValue(), true);
386
387         for (int q = 0; q < selection_node->nChildren(); q++)
388             value_nodes.push_back(selection_node->getChild(q));
389
390         char ** entries = make_char_array(value_nodes.size());
391         for (unsigned int i = 0, j = value_nodes.size() - 1;
392              i < value_nodes.size();
393              i++, j--)
394             entries[i] = strdup((char *)value_nodes[i]->getName());
395         puSelectBox * select =
396             new puSelectBox(x, y, x + width, y + height, entries);
397         setupObject(select, props);
398         return select;
399     } else {
400         return 0;
401     }
402 }
403
404 void
405 FGDialog::setupObject (puObject * object, SGPropertyNode * props)
406 {
407     object->setLabelPlace(PUPLACE_CENTERED_RIGHT);
408
409     if (props->hasValue("legend"))
410         object->setLegend(props->getStringValue("legend"));
411
412     if (props->hasValue("label"))
413         object->setLabel(props->getStringValue("label"));
414
415     if (props->hasValue("property")) {
416         const char * name = props->getStringValue("name");
417         if (name == 0)
418             name = "";
419         const char * propname = props->getStringValue("property");
420         SGPropertyNode_ptr node = fgGetNode(propname, true);
421         copy_to_pui(node, object);
422         PropertyObject* po = new PropertyObject(name, object, node);
423         _propertyObjects.push_back(po);
424         if(props->getBoolValue("live"))
425             _liveObjects.push_back(po);
426     }
427
428     vector<SGPropertyNode_ptr> nodes = props->getChildren("binding");
429     if (nodes.size() > 0) {
430         GUIInfo * info = new GUIInfo(this);
431
432         for (unsigned int i = 0; i < nodes.size(); i++)
433             info->bindings.push_back(new FGBinding(nodes[i]));
434         object->setCallback(action_callback);
435         object->setUserData(info);
436         _info.push_back(info);
437     }
438
439     object->makeReturnDefault(props->getBoolValue("default"));
440 }
441
442 void
443 FGDialog::setupGroup (puGroup * group, SGPropertyNode * props,
444                     int width, int height, bool makeFrame)
445 {
446     setupObject(group, props);
447
448     if (makeFrame) {
449         puFrame* f = new puFrame(0, 0, width, height);
450         f->setColorScheme(0.8, 0.8, 0.9, 0.85);
451     }
452
453     int nChildren = props->nChildren();
454     for (int i = 0; i < nChildren; i++)
455         makeObject(props->getChild(i), width, height);
456     group->close();
457 }
458
459 char **
460 FGDialog::make_char_array (int size)
461 {
462     char ** list = new char*[size+1];
463     for (int i = 0; i <= size; i++)
464         list[i] = 0;
465     _char_arrays.push_back(list);
466     return list;
467 }
468
469
470 \f
471 ////////////////////////////////////////////////////////////////////////
472 // Implementation of FGDialog::PropertyObject.
473 ////////////////////////////////////////////////////////////////////////
474
475 FGDialog::PropertyObject::PropertyObject (const char * n,
476                                            puObject * o,
477                                            SGPropertyNode_ptr p)
478     : name(n),
479       object(o),
480       node(p)
481 {
482 }
483
484
485 // end of dialog.cxx