]> git.saurik.com Git - wxWidgets.git/blobdiff - src/cocoa/mbarman.mm
Use wxWindow::Refresh() instead of artificial wxPaintEvent in the test.
[wxWidgets.git] / src / cocoa / mbarman.mm
index 63bbdd4e915961f318f96f89830878bbea9c9337..1f0e5c71f8c706cb46e4cdea08fab94fc6c4b205 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        cocoa/mbarman.cpp
+// Name:        src/cocoa/mbarman.mm
 // Purpose:     wxMenuBarManager implementation
 // Author:      David Elliott
 // Modified by:
 
 #include "wx/cocoa/mbarman.h"
 #include "wx/cocoa/autorelease.h"
+#include "wx/cocoa/objc/objc_uniquifying.h"
 
 #import <Foundation/NSString.h>
+#import <Foundation/NSNotification.h>
 #import <AppKit/NSMenu.h>
 #import <AppKit/NSApplication.h>
+#import <AppKit/NSWindow.h>
+
+#ifndef wxUSE_FSCRIPT
+#define wxUSE_FSCRIPT 0
+#endif
+
+#if wxUSE_FSCRIPT
+    #import <FScript/FScriptMenuItem.h>
+#endif
+
+// Declare setAppleMenu: in an NSApplication category since Tiger and later
+// releases support it but don't declare it as it's considered deprecated.
+@interface NSApplication(wxDeprecatedMethodsWeWantToUse)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+
+// ============================================================================
+// wxMenuBarManagerObserver
+// ============================================================================
+@interface wxMenuBarManagerObserver : NSObject
+{
+    wxMenuBarManager *m_mbarman;
+}
+
+- (id)init;
+- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman;
+- (void)windowDidBecomeKey: (NSNotification *)notification;
+#if 0
+- (void)windowDidResignKey: (NSNotification *)notification;
+- (void)windowDidBecomeMain: (NSNotification *)notification;
+- (void)windowDidResignMain: (NSNotification *)notification;
+- (void)windowWillClose: (NSNotification *)notification;
+#endif // 0
+@end // interface wxMenuBarManagerObserver : NSObject
+WX_DECLARE_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
+
+@implementation wxMenuBarManagerObserver : NSObject
+- (id)init
+{
+    wxFAIL_MSG(wxT("[wxMenuBarManagerObserver -init] should never be called!"));
+    m_mbarman = NULL;
+    return self;
+}
+
+- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman
+{
+    wxASSERT(mbarman);
+    m_mbarman = mbarman;
+    return [super init];
+}
+
+- (void)windowDidBecomeKey: (NSNotification *)notification
+{
+    wxASSERT(m_mbarman);
+    m_mbarman->WindowDidBecomeKey(notification);
+}
+
+#if 0
+- (void)windowDidResignKey: (NSNotification *)notification
+{
+    wxASSERT(m_mbarman);
+    m_mbarman->WindowDidResignKey(notification);
+}
+
+- (void)windowDidBecomeMain: (NSNotification *)notification
+{
+    wxASSERT(m_mbarman);
+    m_mbarman->WindowDidBecomeMain(notification);
+}
+
+- (void)windowDidResignMain: (NSNotification *)notification
+{
+    wxASSERT(m_mbarman);
+    m_mbarman->WindowDidResignMain(notification);
+}
+
+- (void)windowWillClose: (NSNotification *)notification
+{
+    wxASSERT(m_mbarman);
+    m_mbarman->WindowWillClose(notification);
+}
+#endif // 0
+
+@end // implementation wxMenuBarManagerObserver : NSObject
+WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
 
 // ============================================================================
 // wxMenuBarManager
 // ============================================================================
 wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL;
 
