]> git.mxchange.org Git - flightgear.git/blobdiff - src/GUI/new_gui.cxx
Launcher: Maintain aircraft selection better
[flightgear.git] / src / GUI / new_gui.cxx
index b8834fdcaed7924a5ba4a7e7aaf64efe9fd91c50..bb8eb52dba624941d88c664b9c1f198935811ff4 100644 (file)
@@ -19,6 +19,7 @@
 #include <simgear/misc/sg_dir.hxx>
 
 #include <boost/algorithm/string/case_conv.hpp>
+#include <boost/foreach.hpp>
 
 #include <Main/fg_props.hxx>
 
 #include "GL/glx.h"
 #endif
 
-#include "menubar.hxx"
-#include "dialog.hxx"
+#include "FGPUIMenuBar.hxx"
 
-extern puFont FONT_HELVETICA_14;
-extern puFont FONT_SANS_12B;
+#if defined(SG_MAC)
+#include "FGCocoaMenuBar.hxx"
+#endif
+
+#if defined(SG_WINDOWS)
+#include "FGWindowsMenuBar.hxx"
+#endif
 
+#include "FGPUIDialog.hxx"
+#include "FGFontCache.hxx"
+#include "FGColor.hxx"
 
+// ignore the word Navaid here, it's a DataCache
+#include <Navaids/NavDataCache.hxx>
+
+using std::map;
+using std::string;
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of NewGUI.
 ////////////////////////////////////////////////////////////////////////
 
 
 
-NewGUI::NewGUI ()
-    : _menubar(new FGMenuBar),
-      _active_dialog(0)
+NewGUI::NewGUI () :
+  _active_dialog(0)
 {
 }
 
 NewGUI::~NewGUI ()
 {
-    delete _menubar;
-    _dialog_props.clear();
     for (_itt_t it = _colors.begin(); it != _colors.end(); ++it)
         delete it->second;
 }
@@ -58,12 +67,38 @@ NewGUI::~NewGUI ()
 void
 NewGUI::init ()
 {
+    createMenuBarImplementation();
+    fgTie("/sim/menubar/visibility", this,
+          &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
+    
     setStyle();
     SGPath p(globals->get_fg_root(), "gui/dialogs");
     readDir(p);
+    
+    SGPath aircraftDialogDir(string(fgGetString("/sim/aircraft-dir")), "gui/dialogs");
+    if (aircraftDialogDir.exists()) {
+        readDir(aircraftDialogDir);
+    }
+    
+    // Fix for http://code.google.com/p/flightgear-bugs/issues/detail?id=947
+    fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true);
     _menubar->init();
 }
 
+void
+NewGUI::shutdown()
+{
+    DialogDict::iterator it = _active_dialogs.begin();
+    for (; it != _active_dialogs.end(); ++it) {
+        delete it->second;
+    }
+    _active_dialogs.clear();
+    
+    fgUntie("/sim/menubar/visibility");
+    _menubar.reset();
+    _dialog_props.clear();
+}
+
 void
 NewGUI::reinit ()
 {
@@ -77,55 +112,72 @@ NewGUI::redraw ()
     reset(false);
 }
 
