1 /////////////////////////////////////////////////////////////////////////////
 
   2 // Name:        cocoa/mbarman.cpp
 
   3 // Purpose:     wxMenuBarManager implementation
 
   4 // Author:      David Elliott
 
   8 // Copyright:   (c) 2003 David Elliott
 
   9 // Licence:     wxWidgets licence
 
  10 /////////////////////////////////////////////////////////////////////////////
 
  12 #include "wx/wxprec.h"
 
  18     #include "wx/toplevel.h"
 
  21 #include "wx/cocoa/mbarman.h"
 
  22 #include "wx/cocoa/autorelease.h"
 
  23 #include "wx/cocoa/objc/objc_uniquifying.h"
 
  25 #import <Foundation/NSString.h>
 
  26 #import <Foundation/NSNotification.h>
 
  27 #import <AppKit/NSMenu.h>
 
  28 #import <AppKit/NSApplication.h>
 
  29 #import <AppKit/NSWindow.h>
 
  32 #define wxUSE_FSCRIPT 0
 
  36     #import <FScript/FScriptMenuItem.h>
 
  39 // Declare setAppleMenu: in an NSApplication category since Tiger and later
 
  40 // releases support it but don't declare it as it's considered deprecated.
 
  41 @interface NSApplication(wxDeprecatedMethodsWeWantToUse)
 
  42 - (void)setAppleMenu:(NSMenu *)menu;
 
  45 // ============================================================================
 
  46 // wxMenuBarManagerObserver
 
  47 // ============================================================================
 
  48 @interface wxMenuBarManagerObserver : NSObject
 
  50     wxMenuBarManager *m_mbarman;
 
  54 - (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman;
 
  55 - (void)windowDidBecomeKey: (NSNotification *)notification;
 
  57 - (void)windowDidResignKey: (NSNotification *)notification;
 
  58 - (void)windowDidBecomeMain: (NSNotification *)notification;
 
  59 - (void)windowDidResignMain: (NSNotification *)notification;
 
  60 - (void)windowWillClose: (NSNotification *)notification;
 
  62 @end // interface wxMenuBarManagerObserver : NSObject
 
  63 WX_DECLARE_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
 
  65 @implementation wxMenuBarManagerObserver : NSObject
 
  68     wxFAIL_MSG(wxT("[wxMenuBarManagerObserver -init] should never be called!"));
 
  73 - (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman
 
  80 - (void)windowDidBecomeKey: (NSNotification *)notification
 
  83     m_mbarman->WindowDidBecomeKey(notification);
 
  87 - (void)windowDidResignKey: (NSNotification *)notification
 
  90     m_mbarman->WindowDidResignKey(notification);
 
  93 - (void)windowDidBecomeMain: (NSNotification *)notification
 
  96     m_mbarman->WindowDidBecomeMain(notification);
 
  99 - (void)windowDidResignMain: (NSNotification *)notification
 
 102     m_mbarman->WindowDidResignMain(notification);
 
 105 - (void)windowWillClose: (NSNotification *)notification
 
 108     m_mbarman->WindowWillClose(notification);
 
 112 @end // implementation wxMenuBarManagerObserver : NSObject
 
 113 WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
 
 115 // ============================================================================
 
 117 // ============================================================================
 
 118 wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL;
 
 120 static void AddFScriptItem(NSMenu *menu)
 
 123     NSMenuItem *item = [[FScriptMenuItem alloc] init];
 
 124     [menu addItem: item];
 
 131 wxMenuBarManager::wxMenuBarManager()
 
 133     m_observer = [[WX_GET_OBJC_CLASS(wxMenuBarManagerObserver) alloc]
 
 134             initWithWxMenuBarManager:this];
 
 135     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 136             selector:@selector(windowDidBecomeKey:)
 
 137             name:NSWindowDidBecomeKeyNotification object:nil];
 
 139     // HACK: Reuse the same selector and eventual C++ method and make it
 
 140     // check for whether the notification is to become key or main.
 
 141     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 142             selector:@selector(windowDidBecomeKey:)
 
 143             name:NSWindowDidBecomeMainNotification object:nil];
 
 145     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 146             selector:@selector(windowDidResignKey:)
 
 147             name:NSWindowDidResignKeyNotification object:nil];
 
 148     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 149             selector:@selector(windowDidBecomeMain:)
 
 150             name:NSWindowDidBecomeMainNotification object:nil];
 
 151     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 152             selector:@selector(windowDidResignMain:)
 
 153             name:NSWindowDidResignMainNotification object:nil];
 
 154     [[NSNotificationCenter defaultCenter] addObserver:m_observer
 
 155             selector:@selector(windowWillClose:)
 
 156             name:NSWindowWillCloseNotification object:nil];
 
 159     m_menuServices = nil;
 
 162     m_mainMenuBarInstalled = true;
 
 163     m_mainMenuBar = NULL;
 
 164     m_currentNSWindow = nil;
 
 166     NSApplication *theNSApplication = wxTheApp->GetNSApplication();
 
 167     // Create the services menu.
 
 168     m_menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
 
 169     [theNSApplication setServicesMenu:m_menuServices];
 
 171     NSMenuItem *menuitem;
 
 172     // Create the application (Apple) menu.
 
 173     m_menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
 
 175 /**/[m_menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""];
 
 176 /**/[m_menuApp addItem: [NSMenuItem separatorItem]];
 
 177 /**/AddFScriptItem(m_menuApp);
 
 178 /**/menuitem = [[NSMenuItem alloc] initWithTitle: @"Services" action:nil keyEquivalent:@""];
 
 179     [menuitem setSubmenu:m_menuServices];
 
 180     [m_menuApp addItem: menuitem];
 
 182 /**/[m_menuApp addItem: [NSMenuItem separatorItem]];
 
 183 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@""];
 
 184     [menuitem setTarget: theNSApplication];
 
 185     [m_menuApp addItem: menuitem];
 
 187 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""];
 
 188     [menuitem setTarget: theNSApplication];
 
 189     [m_menuApp addItem: menuitem];
 
 191 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
 
 192     [menuitem setTarget: theNSApplication];
 
 193     [m_menuApp addItem: menuitem];
 
 195 /**/[m_menuApp addItem: [NSMenuItem separatorItem]];
 
 196 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
 
 197     [menuitem setTarget: theNSApplication];
 
 198     [m_menuApp addItem: menuitem];
 
 201     [theNSApplication setAppleMenu:m_menuApp];
 
 203     // Create the Windows menu
 
 204     m_menuWindows = [[NSMenu alloc] initWithTitle: @"Window"];
 
 206 /**/[m_menuWindows addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""];
 
 207 /**/[m_menuWindows addItem: [NSMenuItem separatorItem]];
 
 208 /**/[m_menuWindows addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""];
 
 210     [theNSApplication setWindowsMenu:m_menuWindows];
 
 212     // Create the main menubar
 
 213     m_menuMain = [[NSMenu alloc] initWithTitle: @"wxApp Menu"];
 
 214 /**/NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@"App menu"
 
 215         /* Note: title gets clobbered by app name anyway */
 
 216         action:nil keyEquivalent:@""];
 
 217     [dummyItem setSubmenu:m_menuApp];
 
 218     [m_menuMain addItem:dummyItem];
 
 220 /**/dummyItem = [[NSMenuItem alloc] initWithTitle:@"Window"
 
 221         action:nil keyEquivalent:@""];
 
 222     [dummyItem setSubmenu:m_menuWindows];
 
 223     [m_menuMain addItem:dummyItem];
 
 226     [theNSApplication setMainMenu: m_menuMain];
 
 230 wxMenuBarManager::~wxMenuBarManager()
 
 232     [m_observer release];
 
 235 void wxMenuBarManager::CreateInstance()
 
 237     sm_mbarmanInstance = new wxMenuBarManager;
 
 240 void wxMenuBarManager::DestroyInstance()
 
 242     delete sm_mbarmanInstance;
 
 243     sm_mbarmanInstance = NULL;
 
 246 void wxMenuBarManager::SetMenuBar(wxMenuBar* menubar)
 
 248     m_mainMenuBarInstalled = false;
 
 251         [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
 
 252         [[menubar->GetNSMenu() itemAtIndex:0] setSubmenu:m_menuApp];
 
 253         [wxTheApp->GetNSApplication() setMainMenu:menubar->GetNSMenu()];
 
 259 void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar)
 
 261     m_mainMenuBar = menubar;
 
 262     if(m_mainMenuBarInstalled)
 
 266 void wxMenuBarManager::InstallMainMenu()
 
 269         SetMenuBar(m_mainMenuBar);
 
 272         m_mainMenuBarInstalled = true;
 
 273         [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
 
 274         [[m_menuMain itemAtIndex:0] setSubmenu:m_menuApp];
 
 275         [wxTheApp->GetNSApplication() setMainMenu:m_menuMain];
 
 279 void wxMenuBarManager::WindowDidBecomeKey(NSNotification *notification)
 
 281     /* NOTE: m_currentNSWindow might be destroyed but we only ever use it
 
 282        to look it up in the hash table.  Do not send messages to it. */
 
 284     /*  Update m_currentNSWindow only if we really should.  For instance,
 
 285         if a non-wx window is becoming key but a wx window remains main
 
 286         then don't change out the menubar.  However, if a non-wx window
 
 287         (whether the same window or not) is main, then switch to the
 
 288         generic menubar so the wx window that last installed a menubar
 
 289         doesn't get menu events it doesn't expect.
 
 291         If a wx window is becoming main then check to see if the key
 
 292         window is a wx window and if so do nothing because that
 
 293         is what would have been done before.
 
 295         If a non-wx window is becoming main and 
 
 297     NSString *notificationName = [notification name];
 
 298     if(NULL == notificationName)
 
 300     else if([NSWindowDidBecomeKeyNotification isEqualTo:notificationName])
 
 301     {   // This is the only one that was handled in 2.8 as shipped
 
 302         // Generally the key window can change without the main window changing.
 
 303         // The user can do this simply by clicking on something in a palette window
 
 304         // that needs to become key.
 
 305         NSWindow *newKeyWindow = [notification object];
 
 306         wxCocoaNSWindow *theWxKeyWindow = wxCocoaNSWindow::GetFromCocoa(newKeyWindow);
 
 307         if(theWxKeyWindow != NULL)
 
 308         {   // If the new key window is a wx window, handle it as before
 
 309             // even if it has not actually changed.
 
 310             m_currentNSWindow = newKeyWindow;
 
 313         {   // If the new key window is not wx then check the main window.
 
 314             NSWindow *mainWindow = [[NSApplication sharedApplication] mainWindow];
 
 315             if(m_currentNSWindow == mainWindow)
 
 316                 // Don't reset if the menubar doesn't need to change.
 
 319                 // This is strange because theoretically we should have picked this up
 
 320                 // already in the main window notification but it's possible that
 
 321                 // we simply haven't gotten it yet and will about as soon as we return.
 
 322                 // We already know that the key window isn't wx so fall back to this
 
 323                 // one and let the code go ahead and set the wx menubar if it is
 
 324                 // a wx window and set the generic one if it isn't.
 
 325                 m_currentNSWindow = mainWindow;
 
 328     else if([NSWindowDidBecomeMainNotification isEqualTo:notificationName])
 
 329     {   // Handling this is new
 
 330         // Generally the main window cannot change without the key window changing
 
 331         // because if the user clicks on a window that can become main then the
 
 332         // window will also become key.
 
 333         // However, it's possible that when it becomes main it automatically makes
 
 334         // some palette the key window.
 
 335         NSWindow *newMainWindow = [notification object];
 
 336         // If we already know about the window, bail.
 
 337         if(newMainWindow == m_currentNSWindow)
 
 341             NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
 
 342             if(keyWindow == m_currentNSWindow)
 
 343                 // if we already know about the key window, bail
 
 346             {   // As above, sort of strange.  Neither one is current.  Prefer key over main.
 
 347                 wxCocoaNSWindow *theWxMainWindow = wxCocoaNSWindow::GetFromCocoa(keyWindow);
 
 348                 if(theWxMainWindow != NULL)
 
 349                     m_currentNSWindow = keyWindow;
 
 351                     m_currentNSWindow = newMainWindow;
 
 355     m_currentNSWindow = [notification object];
 
 356     wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
 
 358         InstallMenuBarForWindow(win);
 
 364 void wxMenuBarManager::WindowDidResignKey(NSNotification *notification)
 
 368 void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification)
 
 372 void wxMenuBarManager::WindowDidResignMain(NSNotification *notification)
 
 376 void wxMenuBarManager::WindowWillClose(NSNotification *notification)
 
 381 void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win)
 
 384     wxMenuBar *menubar = win->GetAppMenuBar(win);
 
 385     wxLogTrace(wxTRACE_COCOA,wxT("Found menubar=%p for window=%p."),menubar,win);
 
 389 void wxMenuBarManager::UpdateMenuBar()
 
 391     if(m_currentNSWindow)
 
 393         wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
 
 395             InstallMenuBarForWindow(win);
 
 399 #endif // wxUSE_MENUS