]> git.mxchange.org Git - flightgear.git/commitdiff
And make FGMenuBar abstract too, sinking the current implementation into FGPUIMenubar
authorJames Turner <zakalawe@mac.com>
Sat, 19 Nov 2011 22:04:35 +0000 (22:04 +0000)
committerJames Turner <zakalawe@mac.com>
Sat, 19 Nov 2011 22:04:35 +0000 (22:04 +0000)
src/GUI/CMakeLists.txt
src/GUI/FGPUIMenuBar.cxx [new file with mode: 0644]
src/GUI/FGPUIMenuBar.hxx [new file with mode: 0644]
src/GUI/menubar.cxx
src/GUI/menubar.hxx
src/GUI/new_gui.cxx
src/GUI/new_gui.hxx

index 6fd3f47043a918105b48b6e21a7d245f182b9e2a..030416ef0f6c70ce978ffad6bfa400595d746074 100644 (file)
@@ -13,6 +13,7 @@ set(SOURCES
        layout-props.cxx
        layout.cxx
        menubar.cxx
+       FGPUIMenuBar.cxx
        new_gui.cxx
        property_list.cxx
        FGFontCache.cxx
@@ -29,6 +30,7 @@ set(HEADERS
        gui.h
        layout.hxx
        menubar.hxx
+       FGPUIMenuBar.hxx
        new_gui.hxx
        property_list.hxx
        FGFontCache.hxx
diff --git a/src/GUI/FGPUIMenuBar.cxx b/src/GUI/FGPUIMenuBar.cxx
new file mode 100644 (file)
index 0000000..a7d8ba0
--- /dev/null
@@ -0,0 +1,376 @@
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <iostream>
+#include <plib/pu.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/structure/SGBinding.hxx>
+#include <simgear/props/props_io.hxx>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+
+#include "new_gui.hxx"
+#include "FGPUIMenuBar.hxx"
+
+using std::vector;
+using std::string;
+using std::map;\f
+////////////////////////////////////////////////////////////////////////
+// FIXME!!
+//
+// Deprecated wrappers for old menu commands.
+//
+// DO NOT ADD TO THESE.  THEY WILL BE DELETED SOON!
+//
+// These are defined in gui_funcs.cxx.  They should be replaced with
+// user-configured dialogs and new commands where necessary.
+////////////////////////////////////////////////////////////////////////
+
+#if defined(TR_HIRES_SNAP)
+extern void dumpHiResSnapShot ();
+static bool
+do_hires_snapshot_dialog (const SGPropertyNode * arg)
+{
+    dumpHiResSnapShot();
+    return true;
+}
+#endif // TR_HIRES_SNAP
+
+static struct {
+    const char * name;
+    SGCommandMgr::command_t command;
+} deprecated_dialogs [] = {
+#if defined(TR_HIRES_SNAP)
+    { "old-hires-snapshot-dialog", do_hires_snapshot_dialog },
+#endif
+    { 0, 0 }
+};
+
+static void
+add_deprecated_dialogs ()
+{
+  SG_LOG(SG_GENERAL, SG_INFO, "Initializing old dialog commands:");
+  for (int i = 0; deprecated_dialogs[i].name != 0; i++) {
+    SG_LOG(SG_GENERAL, SG_INFO, "  " << deprecated_dialogs[i].name);
+    globals->get_commands()->addCommand(deprecated_dialogs[i].name,
+                                       deprecated_dialogs[i].command);
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Static functions.
+////////////////////////////////////////////////////////////////////////
+
+
+static void
+menu_callback (puObject * object)
+{
+    NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
+    FGPUIMenuBar* mb = static_cast<FGPUIMenuBar*>(gui->getMenuBar());
+    mb->fireItem(object);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGPUIMenuBar.
+////////////////////////////////////////////////////////////////////////
+
+
+FGPUIMenuBar::FGPUIMenuBar ()
+    : _visible(false),
+      _menuBar(0)
+{
+}
+
+FGPUIMenuBar::~FGPUIMenuBar ()
+{
+    destroy_menubar();
+}
+
+void
+FGPUIMenuBar::init ()
+{
+    delete _menuBar;            // FIXME: check if PUI owns the pointer
+    make_menubar();
+                                // FIXME: temporary commands to get at
+                                // old, hard-coded dialogs.
+    add_deprecated_dialogs();
+}
+
+void
+FGPUIMenuBar::show ()
+{
+    if (_menuBar != 0)
+        _menuBar->reveal();
+    _visible = true;
+}
+
+void
+FGPUIMenuBar::hide ()
+{
+    if (_menuBar != 0)
+        _menuBar->hide();
+    _visible = false;
+}
+
+bool
+FGPUIMenuBar::isVisible () const
+{
+    return _visible;
+}
+
+void
+FGPUIMenuBar::fireItem (puObject * item)
+{
+    const char * name = item->getLegend();
+    vector<SGBinding *> &bindings = _bindings[name];
+    int nBindings = bindings.size();
+
+    for (int i = 0; i < nBindings; i++)
+        bindings[i]->fire();
+}
+
+void
+FGPUIMenuBar::make_menu (SGPropertyNode * node)
+{
+    const char * name = strdup(node->getStringValue("label"));
+    vector<SGPropertyNode_ptr> item_nodes = node->getChildren("item");
+
+    int array_size = item_nodes.size();
+
+    char ** items = make_char_array(array_size);
+    puCallback * callbacks = make_callback_array(array_size);
+
+    for (unsigned int i = 0, j = item_nodes.size() - 1;
+         i < item_nodes.size();
+         i++, j--) {
+
+                                // Set up the PUI entries for this item
+        items[j] = strdup((char *)item_nodes[i]->getStringValue("label"));
+        callbacks[j] = menu_callback;
+
+                                // Load all the bindings for this item
+        vector<SGPropertyNode_ptr> bindings = item_nodes[i]->getChildren("binding");
+        SGPropertyNode * dest = fgGetNode("/sim/bindings/menu", true);
+
+        for (unsigned int k = 0; k < bindings.size(); k++) {
+            unsigned int m = 0;
+            SGPropertyNode_ptr binding;
+            while (dest->getChild("binding", m))
+                m++;
+
+            binding = dest->getChild("binding", m, true);
+            copyProperties(bindings[k], binding);
+            _bindings[items[j]].push_back(new SGBinding(binding, globals->get_props()));
+        }
+    }
+
+    _menuBar->add_submenu(name, items, callbacks);
+}
+
+void
+FGPUIMenuBar::make_menubar ()
+{
+    SGPropertyNode *targetpath;
+   
+    targetpath = fgGetNode("/sim/menubar/default",true);
+    // fgLoadProps("gui/menubar.xml", targetpath);
+    
+    /* NOTE: there is no check to see whether there's any usable data at all
+     *
+     * This would also have the advantage of being able to create some kind of
+     * 'fallback' menu - just in case that either menubar.xml is empty OR that
+     * its XML data is not valid, that way we would avoid displaying an
+     * unusable menubar without any functionality - if we decided to add another
+     * char * element to the commands structure in
+     *  $FG_SRC/src/Main/fgcommands.cxx 
+     * we could additionally save each function's (short) description and use
+     * this as label for the fallback PUI menubar item labels - as a workaround
+     * one might simply use the internal fgcommands and put them into the 
+     * fallback menu, so that the user is at least able to re-init the menu
+     * loading - just in case there was some malformed XML in it
+     * (it happend to me ...)
+     */
+    
+    make_menubar(targetpath);
+}
+
+/* WARNING: We aren't yet doing any validation of what's found - but since
+ * this isn't done with menubar.xml either, it should not really matter
+ * right now. Although one should later on consider to validate the
+ * contents, whether they are representing a 'legal' menubar structure.
+ */
+void
+FGPUIMenuBar::make_menubar(SGPropertyNode * props) 
+{    
+    // Just in case.
+    destroy_menubar();
+    _menuBar = new puMenuBar;
+
+    vector<SGPropertyNode_ptr> menu_nodes = props->getChildren("menu");
+    for (unsigned int i = 0; i < menu_nodes.size(); i++)
+        make_menu(menu_nodes[i]);
+
+    _menuBar->close();
+    make_object_map(props);
+
+    if (_visible)
+        _menuBar->reveal();
+    else
+        _menuBar->hide();
+}
+
+void
+FGPUIMenuBar::destroy_menubar ()
+{
+    if ( _menuBar == 0 )
+        return;
+
+    hide();
+    puDeleteObject(_menuBar);
+
+    unsigned int i;
+
+                                // Delete all the character arrays
+                                // we were forced to keep around for
+                                // plib.
+    SG_LOG(SG_GENERAL, SG_BULK, "Deleting char arrays");
+    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 callback arrays
+                                // we were forced to keep around for
+                                // plib.
+    SG_LOG(SG_GENERAL, SG_BULK, "Deleting callback arrays");
+    for (i = 0; i < _callback_arrays.size(); i++)
+        delete[] _callback_arrays[i];
+
+                                // Delete all those bindings
+    SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings");
+    map<string,vector<SGBinding *> >::iterator it;
+    for (it = _bindings.begin(); it != _bindings.end(); it++) {
+        SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings for " << it->first);
+        for ( i = 0; i < it->second.size(); i++ )
+            delete it->second[i];
+    }
+
+    SG_LOG(SG_GENERAL, SG_BULK, "Done.");
+}
+
+void
+FGPUIMenuBar::make_object_map(SGPropertyNode * node)
+{
+    unsigned int menu_index = 0;
+    vector<SGPropertyNode_ptr> menus = node->getChildren("menu");
+    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 (menu_index >= menus.size()) {
+            SG_LOG(SG_GENERAL, SG_WARN, "'menu' object without node: "
+                    << node->getPath() << "/menu[" << menu_index << ']');
+            return;
+        }
+
+        SGPropertyNode *menu = menus.at(menu_index);
+        _objects[menu->getPath()] = obj;
+        add_enabled_listener(menu);
+
+        puGroup *popup = (puGroup *)obj->getUserData();
+        if (!popup)
+            continue;
+
+        // the entries are for some reason reversed (last first), and we
+        // don't know yet how many there will be; so we collect first
+        vector<puObject *> e;
+        for (puObject *me = popup->getFirstChild(); me; me = me->getNextObject())
+            e.push_back(me);
+
+        vector<SGPropertyNode_ptr> items = menu->getChildren("item");
+        for (unsigned int i = 0; i < e.size(); i++) {
+            if (i >= items.size()) {
+                SG_LOG(SG_GENERAL, SG_WARN, "'item' object without node: "
+                        << menu->getPath() << "/item[" << i << ']');
+                break;
+            }
+            SGPropertyNode *item = items.at(e.size() - i - 1);
+            _objects[item->getPath()] = e[i];
+            add_enabled_listener(item);
+        }
+        menu_index++;
+    }
+}
+
+struct EnabledListener : SGPropertyChangeListener {
+    void valueChanged(SGPropertyNode *node) {
+        NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
+        if (!gui)
+            return;
+        FGPUIMenuBar* menubar = static_cast<FGPUIMenuBar*>(gui->getMenuBar());
+        if (menubar)
+            menubar->enable_item(node->getParent(), node->getBoolValue());
+    }
+};
+
+void
+FGPUIMenuBar::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());
+}
+
+bool
+FGPUIMenuBar::enable_item(const SGPropertyNode * node, bool state)
+{
+    string path = node->getPath();
+    if (_objects.find(path) == _objects.end()) {
+        SG_LOG(SG_GENERAL, SG_ALERT, "Trying to enable/disable "
+            "non-existent menu item for node `" << path << '\'');
+        return false;
+    }
+    puObject *object = _objects[path];
+    if (state)
+        object->activate();
+    else
+        object->greyOut();
+
+    return true;
+}
+
+char **
+FGPUIMenuBar::make_char_array (int size)
+{
+    char ** list = new char*[size+1];
+    for (int i = 0; i <= size; i++)
+        list[i] = 0;
+    _char_arrays.push_back(list);
+    return list;
+}
+
+puCallback *
+FGPUIMenuBar::make_callback_array (int size)
+{
+    puCallback * list = new puCallback[size+1];
+    for (int i = 0; i <= size; i++)
+        list[i] = 0;
+    _callback_arrays.push_back(list);
+    return list;
+}
+
+// end of menubar.cxx
diff --git a/src/GUI/FGPUIMenuBar.hxx b/src/GUI/FGPUIMenuBar.hxx
new file mode 100644 (file)
index 0000000..fa21c05
--- /dev/null
@@ -0,0 +1,138 @@
+// menubar.hxx - XML-configured menu bar.
+
+#ifndef FG_PUI_MENUBAR_HXX
+#define FG_PUI_MENUBAR_HXX 1
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+#include <GUI/menubar.hxx>
+
+#include <map>
+#include <vector>
+
+// forward decls, avoid pulling in PLIB headers here
+class puMenuBar;
+class puObject;
+class SGPropertyNode;
+class SGBinding;
+
+typedef void (*puCallback)(class puObject *) ;
+
+/**
+ * XML-configured PUI menu bar.
+ *
+ * This class creates a menu bar from a tree of XML properties.  These
+ * properties are not part of the main FlightGear property tree, but
+ * are read from a separate file ($FG_ROOT/gui/menubar.xml).
+ *
+ * WARNING: because PUI provides no easy way to attach user data to a
+ * menu item, all menu item strings must be unique; otherwise, this
+ * class will always use the first binding with any given name.
+ */
+class FGPUIMenuBar : public FGMenuBar
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    FGPUIMenuBar ();
+
+
+    /**
+     * Destructor.
+     */
+    virtual ~FGPUIMenuBar ();
+
+
+    /**
+     * Initialize the menu bar from $FG_ROOT/gui/menubar.xml
+     */
+    virtual void init ();
+    
+    /**
+     * Make the menu bar visible.
+     */
+    virtual void show ();
+
+
+    /**
+     * Make the menu bar invisible.
+     */
+    virtual void hide ();
+
+
+    /**
+     * Test whether the menu bar is visible.
+     */
+    virtual bool isVisible () const;
+
+
+    /**
+     * IGNORE THIS METHOD!!!
+     *
+     * This is necessary only because plib does not provide any easy
+     * way to attach user data to a menu item.  FlightGear should not
+     * have to know about PUI internals, but this method allows the
+     * callback to pass the menu item one-shot on to the current menu.
+     */
+    virtual void fireItem (puObject * item);
+
+
+    /**
+     * create a menubar based on a PropertyList within the PropertyTree
+     */
+    void make_menubar (SGPropertyNode * props);
+
+
+    /**
+     * destroy a menubar based on a PropertyList within the PropertyTree
+     */
+    void destroy_menubar ();
+
+
+    /**
+     * Disable/enable menu titles and entries
+     */
+    bool enable_item (const SGPropertyNode * item, bool state);
+
+
+private:
+
+    // Make a single menu.
+    void make_menu (SGPropertyNode * node);
+
+    // Make the top-level menubar.
+    void make_menubar ();
+
+    // Create a property-path -> puObject map for menu node
+    void make_object_map(SGPropertyNode * node);
+
+    // Add <enabled> listener that enables/disables menu entries.
+    void add_enabled_listener(SGPropertyNode * node);
+
+    // Is the menu visible?
+    bool _visible;
+
+    // The top-level menubar itself.
+    puMenuBar * _menuBar;
+
+    // A map of bindings for the menubar.
+    std::map<std::string,std::vector<SGBinding *> > _bindings;
+
+    // These are hoops that we have to jump through because PUI doesn't
+    // do memory management for lists.  We have to allocate the arrays,
+    // hang onto pointers, and then delete them when the menubar is
+    // freed.
+    char ** make_char_array (int size);
+    puCallback * make_callback_array (int size);
+    std::vector<char **> _char_arrays;
+    std::vector<puCallback *> _callback_arrays;
+
+    // A map for {menu node path}->puObject translation.
+    std::map<std::string, puObject *> _objects;
+};
+
+#endif // __MENUBAR_HXX
index b21b9ce64f5357b62fa16b315d777c6f8e8b9048..4295f808b988236bcb41c94546eedb45aae293eb 100644 (file)
 #  include <config.h>
 #endif
 
-#include <string.h>
-#include <iostream>
-#include <plib/pu.h>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/structure/SGBinding.hxx>
-#include <simgear/props/props_io.hxx>
 
-#include <Main/globals.hxx>
-
-#include "new_gui.hxx"
 #include "menubar.hxx"
 
-using std::vector;
-using std::string;
-using std::map;\f
-////////////////////////////////////////////////////////////////////////
-// FIXME!!
-//
-// Deprecated wrappers for old menu commands.
-//
-// DO NOT ADD TO THESE.  THEY WILL BE DELETED SOON!
-//
-// These are defined in gui_funcs.cxx.  They should be replaced with
-// user-configured dialogs and new commands where necessary.
-////////////////////////////////////////////////////////////////////////
-
-#if defined(TR_HIRES_SNAP)
-extern void dumpHiResSnapShot ();
-static bool
-do_hires_snapshot_dialog (const SGPropertyNode * arg)
-{
-    dumpHiResSnapShot();
-    return true;
-}
-#endif // TR_HIRES_SNAP
-
-static struct {
-    const char * name;
-    SGCommandMgr::command_t command;
-} deprecated_dialogs [] = {
-#if defined(TR_HIRES_SNAP)
-    { "old-hires-snapshot-dialog", do_hires_snapshot_dialog },
-#endif
-    { 0, 0 }
-};
-
-static void
-add_deprecated_dialogs ()
-{
-  SG_LOG(SG_GENERAL, SG_INFO, "Initializing old dialog commands:");
-  for (int i = 0; deprecated_dialogs[i].name != 0; i++) {
-    SG_LOG(SG_GENERAL, SG_INFO, "  " << deprecated_dialogs[i].name);
-    globals->get_commands()->addCommand(deprecated_dialogs[i].name,
-                                       deprecated_dialogs[i].command);
-  }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Static functions.
-////////////////////////////////////////////////////////////////////////
-
-
-static void
-menu_callback (puObject * object)
-{
-    NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
-    gui->getMenuBar()->fireItem(object);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGMenuBar.
-////////////////////////////////////////////////////////////////////////
-
-
-FGMenuBar::FGMenuBar ()
-    : _visible(false),
-      _menuBar(0)
-{
-}
-
 FGMenuBar::~FGMenuBar ()
 {
-    destroy_menubar();
-}
-
-void
-FGMenuBar::init ()
-{
-    delete _menuBar;            // FIXME: check if PUI owns the pointer
-    make_menubar();
-                                // FIXME: temporary commands to get at
-                                // old, hard-coded dialogs.
-    add_deprecated_dialogs();
-}
-
-void
-FGMenuBar::show ()
-{
-    if (_menuBar != 0)
-        _menuBar->reveal();
-    _visible = true;
-}
-
-void
-FGMenuBar::hide ()
-{
-    if (_menuBar != 0)
-        _menuBar->hide();
-    _visible = false;
-}
-
-bool
-FGMenuBar::isVisible () const
-{
-    return _visible;
-}
-
-void
-FGMenuBar::fireItem (puObject * item)
-{
-    const char * name = item->getLegend();
-    vector<SGBinding *> &bindings = _bindings[name];
-    int nBindings = bindings.size();
-
-    for (int i = 0; i < nBindings; i++)
-        bindings[i]->fire();
-}
-
-void
-FGMenuBar::make_menu (SGPropertyNode * node)
-{
-    const char * name = strdup(node->getStringValue("label"));
-    vector<SGPropertyNode_ptr> item_nodes = node->getChildren("item");
-
-    int array_size = item_nodes.size();
-
-    char ** items = make_char_array(array_size);
-    puCallback * callbacks = make_callback_array(array_size);
-
-    for (unsigned int i = 0, j = item_nodes.size() - 1;
-         i < item_nodes.size();
-         i++, j--) {
-
-                                // Set up the PUI entries for this item
-        items[j] = strdup((char *)item_nodes[i]->getStringValue("label"));
-        callbacks[j] = menu_callback;
-
-                                // Load all the bindings for this item
-        vector<SGPropertyNode_ptr> bindings = item_nodes[i]->getChildren("binding");
-        SGPropertyNode * dest = fgGetNode("/sim/bindings/menu", true);
-
-        for (unsigned int k = 0; k < bindings.size(); k++) {
-            unsigned int m = 0;
-            SGPropertyNode_ptr binding;
-            while (dest->getChild("binding", m))
-                m++;
-
-            binding = dest->getChild("binding", m, true);
-            copyProperties(bindings[k], binding);
-            _bindings[items[j]].push_back(new SGBinding(binding, globals->get_props()));
-        }
-    }
-
-    _menuBar->add_submenu(name, items, callbacks);
-}
-
-void
-FGMenuBar::make_menubar ()
-{
-    SGPropertyNode *targetpath;
-   
-    targetpath = fgGetNode("/sim/menubar/default",true);
-    // fgLoadProps("gui/menubar.xml", targetpath);
-    
-    /* NOTE: there is no check to see whether there's any usable data at all
-     *
-     * This would also have the advantage of being able to create some kind of
-     * 'fallback' menu - just in case that either menubar.xml is empty OR that
-     * its XML data is not valid, that way we would avoid displaying an
-     * unusable menubar without any functionality - if we decided to add another
-     * char * element to the commands structure in
-     *  $FG_SRC/src/Main/fgcommands.cxx 
-     * we could additionally save each function's (short) description and use
-     * this as label for the fallback PUI menubar item labels - as a workaround
-     * one might simply use the internal fgcommands and put them into the 
-     * fallback menu, so that the user is at least able to re-init the menu
-     * loading - just in case there was some malformed XML in it
-     * (it happend to me ...)
-     */
-    
-    make_menubar(targetpath);
-}
-
-/* WARNING: We aren't yet doing any validation of what's found - but since
- * this isn't done with menubar.xml either, it should not really matter
- * right now. Although one should later on consider to validate the
- * contents, whether they are representing a 'legal' menubar structure.
- */
-void
-FGMenuBar::make_menubar(SGPropertyNode * props) 
-{    
-    // Just in case.
-    destroy_menubar();
-    _menuBar = new puMenuBar;
-
-    vector<SGPropertyNode_ptr> menu_nodes = props->getChildren("menu");
-    for (unsigned int i = 0; i < menu_nodes.size(); i++)
-        make_menu(menu_nodes[i]);
-
-    _menuBar->close();
-    make_object_map(props);
-
-    if (_visible)
-        _menuBar->reveal();
-    else
-        _menuBar->hide();
-}
-
-void
-FGMenuBar::destroy_menubar ()
-{
-    if ( _menuBar == 0 )
-        return;
-
-    hide();
-    puDeleteObject(_menuBar);
-
-    unsigned int i;
-
-                                // Delete all the character arrays
-                                // we were forced to keep around for
-                                // plib.
-    SG_LOG(SG_GENERAL, SG_BULK, "Deleting char arrays");
-    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 callback arrays
-                                // we were forced to keep around for
-                                // plib.
-    SG_LOG(SG_GENERAL, SG_BULK, "Deleting callback arrays");
-    for (i = 0; i < _callback_arrays.size(); i++)
-        delete[] _callback_arrays[i];
-
-                                // Delete all those bindings
-    SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings");
-    map<string,vector<SGBinding *> >::iterator it;
-    for (it = _bindings.begin(); it != _bindings.end(); it++) {
-        SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings for " << it->first);
-        for ( i = 0; i < it->second.size(); i++ )
-            delete it->second[i];
-    }
-
-    SG_LOG(SG_GENERAL, SG_BULK, "Done.");
-}
-
-void
-FGMenuBar::make_object_map(SGPropertyNode * node)
-{
-    unsigned int menu_index = 0;
-    vector<SGPropertyNode_ptr> menus = node->getChildren("menu");
-    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 (menu_index >= menus.size()) {
-            SG_LOG(SG_GENERAL, SG_WARN, "'menu' object without node: "
-                    << node->getPath() << "/menu[" << menu_index << ']');
-            return;
-        }
-
-        SGPropertyNode *menu = menus.at(menu_index);
-        _objects[menu->getPath()] = obj;
-        add_enabled_listener(menu);
-
-        puGroup *popup = (puGroup *)obj->getUserData();
-        if (!popup)
-            continue;
-
-        // the entries are for some reason reversed (last first), and we
-        // don't know yet how many there will be; so we collect first
-        vector<puObject *> e;
-        for (puObject *me = popup->getFirstChild(); me; me = me->getNextObject())
-            e.push_back(me);
-
-        vector<SGPropertyNode_ptr> items = menu->getChildren("item");
-        for (unsigned int i = 0; i < e.size(); i++) {
-            if (i >= items.size()) {
-                SG_LOG(SG_GENERAL, SG_WARN, "'item' object without node: "
-                        << menu->getPath() << "/item[" << i << ']');
-                break;
-            }
-            SGPropertyNode *item = items.at(e.size() - i - 1);
-            _objects[item->getPath()] = e[i];
-            add_enabled_listener(item);
-        }
-        menu_index++;
-    }
-}
-
-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());
-}
-
-bool
-FGMenuBar::enable_item(const SGPropertyNode * node, bool state)
-{
-    string path = node->getPath();
-    if (_objects.find(path) == _objects.end()) {
-        SG_LOG(SG_GENERAL, SG_ALERT, "Trying to enable/disable "
-            "non-existent menu item for node `" << path << '\'');
-        return false;
-    }
-    puObject *object = _objects[path];
-    if (state)
-        object->activate();
-    else
-        object->greyOut();
-
-    return true;
-}
-
-char **
-FGMenuBar::make_char_array (int size)
-{
-    char ** list = new char*[size+1];
-    for (int i = 0; i <= size; i++)
-        list[i] = 0;
-    _char_arrays.push_back(list);
-    return list;
-}
-
-puCallback *
-FGMenuBar::make_callback_array (int size)
-{
-    puCallback * list = new puCallback[size+1];
-    for (int i = 0; i <= size; i++)
-        list[i] = 0;
-    _callback_arrays.push_back(list);
-    return list;
 }
 
 // end of menubar.cxx
