From 5e7574c9c2b52c40b84e3e70657ffdedad39eafb Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 15 Jan 2014 22:00:09 +0000 Subject: [PATCH] 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. --- src/GUI/FGCocoaMenuBar.mm | 87 ++++++++++++++++++++++----------------- src/GUI/new_gui.cxx | 34 ++++++++------- src/GUI/new_gui.hxx | 2 + 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/src/GUI/FGCocoaMenuBar.mm b/src/GUI/FGCocoaMenuBar.mm index 9ec02523a..048eb5769 100644 --- a/src/GUI/FGCocoaMenuBar.mm +++ b/src/GUI/FGCocoaMenuBar.mm @@ -24,6 +24,41 @@ typedef std::map MenuItemBindings; @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 { public: @@ -38,7 +73,7 @@ public: CocoaMenuDelegate* delegate; MenuItemBindings itemBindings; - std::vector listeners; + std::vector listeners; }; // 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]; } -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() { delegate = [[CocoaMenuDelegate alloc] init]; @@ -195,9 +201,9 @@ void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGProperty setItemShortcutFromString(item, shortcut); } - SGPropertyChangeListener* enableListener = new CocoaEnabledListener(item); - listeners.push_back(enableListener); - n->getNode("enabled")->addChangeListener(enableListener); + CocoaEnabledListener* cl = new CocoaEnabledListener(n, item); + listeners.push_back(cl); + [item setTarget:delegate]; [item setAction:@selector(itemAction:)]; } @@ -243,10 +249,18 @@ FGCocoaMenuBar::~FGCocoaMenuBar() [topLevelItem.submenu removeAllItems]; } - std::vector::iterator it; + std::vector::iterator it; for (it = p->listeners.begin(); it != p->listeners.end(); ++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() @@ -291,9 +305,8 @@ void FGCocoaMenuBar::init() n->setBoolValue("enabled", true); } - SGPropertyChangeListener* l = new CocoaEnabledListener(item); + CocoaEnabledListener* l = new CocoaEnabledListener( n, item); p->listeners.push_back(l); - n->getNode("enabled")->addChangeListener(l); } } diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index af0738121..d679c44c9 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -63,23 +63,18 @@ NewGUI::~NewGUI () void NewGUI::init () { -#if defined(SG_MAC) - if (fgGetBool("/sim/menubar/native", true)) { - _menubar.reset(new FGCocoaMenuBar); - } -#endif - if (!_menubar.get()) { - _menubar.reset(new FGPUIMenuBar); - } - + createMenuBarImplementation(); fgTie("/sim/menubar/visibility", this, &NewGUI::getMenuBarVisible, &NewGUI::setMenuBarVisible); setStyle(); SGPath p(globals->get_fg_root(), "gui/dialogs"); 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 fgGetNode("sim/menubar")->setAttribute(SGPropertyNode::PRESERVE, true); @@ -107,6 +102,19 @@ NewGUI::redraw () 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 NewGUI::reset (bool reload) { @@ -122,15 +130,13 @@ NewGUI::reset (bool reload) setStyle(); unbind(); -#if !defined(SG_MAC) - _menubar.reset(new FGPUIMenuBar); -#endif if (reload) { _dialog_props.clear(); _dialog_names.clear(); init(); } else { + createMenuBarImplementation(); _menubar->init(); } diff --git a/src/GUI/new_gui.hxx b/src/GUI/new_gui.hxx index d3ee2979e..1ab1361c8 100644 --- a/src/GUI/new_gui.hxx +++ b/src/GUI/new_gui.hxx @@ -201,6 +201,8 @@ protected: virtual void reset (bool reload); private: + void createMenuBarImplementation(); + struct ltstr { bool operator()(const char* s1, const char* s2) const {