]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/mbarman.mm
set m_isBeingDeleted to true (only) in SendDestroyEvent(); call it as early as possib...
[wxWidgets.git] / src / cocoa / mbarman.mm
CommitLineData
268bec5a
DE
1/////////////////////////////////////////////////////////////////////////////
2// Name: cocoa/mbarman.cpp
3// Purpose: wxMenuBarManager implementation
4// Author: David Elliott
5// Modified by:
6// Created: 2003/09/04
7// RCS-ID: $Id$
8// Copyright: (c) 2003 David Elliott
065e208e 9// Licence: wxWidgets licence
268bec5a
DE
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13#if wxUSE_MENUS
14#ifndef WX_PRECOMP
15 #include "wx/log.h"
16 #include "wx/app.h"
17 #include "wx/menu.h"
18 #include "wx/toplevel.h"
19#endif // WX_PRECOMP
20
21#include "wx/cocoa/mbarman.h"
22#include "wx/cocoa/autorelease.h"
e7e1ad7d 23#include "wx/cocoa/objc/objc_uniquifying.h"
268bec5a
DE
24
25#import <Foundation/NSString.h>
f1d04a42 26#import <Foundation/NSNotification.h>
268bec5a
DE
27#import <AppKit/NSMenu.h>
28#import <AppKit/NSApplication.h>
f1d04a42
DE
29#import <AppKit/NSWindow.h>
30
26f1176e 31#ifndef wxUSE_FSCRIPT
0206be63 32#define wxUSE_FSCRIPT 0
26f1176e
DE
33#endif
34
82ba4885
DE
35#if wxUSE_FSCRIPT
36 #import <FScript/FScriptMenuItem.h>
37#endif
38
9c41aa97
DE
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;
43@end
44
f1d04a42
DE
45// ============================================================================
46// wxMenuBarManagerObserver
47// ============================================================================
48@interface wxMenuBarManagerObserver : NSObject
49{
50 wxMenuBarManager *m_mbarman;
51}
52
53- (id)init;
54- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman;
55- (void)windowDidBecomeKey: (NSNotification *)notification;
56#if 0
57- (void)windowDidResignKey: (NSNotification *)notification;
58- (void)windowDidBecomeMain: (NSNotification *)notification;
59- (void)windowDidResignMain: (NSNotification *)notification;
60- (void)windowWillClose: (NSNotification *)notification;
61#endif // 0
62@end // interface wxMenuBarManagerObserver : NSObject
e7e1ad7d 63WX_DECLARE_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
f1d04a42
DE
64
65@implementation wxMenuBarManagerObserver : NSObject
66- (id)init
67{
2b030203 68 wxFAIL_MSG(wxT("[wxMenuBarManagerObserver -init] should never be called!"));
f1d04a42
DE
69 m_mbarman = NULL;
70 return self;
71}
72
73- (id)initWithWxMenuBarManager: (wxMenuBarManager *)mbarman
74{
75 wxASSERT(mbarman);
76 m_mbarman = mbarman;
77 return [super init];
78}
79
80- (void)windowDidBecomeKey: (NSNotification *)notification
81{
82 wxASSERT(m_mbarman);
83 m_mbarman->WindowDidBecomeKey(notification);
84}
85
86#if 0
87- (void)windowDidResignKey: (NSNotification *)notification
88{
89 wxASSERT(m_mbarman);
90 m_mbarman->WindowDidResignKey(notification);
91}
92
93- (void)windowDidBecomeMain: (NSNotification *)notification
94{
95 wxASSERT(m_mbarman);
96 m_mbarman->WindowDidBecomeMain(notification);
97}
98
99- (void)windowDidResignMain: (NSNotification *)notification
100{
101 wxASSERT(m_mbarman);
102 m_mbarman->WindowDidResignMain(notification);
103}
104
105- (void)windowWillClose: (NSNotification *)notification
106{
107 wxASSERT(m_mbarman);
108 m_mbarman->WindowWillClose(notification);
109}
110#endif // 0
111
112@end // implementation wxMenuBarManagerObserver : NSObject
e7e1ad7d 113WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
268bec5a
DE
114
115// ============================================================================
116// wxMenuBarManager
117// ============================================================================
118wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL;
119
82ba4885
DE
120static void AddFScriptItem(NSMenu *menu)
121#if wxUSE_FSCRIPT
122{
123 NSMenuItem *item = [[FScriptMenuItem alloc] init];
124 [menu addItem: item];
125 [item release];
126}
127#else
128{}
129#endif
130
268bec5a
DE
131wxMenuBarManager::wxMenuBarManager()
132{
e7e1ad7d 133 m_observer = [[WX_GET_OBJC_CLASS(wxMenuBarManagerObserver) alloc]
f1d04a42
DE
134 initWithWxMenuBarManager:this];
135 [[NSNotificationCenter defaultCenter] addObserver:m_observer
136 selector:@selector(windowDidBecomeKey:)
137 name:NSWindowDidBecomeKeyNotification object:nil];
f8aeb3e3
DE
138
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];
f1d04a42
DE
144#if 0
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];
157#endif // 0
268bec5a
DE
158 m_menuApp = nil;
159 m_menuServices = nil;
160 m_menuWindows = nil;
161 m_menuMain = nil;
268bec5a
DE
162 m_mainMenuBarInstalled = true;
163 m_mainMenuBar = NULL;
af849107 164 m_currentNSWindow = nil;
268bec5a
DE
165
166 NSApplication *theNSApplication = wxTheApp->GetNSApplication();
167 // Create the services menu.
168 m_menuServices = [[NSMenu alloc] initWithTitle: @"Services"];
169 [theNSApplication setServicesMenu:m_menuServices];
170
171 NSMenuItem *menuitem;
172 // Create the application (Apple) menu.
173 m_menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"];
174
175/**/[m_menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""];
176/**/[m_menuApp addItem: [NSMenuItem separatorItem]];
82ba4885 177/**/AddFScriptItem(m_menuApp);
268bec5a
DE
178/**/menuitem = [[NSMenuItem alloc] initWithTitle: @"Services" action:nil keyEquivalent:@""];
179 [menuitem setSubmenu:m_menuServices];
180 [m_menuApp addItem: menuitem];
181 [menuitem release];
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];
186 [menuitem release];
187/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""];
188 [menuitem setTarget: theNSApplication];
189 [m_menuApp addItem: menuitem];
190 [menuitem release];
191/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
192 [menuitem setTarget: theNSApplication];
193 [m_menuApp addItem: menuitem];
194 [menuitem release];
195/**/[m_menuApp addItem: [NSMenuItem separatorItem]];
372208c2 196/**/menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
268bec5a
DE
197 [menuitem setTarget: theNSApplication];
198 [m_menuApp addItem: menuitem];
199 [menuitem release];
200
201 [theNSApplication setAppleMenu:m_menuApp];
202
203 // Create the Windows menu
204 m_menuWindows = [[NSMenu alloc] initWithTitle: @"Window"];
205
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:@""];
209
210 [theNSApplication setWindowsMenu:m_menuWindows];
211
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];
219 [dummyItem release];
220/**/dummyItem = [[NSMenuItem alloc] initWithTitle:@"Window"
221 action:nil keyEquivalent:@""];
222 [dummyItem setSubmenu:m_menuWindows];
223 [m_menuMain addItem:dummyItem];
224 [dummyItem release];
225
226 [theNSApplication setMainMenu: m_menuMain];
227
228}
229
230wxMenuBarManager::~wxMenuBarManager()
231{
f1d04a42 232 [m_observer release];
268bec5a
DE
233}
234
235void wxMenuBarManager::CreateInstance()
236{
237 sm_mbarmanInstance = new wxMenuBarManager;
238}
239
240void wxMenuBarManager::DestroyInstance()
241{
242 delete sm_mbarmanInstance;
243 sm_mbarmanInstance = NULL;
244}
245
268bec5a
DE
246void wxMenuBarManager::SetMenuBar(wxMenuBar* menubar)
247{
248 m_mainMenuBarInstalled = false;
268bec5a
DE
249 if(menubar)
250 {
251 [[[wxTheApp->GetNSApplication() mainMenu] itemAtIndex:0] setSubmenu:nil];
252 [[menubar->GetNSMenu() itemAtIndex:0] setSubmenu:m_menuApp];
253 [wxTheApp->GetNSApplication() setMainMenu:menubar->GetNSMenu()];
254 }
f1d04a42
DE
255 else
256 InstallMainMenu();
268bec5a
DE
257}
258
259void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar)
260{
261 m_mainMenuBar = menubar;
262 if(m_mainMenuBarInstalled)
263 InstallMainMenu();
264}
265
266void wxMenuBarManager::InstallMainMenu()
267{
268 if(m_mainMenuBar)
269 SetMenuBar(m_mainMenuBar);
270 else
271 {
268bec5a
DE
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];
276 }
277}
278
f1d04a42
DE
279void wxMenuBarManager::WindowDidBecomeKey(NSNotification *notification)
280{
af849107
DE
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. */
f8aeb3e3
DE
283
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.
290
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.
294
295 If a non-wx window is becoming main and
296 */
297 NSString *notificationName = [notification name];
298 if(NULL == notificationName)
299 return;
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;
311 }
312 else
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.
317 return;
318 else
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;
326 }
327 }
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)
338 return;
339 else
340 {
341 NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
342 if(keyWindow == m_currentNSWindow)
343 // if we already know about the key window, bail
344 return;
345 else
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;
350 else
351 m_currentNSWindow = newMainWindow;
352 }
353 }
354 }
af849107
DE
355 m_currentNSWindow = [notification object];
356 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
f1d04a42
DE
357 if(win)
358 InstallMenuBarForWindow(win);
359 else
360 SetMenuBar(NULL);
361}
362
363#if 0
364void wxMenuBarManager::WindowDidResignKey(NSNotification *notification)
268bec5a 365{
268bec5a
DE
366}
367
f1d04a42 368void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification)
268bec5a 369{
268bec5a
DE
370}
371
f1d04a42 372void wxMenuBarManager::WindowDidResignMain(NSNotification *notification)
268bec5a 373{
268bec5a
DE
374}
375
f1d04a42 376void wxMenuBarManager::WindowWillClose(NSNotification *notification)
268bec5a 377{
268bec5a 378}
f1d04a42 379#endif // 0
268bec5a 380
f1d04a42 381void wxMenuBarManager::InstallMenuBarForWindow(wxCocoaNSWindow *win)
268bec5a 382{
3e84f98f 383 wxASSERT(win);
8ded703d 384 wxMenuBar *menubar = win->GetAppMenuBar(win);
48580976 385 wxLogTrace(wxTRACE_COCOA,wxT("Found menubar=%p for window=%p."),menubar,win);
268bec5a
DE
386 SetMenuBar(menubar);
387}
388
243f5c2d 389void wxMenuBarManager::UpdateMenuBar()
268bec5a 390{
af849107
DE
391 if(m_currentNSWindow)
392 {
393 wxCocoaNSWindow *win = wxCocoaNSWindow::GetFromCocoa(m_currentNSWindow);
394 if(win)
395 InstallMenuBarForWindow(win);
396 }
268bec5a
DE
397}
398
399#endif // wxUSE_MENUS