X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f5382a2f00a6002bf2fb06920e983c2702f80078..d2e66707deb10dea6f92e3e4092f8a43ef942a5d:/src/cocoa/mbarman.mm diff --git a/src/cocoa/mbarman.mm b/src/cocoa/mbarman.mm index b2b916054d..1f0e5c71f8 100644 --- a/src/cocoa/mbarman.mm +++ b/src/cocoa/mbarman.mm @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: cocoa/mbarman.cpp +// Name: src/cocoa/mbarman.mm // Purpose: wxMenuBarManager implementation // Author: David Elliott // Modified by: @@ -20,27 +20,148 @@ #include "wx/cocoa/mbarman.h" #include "wx/cocoa/autorelease.h" +#include "wx/cocoa/objc/objc_uniquifying.h" #import +#import #import #import +#import + +#ifndef wxUSE_FSCRIPT +#define wxUSE_FSCRIPT 0 +#endif + +#if wxUSE_FSCRIPT + #import +#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]; @@ -71,7 +193,7 @@ wxMenuBarManager::wxMenuBarManager() [m_menuApp addItem: menuitem]; [menuitem release]; /**/[m_menuApp addItem: [NSMenuItem separatorItem]]; -/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"Q"]; +/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; [menuitem setTarget: theNSApplication]; [m_menuApp addItem: menuitem]; [menuitem release]; @@ -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,47 +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) +#if 0 +void wxMenuBarManager::WindowDidResignKey(NSNotification *notification) { - wxASSERT(m_windowKey==win); - m_windowKey = NULL; - SetMenuBar(NULL); } -void wxMenuBarManager::WindowDidBecomeMain(wxTopLevelWindowNative *win) +void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification) { -// wxASSERT(!m_windowMain); - m_windowMain = win; } -void wxMenuBarManager::WindowDidResignMain(wxTopLevelWindowNative *win) +void wxMenuBarManager::WindowDidResignMain(NSNotification *notification) { - wxASSERT(m_windowMain==win); - m_windowMain = NULL; } -void wxMenuBarManager::InstallMenuBarForWindow(wxTopLevelWindowNative *win) +void wxMenuBarManager::WindowWillClose(NSNotification *notification) { - wxMenuBar *menubar = NULL; - for(wxTopLevelWindowNative *destwin = win; - !menubar && destwin; - destwin = wxDynamicCast(destwin->GetParent(), wxTopLevelWindow)) - { - menubar = destwin->GetAppMenuBar(); - } +} +#endif // 0 + +void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win) +{ + wxASSERT(win); + 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() { - InstallMenuBarForWindow(m_windowKey); + if(m_currentNSWindow) + { + wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow); + if(win) + InstallMenuBarForWindow(win); + } } #endif // wxUSE_MENUS