Bug-fix: Cocoa menus work in aircraft with custom dialogs.
Restarting the GUI could cause Cocoa menus to misbehave, due to destruction behaviour of SGBinding. Use new clear() helper in the short term to work around this.
This commit is contained in:
parent
2b55acd3eb
commit
5e7574c9c2
3 changed files with 72 additions and 51 deletions
|
@ -24,6 +24,41 @@ typedef std::map<NSMenuItem*, SGBindingList> MenuItemBindings;
|
||||||
|
|
||||||
@class CocoaMenuDelegate;
|
@class CocoaMenuDelegate;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class CocoaEnabledListener : public SGPropertyChangeListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CocoaEnabledListener(SGPropertyNode_ptr prop, NSMenuItem* i) :
|
||||||
|
property(prop->getNode("enabled")),
|
||||||
|
item(i)
|
||||||
|
{
|
||||||
|
if (property.get()) {
|
||||||
|
property->addChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~CocoaEnabledListener()
|
||||||
|
{
|
||||||
|
if (property.get()) {
|
||||||
|
property->removeChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void valueChanged(SGPropertyNode *node)
|
||||||
|
{
|
||||||
|
CocoaAutoreleasePool pool;
|
||||||
|
BOOL b = node->getBoolValue();
|
||||||
|
[item setEnabled:b];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SGPropertyNode_ptr property;
|
||||||
|
NSMenuItem* item;
|
||||||
|
};
|
||||||
|
} // of anonymous namespace
|
||||||
|
|
||||||
class FGCocoaMenuBar::CocoaMenuBarPrivate
|
class FGCocoaMenuBar::CocoaMenuBarPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -38,7 +73,7 @@ public:
|
||||||
CocoaMenuDelegate* delegate;
|
CocoaMenuDelegate* delegate;
|
||||||
|
|
||||||
MenuItemBindings itemBindings;
|
MenuItemBindings itemBindings;
|
||||||
std::vector<SGPropertyChangeListener*> listeners;
|
std::vector<CocoaEnabledListener*> listeners;
|
||||||
};
|
};
|
||||||
|
|
||||||
// prior to the 10.6 SDK, NSMenuDelegate was an informal protocol
|
// prior to the 10.6 SDK, NSMenuDelegate was an informal protocol
|
||||||
|
@ -126,35 +161,6 @@ static void setItemShortcutFromString(NSMenuItem* item, const string& s)
|
||||||
[item setKeyEquivalentModifierMask:modifiers];
|
[item setKeyEquivalentModifierMask:modifiers];
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class CocoaEnabledListener : public SGPropertyChangeListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CocoaEnabledListener(NSMenuItem* i) :
|
|
||||||
item(i)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
~CocoaEnabledListener()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual void valueChanged(SGPropertyNode *node)
|
|
||||||
{
|
|
||||||
CocoaAutoreleasePool pool;
|
|
||||||
BOOL b = node->getBoolValue();
|
|
||||||
[item setEnabled:b];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
NSMenuItem* item;
|
|
||||||
};
|
|
||||||
} // of anonymous namespace
|
|
||||||
|
|
||||||
FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
|
FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
|
||||||
{
|
{
|
||||||
delegate = [[CocoaMenuDelegate alloc] init];
|
delegate = [[CocoaMenuDelegate alloc] init];
|
||||||
|
@ -195,9 +201,9 @@ void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGProperty
|
||||||
setItemShortcutFromString(item, shortcut);
|
setItemShortcutFromString(item, shortcut);
|
||||||
}
|
}
|
||||||
|
|
||||||
SGPropertyChangeListener* enableListener = new CocoaEnabledListener(item);
|
CocoaEnabledListener* cl = new CocoaEnabledListener(n, item);
|
||||||
listeners.push_back(enableListener);
|
listeners.push_back(cl);
|
||||||
n->getNode("enabled")->addChangeListener(enableListener);
|
|
||||||
[item setTarget:delegate];
|
[item setTarget:delegate];
|
||||||
[item setAction:@selector(itemAction:)];
|
[item setAction:@selector(itemAction:)];
|
||||||
}
|
}
|
||||||
|
@ -243,10 +249,18 @@ FGCocoaMenuBar::~FGCocoaMenuBar()
|
||||||
[topLevelItem.submenu removeAllItems];
|
[topLevelItem.submenu removeAllItems];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SGPropertyChangeListener*>::iterator it;
|
std::vector<CocoaEnabledListener*>::iterator it;
|
||||||
for (it = p->listeners.begin(); it != p->listeners.end(); ++it) {
|
for (it = p->listeners.begin(); it != p->listeners.end(); ++it) {
|
||||||
delete *it;
|
delete *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// owing to the bizarre destructor behaviour of SGBinding, we need
|
||||||
|
// to explicitly clear these bindings. (PUIMenuBar takes a different
|
||||||
|
// approach, and copies each binding into /sim/bindings)
|
||||||
|
MenuItemBindings::iterator j;
|
||||||
|
for (j = p->itemBindings.begin(); j != p->itemBindings.end(); ++j) {
|
||||||
|
clearBindingList(j->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGCocoaMenuBar::init()
|
void FGCocoaMenuBar::init()
|
||||||
|
@ -291,9 +305,8 @@ void FGCocoaMenuBar::init()
|
||||||
n->setBoolValue("enabled", true);
|
n->setBoolValue("enabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SGPropertyChangeListener* l = new CocoaEnabledListener(item);
|
CocoaEnabledListener* l = new CocoaEnabledListener( n, item);
|
||||||
p->listeners.push_back(l);
|
p->listeners.push_back(l);
|
||||||
n->getNode("enabled")->addChangeListener(l);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,23 +63,18 @@ NewGUI::~NewGUI ()
|
||||||
void
|
void
|
||||||
NewGUI::init ()
|
NewGUI::init ()
|
||||||
{
|
{
|
||||||
#if defined(SG_MAC)
|
createMenuBarImplementation();
|
||||||
if (fgGetBool("/sim/menubar/native", true)) {
|
|
||||||
_menubar.reset(new FGCocoaMenuBar);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!_menubar.get()) {
|
|
||||||
_menubar.reset(new FGPUIMenuBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
fgTie("/sim/menubar/visibility", this,
|
fgTie("/sim/menubar/visibility", this,
|
||||||
&NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
|
&NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible);
|
||||||
|
|
||||||
setStyle();
|
setStyle();
|
||||||
SGPath p(globals->get_fg_root(), "gui/dialogs");
|
SGPath p(globals->get_fg_root(), "gui/dialogs");
|
||||||
readDir(p);
|
readDir(p);
|
||||||
const std::string aircraft_dir(fgGetString("/sim/aircraft-dir"));
|
|
||||||
readDir( SGPath(aircraft_dir, "gui/dialogs") );
|
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
|
// Fix for http://code.google.com/p/flightgear-bugs/issues/detail?id=947
|
||||||
fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true);
|
fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true);
|
||||||
|
@ -107,6 +102,19 @@ NewGUI::redraw ()
|
||||||
reset(false);
|
reset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NewGUI::createMenuBarImplementation()
|
||||||
|
{
|
||||||
|
#if defined(SG_MAC)
|
||||||
|
if (fgGetBool("/sim/menubar/native", true)) {
|
||||||
|
_menubar.reset(new FGCocoaMenuBar);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!_menubar.get()) {
|
||||||
|
_menubar.reset(new FGPUIMenuBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NewGUI::reset (bool reload)
|
NewGUI::reset (bool reload)
|
||||||
{
|
{
|
||||||
|
@ -122,15 +130,13 @@ NewGUI::reset (bool reload)
|
||||||
setStyle();
|
setStyle();
|
||||||
|
|
||||||
unbind();
|
unbind();
|
||||||
#if !defined(SG_MAC)
|
|
||||||
_menubar.reset(new FGPUIMenuBar);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (reload) {
|
if (reload) {
|
||||||
_dialog_props.clear();
|
_dialog_props.clear();
|
||||||
_dialog_names.clear();
|
_dialog_names.clear();
|
||||||
init();
|
init();
|
||||||
} else {
|
} else {
|
||||||
|
createMenuBarImplementation();
|
||||||
_menubar->init();
|
_menubar->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,8 @@ protected:
|
||||||
virtual void reset (bool reload);
|
virtual void reset (bool reload);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void createMenuBarImplementation();
|
||||||
|
|
||||||
struct ltstr
|
struct ltstr
|
||||||
{
|
{
|
||||||
bool operator()(const char* s1, const char* s2) const {
|
bool operator()(const char* s1, const char* s2) const {
|
||||||
|
|
Loading…
Reference in a new issue