+static void AddFScriptItem(NSMenu *menu)
+#if wxUSE_FSCRIPT
+{
+    NSMenuItem *item = [[FScriptMenuItem alloc] init];
+    [menu addItem: item];
+    [item release];
+}
+#else
+{}
+#endif
+
 wxMenuBarManager::wxMenuBarManager()
 {
+    m_observer = [[WX_GET_OBJC_CLASS(wxMenuBarManagerObserver) alloc]
+            initWithWxMenuBarManager:this];
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowDidBecomeKey:)
+            name:NSWindowDidBecomeKeyNotification object:nil];
+
+    // HACK: Reuse the same selector and eventual C++ method and make it
+    // check for whether the notification is to become key or main.
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowDidBecomeKey:)
+            name:NSWindowDidBecomeMainNotification object:nil];
+#if 0
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowDidResignKey:)
+            name:NSWindowDidResignKeyNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowDidBecomeMain:)
+            name:NSWindowDidBecomeMainNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowDidResignMain:)
+            name:NSWindowDidResignMainNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:m_observer
+            selector:@selector(windowWillClose:)
+            name:NSWindowWillCloseNotification object:nil];
+#endif // 0
     m_menuApp = nil;
     m_menuServices = nil;
     m_menuWindows = nil;
     m_menuMain = nil;
-    m_needMenuBar = true;
     m_mainMenuBarInstalled = true;
     m_mainMenuBar = NULL;
-    m_windowKey = NULL;
-    m_windowMain = NULL;
+    m_currentNSWindow = nil;
 
     NSApplication *theNSApplication = wxTheApp->GetNSApplication();
     // Create the services menu.
@@ -53,6 +174,7 @@ wxMenuBarManager::wxMenuBarManager()
 
 /**/[m_menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""];
 /**/[m_menuApp addItem: [NSMenuItem separatorItem]];
+/**/AddFScriptItem(m_menuApp);
 /**/menuitem = [[NSMenuItem alloc] initWithTitle: @"Services" action:nil keyEquivalent:@""];
     [menuitem setSubmenu:m_menuServices];
     [m_menuApp addItem: menuitem];
@@ -107,6 +229,7 @@ wxMenuBarManager::wxMenuBarManager()
 
 wxMenuBarManager::~wxMenuBarManager()
 {
+    [m_observer release];
 }
 
 void wxMenuBarManager::CreateInstance()
@@ -120,22 +243,17 @@ void wxMenuBarManager::DestroyInstance()
     sm_mbarmanInstance = NULL;
 }
 
-void wxMenuBarManager::CocoaInternalIdle()
-{
-    if(m_needMenuBar)
-        InstallMainMenu();
-}
-
 void wxMenuBarManager::SetMenuBar(wxMenuBar* menubar)
 {
     m_mainMenuBarInstalled = false;
-    m_needMenuBar = !menubar;
     if(menubar)
     {
         [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
         [[menubar->GetNSMenu() itemAtIndex:0] setSubmenu:m_menuApp];
         [wxTheApp->GetNSApplication() setMainMenu:menubar->GetNSMenu()];
     }
+    else
+        InstallMainMenu();
 }
 
 void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar)
@@ -151,7 +269,6 @@ void wxMenuBarManager::InstallMainMenu()
         SetMenuBar(m_mainMenuBar);
     else
     {
-        m_needMenuBar = false;
         m_mainMenuBarInstalled = true;
         [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
         [[m_menuMain itemAtIndex:0] setSubmenu:m_menuApp];
@@ -159,44 +276,124 @@ void wxMenuBarManager::InstallMainMenu()
     }
 }
 