index 79e0af8146d9e4802824c4fe113c96d8743a258f..fa3f43ca506292ec282a2714bc46a61159ca47a1 100644 (file)
@@ -3,42 +3,19 @@
 #ifndef __MENUBAR_HXX
 #define __MENUBAR_HXX 1
 
-#ifndef __cplusplus
-# error This library requires C++
-#endif
-
-#include <Main/fg_props.hxx>
-
-#include <plib/pu.h>
-
-#include <map>
-#include <vector>
-
-class puMenuBar;
-class puObject;
-class SGBinding;
-
 
 /**
- * XML-configured PUI menu bar.
+ * XML-configured menu bar interface
  *
  * This class creates a menu bar from a tree of XML properties.  These
  * properties are not part of the main FlightGear property tree, but
  * are read from a separate file ($FG_ROOT/gui/menubar.xml).
- *
- * WARNING: because PUI provides no easy way to attach user data to a
- * menu item, all menu item strings must be unique; otherwise, this
- * class will always use the first binding with any given name.
+
  */
 class FGMenuBar
 {
 public:
 
-    /**
-     * Constructor.
-     */
-    FGMenuBar ();
-
 
     /**
      * Destructor.
@@ -49,89 +26,25 @@ public:
     /**
      * Initialize the menu bar from $FG_ROOT/gui/menubar.xml
      */
-    virtual void init ();
+    virtual void init () = 0;
     
     /**
      * Make the menu bar visible.
      */
-    virtual void show ();
+    virtual void show () = 0;
 
 
     /**
      * Make the menu bar invisible.
      */
-    virtual void hide ();
+    virtual void hide () = 0;
 
 
     /**
      * Test whether the menu bar is visible.
      */
-    virtual bool isVisible () const;
-
-
-    /**
-     * IGNORE THIS METHOD!!!
-     *
-     * This is necessary only because plib does not provide any easy
-     * way to attach user data to a menu item.  FlightGear should not
-     * have to know about PUI internals, but this method allows the
-     * callback to pass the menu item one-shot on to the current menu.
-     */
-    virtual void fireItem (puObject * item);
-
-
-    /**
-     * create a menubar based on a PropertyList within the PropertyTree
-     */
-    void make_menubar (SGPropertyNode * props);
-
-
-    /**
-     * destroy a menubar based on a PropertyList within the PropertyTree
-     */
-    void destroy_menubar ();
-
-
-    /**
-     * Disable/enable menu titles and entries
-     */
-    bool enable_item (const SGPropertyNode * item, bool state);
-
-
-private:
-
-    // Make a single menu.
-    void make_menu (SGPropertyNode * node);
-
-    // Make the top-level menubar.
-    void make_menubar ();
-
-    // Create a property-path -> puObject map for menu node
-    void make_object_map(SGPropertyNode * node);
-
-    // Add <enabled> listener that enables/disables menu entries.
-    void add_enabled_listener(SGPropertyNode * node);
-
-    // Is the menu visible?
-    bool _visible;
-
-    // The top-level menubar itself.
-    puMenuBar * _menuBar;
-
-    // A map of bindings for the menubar.
-    std::map<std::string,std::vector<SGBinding *> > _bindings;
-
-    // These are hoops that we have to jump through because PUI doesn't
-    // do memory management for lists.  We have to allocate the arrays,
-    // hang onto pointers, and then delete them when the menubar is
-    // freed.
-    char ** make_char_array (int size);
-    puCallback * make_callback_array (int size);
-    std::vector<char **> _char_arrays;
-    std::vector<puCallback *> _callback_arrays;
+    virtual bool isVisible () const = 0;
 
-    // A map for {menu node path}->puObject translation.
-    std::map<std::string, puObject *> _objects;
 };
 
 #endif // __MENUBAR_HXX
