X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FGUI%2Fdialog.cxx;h=449979e7ede5775f2cb92dd3a0f428ec4fb5b8fe;hb=fc71333bdd0217a9ff4880305daf6d4d10fb0b40;hp=d09d572e98168a6b1fcbce6b5e9e391fb49a1ce9;hpb=a04aad7a99b2e31f14a1f1e59b38178ad9df00ff;p=flightgear.git diff --git a/src/GUI/dialog.cxx b/src/GUI/dialog.cxx index d09d572e9..449979e7e 100644 --- a/src/GUI/dialog.cxx +++ b/src/GUI/dialog.cxx @@ -4,18 +4,78 @@ # include "config.h" #endif -#include // atof() - -#include +#include #include +#include
#include "dialog.hxx" #include "new_gui.hxx" - -#include "puList.hxx" #include "AirportList.hxx" +#include "property_list.hxx" #include "layout.hxx" + +enum format_type { f_INVALID, f_INT, f_LONG, f_FLOAT, f_DOUBLE, f_STRING }; +static const int FORMAT_BUFSIZE = 255; +static const int RESIZE_MARGIN = 7; + + +/** + * Makes sure the format matches '%[ -+#]?\d*(\.\d*)?(l?[df]|s)', with + * only one number or string placeholder and otherwise arbitrary prefix + * and postfix containing only quoted percent signs (%%). + */ +static format_type +validate_format(const char *f) +{ + bool l = false; + format_type type; + for (; *f; f++) { + if (*f == '%') { + if (f[1] == '%') + f++; + else + break; + } + } + if (*f++ != '%') + return f_INVALID; + while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0') + f++; + while (*f && isdigit(*f)) + f++; + if (*f == '.') { + f++; + while (*f && isdigit(*f)) + f++; + } + + if (*f == 'l') + l = true, f++; + + if (*f == 'd') { + type = l ? f_LONG : f_INT; + } else if (*f == 'f') + type = l ? f_DOUBLE : f_FLOAT; + else if (*f == 's') { + if (l) + return f_INVALID; + type = f_STRING; + } else + return f_INVALID; + + for (++f; *f; f++) { + if (*f == '%') { + if (f[1] == '%') + f++; + else + return f_INVALID; + } + } + return type; +} + + //////////////////////////////////////////////////////////////////////// // Implementation of GUIInfo. //////////////////////////////////////////////////////////////////////// @@ -25,17 +85,22 @@ */ struct GUIInfo { - GUIInfo (FGDialog * d); - virtual ~GUIInfo (); + GUIInfo(FGDialog *d); + virtual ~GUIInfo(); + void apply_format(SGPropertyNode *); - FGDialog * dialog; - vector bindings; + FGDialog *dialog; + SGPropertyNode_ptr node; + vector bindings; int key; + string label, legend, text, format; + format_type fmt_type; }; -GUIInfo::GUIInfo (FGDialog * d) - : dialog(d), - key(-1) +GUIInfo::GUIInfo (FGDialog *d) : + dialog(d), + key(-1), + fmt_type(f_INVALID) { } @@ -47,6 +112,25 @@ GUIInfo::~GUIInfo () } } +void GUIInfo::apply_format(SGPropertyNode *n) +{ + char buf[FORMAT_BUFSIZE + 1]; + if (fmt_type == f_INT) + snprintf(buf, FORMAT_BUFSIZE, format.c_str(), n->getIntValue()); + else if (fmt_type == f_LONG) + snprintf(buf, FORMAT_BUFSIZE, format.c_str(), n->getLongValue()); + else if (fmt_type == f_FLOAT) + snprintf(buf, FORMAT_BUFSIZE, format.c_str(), n->getFloatValue()); + else if (fmt_type == f_DOUBLE) + snprintf(buf, FORMAT_BUFSIZE, format.c_str(), n->getDoubleValue()); + else + snprintf(buf, FORMAT_BUFSIZE, format.c_str(), n->getStringValue()); + + buf[FORMAT_BUFSIZE] = '\0'; + text = buf; +} + + /** * Key handler. @@ -77,7 +161,7 @@ int fgPopup::checkKey(int key, int updown) puObject *fgPopup::getKeyObject(puObject *object, int key) { puObject *ret; - if(object->getType() & PUCLASS_GROUP) + if (object->getType() & PUCLASS_GROUP) for (puObject *obj = ((puGroup *)object)->getFirstChild(); obj; obj = obj->getNextObject()) if ((ret = getKeyObject(obj, key))) @@ -93,13 +177,14 @@ puObject *fgPopup::getKeyObject(puObject *object, int key) puObject *fgPopup::getActiveInputField(puObject *object) { puObject *ret; - if(object->getType() & PUCLASS_GROUP) + if (object->getType() & PUCLASS_GROUP) for (puObject *obj = ((puGroup *)object)->getFirstChild(); obj; obj = obj->getNextObject()) if ((ret = getActiveInputField(obj))) return ret; - if (object->getType() & PUCLASS_INPUT && ((puInput *)object)->isAcceptingInput()) + if (object->getType() & (PUCLASS_INPUT|PUCLASS_LARGEINPUT) + && ((puInput *)object)->isAcceptingInput()) return object; return 0; @@ -110,9 +195,11 @@ puObject *fgPopup::getActiveInputField(puObject *object) */ int fgPopup::checkHit(int button, int updown, int x, int y) { - int result = puPopup::checkHit(button, updown, x, y); + int result = 0; + if (updown != PU_DRAG && !_dragging) + result = puPopup::checkHit(button, updown, x, y); - if ( !_draggable) + if (!_draggable) return result; // This is annoying. We would really want a true result from the @@ -121,22 +208,98 @@ int fgPopup::checkHit(int button, int updown, int x, int y) // intersection test (again) to make sure we don't start a drag // when inside controls. - if(updown == PU_DOWN && !_dragging) { - if(!result) + if (updown == PU_DOWN && !_dragging) { + if (!result) return 0; + int global_drag = fgGetKeyModifiers() & KEYMOD_SHIFT; + int global_resize = fgGetKeyModifiers() & KEYMOD_CTRL; int hit = getHitObjects(this, x, y); - if(hit & (PUCLASS_BUTTON|PUCLASS_ONESHOT|PUCLASS_INPUT)) + if (hit & PUCLASS_LIST) // ctrl-click in property browser (toggle bool) + return result; + if (!global_resize && hit & (PUCLASS_BUTTON|PUCLASS_ONESHOT|PUCLASS_INPUT + |PUCLASS_LARGEINPUT|PUCLASS_SCROLLBAR)) return result; - int px, py; - getPosition(&px, &py); + getPosition(&_dlgX, &_dlgY); + getSize(&_dlgW, &_dlgH); + _start_cursor = fgGetMouseCursor(); _dragging = true; - _dX = px - x; - _dY = py - y; - } else if(updown == PU_DRAG && _dragging) { - setPosition(x + _dX, y + _dY); - } else { + _startX = x; + _startY = y; + + // check and prepare for resizing + static const int cursor[] = { + MOUSE_CURSOR_POINTER, MOUSE_CURSOR_LEFTSIDE, MOUSE_CURSOR_RIGHTSIDE, 0, + MOUSE_CURSOR_TOPSIDE, MOUSE_CURSOR_TOPLEFT, MOUSE_CURSOR_TOPRIGHT, 0, + MOUSE_CURSOR_BOTTOMSIDE, MOUSE_CURSOR_BOTTOMLEFT, MOUSE_CURSOR_BOTTOMRIGHT, 0, + }; + + _resizing = 0; + if (!global_drag && _resizable) { + int hmargin = global_resize ? _dlgW / 3 : RESIZE_MARGIN; + int vmargin = global_resize ? _dlgH / 3 : RESIZE_MARGIN; + + if (y - _dlgY < vmargin) + _resizing |= BOTTOM; + else if (_dlgY + _dlgH - y < vmargin) + _resizing |= TOP; + + if (x - _dlgX < hmargin) + _resizing |= LEFT; + else if (_dlgX + _dlgW - x < hmargin) + _resizing |= RIGHT; + + if (!_resizing && global_resize) + _resizing = BOTTOM|RIGHT; + + _cursor = cursor[_resizing]; + if (_resizing && _resizable) + fgSetMouseCursor(_cursor); + } + + } else if (updown == PU_DRAG && _dragging) { + if (_resizing) { + GUIInfo *info = (GUIInfo *)getUserData(); + if (_resizable && info && info->node) { + int w = _dlgW; + int h = _dlgH; + if (_resizing & LEFT) + w += _startX - x; + if (_resizing & RIGHT) + w += x - _startX; + if (_resizing & TOP) + h += y - _startY; + if (_resizing & BOTTOM) + h += _startY - y; + + int prefw, prefh; + LayoutWidget wid(info->node); + wid.calcPrefSize(&prefw, &prefh); + if (w < prefw) + w = prefw; + if (h < prefh) + h = prefh; + + int x = _dlgX; + int y = _dlgY; + if (_resizing & LEFT) + x += _dlgW - w; + if (_resizing & BOTTOM) + y += _dlgH - h; + + wid.layout(x, y, w, h); + setSize(w, h); + setPosition(x, y); + applySize(static_cast(this)); + getFirstChild()->setSize(w, h); // dialog background puFrame + } + } else { + setPosition(x + _dlgX - _startX, y + _dlgY - _startY); + } + + } else if (_dragging) { + fgSetMouseCursor(_start_cursor); _dragging = false; } return result; @@ -144,8 +307,11 @@ int fgPopup::checkHit(int button, int updown, int x, int y) int fgPopup::getHitObjects(puObject *object, int x, int y) { + if (!object->isVisible()) + return 0; + int type = 0; - if(object->getType() & PUCLASS_GROUP) + if (object->getType() & PUCLASS_GROUP) for (puObject *obj = ((puGroup *)object)->getFirstChild(); obj; obj = obj->getNextObject()) type |= getHitObjects(obj, x, y); @@ -153,11 +319,41 @@ int fgPopup::getHitObjects(puObject *object, int x, int y) int cx, cy, cw, ch; object->getAbsolutePosition(&cx, &cy); object->getSize(&cw, &ch); - if(x >= cx && x < cx + cw && y >= cy && y < cy + ch) + if (x >= cx && x < cx + cw && y >= cy && y < cy + ch) type |= object->getType(); return type; } +void fgPopup::applySize(puObject *object) +{ + // compound plib widgets use setUserData() for internal purposes, so refuse + // to descend into anything that has other bits set than the following + const int validUserData = PUCLASS_VALUE|PUCLASS_OBJECT|PUCLASS_GROUP|PUCLASS_INTERFACE + |PUCLASS_FRAME|PUCLASS_TEXT|PUCLASS_BUTTON|PUCLASS_ONESHOT|PUCLASS_INPUT + |PUCLASS_ARROW|PUCLASS_DIAL|PUCLASS_POPUP; + + int type = object->getType(); + if (type & PUCLASS_GROUP && !(type & ~validUserData)) + for (puObject *obj = ((puGroup *)object)->getFirstChild(); + obj; obj = obj->getNextObject()) + applySize(obj); + + GUIInfo *info = (GUIInfo *)object->getUserData(); + if (!info) + return; + + SGPropertyNode *n = info->node; + if (!n) { + SG_LOG(SG_GENERAL, SG_ALERT, "fgPopup::applySize: no props"); + return; + } + int x = n->getIntValue("x"); + int y = n->getIntValue("y"); + int w = n->getIntValue("width", 4); + int h = n->getIntValue("height", 4); + object->setPosition(x, y); + object->setSize(w, h); +} //////////////////////////////////////////////////////////////////////// @@ -168,10 +364,10 @@ int fgPopup::getHitObjects(puObject *object, int x, int y) * Action callback. */ static void -action_callback (puObject * object) +action_callback (puObject *object) { - GUIInfo * info = (GUIInfo *)object->getUserData(); - NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); + GUIInfo *info = (GUIInfo *)object->getUserData(); + NewGUI *gui = (NewGUI *)globals->get_subsystem("gui"); gui->setActiveDialog(info->dialog); int nBindings = info->bindings.size(); for (int i = 0; i < nBindings; i++) { @@ -183,71 +379,6 @@ action_callback (puObject * object) } -static void -format_callback(puObject *obj, int dx, int dy, void *n) -{ - SGPropertyNode *node = (SGPropertyNode *)n; - const char *format = node->getStringValue("format"), *f = format; - bool number, l = false; - // make sure the format matches '[ -+#]?\d*(\.\d*)?l?[fs]' - for (; *f; f++) { - if (*f == '%') { - if (f[1] == '%') - f++; - else - break; - } - } - if (*f++ != '%') - return; - if (*f == ' ' || *f == '+' || *f == '-' || *f == '#') - f++; - while (*f && isdigit(*f)) - f++; - if (*f == '.') { - f++; - while (*f && isdigit(*f)) - f++; - } - if (*f == 'l') - l = true, f++; - - if (*f == 'f') - number = true; - else if (*f == 's') { - if (l) - return; - number = false; - } else - return; - - for (++f; *f; f++) { - if (*f == '%') { - if (f[1] == '%') - f++; - else - return; - } - } - - char buf[256]; - const char *src = obj->getLabel(); - - if (number) { - float value = atof(src); - snprintf(buf, 256, format, value); - } else { - snprintf(buf, 256, format, src); - } - - buf[255] = '\0'; - - SGPropertyNode *result = node->getNode("formatted", true); - result->setStringValue(buf); - obj->setLabel(result->getStringValue()); -} - - //////////////////////////////////////////////////////////////////////// // Static helper functions. @@ -257,61 +388,67 @@ format_callback(puObject *obj, int dx, int dy, void *n) * Copy a property value to a PUI object. */ static void -copy_to_pui (SGPropertyNode * node, puObject * object) +copy_to_pui (SGPropertyNode *node, puObject *object) { + using namespace simgear; + GUIInfo *info = (GUIInfo *)object->getUserData(); + if (!info) { + SG_LOG(SG_GENERAL, SG_ALERT, "dialog: widget without GUIInfo!"); + return; // this can't really happen + } + // Treat puText objects specially, so their "values" can be set // from properties. - if(object->getType() & PUCLASS_TEXT) { - object->setLabel(node->getStringValue()); + if (object->getType() & PUCLASS_TEXT) { + if (info->fmt_type != f_INVALID) + info->apply_format(node); + else + info->text = node->getStringValue(); + + object->setLabel(info->text.c_str()); return; } switch (node->getType()) { - case SGPropertyNode::BOOL: - case SGPropertyNode::INT: - case SGPropertyNode::LONG: + case props::BOOL: + case props::INT: + case props::LONG: object->setValue(node->getIntValue()); break; - case SGPropertyNode::FLOAT: - case SGPropertyNode::DOUBLE: + case props::FLOAT: + case props::DOUBLE: object->setValue(node->getFloatValue()); break; default: - object->setValue(node->getStringValue()); + info->text = node->getStringValue(); + object->setValue(info->text.c_str()); break; } } static void -copy_from_pui (puObject * object, SGPropertyNode * node) +copy_from_pui (puObject *object, SGPropertyNode *node) { + using namespace simgear; // puText objects are immutable, so should not be copied out - if(object->getType() & PUCLASS_TEXT) + if (object->getType() & PUCLASS_TEXT) return; switch (node->getType()) { - case SGPropertyNode::BOOL: - case SGPropertyNode::INT: - case SGPropertyNode::LONG: + case props::BOOL: + case props::INT: + case props::LONG: node->setIntValue(object->getIntegerValue()); break; - case SGPropertyNode::FLOAT: - case SGPropertyNode::DOUBLE: + case props::FLOAT: + case props::DOUBLE: node->setFloatValue(object->getFloatValue()); break; default: - // Special case to handle lists, as getStringValue cannot be overridden - if(object->getType() & PUCLASS_LIST) - { - const char *s = ((puList *) object)->getListStringValue(); - if (s) - node->setStringValue(s); - } - else - { - node->setStringValue(object->getStringValue()); - } + const char *s = object->getStringValue(); + if (s) + node->setStringValue(s); break; } } @@ -322,10 +459,10 @@ copy_from_pui (puObject * object, SGPropertyNode * node) // Implementation of FGDialog. //////////////////////////////////////////////////////////////////////// -FGDialog::FGDialog (SGPropertyNode * props) - : _object(0), - _gui((NewGUI *)globals->get_subsystem("gui")), - _props(props) +FGDialog::FGDialog (SGPropertyNode *props) : + _object(0), + _gui((NewGUI *)globals->get_subsystem("gui")), + _props(props) { _module = string("__dlg:") + props->getStringValue("name", "[unnamed]"); SGPropertyNode *nasal = props->getNode("nasal"); @@ -335,7 +472,8 @@ FGDialog::FGDialog (SGPropertyNode * props) if (open) { const char *s = open->getStringValue(); FGNasalSys *nas = (FGNasalSys *)globals->get_subsystem("nasal"); - nas->createModule(_module.c_str(), _module.c_str(), s, strlen(s)); + if (nas) + nas->createModule(_module.c_str(), _module.c_str(), s, strlen(s), props); } } display(props); @@ -349,26 +487,17 @@ FGDialog::~FGDialog () _props->setIntValue("lasty", y); FGNasalSys *nas = (FGNasalSys *)globals->get_subsystem("nasal"); - if (_nasal_close) { - const char *s = _nasal_close->getStringValue(); - nas->createModule(_module.c_str(), _module.c_str(), s, strlen(s)); + if (nas) { + if (_nasal_close) { + const char *s = _nasal_close->getStringValue(); + nas->createModule(_module.c_str(), _module.c_str(), s, strlen(s), _props); + } + nas->deleteModule(_module.c_str()); } - nas->deleteModule(_module.c_str()); puDeleteObject(_object); unsigned int i; - - // Delete all the arrays we made - // and were forced to keep around - // because PUI won't do its own - // memory management. - for (i = 0; i < _char_arrays.size(); i++) { - for (int j = 0; _char_arrays[i][j] != 0; j++) - free(_char_arrays[i][j]); // added with strdup - delete[] _char_arrays[i]; - } - // Delete all the info objects we // were forced to keep around because // PUI cannot delete its own user data. @@ -376,7 +505,6 @@ FGDialog::~FGDialog () delete (GUIInfo *)_info[i]; _info[i] = 0; } - // Finally, delete the property links. for (i = 0; i < _propertyObjects.size(); i++) { delete _propertyObjects[i]; @@ -385,39 +513,39 @@ FGDialog::~FGDialog () } void -FGDialog::updateValue (const char * objectName) +FGDialog::updateValues (const char *objectName) { + if (objectName && !objectName[0]) + objectName = 0; + for (unsigned int i = 0; i < _propertyObjects.size(); i++) { const string &name = _propertyObjects[i]->name; - if (name == objectName) - copy_to_pui(_propertyObjects[i]->node, - _propertyObjects[i]->object); - } -} + if (objectName && name != objectName) + continue; -void -FGDialog::applyValue (const char * objectName) -{ - for (unsigned int i = 0; i < _propertyObjects.size(); i++) { - if (_propertyObjects[i]->name == objectName) - copy_from_pui(_propertyObjects[i]->object, - _propertyObjects[i]->node); + puObject *obj = _propertyObjects[i]->object; + if ((obj->getType() & PUCLASS_LIST) && (dynamic_cast(obj)->id & FGCLASS_LIST)) { + fgList *pl = static_cast(obj); + pl->update(); + } else + copy_to_pui(_propertyObjects[i]->node, obj); } } void -FGDialog::updateValues () +FGDialog::applyValues (const char *objectName) { - for (unsigned int i = 0; i < _propertyObjects.size(); i++) - copy_to_pui(_propertyObjects[i]->node, _propertyObjects[i]->object); -} + if (objectName && !objectName[0]) + objectName = 0; + + for (unsigned int i = 0; i < _propertyObjects.size(); i++) { + const string &name = _propertyObjects[i]->name; + if (objectName && name != objectName) + continue; -void -FGDialog::applyValues () -{ - for (unsigned int i = 0; i < _propertyObjects.size(); i++) copy_from_pui(_propertyObjects[i]->object, _propertyObjects[i]->node); + } } void @@ -433,7 +561,7 @@ FGDialog::update () } void -FGDialog::display (SGPropertyNode * props) +FGDialog::display (SGPropertyNode *props) { if (_object != 0) { SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active"); @@ -453,16 +581,16 @@ FGDialog::display (SGPropertyNode * props) SGPropertyNode *fontnode = props->getNode("font"); if (fontnode) { - FGFontCache *fc = _gui->get_fontcache(); + FGFontCache *fc = globals->get_fontcache(); _font = fc->get(fontnode); } else { _font = _gui->getDefaultFont(); } wid.setDefaultFont(_font, int(_font->getPointSize())); - int pw=0, ph=0; + int pw = 0, ph = 0; int px, py, savex, savey; - if(!userw || !userh) + if (!userw || !userh) wid.calcPrefSize(&pw, &ph); pw = props->getIntValue("width", pw); ph = props->getIntValue("height", ph); @@ -486,18 +614,18 @@ FGDialog::display (SGPropertyNode * props) // Remove automatically generated properties, so the layout looks // the same next time around, or restore x and y to preserve negative coords. - if(userx) + if (userx) props->setIntValue("x", savex); else props->removeChild("x"); - if(usery) + if (usery) props->setIntValue("y", savey); else props->removeChild("y"); - if(!userw) props->removeChild("width"); - if(!userh) props->removeChild("height"); + if (!userw) props->removeChild("width"); + if (!userh) props->removeChild("height"); if (_object != 0) { _object->reveal(); @@ -509,9 +637,9 @@ FGDialog::display (SGPropertyNode * props) } puObject * -FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) +FGDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeight) { - if (props->getBoolValue("hide")) + if (!props->getBoolValue("enabled", true)) return 0; bool presetSize = props->hasValue("width") && props->hasValue("height"); @@ -521,135 +649,136 @@ FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) int y = props->getIntValue("y", (parentHeight - height) / 2); string type = props->getName(); - if (type == "") + if (type.empty()) type = "dialog"; if (type == "dialog") { - puPopup * obj; + puPopup *obj; bool draggable = props->getBoolValue("draggable", true); + bool resizable = props->getBoolValue("resizable", false); if (props->getBoolValue("modal", false)) obj = new puDialogBox(x, y); else - obj = new fgPopup(x, y, draggable); + obj = new fgPopup(x, y, resizable, draggable); setupGroup(obj, props, width, height, true); setColor(obj, props); return obj; } else if (type == "group") { - puGroup * obj = new puGroup(x, y); + puGroup *obj = new puGroup(x, y); setupGroup(obj, props, width, height, false); setColor(obj, props); return obj; } else if (type == "frame") { - puGroup * obj = new puGroup(x, y); + puGroup *obj = new puGroup(x, y); setupGroup(obj, props, width, height, true); setColor(obj, props); return obj; } else if (type == "hrule" || type == "vrule") { - puFrame * obj = new puFrame(x, y, x + width, y + height); + puFrame *obj = new puFrame(x, y, x + width, y + height); obj->setBorderThickness(0); + setupObject(obj, props); setColor(obj, props, BACKGROUND|FOREGROUND|HIGHLIGHT); return obj; } else if (type == "list") { - vector value_nodes = props->getChildren("value"); - char ** entries = make_char_array(value_nodes.size()); - for (unsigned int i = 0; i < value_nodes.size(); i++) - entries[i] = strdup((char *)value_nodes[i]->getStringValue()); - - puList * obj = new puList(x, y, x + width, y + height, entries); + int slider_width = props->getIntValue("slider", 20); + fgList *obj = new fgList(x, y, x + width, y + height, props, slider_width); + if (presetSize) + obj->setSize(width, height); setupObject(obj, props); setColor(obj, props); return obj; } else if (type == "airport-list") { - AirportList * obj = new AirportList(x, y, x + width, y + height); + AirportList *obj = new AirportList(x, y, x + width, y + height); + if (presetSize) + obj->setSize(width, height); + setupObject(obj, props); + setColor(obj, props); + return obj; + + } else if (type == "property-list") { + PropertyList *obj = new PropertyList(x, y, x + width, y + height, globals->get_props()); + if (presetSize) + obj->setSize(width, height); setupObject(obj, props); setColor(obj, props); return obj; } else if (type == "input") { - puInput * obj = new puInput(x, y, x + width, y + height); + puInput *obj = new puInput(x, y, x + width, y + height); setupObject(obj, props); setColor(obj, props, FOREGROUND|LABEL); return obj; } else if (type == "text") { - puText * obj = new puText(x, y); + puText *obj = new puText(x, y); setupObject(obj, props); - if (props->getNode("format")) { - SGPropertyNode *live = props->getNode("live"); - if (live && live->getBoolValue()) - obj->setRenderCallback(format_callback, props); - else - format_callback(obj, x, y, props); - } // Layed-out objects need their size set, and non-layout ones // get a different placement. - if(presetSize) obj->setSize(width, height); - else obj->setLabelPlace(PUPLACE_LABEL_DEFAULT); + if (presetSize) + obj->setSize(width, height); + else + obj->setLabelPlace(PUPLACE_LABEL_DEFAULT); setColor(obj, props, LABEL); return obj; } else if (type == "checkbox") { - puButton * obj; + puButton *obj; obj = new puButton(x, y, x + width, y + height, PUBUTTON_XCHECK); setupObject(obj, props); setColor(obj, props, FOREGROUND|LABEL); return obj; } else if (type == "radio") { - puButton * obj; + puButton *obj; obj = new puButton(x, y, x + width, y + height, PUBUTTON_CIRCLE); setupObject(obj, props); setColor(obj, props, FOREGROUND|LABEL); return obj; } else if (type == "button") { - puButton * obj; - const char * legend = props->getStringValue("legend", "[none]"); + puButton *obj; + const char *legend = props->getStringValue("legend", "[none]"); if (props->getBoolValue("one-shot", true)) obj = new puOneShot(x, y, legend); else obj = new puButton(x, y, legend); - if(presetSize) + if (presetSize) obj->setSize(width, height); setupObject(obj, props); setColor(obj, props); return obj; } else if (type == "combo") { - vector value_nodes = props->getChildren("value"); - char ** entries = make_char_array(value_nodes.size()); - for (unsigned int i = 0; i < value_nodes.size(); i++) - entries[i] = strdup((char *)value_nodes[i]->getStringValue()); - - puComboBox * obj = new puComboBox(x, y, x + width, y + height, entries, - props->getBoolValue("editable", false)); + fgComboBox *obj = new fgComboBox(x, y, x + width, y + height, props, + props->getBoolValue("editable", false)); setupObject(obj, props); -#ifdef PUCOL_EDITFIELD // plib > 0.8.4 setColor(obj, props, EDITFIELD); -#else - setColor(obj, props, FOREGROUND|LABEL); -#endif return obj; } else if (type == "slider") { bool vertical = props->getBoolValue("vertical", false); - puSlider * obj = new puSlider(x, y, (vertical ? height : width)); + puSlider *obj = new puSlider(x, y, (vertical ? height : width), vertical); obj->setMinValue(props->getFloatValue("min", 0.0)); obj->setMaxValue(props->getFloatValue("max", 1.0)); + obj->setStepSize(props->getFloatValue("step")); + obj->setSliderFraction(props->getFloatValue("fraction")); +#if PLIB_VERSION > 185 + obj->setPageStepSize(props->getFloatValue("pagestep")); +#endif setupObject(obj, props); - if(presetSize) + if (presetSize) obj->setSize(width, height); setColor(obj, props, FOREGROUND|LABEL); return obj; } else if (type == "dial") { - puDial * obj = new puDial(x, y, width); + puDial *obj = new puDial(x, y, width); obj->setMinValue(props->getFloatValue("min", 0.0)); obj->setMaxValue(props->getFloatValue("max", 1.0)); obj->setWrap(props->getBoolValue("wrap", true)); @@ -658,42 +787,34 @@ FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) return obj; } else if (type == "textbox") { - int slider_width = props->getIntValue("slider", parentHeight); + int slider_width = props->getIntValue("slider", 20); int wrap = props->getBoolValue("wrap", true); - if (slider_width==0) slider_width=20; - puLargeInput * obj = new puLargeInput(x, y, - x+width, x+height, 2, slider_width, wrap); +#if PLIB_VERSION > 185 + puaLargeInput *obj = new puaLargeInput(x, y, + x + width, x + height, 11, slider_width, wrap); +#else + puaLargeInput *obj = new puaLargeInput(x, y, + x + width, x + height, 2, slider_width, wrap); +#endif - if (props->hasValue("editable")) { - if (props->getBoolValue("editable")==false) - obj->disableInput(); - else - obj->enableInput(); - } + if (props->getBoolValue("editable")) + obj->enableInput(); + else + obj->disableInput(); + + if (presetSize) + obj->setSize(width, height); setupObject(obj, props); setColor(obj, props, FOREGROUND|LABEL); + + int top = props->getIntValue("top-line", 0); + obj->setTopLineInWindow(top < 0 ? unsigned(-1) >> 1 : top); return obj; } else if (type == "select") { - vector value_nodes; - SGPropertyNode * selection_node = - fgGetNode(props->getChild("selection")->getStringValue(), true); - - for (int q = 0; q < selection_node->nChildren(); q++) - value_nodes.push_back(selection_node->getChild(q)); - - char ** entries = make_char_array(value_nodes.size()); - for (unsigned int i = 0; i < value_nodes.size(); i++) - entries[i] = strdup((char *)value_nodes[i]->getName()); - - puSelectBox * obj = - new puSelectBox(x, y, x + width, y + height, entries); + fgSelectBox *obj = new fgSelectBox(x, y, x + width, y + height, props); setupObject(obj, props); -#ifdef PUCOL_EDITFIELD // plib > 0.8.4 setColor(obj, props, EDITFIELD); -#else - setColor(obj, props, FOREGROUND|LABEL); -#endif return obj; } else { return 0; @@ -701,51 +822,77 @@ FGDialog::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) } void -FGDialog::setupObject (puObject * object, SGPropertyNode * props) +FGDialog::setupObject (puObject *object, SGPropertyNode *props) { + GUIInfo *info = new GUIInfo(this); + object->setUserData(info); + _info.push_back(info); object->setLabelPlace(PUPLACE_CENTERED_RIGHT); + object->makeReturnDefault(props->getBoolValue("default")); + info->node = props; - if (props->hasValue("legend")) - object->setLegend(props->getStringValue("legend")); + if (props->hasValue("legend")) { + info->legend = props->getStringValue("legend"); + object->setLegend(info->legend.c_str()); + } - if (props->hasValue("label")) - object->setLabel(props->getStringValue("label")); + if (props->hasValue("label")) { + info->label = props->getStringValue("label"); + object->setLabel(info->label.c_str()); + } if (props->hasValue("border")) object->setBorderThickness( props->getIntValue("border", 2) ); - if ( SGPropertyNode *nft = props->getNode("font", false) ) { - FGFontCache *fc = _gui->get_fontcache(); + if (SGPropertyNode *nft = props->getNode("font", false)) { + FGFontCache *fc = globals->get_fontcache(); puFont *lfnt = fc->get(nft); object->setLabelFont(*lfnt); + object->setLegendFont(*lfnt); } else { object->setLabelFont(*_font); } + string type = props->getName(); + if (type == "input" && props->getBoolValue("live")) + object->setDownCallback(action_callback); + + if (type == "text") { + const char *format = props->getStringValue("format", 0); + if (format) { + info->fmt_type = validate_format(format); + if (info->fmt_type != f_INVALID) + info->format = format; + else + SG_LOG(SG_GENERAL, SG_ALERT, "DIALOG: invalid '" + << format << '\''); + } + } + if (props->hasValue("property")) { - const char * name = props->getStringValue("name"); + const char *name = props->getStringValue("name"); if (name == 0) name = ""; - const char * propname = props->getStringValue("property"); + const char *propname = props->getStringValue("property"); SGPropertyNode_ptr node = fgGetNode(propname, true); copy_to_pui(node, object); - PropertyObject* po = new PropertyObject(name, object, node); + + PropertyObject *po = new PropertyObject(name, object, node); _propertyObjects.push_back(po); - if(props->getBoolValue("live")) + if (props->getBoolValue("live")) _liveObjects.push_back(po); } - SGPropertyNode * dest = fgGetNode("/sim/bindings/gui", true); + SGPropertyNode *dest = fgGetNode("/sim/bindings/gui", true); vector bindings = props->getChildren("binding"); if (bindings.size() > 0) { - GUIInfo * info = new GUIInfo(this); info->key = props->getIntValue("keynum", -1); if (props->hasValue("key")) info->key = getKeyCode(props->getStringValue("key", "")); for (unsigned int i = 0; i < bindings.size(); i++) { unsigned int j = 0; - SGPropertyNode *binding; + SGPropertyNode_ptr binding; while (dest->getChild("binding", j)) j++; @@ -755,19 +902,15 @@ FGDialog::setupObject (puObject * object, SGPropertyNode * props) binding = dest->getChild("binding", j, true); copyProperties(bindings[i], binding); - info->bindings.push_back(new FGBinding(binding)); + info->bindings.push_back(new SGBinding(binding, globals->get_props())); } object->setCallback(action_callback); - object->setUserData(info); - _info.push_back(info); } - - object->makeReturnDefault(props->getBoolValue("default")); } void -FGDialog::setupGroup (puGroup * group, SGPropertyNode * props, - int width, int height, bool makeFrame) +FGDialog::setupGroup(puGroup *group, SGPropertyNode *props, + int width, int height, bool makeFrame) { setupObject(group, props); @@ -783,19 +926,19 @@ FGDialog::setupGroup (puGroup * group, SGPropertyNode * props, } void -FGDialog::setColor(puObject * object, SGPropertyNode * props, int which) +FGDialog::setColor(puObject *object, SGPropertyNode *props, int which) { string type = props->getName(); - if (type == "") + if (type.empty()) type = "dialog"; if (type == "textbox" && props->getBoolValue("editable")) type += "-editable"; - FGColor *c = new FGColor(_gui->getColor("background")); - c->merge(_gui->getColor(type)); - c->merge(props->getNode("color")); - if (c->isValid()) - object->setColourScheme(c->red(), c->green(), c->blue(), c->alpha()); + FGColor c(_gui->getColor("background")); + c.merge(_gui->getColor(type)); + c.merge(props->getNode("color")); + if (c.isValid()) + object->setColourScheme(c.red(), c.green(), c.blue(), c.alpha()); const struct { int mask; @@ -809,27 +952,27 @@ FGDialog::setColor(puObject * object, SGPropertyNode * props, int which) { LABEL, PUCOL_LABEL, "label", "color-label" }, { LEGEND, PUCOL_LEGEND, "legend", "color-legend" }, { MISC, PUCOL_MISC, "misc", "color-misc" }, -#ifdef PUCOL_EDITFIELD // plib > 0.8.4 { EDITFIELD, PUCOL_EDITFIELD, "editfield", "color-editfield" }, -#endif }; const int numcol = sizeof(pucol) / sizeof(pucol[0]); for (int i = 0; i < numcol; i++) { bool dirty = false; - c->clear(); - c->setAlpha(1.0); + c.clear(); + c.setAlpha(1.0); - dirty |= c->merge(_gui->getColor(type + '-' + pucol[i].name)); + dirty |= c.merge(_gui->getColor(type + '-' + pucol[i].name)); if (which & pucol[i].mask) - dirty |= c->merge(props->getNode("color")); + dirty |= c.merge(props->getNode("color")); + + if ((pucol[i].mask == LABEL) && !c.isValid()) + dirty |= c.merge(_gui->getColor("label")); - if ((pucol[i].mask == LABEL) && !c->isValid()) - dirty |= c->merge(_gui->getColor("label")); + dirty |= c.merge(props->getNode(pucol[i].cname)); - if (c->isValid() && dirty) - object->setColor(pucol[i].id, c->red(), c->green(), c->blue(), c->alpha()); + if (c.isValid() && dirty) + object->setColor(pucol[i].id, c.red(), c.green(), c.blue(), c.alpha()); } } @@ -911,9 +1054,10 @@ FGDialog::getKeyCode(const char *str) if (mod & SHIFT) key = toupper(key); if (mod & CTRL) - key = toupper(key) - 64; - if (mod & ALT) + key = toupper(key) - '@'; + /* if (mod & ALT) ; // Alt not propagated to the gui + */ } else { for (char *t = s; *t; t++) *t = tolower(*t); @@ -928,30 +1072,75 @@ FGDialog::getKeyCode(const char *str) return key; } -char ** -FGDialog::make_char_array (int size) + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGDialog::PropertyObject. +//////////////////////////////////////////////////////////////////////// + +FGDialog::PropertyObject::PropertyObject(const char *n, + puObject *o, SGPropertyNode_ptr p) : + name(n), + object(o), + node(p) { - char ** list = new char*[size+1]; - for (int i = 0; i <= size; i++) - list[i] = 0; - _char_arrays.push_back(list); - return list; } + //////////////////////////////////////////////////////////////////////// -// Implementation of FGDialog::PropertyObject. +// Implementation of fgValueList and derived pui widgets //////////////////////////////////////////////////////////////////////// -FGDialog::PropertyObject::PropertyObject (const char * n, - puObject * o, - SGPropertyNode_ptr p) - : name(n), - object(o), - node(p) + +fgValueList::fgValueList(SGPropertyNode *p) : + _props(p) +{ + make_list(); +} + +void +fgValueList::update() +{ + destroy_list(); + make_list(); +} + +fgValueList::~fgValueList() +{ + destroy_list(); +} + +void +fgValueList::make_list() { + vector value_nodes = _props->getChildren("value"); + _list = new char *[value_nodes.size() + 1]; + unsigned int i; + for (i = 0; i < value_nodes.size(); i++) + _list[i] = strdup((char *)value_nodes[i]->getStringValue()); + _list[i] = 0; } +void +fgValueList::destroy_list() +{ + for (int i = 0; _list[i] != 0; i++) + if (_list[i]) + free(_list[i]); + delete[] _list; +} + + + +void +fgList::update() +{ + fgValueList::update(); + int top = getTopItem(); + newList(_list); + setTopItem(top); +} // end of dialog.cxx