#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.
/**/[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];
[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];
wxMenuBarManager::~wxMenuBarManager()
{
+ [m_observer release];
}
void wxMenuBarManager::CreateInstance()
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)
SetMenuBar(m_mainMenuBar);
else
{
- m_needMenuBar = false;
m_mainMenuBarInstalled = true;
[[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
[[m_menuMain itemAtIndex:0] setSubmenu:m_menuApp];
}
}
-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