-void wxMenuBarManager::WindowDidBecomeKey(wxTopLevelWindowNative *win)
+void wxMenuBarManager::WindowDidBecomeKey(NSNotification *notification)
 {
-    wxASSERT(!m_windowKey);
-    m_windowKey = win;
-    InstallMenuBarForWindow(win);
+    /* NOTE: m_currentNSWindow might be destroyed but we only ever use it
+       to look it up in the hash table.  Do not send messages to it. */
+
+    /*  Update m_currentNSWindow only if we really should.  For instance,
+        if a non-wx window is becoming key but a wx window remains main
+        then don't change out the menubar.  However, if a non-wx window
+        (whether the same window or not) is main, then switch to the
+        generic menubar so the wx window that last installed a menubar
+        doesn't get menu events it doesn't expect.
+
+        If a wx window is becoming main then check to see if the key
+        window is a wx window and if so do nothing because that
+        is what would have been done before.
+
+        If a non-wx window is becoming main and 
+     */
+    NSString *notificationName = [notification name];
+    if(NULL == notificationName)
+        return;
+    else if([NSWindowDidBecomeKeyNotification isEqualTo:notificationName])
+    {   // This is the only one that was handled in 2.8 as shipped
+        // Generally the key window can change without the main window changing.
+        // The user can do this simply by clicking on something in a palette window
+        // that needs to become key.
+        NSWindow *newKeyWindow = [notification object];
+        wxCocoaNSWindow *theWxKeyWindow = wxCocoaNSWindow::GetFromCocoa(newKeyWindow);
+        if(theWxKeyWindow != NULL)
+        {   // If the new key window is a wx window, handle it as before
+            // even if it has not actually changed.
+            m_currentNSWindow = newKeyWindow;
+        }
+        else
+        {   // If the new key window is not wx then check the main window.
+            NSWindow *mainWindow = [[NSApplication sharedApplication] mainWindow];
+            if(m_currentNSWindow == mainWindow)
+                // Don't reset if the menubar doesn't need to change.
+                return;
+            else
+                // This is strange because theoretically we should have picked this up
+                // already in the main window notification but it's possible that
+                // we simply haven't gotten it yet and will about as soon as we return.
+                // We already know that the key window isn't wx so fall back to this
+                // one and let the code go ahead and set the wx menubar if it is
+                // a wx window and set the generic one if it isn't.
+                m_currentNSWindow = mainWindow;
+        }
+    }
+    else if([NSWindowDidBecomeMainNotification isEqualTo:notificationName])
+    {   // Handling this is new
+        // Generally the main window cannot change without the key window changing
+        // because if the user clicks on a window that can become main then the
+        // window will also become key.
+        // However, it's possible that when it becomes main it automatically makes
+        // some palette the key window.
+        NSWindow *newMainWindow = [notification object];
+        // If we already know about the window, bail.
+        if(newMainWindow == m_currentNSWindow)
+            return;
+        else
+        {
+            NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
+            if(keyWindow == m_currentNSWindow)
+                // if we already know about the key window, bail
+                return;
+            else
+            {   // As above, sort of strange.  Neither one is current.  Prefer key over main.
+                wxCocoaNSWindow *theWxMainWindow = wxCocoaNSWindow::GetFromCocoa(keyWindow);
+                if(theWxMainWindow != NULL)
+                    m_currentNSWindow = keyWindow;
+                else
+                    m_currentNSWindow = newMainWindow;
+            }
+        }
+    }
+    m_currentNSWindow = [notification object];
+    wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
+    if(win)
+        InstallMenuBarForWindow(win);
+    else
+        SetMenuBar(NULL);
 }
 
-void wxMenuBarManager::WindowDidResignKey(wxTopLevelWindowNative *win, bool uninstallMenuBar)
+#if 0
+void wxMenuBarManager::WindowDidResignKey(NSNotification *notification)
+{
+}
+
+void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification)
 {
-    wxASSERT(m_windowKey==win);
-    m_windowKey = NULL;
-    if(uninstallMenuBar)
-        SetMenuBar(NULL);
 }
 
-void wxMenuBarManager::WindowDidBecomeMain(wxTopLevelWindowNative *win)
+void wxMenuBarManager::WindowDidResignMain(NSNotification *notification)
 {
-    wxASSERT(!m_windowMain);
-    m_windowMain = win;
 }
 
-void wxMenuBarManager::WindowDidResignMain(wxTopLevelWindowNative *win)
+void wxMenuBarManager::WindowWillClose(NSNotification *notification)
 {
-    wxASSERT(m_windowMain==win);
-    m_windowMain = NULL;
 }
+#endif // 0
 
-void wxMenuBarManager::InstallMenuBarForWindow(wxTopLevelWindowNative *win)
+void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win)
 {
     wxASSERT(win);
-    wxMenuBar *menubar = win->GetAppMenuBar();
+    wxMenuBar *menubar = win->GetAppMenuBar(win);
+    wxLogTrace(wxTRACE_COCOA,wxT("Found menubar=%p for window=%p."),menubar,win);
     SetMenuBar(menubar);
 }
 
-void wxMenuBarManager::UpdateWindowMenuBar(wxTopLevelWindowNative *win)
+void wxMenuBarManager::UpdateMenuBar()
 {
-    if(m_windowKey)
-        InstallMenuBarForWindow(m_windowKey);
+    if(m_currentNSWindow)
+    {
+        wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
+        if(win)
+            InstallMenuBarForWindow(win);
+    }
 }
 
 #endif // wxUSE_MENUS