1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/mbarman.mm
3 // Purpose: wxMenuBarManager implementation
4 // Author: David Elliott
7 // Copyright: (c) 2003 David Elliott
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
17 #include "wx/toplevel.h"
20 #include "wx/cocoa/mbarman.h"
21 #include "wx/cocoa/autorelease.h"
22 #include "wx/cocoa/objc/objc_uniquifying.h"
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>
31 #define wxUSE_FSCRIPT 0
35 #import <FScript/FScriptMenuItem.h>
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;
44 // ============================================================================
45 // wxMenuBarManagerObserver
46 // ============================================================================
47 @interface wxMenuBarManagerObserver : NSObject
49 wxMenuBarManager *m_mbarman;
53 - (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman;
54 - (void)windowDidBecomeKey: (NSNotification *)notification;
56 - (void)windowDidResignKey: (NSNotification *)notification;
57 - (void)windowDidBecomeMain: (NSNotification *)notification;
58 - (void)windowDidResignMain: (NSNotification *)notification;
59 - (void)windowWillClose: (NSNotification *)notification;
61 @end // interface wxMenuBarManagerObserver : NSObject
62 WX_DECLARE_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
64 @implementation wxMenuBarManagerObserver : NSObject
67 wxFAIL_MSG(wxT("[wxMenuBarManagerObserver -init] should never be called!"));
72 - (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman
79 - (void)windowDidBecomeKey: (NSNotification *)notification
82 m_mbarman->WindowDidBecomeKey(notification);
86 - (void)windowDidResignKey: (NSNotification *)notification
89 m_mbarman->WindowDidResignKey(notification);
92 - (void)windowDidBecomeMain: (NSNotification *)notification
95 m_mbarman->WindowDidBecomeMain(notification);
98 - (void)windowDidResignMain: (NSNotification *)notification
101 m_mbarman->WindowDidResignMain(notification);
104 - (void)windowWillClose: (NSNotification *)notification
107 m_mbarman->WindowWillClose(notification);
111 @end // implementation wxMenuBarManagerObserver : NSObject
112 WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
114 // ============================================================================
116 // ============================================================================
117 wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL;
119 static void AddFScriptItem(NSMenu *menu)
122 NSMenuItem *item = [[FScriptMenuItem alloc] init];
123 [menu addItem: item];
130 wxMenuBarManager::wxMenuBarManager()
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];
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];
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];
158 m_menuServices = nil;
161 m_mainMenuBarInstalled = true;
162 m_mainMenuBar = NULL;
163 m_currentNSWindow = nil;
165 NSApplication *theNSApplication = wxTheApp->GetNSApplication();
166 // Create the services menu.
167 m_menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
168 [theNSApplication setServicesMenu:m_menuServices];
170 NSMenuItem *menuitem;
171 // Create the application (Apple) menu.
172 m_menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
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];
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];
186 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""];
187 [menuitem setTarget: theNSApplication];
188 [m_menuApp addItem: menuitem];
190 /**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
191 [menuitem setTarget: theNSApplication];
192 [m_menuApp addItem: menuitem];
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];
200 [theNSApplication setAppleMenu:m_menuApp];
202 // Create the Windows menu
203 m_menuWindows = [[NSMenu alloc] initWithTitle: @"Window"];
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:@""];
209 [theNSApplication setWindowsMenu:m_menuWindows];
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];
219 /**/dummyItem = [[NSMenuItem alloc] initWithTitle:@"Window"
220 action:nil keyEquivalent:@""];
221 [dummyItem setSubmenu:m_menuWindows];
222 [m_menuMain addItem:dummyItem];
225 [theNSApplication setMainMenu: m_menuMain];
229 wxMenuBarManager::~wxMenuBarManager()
231 [m_observer release];
234 void wxMenuBarManager::CreateInstance()
236 sm_mbarmanInstance = new wxMenuBarManager;
239 void wxMenuBarManager::DestroyInstance()
241 delete sm_mbarmanInstance;
242 sm_mbarmanInstance = NULL;
245 void wxMenuBarManager::SetMenuBar(wxMenuBar* menubar)
247 m_mainMenuBarInstalled = false;
250 [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
251 [[menubar->GetNSMenu() itemAtIndex:0] setSubmenu:m_menuApp];
252 [wxTheApp->GetNSApplication() setMainMenu:menubar->GetNSMenu()];
258 void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar)
260 m_mainMenuBar = menubar;
261 if(m_mainMenuBarInstalled)
265 void wxMenuBarManager::InstallMainMenu()
268 SetMenuBar(m_mainMenuBar);
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];
278 void wxMenuBarManager::WindowDidBecomeKey(NSNotification *notification)
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. */
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.
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.
294 If a non-wx window is becoming main and
296 NSString *notificationName = [notification name];
297 if(NULL == notificationName)
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;
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.
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;
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)
340 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
341 if(keyWindow == m_currentNSWindow)
342 // if we already know about the key window, bail
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;
350 m_currentNSWindow = newMainWindow;
354 m_currentNSWindow = [notification object];
355 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
357 InstallMenuBarForWindow(win);
363 void wxMenuBarManager::WindowDidResignKey(NSNotification *notification)
367 void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification)
371 void wxMenuBarManager::WindowDidResignMain(NSNotification *notification)
375 void wxMenuBarManager::WindowWillClose(NSNotification *notification)
380 void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win)
383 wxMenuBar *menubar = win->GetAppMenuBar(win);
384 wxLogTrace(wxTRACE_COCOA,wxT("Found menubar=%p for window=%p."),menubar,win);
388 void wxMenuBarManager::UpdateMenuBar()
390 if(m_currentNSWindow)
392 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
394 InstallMenuBarForWindow(win);
398 #endif // wxUSE_MENUS