+void
+NewGUI::createMenuBarImplementation()
+{
+#if defined(SG_MAC)
+    if (fgGetBool("/sim/menubar/native", true)) {
+        _menubar.reset(new FGCocoaMenuBar);
+    }
+#endif
+#if defined(SG_WINDOWS)
+       if (fgGetBool("/sim/menubar/native", true)) {
+       // Windows-native menubar disabled for the moment, fall-through
+       // to PUI version
+   //     _menubar.reset(new FGWindowsMenuBar);
+    }
+#endif
+    if (!_menubar.get()) {
+        _menubar.reset(new FGPUIMenuBar);
+    }
+}
+
 void
 NewGUI::reset (bool reload)
 {
-    map<string,FGDialog *>::iterator iter;
-    vector<string> dlg;
+    DialogDict::iterator iter;
+    string_list openDialogs;
     // close all open dialogs and remember them ...
     for (iter = _active_dialogs.begin(); iter != _active_dialogs.end(); ++iter)
-        dlg.push_back(iter->first);
+        openDialogs.push_back(iter->first);
 
-    unsigned int i;
-    for (i = 0; i < dlg.size(); i++)
-        closeDialog(dlg[i]);
+    BOOST_FOREACH(string d, openDialogs)
+        closeDialog(d);
 
     setStyle();
 
     unbind();
-    delete _menubar;
-    _menubar = new FGMenuBar;
 
     if (reload) {
         _dialog_props.clear();
+        _dialog_names.clear();
         init();
     } else {
+        createMenuBarImplementation();
         _menubar->init();
     }
 
     bind();
 
     // open dialogs again
-    for (i = 0; i < dlg.size(); i++)
-        showDialog(dlg[i]);
+    BOOST_FOREACH(string d, openDialogs)
+        showDialog(d);
 }
 
 void
 NewGUI::bind ()
 {
-    fgTie("/sim/menubar/visibility", this,
-          &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
 }
 
 void
 NewGUI::unbind ()
 {
-    fgUntie("/sim/menubar/visibility");
 }
 
 void
 NewGUI::update (double delta_time_sec)
 {
+    SG_UNUSED(delta_time_sec);
     map<string,FGDialog *>::iterator iter = _active_dialogs.begin();
     for(/**/; iter != _active_dialogs.end(); iter++)
         iter->second->update();
@@ -134,14 +186,18 @@ NewGUI::update (double delta_time_sec)
 bool
 NewGUI::showDialog (const string &name)
 {
-    if (_dialog_props.find(name) == _dialog_props.end()) {
+    // first, check if it's already shown
+    if (_active_dialogs.find(name) != _active_dialogs.end())
+      return true;
+  
+    // check we know about the dialog by name
+    if (_dialog_names.find(name) == _dialog_names.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;
     }
+    
+    _active_dialogs[name] = new FGPUIDialog(getDialogProperties(name));
+    return true;
 }
 
 bool
@@ -183,11 +239,27 @@ NewGUI::closeDialog (const string& name)
 SGPropertyNode_ptr
 NewGUI::getDialogProperties (const string &name)
 {
-    if(_dialog_props.find(name) != _dialog_props.end())
-        return _dialog_props[name];
+    if (_dialog_names.find(name) == _dialog_names.end()) {
+      SG_LOG(SG_GENERAL, SG_ALERT, "Dialog " << name << " not defined");
+      return NULL;
+    }
+  
+    NameDialogDict::iterator it = _dialog_props.find(name);
+    if (it == _dialog_props.end()) {
+      // load the XML
+      SGPath path = _dialog_names[name];
+      SGPropertyNode_ptr props = new SGPropertyNode;
+      try {
+        readProperties(path.str(), props);
+      } catch (const sg_exception &) {
+        SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog " << path);
+        return NULL;
+      }
+      
+      it = _dialog_props.insert(it, std::make_pair(name, props));
+    }
 
-    SG_LOG(SG_GENERAL, SG_DEBUG, "dialog '" << name << "' missing");
-    return 0;
+    return it->second;
 }
 
 FGDialog *
@@ -215,7 +287,7 @@ NewGUI::getActiveDialog ()
 FGMenuBar *
 NewGUI::getMenuBar ()
 {
-    return _menubar;
+    return _menubar.get();
 }
 
 bool
@@ -242,40 +314,62 @@ NewGUI::newDialog (SGPropertyNode* props)
         return;
     }
     string name = cname;
-    if(_active_dialogs.find(name) == _active_dialogs.end())
+  
+    if(_active_dialogs.find(name) == _active_dialogs.end()) {
         _dialog_props[name] = props;
+    // add a dummy path entry, so we believe the dialog exists
+        _dialog_names[name] = SGPath();
+    }
 }
 
 void
 NewGUI::readDir (const SGPath& path)
 {
     simgear::Dir dir(path);
+    if( !dir.exists() )
+    {
+      SG_LOG(SG_INPUT, SG_INFO, "directory does not exist: " << path.str());
+      return;
+    }
+
+    flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+    flightgear::NavDataCache::Transaction txn(cache);
     simgear::PathList xmls = dir.children(simgear::Dir::TYPE_FILE, ".xml");
     
-    for (unsigned int i=0; i<xmls.size(); ++i) {
-      SGPropertyNode * props = new SGPropertyNode;
+    BOOST_FOREACH(SGPath xmlPath, xmls) {
+      if (!cache->isCachedFileModified(xmlPath)) {
+        // cached, easy
+        string name = cache->readStringProperty(xmlPath.str());
+        _dialog_names[name] = xmlPath;
+        continue;
+      }
+      
+    // we need to parse the actual XML
+      SGPropertyNode_ptr props = new SGPropertyNode;
       try {
-          readProperties(xmls[i].str(), props);
+        readProperties(xmlPath.str(), props);
       } catch (const sg_exception &) {
-          SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog "
-                 << xmls[i].str());
-          delete props;
-          continue;
+        SG_LOG(SG_INPUT, SG_ALERT, "Error parsing dialog " << xmlPath);
+        continue;
       }
+      
       SGPropertyNode *nameprop = props->getNode("name");
       if (!nameprop) {
-          SG_LOG(SG_INPUT, SG_WARN, "dialog " << xmls[i].str()
-             << " has no name; skipping.");
-          delete props;
-          continue;
+        SG_LOG(SG_INPUT, SG_WARN, "dialog " << xmlPath << " has no name; skipping.");
+        continue;
       }
+      
       string name = nameprop->getStringValue();
-      _dialog_props[name] = props;
-    }
-}
-
-
-\f
+      _dialog_names[name] = xmlPath;
+    // update cached values
+        if (!cache->isReadOnly()) {
+            cache->stampCacheFile(xmlPath);
+            cache->writeStringProperty(xmlPath.str(), name);
+        }
+    } // of directory children iteration
+  
+    txn.commit();
+}\f
 ////////////////////////////////////////////////////////////////////////
 // Style handling.
 ////////////////////////////////////////////////////////////////////////
