X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FGUI%2Fnew_gui.cxx;h=ff9c7ad3c337c370a9ed1d79f66e50589fb83778;hb=1ae2b59333dc914d10bcc726bc94e71cbf3e411e;hp=57fad0403a564a40dc1736942a266551a5d88ced;hpb=011118315322b4b3186d30fed4ce368ff4430c04;p=flightgear.git diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index 57fad0403..ff9c7ad3c 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -1,349 +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" - -//////////////////////////////////////////////////////////////////////// -// Callbacks. -//////////////////////////////////////////////////////////////////////// +extern puFont FONT_HELVETICA_14; +extern puFont FONT_SANS_12B; -/** - * Action callback. - */ -static void -action_callback (puObject * object) -{ - GUIInfo * info = (GUIInfo *)object->getUserData(); - NewGUI * gui = - (NewGUI *)globals->get_subsystem_mgr() - ->get_group(FGSubsystemMgr::INIT)->get_subsystem("gui"); - gui->setCurrentWidget(info->widget); - for (int i = 0; i < info->bindings.size(); i++) { - std::cerr << "Firing binding " << i << ": " << info->bindings[i]->getCommandName() << std::endl; - info->bindings[i]->fire(); - std::cerr << "done\n"; - } - std::cerr << "All bindings fired\n"; - gui->setCurrentWidget(0); -} //////////////////////////////////////////////////////////////////////// -// Implementation of GUIInfo. +// Implementation of NewGUI. //////////////////////////////////////////////////////////////////////// -GUIInfo::GUIInfo (GUIWidget * w) - : widget(w) + + +NewGUI::NewGUI () + : _menubar(new FGMenuBar), + _active_dialog(0) { } -GUIInfo::~GUIInfo () +NewGUI::~NewGUI () { - for (int i = 0; i < bindings.size(); i++) { - delete bindings[i]; - bindings[i] = 0; - } + delete _menubar; + _dialog_props.clear(); + for (_itt_t it = _colors.begin(); it != _colors.end(); ++it) + delete it->second; } +void +NewGUI::init () +{ + setStyle(); + char path1[1024]; + char path2[1024]; + ulMakePath(path1, globals->get_fg_root().c_str(), "gui"); + ulMakePath(path2, path1, "dialogs"); + readDir(path2); + _menubar->init(); +} - -//////////////////////////////////////////////////////////////////////// -// Implementation of GUIWidget. -//////////////////////////////////////////////////////////////////////// +void +NewGUI::reinit () +{ + reset(true); + fgSetBool("/sim/signals/reinit-gui", true); +} -GUIWidget::GUIWidget (SGPropertyNode_ptr props) - : _object(0) +void +NewGUI::redraw () { - display(props); + reset(false); } -GUIWidget::~GUIWidget () +void +NewGUI::reset (bool reload) { - std::cerr << "Destroying widget\n"; - delete _object; + 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); - int i; - for (i = 0; i < _info.size(); i++) { - delete _info[i]; - _info[i] = 0; - } + unsigned int i; + for (i = 0; i < dlg.size(); i++) + closeDialog(dlg[i]); + + setStyle(); - for (i = 0; i < _propertyObjects.size(); i++) { - delete _propertyObjects[i]; - _propertyObjects[i] = 0; + 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 -GUIWidget::updateValue (const char * objectName) +NewGUI::bind () { - for (int i = 0; i < _propertyObjects.size(); i++) { - if (_propertyObjects[i]->name == objectName) - _propertyObjects[i]->object - ->setValue(_propertyObjects[i]->node->getStringValue()); - } + fgTie("/sim/menubar/visibility", this, + &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible); } void -GUIWidget::applyValue (const char * objectName) +NewGUI::unbind () { - for (int i = 0; i < _propertyObjects.size(); i++) { - if (_propertyObjects[i]->name == objectName) - _propertyObjects[i]->node - ->setStringValue(_propertyObjects[i] - ->object->getStringValue()); - } + fgUntie("/sim/menubar/visibility"); } void -GUIWidget::updateValues () +NewGUI::update (double delta_time_sec) { - for (int i = 0; i < _propertyObjects.size(); i++) { - puObject * object = _propertyObjects[i]->object; - SGPropertyNode_ptr node = _propertyObjects[i]->node; - object->setValue(node->getStringValue()); - } + map::iterator iter = _active_dialogs.begin(); + for(/**/; iter != _active_dialogs.end(); iter++) + iter->second->update(); } -void -GUIWidget::applyValues () +bool +NewGUI::showDialog (const string &name) { - for (int i = 0; i < _propertyObjects.size(); i++) { - puObject * object = _propertyObjects[i]->object; - SGPropertyNode_ptr node = _propertyObjects[i]->node; - node->setStringValue(object->getStringValue()); + 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; } } -void -GUIWidget::display (SGPropertyNode_ptr props) +bool +NewGUI::closeActiveDialog () { - if (_object != 0) { - SG_LOG(SG_GENERAL, SG_ALERT, "This widget is already active"); - return; + 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; + } } - _object = makeObject(props, 1024, 768); + delete _active_dialog; + _active_dialog = 0; + return true; +} - if (_object != 0) { - _object->reveal(); - } else { - SG_LOG(SG_GENERAL, SG_ALERT, "Widget " - << props->getStringValue("name", "[unnamed]") - << " does not contain a proper GUI definition"); +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; } -puObject * -GUIWidget::makeObject (SGPropertyNode * props, int parentWidth, int parentHeight) +FGDialog * +NewGUI::getDialog (const string &name) { - int width = props->getIntValue("width", parentWidth); - int height = props->getIntValue("height", parentHeight); + if(_active_dialogs.find(name) != _active_dialogs.end()) + return _active_dialogs[name]; - int x = props->getIntValue("x", (parentWidth - width) / 2); - int y = props->getIntValue("y", (parentHeight - height) / 2); + SG_LOG(SG_GENERAL, SG_DEBUG, "dialog '" << name << "' missing"); + return 0; +} + +void +NewGUI::setActiveDialog (FGDialog * dialog) +{ + _active_dialog = dialog; +} - string type = props->getName(); - if (type == "") - type = props->getStringValue("type"); - if (type == "") { - SG_LOG(SG_GENERAL, SG_ALERT, "No type specified for GUI object"); - return 0; +FGDialog * +NewGUI::getActiveDialog () +{ + return _active_dialog; +} + +FGMenuBar * +NewGUI::getMenuBar () +{ + return _menubar; +} + +bool +NewGUI::getMenuBarVisible () const +{ + return _menubar->isVisible(); +} + +void +NewGUI::setMenuBarVisible (bool visible) +{ + if (visible) + _menubar->show(); + else + _menubar->hide(); +} + +static bool +test_extension (const char * path, const char * ext) +{ + 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; +} - 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; +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; } void -GUIWidget::setupObject (puObject * object, SGPropertyNode * props) -{ - if (props->hasValue("legend")) - object->setLegend(props->getStringValue("legend")); - - if (props->hasValue("label")) - object->setLabel(props->getStringValue("label")); - - if (props->hasValue("property")) { - const char * name = props->getStringValue("name"); - if (name == 0) - name = ""; - const char * propname = props->getStringValue("property"); - SGPropertyNode_ptr node = fgGetNode(propname, true); - object->setValue(node->getStringValue()); - if (name != 0) - _propertyObjects.push_back(new PropertyObject(name, object, node)); +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; } - vector nodes = props->getChildren("binding"); - if (nodes.size() > 0) { - GUIInfo * info = new GUIInfo(this); + for (ulDirEnt * dirEnt = ulReadDir(dir); + dirEnt != 0; + dirEnt = ulReadDir(dir)) { - for (int i = 0; i < nodes.size(); i++) - info->bindings.push_back(new FGBinding(nodes[i])); - object->setCallback(action_callback); - object->setUserData(info); - _info.push_back(info); + 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. +//////////////////////////////////////////////////////////////////////// - object->makeReturnDefault(props->getBoolValue("default")); +void +NewGUI::setStyle (void) +{ + _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 -GUIWidget::setupGroup (puGroup * group, SGPropertyNode * props, - int width, int height, bool makeFrame) +NewGUI::setupFont (SGPropertyNode *node) { - setupObject(group, props); + _font = globals->get_fontcache()->get(node); + puSetDefaultFonts(*_font, *_font); + return; +} + + - if (makeFrame) - new puFrame(0, 0, width, height); + +//////////////////////////////////////////////////////////////////////// +// FGColor class. +//////////////////////////////////////////////////////////////////////// + +void +FGColor::print() const { + std::cerr << "red=" << _red << ", green=" << _green + << ", blue=" << _blue << ", alpha=" << _alpha << std::endl; +} - int nChildren = props->nChildren(); - for (int i = 0; i < nChildren; i++) - makeObject(props->getChild(i), width, height); - group->close(); +bool +FGColor::merge(const SGPropertyNode *node) +{ + 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; } -GUIWidget::PropertyObject::PropertyObject (const char * n, - puObject * o, - SGPropertyNode_ptr p) - : name(n), - object(o), - node(p) +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; } + //////////////////////////////////////////////////////////////////////// -// Implementation of NewGUI. +// FGFontCache class. //////////////////////////////////////////////////////////////////////// - -NewGUI::NewGUI () - : _current_widget(0) +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])]; } -NewGUI::~NewGUI () +FGFontCache::FGFontCache() : + _initialized(false) { } -void -NewGUI::init () +FGFontCache::~FGFontCache() { - char path[1024]; - ulMakePath(path, getenv("FG_ROOT"), "gui"); - readDir(path); + PuFontMap::iterator it, end = _puFonts.end(); + for (it = _puFonts.begin(); it != end; ++it) + delete it->second; } -void -NewGUI::update (double delta_time_sec) +inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1, + const FntParams& f2) const { - // NO OP + 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; } -void -NewGUI::display (const string &name) +struct FGFontCache::fnt * +FGFontCache::getfnt(const char *name, float size, float slant) { - if (_widgets.find(name) == _widgets.end()) - SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined"); - else - new GUIWidget(_widgets[name]); + 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; } -void -NewGUI::setCurrentWidget (GUIWidget * widget) +puFont * +FGFontCache::get(const char *name, float size, float slant) { - _current_widget = widget; + return getfnt(name, size, slant)->pufont; } -GUIWidget * -NewGUI::getCurrentWidget () +fntTexFont * +FGFontCache::getTexFont(const char *name, float size, float slant) { - return _current_widget; + return getfnt(name, size, slant)->texfont; } -void -NewGUI::readDir (const char * path) +puFont * +FGFontCache::get(SGPropertyNode *node) { - ulDir * dir = ulOpenDir(path); + if (!node) + return get("Helvetica.txf", 15.0, 0.0); - if (dir == 0) { - SG_LOG(SG_GENERAL, SG_ALERT, "Failed to read GUI files from " - << path); - return; + 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); +} + +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; } +} - ulDirEnt * dirEnt = ulReadDir(dir); - while (dirEnt != 0) { - char subpath[1024]; +SGPath +FGFontCache::getfntpath(const char *name) +{ + init(); + SGPath path(_path); + if (name && std::string(name) != "") { + path.append(name); + if (path.exists()) + return path; + } - ulMakePath(subpath, path, dirEnt->d_name); + path = SGPath(_path); + path.append("Helvetica.txf"); + + return path; +} - if (dirEnt->d_isdir && dirEnt->d_name[0] != '.') { - readDir(subpath); - } else { - SGPropertyNode_ptr props = new SGPropertyNode; - try { - readProperties(subpath, props); - } catch (const sg_exception &ex) { - SG_LOG(SG_INPUT, SG_ALERT, "Error parsing GUI file " - << subpath); - } - if (!props->hasValue("name")) { - SG_LOG(SG_INPUT, SG_WARN, "GUI file " << subpath - << " has no name; skipping."); - } else { - string name = props->getStringValue("name"); - SG_LOG(SG_INPUT, SG_BULK, "Saving GUI node " << name); - _widgets[name] = props; - } +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; } - dirEnt = ulReadDir(dir); } - ulCloseDir(dir); + ulCloseDir(fontdir); + return true; } // end of new_gui.cxx