index a374b2912ae3e0e23609137b8b5b7505fb6b45c6..f9d7941c26270ecf7ae8a3d808749142797a0a5b 100644 (file)
@@ -26,7 +26,7 @@
 #include "GL/glx.h"
 #endif
 
-#include "menubar.hxx"
+#include "FGPUIMenuBar.hxx"
 #include "FGPUIDialog.hxx"
 #include "FGFontCache.hxx"
 #include "FGColor.hxx"
@@ -41,14 +41,13 @@ using std::string;
 
 
 NewGUI::NewGUI ()
-    : _menubar(new FGMenuBar),
+    : _menubar(new FGPUIMenuBar),
       _active_dialog(0)
 {
 }
 
 NewGUI::~NewGUI ()
 {
-    delete _menubar;
     _dialog_props.clear();
     for (_itt_t it = _colors.begin(); it != _colors.end(); ++it)
         delete it->second;
@@ -92,8 +91,7 @@ NewGUI::reset (bool reload)
     setStyle();
 
     unbind();
-    delete _menubar;
-    _menubar = new FGMenuBar;
+    _menubar.reset(new FGPUIMenuBar);
 
     if (reload) {
         _dialog_props.clear();
@@ -214,7 +212,7 @@ NewGUI::getActiveDialog ()
 FGMenuBar *
 NewGUI::getMenuBar ()
 {
-    return _menubar;
+    return _menubar.get();
 }
 
 bool
index 3f69cab951202134e09b025cecc598af655c2640..08a01b27f5a9e730ea011f36bb8aa94277863b2f 100644 (file)
@@ -219,7 +219,7 @@ private:
     // Read all the configuration files in a directory.
     void readDir (const SGPath& path);
 
-    FGMenuBar * _menubar;
+    std::auto_ptr<FGMenuBar> _menubar;
     FGDialog * _active_dialog;
     std::map<std::string,FGDialog *> _active_dialogs;
     std::map<std::string,SGPropertyNode_ptr> _dialog_props;