]> git.mxchange.org Git - flightgear.git/commitdiff
Cocoa menu-bar implementation.
authorJames Turner <zakalawe@mac.com>
Sun, 20 Nov 2011 13:23:52 +0000 (13:23 +0000)
committerJames Turner <zakalawe@mac.com>
Sun, 20 Nov 2011 16:26:05 +0000 (16:26 +0000)
CMakeLists.txt
src/GUI/CMakeLists.txt
src/GUI/FGCocoaMenuBar.hxx [new file with mode: 0644]
src/GUI/FGCocoaMenuBar.mm [new file with mode: 0644]
src/GUI/new_gui.cxx

index 1fde17c9df22473deee4b42a13bd44bb3494284b..e1b568b0b2a443e7727c61f2c9404669344d4095 100644 (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
index 030416ef0f6c70ce978ffad6bfa400595d746074..b44a90f09979682a55f3ad4a94519dccf9271cf6 100644 (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}")
diff --git a/src/GUI/FGCocoaMenuBar.hxx b/src/GUI/FGCocoaMenuBar.hxx
new file mode 100644 (file)
index 0000000..694561a
--- /dev/null
@@ -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
diff --git a/src/GUI/FGCocoaMenuBar.mm b/src/GUI/FGCocoaMenuBar.mm
new file mode 100644 (file)
index 0000000..7b44c78
--- /dev/null
@@ -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
+}
index f9d7941c26270ecf7ae8a3d808749142797a0a5b..ef50d7731138e6b05214a8a8500ec7eca9100770 100644 (file)
 #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();