]> git.mxchange.org Git - flightgear.git/commitdiff
allow disabling/enabling of menu entries via "enabled" property;
authormfranz <mfranz>
Tue, 6 Dec 2005 17:56:17 +0000 (17:56 +0000)
committermfranz <mfranz>
Tue, 6 Dec 2005 17:56:17 +0000 (17:56 +0000)
Unfortunately, we don't have an easy way to access the puObjects
only by knowing the respective XML property node, because the
menu structure was built by plib from string lists. That's why
we walk the puMenuBar tree and store {property node}->{puObject*}
pairs in a map. With this infrastructure in place we can now
easily enable/disable entries, but we can also make other changes
to menu buttons as we see need. The structure of puMenuBar is
described in the pui documentation, so it's less of a hack than
it looks.  :-)

src/GUI/menubar.cxx
src/GUI/menubar.hxx

index c31c34ee35d98c120a0aaca2a3accae3ed855117..f500e43423d4b5293e0c1650903bb1745dd36a45 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <string.h>
 #include <iostream>
+#include <sstream>
 #include <plib/pu.h>
 #include <simgear/debug/logstream.hxx>
 
@@ -302,6 +303,8 @@ FGMenuBar::make_menubar(const SGPropertyNode * props)
         make_menu(menu_nodes[i]);
 
     _menuBar->close();
+    make_map(props);
+
     if (_visible)
         _menuBar->reveal();
     else
@@ -349,6 +352,104 @@ FGMenuBar::destroy_menubar ()
     SG_LOG(SG_GENERAL, SG_INFO, "Done.");
 }
 
+struct EnabledListener : SGPropertyChangeListener {
+    void valueChanged(SGPropertyNode* node) {
+        NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
+        if (!gui)
+            return;
+        FGMenuBar *menubar = gui->getMenuBar();
+        if (menubar)
+            menubar->enable_item(node->getParent(), node->getBoolValue());
+    }
+};
+
+void
+FGMenuBar::add_enabled_listener(SGPropertyNode * node)
+{
+    if (!node->hasValue("enabled"))
+        node->setBoolValue("enabled", true);
+
+    enable_item(node, node->getBoolValue("enabled"));
+    node->getNode("enabled")->addChangeListener(new EnabledListener());
+}
+
+void
+FGMenuBar::make_map(const SGPropertyNode * node)
+{
+    string base = node->getPath();
+
+    int menu_index = 0;
+    for (puObject *obj = ((puGroup *)_menuBar)->getFirstChild();
+            obj; obj = obj->getNextObject()) {
+
+        // skip puPopupMenus. They are also children of _menuBar,
+        // but we access them via getUserData()  (see below)
+        if (!(obj->getType() & PUCLASS_ONESHOT))
+            continue;
+
+        if (!obj->getLegend())
+            continue;
+
+        std::ostringstream menu;
+        menu << base << "/menu[" << menu_index << "]";
+        SGPropertyNode *prop = fgGetNode(menu.str().c_str());
+        if (!prop) {
+            SG_LOG(SG_GENERAL, SG_WARN, "menu without node: " << menu.str());
+            continue;
+        }
+
+        // push "menu" entry
+        _entries[prop->getPath()] = obj;
+        add_enabled_listener(prop);
+
+        // push "item" entries
+        puPopupMenu *popup = (puPopupMenu *)obj->getUserData();
+        if (!popup)
+            continue;
+
+        // the entries are for some reason reversed (last first), and we
+        // don't know yet how many will be usable; so we collect first
+        vector<puObject *> e;
+        for (puObject *me = ((puGroup *)popup)->getFirstChild();
+                me; me = me->getNextObject()) {
+
+            if (!me->getLegend())
+                continue;
+
+            e.push_back(me);
+        }
+
+        for (unsigned int i = 0; i < e.size(); i++) {
+            std::ostringstream item;
+            item << menu.str() << "/item[" << (e.size() - i - 1) << "]";
+            prop = fgGetNode(item.str().c_str());
+            if (!prop) {
+                SG_LOG(SG_GENERAL, SG_WARN, "item without node: " << item.str());
+                continue;
+            }
+            _entries[prop->getPath()] = e[i];
+            add_enabled_listener(prop);
+        }
+        menu_index++;
+    }
+}
+
+bool
+FGMenuBar::enable_item(const SGPropertyNode * node, bool state)
+{
+    if (!node || _entries.find(node->getPath()) == _entries.end()) {
+        SG_LOG(SG_GENERAL, SG_WARN, "Trying to enable/disable "
+            "non-existent menu item");
+        return false;
+    }
+    puObject *object = _entries[node->getPath()];
+    if (state)
+        object->activate();
+    else
+        object->greyOut();
+
+    return true;
+}
 
 char **
 FGMenuBar::make_char_array (int size)
index 6c7217786f207719af39158f01b70af18b800813..0c77c3af8635e03580aaa9674adf083375043917 100644 (file)
@@ -95,6 +95,11 @@ public:
      */
     void destroy_menubar ();
 
+    /**
+     * Disable/enable menu titles and entries
+     */
+    bool enable_item (const SGPropertyNode * item, bool state);
+
 
 private:
 
@@ -104,6 +109,12 @@ private:
     // Make the top-level menubar.
     void make_menubar ();
 
+    // Create a property-path -> puObject map for menu node
+    void make_map(const SGPropertyNode * node);
+
+    // Add <enabled> listener that enables/disabled menu entries.
+    void add_enabled_listener(SGPropertyNode * node);
+
     // Is the menu visible?
     bool _visible;
 
@@ -121,6 +132,8 @@ private:
     puCallback * make_callback_array (int size);
     vector<char **> _char_arrays;
     vector<puCallback *> _callback_arrays;
+
+    map<string, puObject *> _entries;
 };
 
 #endif // __MENUBAR_HXX