allow disabling/enabling of menu entries via "enabled" property;
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. :-)
This commit is contained in:
parent
435d874e35
commit
ac2c1fcd43
2 changed files with 114 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue