1 // dialog.cxx: implementation of an XML-configurable dialog box.
3 #include <Input/input.hxx>
9 #include "AirportList.hxx"
12 int fgPopup::checkHit(int button, int updown, int x, int y)
14 int result = puPopup::checkHit(button, updown, x, y);
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. A further weirdness: plib inserts a
21 // "ghost" child which covers the whole control. (?) Skip it.
22 if(!result) return result;
23 puObject* child = getFirstChild();
24 if(child) child = child->getNextObject();
27 child->getAbsolutePosition(&cx, &cy);
28 child->getSize(&cw, &ch);
29 if(x >= cx && x < cx + cw && y >= cy && y < cy + ch)
31 child = child->getNextObject();
34 // Finally, handle the mouse event
35 if(updown == PU_DOWN) {
37 getPosition(&px, &py);
41 } else if(updown == PU_DRAG && _dragging) {
42 setPosition(x + _dX, y + _dY);
50 ////////////////////////////////////////////////////////////////////////
52 ////////////////////////////////////////////////////////////////////////
55 * User data for a GUI object.
59 GUIInfo (FGDialog * d);
63 vector <FGBinding *> bindings;
71 action_callback (puObject * object)
73 GUIInfo * info = (GUIInfo *)object->getUserData();
74 NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
75 gui->setActiveDialog(info->dialog);
76 int nBindings = info->bindings.size();
77 for (int i = 0; i < nBindings; i++) {
78 info->bindings[i]->fire();
79 if (gui->getActiveDialog() == 0)
82 gui->setActiveDialog(0);
87 ////////////////////////////////////////////////////////////////////////
88 // Static helper functions.
89 ////////////////////////////////////////////////////////////////////////
92 * Copy a property value to a PUI object.
95 copy_to_pui (SGPropertyNode * node, puObject * object)
97 switch (node->getType()) {
98 case SGPropertyNode::BOOL:
99 case SGPropertyNode::INT:
100 case SGPropertyNode::LONG:
101 object->setValue(node->getIntValue());
103 case SGPropertyNode::FLOAT:
104 case SGPropertyNode::DOUBLE:
105 object->setValue(node->getFloatValue());
108 object->setValue(node->getStringValue());
115 copy_from_pui (puObject * object, SGPropertyNode * node)
117 switch (node->getType()) {
118 case SGPropertyNode::BOOL:
119 case SGPropertyNode::INT:
120 case SGPropertyNode::LONG:
121 node->setIntValue(object->getIntegerValue());
123 case SGPropertyNode::FLOAT:
124 case SGPropertyNode::DOUBLE:
125 node->setFloatValue(object->getFloatValue());
128 node->setStringValue(object->getStringValue());
135 ////////////////////////////////////////////////////////////////////////
136 // Implementation of GUIInfo.
137 ////////////////////////////////////////////////////////////////////////
139 GUIInfo::GUIInfo (FGDialog * d)
146 for (unsigned int i = 0; i < bindings.size(); i++) {
154 ////////////////////////////////////////////////////////////////////////
155 // Implementation of FGDialog.
156 ////////////////////////////////////////////////////////////////////////
158 FGDialog::FGDialog (SGPropertyNode * props)
164 FGDialog::~FGDialog ()
166 puDeleteObject(_object);
170 // Delete all the arrays we made
171 // and were forced to keep around
172 // because PUI won't do its own
173 // memory management.
174 for (i = 0; i < _char_arrays.size(); i++) {
175 for (int j = 0; _char_arrays[i][j] != 0; j++)
176 free(_char_arrays[i][j]); // added with strdup
177 delete[] _char_arrays[i];
180 // Delete all the info objects we
181 // were forced to keep around because
182 // PUI cannot delete its own user data.
183 for (i = 0; i < _info.size(); i++) {
184 delete (GUIInfo *)_info[i];
188 // Finally, delete the property links.
189 for (i = 0; i < _propertyObjects.size(); i++) {
190 delete _propertyObjects[i];
191 _propertyObjects[i] = 0;
196 FGDialog::updateValue (const char * objectName)
198 for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
199 const string &name = _propertyObjects[i]->name;
200 if (name == objectName)
201 copy_to_pui(_propertyObjects[i]->node,
202 _propertyObjects[i]->object);
207 FGDialog::applyValue (const char * objectName)
209 for (unsigned int i = 0; i < _propertyObjects.size(); i++) {
210 if (_propertyObjects[i]->name == objectName)
211 copy_from_pui(_propertyObjects[i]->object,
212 _propertyObjects[i]->node);
217 FGDialog::updateValues ()
219 for (unsigned int i = 0; i < _propertyObjects.size(); i++)
220 copy_to_pui(_propertyObjects[i]->node, _propertyObjects[i]->object);
224 FGDialog::applyValues ()
226 for (unsigned int i = 0; i < _propertyObjects.size(); i++)
227 copy_from_pui(_propertyObjects[i]->object,
228 _propertyObjects[i]->node);
232 FGDialog::display (SGPropertyNode * props)
235 SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active");
239 int screenw = globals->get_props()->getIntValue("/sim/startup/xsize");
240 int screenh = globals->get_props()->getIntValue("/sim/startup/ysize");
242 LayoutWidget wid(props);
244 if(!props->hasValue("width") || !props->hasValue("height"))
245 wid.calcPrefSize(&pw, &ph);
246 pw = props->getIntValue("width", pw);
247 ph = props->getIntValue("height", ph);
248 int px = props->getIntValue("x", (screenw - pw) / 2);
249 int py = props->getIntValue("y", (screenh - ph) / 2);
250 wid.layout(px, py, pw, ph);
252 _object = makeObject(props, screenw, screenh);
257 SG_LOG(SG_GENERAL, SG_ALERT, "Widget "
258 << props->getStringValue("name", "[unnamed]")
259 << " does not contain a proper GUI definition");
264 FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight)
266 bool presetSize = props->hasValue("width") && props->hasValue("height");
267 int width = props->getIntValue("width", parentWidth);
268 int height = props->getIntValue("height", parentHeight);
269 int x = props->getIntValue("x", (parentWidth - width) / 2);
270 int y = props->getIntValue("y", (parentHeight - height) / 2);
272 string type = props->getName();
276 if (type == "dialog") {
278 if (props->getBoolValue("modal", false))
279 dialog = new puDialogBox(x, y);
281 dialog = new fgPopup(x, y);
282 setupGroup(dialog, props, width, height, true);
284 } else if (type == "group") {
285 puGroup * group = new puGroup(x, y);
286 setupGroup(group, props, width, height, false);
288 } else if (type == "list") {
289 puList * list = new puList(x, y, x + width, y + height);
290 setupObject(list, props);
292 } else if (type == "airport-list") {
293 AirportList * list = new AirportList(x, y, x + width, y + height);
294 setupObject(list, props);
296 } else if (type == "input") {
297 puInput * input = new puInput(x, y, x + width, y + height);
298 setupObject(input, props);
300 } else if (type == "text") {
301 puText * text = new puText(x, y);
302 setupObject(text, props);
303 // Layed-out objects need their size set, and non-layout ones
304 // get a different placement.
305 if(presetSize) text->setSize(width, height);
306 else text->setLabelPlace(PUPLACE_LABEL_DEFAULT);
308 } else if (type == "checkbox") {
310 b = new puButton(x, y, x + width, y + height, PUBUTTON_XCHECK);
311 b->setColourScheme(.8, .7, .7); // matches "PUI input pink"
312 setupObject(b, props);
314 } else if (type == "radio") {
316 b = new puButton(x, y, x + width, y + height, PUBUTTON_CIRCLE);
317 b->setColourScheme(.8, .7, .7); // matches "PUI input pink"
318 setupObject(b, props);
320 } else if (type == "button") {
322 const char * legend = props->getStringValue("legend", "[none]");
323 if (props->getBoolValue("one-shot", true))
324 b = new puOneShot(x, y, legend);
326 b = new puButton(x, y, legend);
328 b->setSize(width, height);
329 setupObject(b, props);
331 } else if (type == "combo") {
332 vector<SGPropertyNode_ptr> value_nodes = props->getChildren("value");
333 char ** entries = make_char_array(value_nodes.size());
334 for (unsigned int i = 0, j = value_nodes.size() - 1;
335 i < value_nodes.size();
337 entries[i] = strdup((char *)value_nodes[i]->getStringValue());
339 new puComboBox(x, y, x + width, y + height, entries,
340 props->getBoolValue("editable", false));
341 setupObject(combo, props);
343 } else if (type == "slider") {
344 bool vertical = props->getBoolValue("vertical", false);
345 puSlider * slider = new puSlider(x, y, (vertical ? height : width));
346 slider->setMinValue(props->getFloatValue("min", 0.0));
347 slider->setMaxValue(props->getFloatValue("max", 1.0));
348 setupObject(slider, props);
350 } else if (type == "dial") {
351 puDial * dial = new puDial(x, y, width);
352 dial->setMinValue(props->getFloatValue("min", 0.0));
353 dial->setMaxValue(props->getFloatValue("max", 1.0));
354 dial->setWrap(props->getBoolValue("wrap", true));
355 setupObject(dial, props);
357 } else if (type == "select") {
358 vector<SGPropertyNode_ptr> value_nodes;
359 SGPropertyNode * selection_node =
360 fgGetNode(props->getChild("selection")->getStringValue(), true);
362 for (int q = 0; q < selection_node->nChildren(); q++)
363 value_nodes.push_back(selection_node->getChild(q));
365 char ** entries = make_char_array(value_nodes.size());
366 for (unsigned int i = 0, j = value_nodes.size() - 1;
367 i < value_nodes.size();
369 entries[i] = strdup((char *)value_nodes[i]->getName());
370 puSelectBox * select =
371 new puSelectBox(x, y, x + width, y + height, entries);
372 setupObject(select, props);
380 FGDialog::setupObject (puObject * object, SGPropertyNode * props)
382 object->setLabelPlace(PUPLACE_CENTERED_RIGHT);
384 if (props->hasValue("legend"))
385 object->setLegend(props->getStringValue("legend"));
387 if (props->hasValue("label"))
388 object->setLabel(props->getStringValue("label"));
390 if (props->hasValue("property")) {
391 const char * name = props->getStringValue("name");
394 const char * propname = props->getStringValue("property");
395 SGPropertyNode_ptr node = fgGetNode(propname, true);
396 copy_to_pui(node, object);
398 _propertyObjects.push_back(new PropertyObject(name, object, node));
401 vector<SGPropertyNode_ptr> nodes = props->getChildren("binding");
402 if (nodes.size() > 0) {
403 GUIInfo * info = new GUIInfo(this);
405 for (unsigned int i = 0; i < nodes.size(); i++)
406 info->bindings.push_back(new FGBinding(nodes[i]));
407 object->setCallback(action_callback);
408 object->setUserData(info);
409 _info.push_back(info);
412 object->makeReturnDefault(props->getBoolValue("default"));
416 FGDialog::setupGroup (puGroup * group, SGPropertyNode * props,
417 int width, int height, bool makeFrame)
419 setupObject(group, props);
422 puFrame* f = new puFrame(0, 0, width, height);
423 f->setColorScheme(0.8, 0.8, 0.9, 0.85);
426 int nChildren = props->nChildren();
427 for (int i = 0; i < nChildren; i++)
428 makeObject(props->getChild(i), width, height);
433 FGDialog::make_char_array (int size)
435 char ** list = new char*[size+1];
436 for (int i = 0; i <= size; i++)
438 _char_arrays.push_back(list);
444 ////////////////////////////////////////////////////////////////////////
445 // Implementation of FGDialog::PropertyObject.
446 ////////////////////////////////////////////////////////////////////////
448 FGDialog::PropertyObject::PropertyObject (const char * n,
450 SGPropertyNode_ptr p)