1
0
Fork 0

Cocoa menu-bar implementation.

This commit is contained in:
James Turner 2011-11-20 13:23:52 +00:00
parent e1c1a28288
commit ebfdebeb43
5 changed files with 321 additions and 4 deletions

View file

@ -65,7 +65,8 @@ IF(APPLE)
set(EVENT_INPUT_DEFAULT 1)
find_library(CORESERVICES_LIBRARY CoreServices)
list(APPEND PLATFORM_LIBS ${CORESERVICES_LIBRARY})
find_library(COCOA_LIBRARY Cocoa)
list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY})
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
# disabled while DBus / HAL / udev issues are decided

View file

@ -37,4 +37,9 @@ set(HEADERS
FGColor.hxx
)
if (APPLE)
list(APPEND HEADERS FGCocoaMenuBar.hxx)
list(APPEND SOURCES FGCocoaMenuBar.mm)
endif()
flightgear_component(GUI "${SOURCES}" "${HEADERS}")

View file

@ -0,0 +1,65 @@
// menubar.hxx - XML-configured menu bar.
#ifndef FG_COCOA_MENUBAR_HXX
#define FG_COCOA_MENUBAR_HXX 1
#include <GUI/menubar.hxx>
#include <memory>
/**
* XML-configured Cocoa 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 FGCocoaMenuBar : public FGMenuBar
{
public:
/**
* Constructor.
*/
FGCocoaMenuBar ();
/**
* Destructor.
*/
virtual ~FGCocoaMenuBar ();
/**
* 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;
class CocoaMenuBarPrivate;
private:
std::auto_ptr<CocoaMenuBarPrivate> p;
};
#endif // __MENUBAR_HXX

231
src/GUI/FGCocoaMenuBar.mm Normal file
View file

@ -0,0 +1,231 @@
#include "FGCocoaMenuBar.hxx"
#include <Cocoa/Cocoa.h>
#include <boost/foreach.hpp>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGBinding.hxx>
#include <Main/fg_props.hxx>
#include <iostream>
using std::string;
using std::map;
using std::cout;
typedef std::map<NSMenuItem*, SGBindingList> MenuItemBindings;
@class CocoaMenuDelegate;
class FGCocoaMenuBar::CocoaMenuBarPrivate
{
public:
CocoaMenuBarPrivate();
~CocoaMenuBarPrivate();
bool labelIsSeparator(const std::string& s) const;
void menuFromProps(NSMenu* menu, SGPropertyNode* menuNode);
void fireBindingsForItem(NSMenuItem* item);
public:
CocoaMenuDelegate* delegate;
MenuItemBindings itemBindings;
};
@interface CocoaMenuDelegate : NSObject <NSMenuDelegate> {
@private
FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
}
@property (nonatomic, assign) FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
@end
@implementation CocoaMenuDelegate
@synthesize peer;
- (void) itemAction:(id) sender
{
peer->fireBindingsForItem((NSMenuItem*) sender);
}
@end
static NSString* stdStringToCocoa(const string& s)
{
return [NSString stringWithUTF8String:s.c_str()];
}
class EnabledListener : public SGPropertyChangeListener
{
public:
EnabledListener(NSMenuItem* i) :
item(i)
{}
virtual void valueChanged(SGPropertyNode *node)
{
BOOL b = node->getBoolValue();
[item setEnabled:b];
}
private:
NSMenuItem* item;
};
FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
{
delegate = [[CocoaMenuDelegate alloc] init];
delegate.peer = this;
}
FGCocoaMenuBar::CocoaMenuBarPrivate::~CocoaMenuBarPrivate()
{
[delegate release];
}
bool FGCocoaMenuBar::CocoaMenuBarPrivate::labelIsSeparator(const std::string& s) const
{
for (unsigned int i=0; i<s.size(); ++i) {
if (s[i] != '-') {
return false;
}
}
return true;
}
void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGPropertyNode* menuNode)
{
int index = 0;
BOOST_FOREACH(SGPropertyNode_ptr n, menuNode->getChildren("item")) {
if (!n->hasValue("enabled")) {
n->setBoolValue("enabled", true);
}
string l = n->getStringValue("label");
string::size_type pos = l.find("(");
if (pos != string::npos) {
l = l.substr(0, pos);
}
NSString* label = stdStringToCocoa(l);
NSString* shortcut = @"";
NSMenuItem* item;
if (index >= [menu numberOfItems]) {
if (labelIsSeparator(l)) {
item = [NSMenuItem separatorItem];
[menu addItem:item];
} else {
item = [menu addItemWithTitle:label action:nil keyEquivalent:shortcut];
n->getNode("enabled")->addChangeListener(new EnabledListener(item));
[item setTarget:delegate];
[item setAction:@selector(itemAction:)];
}
} else {
item = [menu itemAtIndex:index];
[item setTitle:label];
}
BOOL enabled = n->getBoolValue("enabled");
[item setEnabled:enabled];
SGBindingList bl;
BOOST_FOREACH(SGPropertyNode_ptr binding, n->getChildren("binding")) {
// have to clone the bindings, since SGBinding takes ownership of the
// passed in node. Seems like something is wrong here, but following the
// PUI code for the moment.
SGPropertyNode* cloned(new SGPropertyNode);
copyProperties(binding, cloned);
bl.push_back(new SGBinding(cloned, globals->get_props()));
}
itemBindings[item] = bl;
++index;
} // of item iteration
}
void FGCocoaMenuBar::CocoaMenuBarPrivate::fireBindingsForItem(NSMenuItem *item)
{
MenuItemBindings::iterator it = itemBindings.find(item);
if (it == itemBindings.end()) {
return;
}
BOOST_FOREACH(SGSharedPtr<SGBinding> b, it->second) {
b->fire();
}
}
FGCocoaMenuBar::FGCocoaMenuBar() :
p(new CocoaMenuBarPrivate)
{
}
FGCocoaMenuBar::~FGCocoaMenuBar()
{
}
void FGCocoaMenuBar::init()
{
NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true);
int index = 0;
NSMenuItem* previousMenu = [mainBar itemAtIndex:0];
if (![[previousMenu title] isEqualToString:@"FlightGear"]) {
[previousMenu setTitle:@"FlightGear"];
}
BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) {
NSString* label = stdStringToCocoa(n->getStringValue("label"));
NSMenuItem* item = [mainBar itemWithTitle:label];
NSMenu* menu;
if (!item) {
NSInteger insertIndex = [mainBar indexOfItem:previousMenu] + 1;
item = [mainBar insertItemWithTitle:label action:nil keyEquivalent:@"" atIndex:insertIndex];
item.tag = index + 400;
menu = [[NSMenu alloc] init];
menu.title = label;
[menu setAutoenablesItems:NO];
[mainBar setSubmenu:menu forItem:item];
[menu autorelease];
} else {
menu = item.submenu;
}
// synchronise menu with properties
p->menuFromProps(menu, n);
++index;
previousMenu = item;
}
}
bool FGCocoaMenuBar::isVisible() const
{
return true;
}
void FGCocoaMenuBar::show()
{
// no-op
}
void FGCocoaMenuBar::hide()
{
// no-op
}

View file

@ -27,6 +27,11 @@
#endif
#include "FGPUIMenuBar.hxx"
#if defined(SG_MAC)
#include "FGCocoaMenuBar.hxx"
#endif
#include "FGPUIDialog.hxx"
#include "FGFontCache.hxx"
#include "FGColor.hxx"
@ -40,10 +45,14 @@ using std::string;
NewGUI::NewGUI ()
: _menubar(new FGPUIMenuBar),
_active_dialog(0)
NewGUI::NewGUI () :
_active_dialog(0)
{
#if defined(SG_MAC)
_menubar.reset(new FGCocoaMenuBar);
#else
_menubar.reset(new FGPUIMenuBar);
#endif
}
NewGUI::~NewGUI ()
@ -91,7 +100,13 @@ NewGUI::reset (bool reload)
setStyle();
unbind();
#if defined(SG_MAC)
if (reload) {
_menubar.reset(new FGCocoaMenuBar);
}
#else
_menubar.reset(new FGPUIMenuBar);
#endif
if (reload) {
_dialog_props.clear();