1///////////////////////////////////////////////////////////////////////////// 2// Name: src/cocoa/mbarman.mm 3// Purpose: wxMenuBarManager implementation 4// Author: David Elliott 5// Modified by: 6// Created: 2003/09/04 7// Copyright: (c) 2003 David Elliott 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12#if wxUSE_MENUS 13#ifndef WX_PRECOMP 14 #include "wx/log.h" 15 #include "wx/app.h" 16 #include "wx/menu.h" 17 #include "wx/toplevel.h" 18#endif // WX_PRECOMP 19 20#include "wx/cocoa/mbarman.h" 21#include "wx/cocoa/autorelease.h" 22#include "wx/cocoa/objc/objc_uniquifying.h" 23 24#import <Foundation/NSString.h> 25#import <Foundation/NSNotification.h> 26#import <AppKit/NSMenu.h> 27#import <AppKit/NSApplication.h> 28#import <AppKit/NSWindow.h> 29 30#ifndef wxUSE_FSCRIPT 31#define wxUSE_FSCRIPT 0 32#endif 33 34#if wxUSE_FSCRIPT 35 #import <FScript/FScriptMenuItem.h> 36#endif 37 38// Declare setAppleMenu: in an NSApplication category since Tiger and later 39// releases support it but don't declare it as it's considered deprecated. 40@interface NSApplication(wxDeprecatedMethodsWeWantToUse) 41- (void)setAppleMenu:(NSMenu *)menu; 42@end 43 44// ============================================================================ 45// wxMenuBarManagerObserver 46// ============================================================================ 47@interface wxMenuBarManagerObserver : NSObject 48{ 49 wxMenuBarManager *m_mbarman; 50} 51 52- (id)init; 53- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman; 54- (void)windowDidBecomeKey: (NSNotification *)notification; 55#if 0 56- (void)windowDidResignKey: (NSNotification *)notification; 57- (void)windowDidBecomeMain: (NSNotification *)notification; 58- (void)windowDidResignMain: (NSNotification *)notification; 59- (void)windowWillClose: (NSNotification *)notification; 60#endif // 0 61@end // interface wxMenuBarManagerObserver : NSObject 62WX_DECLARE_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject) 63 64@implementation wxMenuBarManagerObserver : NSObject 65- (id)init 66{ 67 wxFAIL_MSG(wxT("[wxMenuBarManagerObserver -init] should never be called!")); 68 m_mbarman = NULL; 69 return self; 70} 71 72- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman 73{ 74 wxASSERT(mbarman); 75 m_mbarman = mbarman; 76 return [super init]; 77} 78 79- (void)windowDidBecomeKey: (NSNotification *)notification 80{ 81 wxASSERT(m_mbarman); 82 m_mbarman->WindowDidBecomeKey(notification); 83} 84 85#if 0 86- (void)windowDidResignKey: (NSNotification *)notification 87{ 88 wxASSERT(m_mbarman); 89 m_mbarman->WindowDidResignKey(notification); 90} 91 92- (void)windowDidBecomeMain: (NSNotification *)notification 93{ 94 wxASSERT(m_mbarman); 95 m_mbarman->WindowDidBecomeMain(notification); 96} 97 98- (void)windowDidResignMain: (NSNotification *)notification 99{ 100 wxASSERT(m_mbarman); 101 m_mbarman->WindowDidResignMain(notification); 102} 103 104- (void)windowWillClose: (NSNotification *)notification 105{ 106 wxASSERT(m_mbarman); 107 m_mbarman->WindowWillClose(notification); 108} 109#endif // 0 110 111@end // implementation wxMenuBarManagerObserver : NSObject 112WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject) 113 114// ============================================================================ 115// wxMenuBarManager 116// ============================================================================ 117wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL; 118 119static void AddFScriptItem(NSMenu *menu) 120#if wxUSE_FSCRIPT 121{ 122 NSMenuItem *item = [[FScriptMenuItem alloc] init]; 123 [menu addItem: item]; 124 [item release]; 125} 126#else 127{} 128#endif 129 130wxMenuBarManager::wxMenuBarManager() 131{ 132 m_observer = [[WX_GET_OBJC_CLASS(wxMenuBarManagerObserver) alloc] 133 initWithWxMenuBarManager:this]; 134 [[NSNotificationCenter defaultCenter] addObserver:m_observer 135 selector:@selector(windowDidBecomeKey:) 136 name:NSWindowDidBecomeKeyNotification object:nil]; 137 138 // HACK: Reuse the same selector and eventual C++ method and make it 139 // check for whether the notification is to become key or main. 140 [[NSNotificationCenter defaultCenter] addObserver:m_observer 141 selector:@selector(windowDidBecomeKey:) 142 name:NSWindowDidBecomeMainNotification object:nil]; 143#if 0 144 [[NSNotificationCenter defaultCenter] addObserver:m_observer 145 selector:@selector(windowDidResignKey:) 146 name:NSWindowDidResignKeyNotification object:nil]; 147 [[NSNotificationCenter defaultCenter] addObserver:m_observer 148 selector:@selector(windowDidBecomeMain:) 149 name:NSWindowDidBecomeMainNotification object:nil]; 150 [[NSNotificationCenter defaultCenter] addObserver:m_observer 151 selector:@selector(windowDidResignMain:) 152 name:NSWindowDidResignMainNotification object:nil]; 153 [[NSNotificationCenter defaultCenter] addObserver:m_observer 154 selector:@selector(windowWillClose:) 155 name:NSWindowWillCloseNotification object:nil]; 156#endif // 0 157 m_menuApp = nil; 158 m_menuServices = nil; 159 m_menuWindows = nil; 160 m_menuMain = nil; 161 m_mainMenuBarInstalled = true; 162 m_mainMenuBar = NULL; 163 m_currentNSWindow = nil; 164 165 NSApplication *theNSApplication = wxTheApp->GetNSApplication(); 166 // Create the services menu. 167 m_menuServices = [[NSMenu alloc] initWithTitle: @"Services"]; 168 [theNSApplication setServicesMenu:m_menuServices]; 169 170 NSMenuItem *menuitem; 171 // Create the application (Apple) menu. 172 m_menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"]; 173 174/**/[m_menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""]; 175/**/[m_menuApp addItem: [NSMenuItem separatorItem]]; 176/**/AddFScriptItem(m_menuApp); 177/**/menuitem = [[NSMenuItem alloc] initWithTitle: @"Services" action:nil keyEquivalent:@""]; 178 [menuitem setSubmenu:m_menuServices]; 179 [m_menuApp addItem: menuitem]; 180 [menuitem release]; 181/**/[m_menuApp addItem: [NSMenuItem separatorItem]]; 182/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide" action:@selector(hide:) keyEquivalent:@""]; 183 [menuitem setTarget: theNSApplication]; 184 [m_menuApp addItem: menuitem]; 185 [menuitem release]; 186/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""]; 187 [menuitem setTarget: theNSApplication]; 188 [m_menuApp addItem: menuitem]; 189 [menuitem release]; 190/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; 191 [menuitem setTarget: theNSApplication]; 192 [m_menuApp addItem: menuitem]; 193 [menuitem release]; 194/**/[m_menuApp addItem: [NSMenuItem separatorItem]]; 195/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]; 196 [menuitem setTarget: theNSApplication]; 197 [m_menuApp addItem: menuitem]; 198 [menuitem release]; 199 200 [theNSApplication setAppleMenu:m_menuApp]; 201 202 // Create the Windows menu 203 m_menuWindows = [[NSMenu alloc] initWithTitle: @"Window"]; 204 205/**/[m_menuWindows addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""]; 206/**/[m_menuWindows addItem: [NSMenuItem separatorItem]]; 207/**/[m_menuWindows addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; 208 209 [theNSApplication setWindowsMenu:m_menuWindows]; 210 211 // Create the main menubar 212 m_menuMain = [[NSMenu alloc] initWithTitle: @"wxApp Menu"]; 213/**/NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@"App menu" 214 /* Note: title gets clobbered by app name anyway */ 215 action:nil keyEquivalent:@""]; 216 [dummyItem setSubmenu:m_menuApp]; 217 [m_menuMain addItem:dummyItem]; 218 [dummyItem release]; 219/**/dummyItem = [[NSMenuItem alloc] initWithTitle:@"Window" 220 action:nil keyEquivalent:@""]; 221 [dummyItem setSubmenu:m_menuWindows]; 222 [m_menuMain addItem:dummyItem]; 223 [dummyItem release]; 224 225 [theNSApplication setMainMenu: m_menuMain]; 226 227} 228 229wxMenuBarManager::~wxMenuBarManager() 230{ 231 [m_observer release]; 232} 233 234void wxMenuBarManager::CreateInstance() 235{ 236 sm_mbarmanInstance = new wxMenuBarManager; 237} 238 239void wxMenuBarManager::DestroyInstance() 240{ 241 delete sm_mbarmanInstance; 242 sm_mbarmanInstance = NULL; 243} 244 245void wxMenuBarManager::SetMenuBar(wxMenuBar* menubar) 246{ 247 m_mainMenuBarInstalled = false; 248 if(menubar) 249 { 250 [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil]; 251 [[menubar->GetNSMenu() itemAtIndex:0] setSubmenu:m_menuApp]; 252 [wxTheApp->GetNSApplication() setMainMenu:menubar->GetNSMenu()]; 253 } 254 else 255 InstallMainMenu(); 256} 257 258void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar) 259{ 260 m_mainMenuBar = menubar; 261 if(m_mainMenuBarInstalled) 262 InstallMainMenu(); 263} 264 265void wxMenuBarManager::InstallMainMenu() 266{ 267 if(m_mainMenuBar) 268 SetMenuBar(m_mainMenuBar); 269 else 270 { 271 m_mainMenuBarInstalled = true; 272 [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil]; 273 [[m_menuMain itemAtIndex:0] setSubmenu:m_menuApp]; 274 [wxTheApp->GetNSApplication() setMainMenu:m_menuMain]; 275 } 276} 277 278void wxMenuBarManager::WindowDidBecomeKey(NSNotification *notification) 279{ 280 /* NOTE: m_currentNSWindow might be destroyed but we only ever use it 281 to look it up in the hash table. Do not send messages to it. */ 282 283 /* Update m_currentNSWindow only if we really should. For instance, 284 if a non-wx window is becoming key but a wx window remains main 285 then don't change out the menubar. However, if a non-wx window 286 (whether the same window or not) is main, then switch to the 287 generic menubar so the wx window that last installed a menubar 288 doesn't get menu events it doesn't expect. 289 290 If a wx window is becoming main then check to see if the key 291 window is a wx window and if so do nothing because that 292 is what would have been done before. 293 294 If a non-wx window is becoming main and 295 */ 296 NSString *notificationName = [notification name]; 297 if(NULL == notificationName) 298 return; 299 else if([NSWindowDidBecomeKeyNotification isEqualTo:notificationName]) 300 { // This is the only one that was handled in 2.8 as shipped 301 // Generally the key window can change without the main window changing. 302 // The user can do this simply by clicking on something in a palette window 303 // that needs to become key. 304 NSWindow *newKeyWindow = [notification object]; 305 wxCocoaNSWindow *theWxKeyWindow = wxCocoaNSWindow::GetFromCocoa(newKeyWindow); 306 if(theWxKeyWindow != NULL) 307 { // If the new key window is a wx window, handle it as before 308 // even if it has not actually changed. 309 m_currentNSWindow = newKeyWindow; 310 } 311 else 312 { // If the new key window is not wx then check the main window. 313 NSWindow *mainWindow = [[NSApplication sharedApplication] mainWindow]; 314 if(m_currentNSWindow == mainWindow) 315 // Don't reset if the menubar doesn't need to change. 316 return; 317 else 318 // This is strange because theoretically we should have picked this up 319 // already in the main window notification but it's possible that 320 // we simply haven't gotten it yet and will about as soon as we return. 321 // We already know that the key window isn't wx so fall back to this 322 // one and let the code go ahead and set the wx menubar if it is 323 // a wx window and set the generic one if it isn't. 324 m_currentNSWindow = mainWindow; 325 } 326 } 327 else if([NSWindowDidBecomeMainNotification isEqualTo:notificationName]) 328 { // Handling this is new 329 // Generally the main window cannot change without the key window changing 330 // because if the user clicks on a window that can become main then the 331 // window will also become key. 332 // However, it's possible that when it becomes main it automatically makes 333 // some palette the key window. 334 NSWindow *newMainWindow = [notification object]; 335 // If we already know about the window, bail. 336 if(newMainWindow == m_currentNSWindow) 337 return; 338 else 339 { 340 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; 341 if(keyWindow == m_currentNSWindow) 342 // if we already know about the key window, bail 343 return; 344 else 345 { // As above, sort of strange. Neither one is current. Prefer key over main. 346 wxCocoaNSWindow *theWxMainWindow = wxCocoaNSWindow::GetFromCocoa(keyWindow); 347 if(theWxMainWindow != NULL) 348 m_currentNSWindow = keyWindow; 349 else 350 m_currentNSWindow = newMainWindow; 351 } 352 } 353 } 354 m_currentNSWindow = [notification object]; 355 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow); 356 if(win) 357 InstallMenuBarForWindow(win); 358 else 359 SetMenuBar(NULL); 360} 361 362#if 0 363void wxMenuBarManager::WindowDidResignKey(NSNotification *notification) 364{ 365} 366 367void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification) 368{ 369} 370 371void wxMenuBarManager::WindowDidResignMain(NSNotification *notification) 372{ 373} 374 375void wxMenuBarManager::WindowWillClose(NSNotification *notification) 376{ 377} 378#endif // 0 379 380void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win) 381{ 382 wxASSERT(win); 383 wxMenuBar *menubar = win->GetAppMenuBar(win); 384 wxLogTrace(wxTRACE_COCOA,wxT("Found menubar=%p for window=%p."),menubar,win); 385 SetMenuBar(menubar); 386} 387 388void wxMenuBarManager::UpdateMenuBar() 389{ 390 if(m_currentNSWindow) 391 { 392 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow); 393 if(win) 394 InstallMenuBarForWindow(win); 395 } 396} 397 398#endif // wxUSE_MENUS 399