X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FGUI%2Fnew_gui.cxx;h=ff9c7ad3c337c370a9ed1d79f66e50589fb83778;hb=1ae2b59333dc914d10bcc726bc94e71cbf3e411e;hp=656fc2a2aa2c806eca4b4a56f93bce988457cddc;hpb=4d292daeaa5f014a8015f0c74a3c8bd05b96b5a4;p=flightgear.git diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index 656fc2a2a..ff9c7ad3c 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -1,252 +1,582 @@ // new_gui.cxx: implementation of XML-configurable GUI support. +#include +#include +#include +#include +#include #include "new_gui.hxx" #include +#include -#include -SG_USING_STD(vector); +#include +#include +#include -#include #include
+#include "menubar.hxx" +#include "dialog.hxx" -/** - * Callback to update all property values. - */ -static void -update_callback (puObject * object) -{ - ((NewGUI *)object->getUserData())->updateProperties(); -} +extern puFont FONT_HELVETICA_14; +extern puFont FONT_SANS_12B; + + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of NewGUI. +//////////////////////////////////////////////////////////////////////// -/** - * Callback to close the dialog. - */ -static void -close_callback (puObject * object) + +NewGUI::NewGUI () + : _menubar(new FGMenuBar), + _active_dialog(0) { - ((NewGUI *)object->getUserData())->closeActiveObject(); } +NewGUI::~NewGUI () +{ + delete _menubar; + _dialog_props.clear(); + for (_itt_t it = _colors.begin(); it != _colors.end(); ++it) + delete it->second; +} -/** - * Callback to apply the property value for every field. - */ -static void -apply_callback (puObject * object) +void +NewGUI::init () { - ((NewGUI *)object->getUserData())->applyProperties(); - update_callback(object); + setStyle(); + char path1[1024]; + char path2[1024]; + ulMakePath(path1, globals->get_fg_root().c_str(), "gui"); + ulMakePath(path2, path1, "dialogs"); + readDir(path2); + _menubar->init(); } +void +NewGUI::reinit () +{ + reset(true); + fgSetBool("/sim/signals/reinit-gui", true); +} -/** - * Callback to apply the property values and close the dialog. - */ -static void -close_apply_callback (puObject * object) +void +NewGUI::redraw () { - apply_callback(object); - close_callback(object); + reset(false); } +void +NewGUI::reset (bool reload) +{ + map::iterator iter; + vector dlg; + // close all open dialogs and remember them ... + for (iter = _active_dialogs.begin(); iter != _active_dialogs.end(); ++iter) + dlg.push_back(iter->first); + unsigned int i; + for (i = 0; i < dlg.size(); i++) + closeDialog(dlg[i]); -NewGUI::NewGUI () - : _activeObject(0) + setStyle(); + + unbind(); + delete _menubar; + _menubar = new FGMenuBar; + + if (reload) { + _dialog_props.clear(); + init(); + } else { + _menubar->init(); + } + + bind(); + + // open dialogs again + for (i = 0; i < dlg.size(); i++) + showDialog(dlg[i]); +} + +void +NewGUI::bind () { + fgTie("/sim/menubar/visibility", this, + &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible); } -NewGUI::~NewGUI () +void +NewGUI::unbind () { + fgUntie("/sim/menubar/visibility"); } void -NewGUI::init () +NewGUI::update (double delta_time_sec) { - SGPropertyNode props; + map::iterator iter = _active_dialogs.begin(); + for(/**/; iter != _active_dialogs.end(); iter++) + iter->second->update(); +} - try { - fgLoadProps("gui.xml", &props); - } catch (const sg_exception &ex) { - SG_LOG(SG_INPUT, SG_ALERT, "Error parsing gui.xml: " - << ex.getMessage()); - return; +bool +NewGUI::showDialog (const string &name) +{ + if (_dialog_props.find(name) == _dialog_props.end()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined"); + return false; + } else { + if(!_active_dialogs[name]) + _active_dialogs[name] = new FGDialog(_dialog_props[name]); + return true; } +} - int nChildren = props.nChildren(); - for (int i = 0; i < nChildren; i++) { - SGPropertyNode_ptr child = props.getChild(i); - if (!child->hasValue("name")) { - SG_LOG(SG_INPUT, SG_WARN, "GUI node " << child->getName() - << " has no name; skipping."); - } else { - string name = child->getStringValue("name"); - SG_LOG(SG_INPUT, SG_BULK, "Saving GUI node " << name); - _objects[name] = child; +bool +NewGUI::closeActiveDialog () +{ + if (_active_dialog == 0) + return false; + + // Kill any entries in _active_dialogs... Is there an STL + // algorithm to do (delete map entries by value, not key)? I hate + // the STL :) -Andy + map::iterator iter = _active_dialogs.begin(); + for(/**/; iter != _active_dialogs.end(); iter++) { + if(iter->second == _active_dialog) { + _active_dialogs.erase(iter); + // iter is no longer valid + break; } } + + delete _active_dialog; + _active_dialog = 0; + return true; +} + +bool +NewGUI::closeDialog (const string& name) +{ + if(_active_dialogs.find(name) != _active_dialogs.end()) { + if(_active_dialog == _active_dialogs[name]) + _active_dialog = 0; + delete _active_dialogs[name]; + _active_dialogs.erase(name); + return true; + } + return false; // dialog wasn't open... +} + +SGPropertyNode_ptr +NewGUI::getDialogProperties (const string &name) +{ + if(_dialog_props.find(name) != _dialog_props.end()) + return _dialog_props[name]; + + SG_LOG(SG_GENERAL, SG_DEBUG, "dialog '" << name << "' missing"); + return 0; +} + +FGDialog * +NewGUI::getDialog (const string &name) +{ + if(_active_dialogs.find(name) != _active_dialogs.end()) + return _active_dialogs[name]; + + SG_LOG(SG_GENERAL, SG_DEBUG, "dialog '" << name << "' missing"); + return 0; } void -NewGUI::update (double delta_time_sec) +NewGUI::setActiveDialog (FGDialog * dialog) +{ + _active_dialog = dialog; +} + +FGDialog * +NewGUI::getActiveDialog () +{ + return _active_dialog; +} + +FGMenuBar * +NewGUI::getMenuBar () { - // NO OP + return _menubar; +} + +bool +NewGUI::getMenuBarVisible () const +{ + return _menubar->isVisible(); } void -NewGUI::display (const string &name) +NewGUI::setMenuBarVisible (bool visible) +{ + if (visible) + _menubar->show(); + else + _menubar->hide(); +} + +static bool +test_extension (const char * path, const char * ext) { - if (_activeObject != 0) { - SG_LOG(SG_GENERAL, SG_ALERT, "Another GUI object is still active"); + int pathlen = strlen(path); + int extlen = strlen(ext); + + for (int i = 1; i <= pathlen && i <= extlen; i++) { + if (path[pathlen-i] != ext[extlen-i]) + return false; + } + return true; +} + +void +NewGUI::newDialog (SGPropertyNode* props) +{ + const char* cname = props->getStringValue("name"); + if(!cname) { + SG_LOG(SG_GENERAL, SG_ALERT, "New dialog has no property"); return; } + string name = cname; + if(_active_dialogs.find(name) == _active_dialogs.end()) + _dialog_props[name] = props; +} - if (_objects.find(name) == _objects.end()) { - SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined"); +void +NewGUI::readDir (const char * path) +{ + ulDir * dir = ulOpenDir(path); + + if (dir == 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "Failed to read GUI files from " + << path); return; } - SGPropertyNode_ptr props = _objects[name]; - - _activeObject = makeObject(props, 1024, 768); - if (_activeObject != 0) { - _activeObject->reveal(); - } else { - SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name - << " does not contain a proper GUI definition"); + for (ulDirEnt * dirEnt = ulReadDir(dir); + dirEnt != 0; + dirEnt = ulReadDir(dir)) { + + char subpath[1024]; + + ulMakePath(subpath, path, dirEnt->d_name); + + if (!dirEnt->d_isdir && test_extension(subpath, ".xml")) { + SGPropertyNode * props = new SGPropertyNode; + try { + readProperties(subpath, props); + } catch (const sg_exception &) { + SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog " + << subpath); + delete props; + continue; + } + SGPropertyNode *nameprop = props->getNode("name"); + if (!nameprop) { + SG_LOG(SG_INPUT, SG_WARN, "dialog " << subpath + << " has no name; skipping."); + delete props; + continue; + } + string name = nameprop->getStringValue(); + if (_dialog_props[name]) + delete (SGPropertyNode *)_dialog_props[name]; + + _dialog_props[name] = props; + } } + ulCloseDir(dir); } + + +//////////////////////////////////////////////////////////////////////// +// Style handling. +//////////////////////////////////////////////////////////////////////// + void -NewGUI::applyProperties () +NewGUI::setStyle (void) { - for (int i = 0; i < _propertyObjects.size(); i++) { - puObject * object = _propertyObjects[i].object; - SGPropertyNode_ptr node = _propertyObjects[i].node; - node->setStringValue(object->getStringValue()); + _itt_t it; + for (it = _colors.begin(); it != _colors.end(); ++it) + delete it->second; + _colors.clear(); + + // set up the traditional colors as default + _colors["background"] = new FGColor(0.8f, 0.8f, 0.9f, 0.85f); + _colors["foreground"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f); + _colors["highlight"] = new FGColor(0.7f, 0.7f, 0.7f, 1.0f); + _colors["label"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f); + _colors["legend"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f); + _colors["misc"] = new FGColor(0.0f, 0.0f, 0.0f, 1.0f); + _colors["inputfield"] = new FGColor(0.8f, 0.7f, 0.7f, 1.0f); + + //puSetDefaultStyle(); + + int which = fgGetInt("/sim/gui/current-style", 0); + SGPropertyNode *sim = globals->get_props()->getNode("sim/gui", true); + SGPropertyNode *n = sim->getChild("style", which); + if (!n) + n = sim->getChild("style", 0, true); + + setupFont(n->getNode("fonts/gui", true)); + n = n->getNode("colors", true); + + for (int i = 0; i < n->nChildren(); i++) { + SGPropertyNode *child = n->getChild(i); + _colors[child->getName()] = new FGColor(child); } + + FGColor *c = _colors["background"]; + puSetDefaultColourScheme(c->red(), c->green(), c->blue(), c->alpha()); } + void -NewGUI::updateProperties () +NewGUI::setupFont (SGPropertyNode *node) { - for (int i = 0; i < _propertyObjects.size(); i++) { - puObject * object = _propertyObjects[i].object; - SGPropertyNode_ptr node = _propertyObjects[i].node; - object->setValue(node->getStringValue()); - } + _font = globals->get_fontcache()->get(node); + puSetDefaultFonts(*_font, *_font); + return; } + + + +//////////////////////////////////////////////////////////////////////// +// FGColor class. +//////////////////////////////////////////////////////////////////////// + void -NewGUI::closeActiveObject () -{ - delete _activeObject; - _activeObject = 0; - _propertyObjects.clear(); -} - -puObject * -NewGUI::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) -{ - int width = props->getIntValue("width", parentWidth); - int height = props->getIntValue("height", parentHeight); - - int x = props->getIntValue("x", (parentWidth - width) / 2); - int y = props->getIntValue("y", (parentHeight - height) / 2); - - string type = props->getName(); - - if (type == "dialog") { - puPopup * dialog; - if (props->getBoolValue("modal", false)) - dialog = new puDialogBox(x, y); - else - dialog = new puPopup(x, y); - setupGroup(dialog, props, width, height, true); - return dialog; - } else if (type == "group") { - puGroup * group = new puGroup(x, y); - setupGroup(group, props, width, height, false); - return group; - } else if (type == "input") { - puInput * input = new puInput(x, y, x + width, y + height); - setupObject(input, props); - return input; - } else if (type == "text") { - puText * text = new puText(x, y); - setupObject(text, props); - return text; - } else if (type == "button") { - puButton * b; - const char * legend = props->getStringValue("legend", "[none]"); - if (props->getBoolValue("one-shot", true)) - b = new puOneShot(x, y, legend); - else - b = new puButton(x, y, legend); - setupObject(b, props); - return b; - } else { - return 0; - } +FGColor::print() const { + std::cerr << "red=" << _red << ", green=" << _green + << ", blue=" << _blue << ", alpha=" << _alpha << std::endl; } -void -NewGUI::setupObject (puObject * object, SGPropertyNode * props) +bool +FGColor::merge(const SGPropertyNode *node) { - object->setUserData(this); + if (!node) + return false; + + bool dirty = false; + const SGPropertyNode * n; + if ((n = node->getNode("red"))) + _red = n->getFloatValue(), dirty = true; + if ((n = node->getNode("green"))) + _green = n->getFloatValue(), dirty = true; + if ((n = node->getNode("blue"))) + _blue = n->getFloatValue(), dirty = true; + if ((n = node->getNode("alpha"))) + _alpha = n->getFloatValue(), dirty = true; + return dirty; +} - if (props->hasValue("legend")) - object->setLegend(props->getStringValue("legend")); +bool +FGColor::merge(const FGColor *color) +{ + bool dirty = false; + if (color && color->_red >= 0.0) + _red = color->_red, dirty = true; + if (color && color->_green >= 0.0) + _green = color->_green, dirty = true; + if (color && color->_blue >= 0.0) + _blue = color->_blue, dirty = true; + if (color && color->_alpha >= 0.0) + _alpha = color->_alpha, dirty = true; + return dirty; +} - if (props->hasValue("label")) - object->setLabel(props->getStringValue("label")); - if (props->hasValue("default-value-prop")) { - const char * name = props->getStringValue("default-value-prop"); - SGPropertyNode_ptr node = fgGetNode(name, true); - object->setValue(node->getStringValue()); - _propertyObjects.push_back(PropertyObject(object, node)); - } - if (props->hasValue("action")) { - string action = props->getStringValue("action"); - if (action == "update") - object->setCallback(update_callback); - else if (action == "close") - object->setCallback(close_callback); - else if (action == "apply") - object->setCallback(apply_callback); - else if (action == "close-apply") - object->setCallback(close_apply_callback); - else - SG_LOG(SG_GENERAL, SG_ALERT, "Unknown GUI action " + action); + +//////////////////////////////////////////////////////////////////////// +// FGFontCache class. +//////////////////////////////////////////////////////////////////////// + +namespace +{ +struct GuiFont +{ + const char *name; + puFont *font; + struct Predicate + : public std::unary_function + { + Predicate(const char* name_) : name(name_) {} + bool operator() (const GuiFont& f1) const + { + return std::strcmp(f1.name, name) == 0; + } + const char* name; + }; +}; + +const GuiFont guifonts[] = { + { "default", &FONT_HELVETICA_14 }, + { "FIXED_8x13", &PUFONT_8_BY_13 }, + { "FIXED_9x15", &PUFONT_9_BY_15 }, + { "TIMES_10", &PUFONT_TIMES_ROMAN_10 }, + { "TIMES_24", &PUFONT_TIMES_ROMAN_24 }, + { "HELVETICA_10", &PUFONT_HELVETICA_10 }, + { "HELVETICA_12", &PUFONT_HELVETICA_12 }, + { "HELVETICA_14", &FONT_HELVETICA_14 }, + { "HELVETICA_18", &PUFONT_HELVETICA_18 }, + { "SANS_12B", &FONT_SANS_12B } +}; + +const GuiFont* guifontsEnd = &guifonts[sizeof(guifonts)/ sizeof(guifonts[0])]; +} + +FGFontCache::FGFontCache() : + _initialized(false) +{ +} + +FGFontCache::~FGFontCache() +{ + PuFontMap::iterator it, end = _puFonts.end(); + for (it = _puFonts.begin(); it != end; ++it) + delete it->second; +} + +inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1, + const FntParams& f2) const +{ + int comp = f1.name.compare(f2.name); + if (comp < 0) + return true; + else if (comp > 0) + return false; + if (f1.size < f2.size) + return true; + else if (f1.size > f2.size) + return false; + return f1.slant < f2.slant; +} + +struct FGFontCache::fnt * +FGFontCache::getfnt(const char *name, float size, float slant) +{ + string fontName(name); + FntParams fntParams(fontName, size, slant); + PuFontMap::iterator i = _puFonts.find(fntParams); + if (i != _puFonts.end()) + return i->second; + // fntTexFont s are all preloaded into the _texFonts map + TexFontMap::iterator texi = _texFonts.find(fontName); + fntTexFont* texfont = 0; + puFont* pufont = 0; + if (texi != _texFonts.end()) { + texfont = texi->second; + } else { + const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd, + GuiFont::Predicate(name)); + if (guifont != guifontsEnd) { + pufont = guifont->font; + } + } + fnt* f = new fnt; + if (pufont) { + f->pufont = pufont; + } else if (texfont) { + f->texfont = texfont; + f->pufont = new puFont; + f->pufont->initialize(static_cast(f->texfont), size, slant); + } else { + f->pufont = guifonts[0].font; } + _puFonts[fntParams] = f; + return f; +} - object->makeReturnDefault(props->getBoolValue("default")); +puFont * +FGFontCache::get(const char *name, float size, float slant) +{ + return getfnt(name, size, slant)->pufont; } -void -NewGUI::setupGroup (puGroup * group, SGPropertyNode * props, - int width, int height, bool makeFrame) +fntTexFont * +FGFontCache::getTexFont(const char *name, float size, float slant) { - setupObject(group, props); + return getfnt(name, size, slant)->texfont; +} + +puFont * +FGFontCache::get(SGPropertyNode *node) +{ + if (!node) + return get("Helvetica.txf", 15.0, 0.0); - if (makeFrame) - new puFrame(0, 0, width, height); + const char *name = node->getStringValue("name", "Helvetica.txf"); + float size = node->getFloatValue("size", 15.0); + float slant = node->getFloatValue("slant", 0.0); + + return get(name, size, slant); +} - int nChildren = props->nChildren(); - for (int i = 0; i < nChildren; i++) - makeObject(props->getChild(i), width, height); - group->close(); +void FGFontCache::init() +{ + if (!_initialized) { + char *envp = ::getenv("FG_FONTS"); + if (envp != NULL) { + _path.set(envp); + } else { + _path.set(globals->get_fg_root()); + _path.append("Fonts"); + } + _initialized = true; + } } -NewGUI::PropertyObject::PropertyObject (puObject * o, SGPropertyNode_ptr n) - : object(o), - node(n) +SGPath +FGFontCache::getfntpath(const char *name) { + init(); + SGPath path(_path); + if (name && std::string(name) != "") { + path.append(name); + if (path.exists()) + return path; + } + + path = SGPath(_path); + path.append("Helvetica.txf"); + + return path; +} + +bool FGFontCache::initializeFonts() +{ + static string fontext("txf"); + init(); + ulDir* fontdir = ulOpenDir(_path.c_str()); + if (!fontdir) + return false; + const ulDirEnt *dirEntry; + while ((dirEntry = ulReadDir(fontdir)) != 0) { + SGPath path(_path); + path.append(dirEntry->d_name); + if (path.extension() == fontext) { + fntTexFont* f = new fntTexFont; + if (f->load((char *)path.c_str())) + _texFonts[string(dirEntry->d_name)] = f; + else + delete f; + } + } + ulCloseDir(fontdir); + return true; } // end of new_gui.cxx