@@ -321,252 +415,9 @@ NewGUI::setStyle (void)
 void
 NewGUI::setupFont (SGPropertyNode *node)
 {
-    _font = globals->get_fontcache()->get(node);
+    _font = FGFontCache::instance()->get(node);
     puSetDefaultFonts(*_font, *_font);
     return;
 }
 
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// FGColor class.
-////////////////////////////////////////////////////////////////////////
-
-void
-FGColor::print() const {
-    std::cerr << "red=" << _red << ", green=" << _green
-              << ", blue=" << _blue << ", alpha=" << _alpha << std::endl;
-}
-
-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;
-}
-
-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;
-}
-
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// FGFontCache class.
-////////////////////////////////////////////////////////////////////////
-
-namespace
-{
-struct GuiFont
-{
-    const char *name;
-    puFont *font;
-    struct Predicate
-        : public std::unary_function<const GuiFont, bool>
-    {
-        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()
-{
-#if defined(SG_UNIX) && !defined(SG_MAC) 
-   // Ugly workaround for a crash on exit with multiple screens configured
-   if (!glXGetCurrentContext())
-      return;
-#endif
-   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 = boost::to_lower_copy(string(name));
-    FntParams fntParams(fontName, size, slant);
-    PuFontMap::iterator i = _puFonts.find(fntParams);
-    if (i != _puFonts.end()) {
-        // found in the puFonts map, all done
-        return i->second;
-    }
-    
-    // fntTexFont s are all preloaded into the _texFonts map
-    TexFontMap::iterator texi = _texFonts.find(fontName);
-    fntTexFont* texfont = NULL;
-    puFont* pufont = NULL;
-    if (texi != _texFonts.end()) {
-        texfont = texi->second;
-    } else {
-        // check the built-in PUI fonts (in guifonts array)
-        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<fntFont *>(f->texfont), size, slant);
-    } else {
-        f->pufont = guifonts[0].font;
-    }
-    _puFonts[fntParams] = f;
-    return f;
-}
-
-puFont *
-FGFontCache::get(const char *name, float size, float slant)
-{
-    return getfnt(name, size, slant)->pufont;
-}
-
-fntTexFont *
-FGFontCache::getTexFont(const char *name, float size, float slant)
-{
-    return getfnt(name, size, slant)->texfont;
-}
-
-puFont *
-FGFontCache::get(SGPropertyNode *node)
-{
-    if (!node)
-        return get("Helvetica.txf", 15.0, 0.0);
-
-    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) {
-        return;
-    }
-    
-    char *envp = ::getenv("FG_FONTS");
-    if (envp != NULL) {
-        _path.set(envp);
-    } else {
-        _path.set(globals->get_fg_root());
-        _path.append("Fonts");
-    }
-    _initialized = true;
-}
-
-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");
-    SG_LOG(SG_GENERAL, SG_WARN, "Unknown font name '" << name << "', defaulting to Helvetica");
-    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())) {
-                // convert font names in the map to lowercase for matching
-                string fontName = boost::to_lower_copy(string(dirEntry->d_name));
-                _texFonts[fontName] = f;
-            } else
-                delete f;
-        }
-    }
-    ulCloseDir(fontdir);
-    return true;
-}
-
 // end of new_gui.cxx