Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / cocoa / mbarman.mm
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
62 WX_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
112 WX_IMPLEMENT_GET_OBJC_CLASS(wxMenuBarManagerObserver,NSObject)
113
114 // ============================================================================
115 // wxMenuBarManager
116 // ============================================================================
117 wxMenuBarManager *wxMenuBarManager::sm_mbarmanInstance = NULL;
118
119 static 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
130 wxMenuBarManager::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
229 wxMenuBarManager::~wxMenuBarManager()
230 {
231     [m_observer release];
232 }
233
234 void wxMenuBarManager::CreateInstance()
235 {
236     sm_mbarmanInstance = new wxMenuBarManager;
237 }
238
239 void wxMenuBarManager::DestroyInstance()
240 {
241     delete sm_mbarmanInstance;
242     sm_mbarmanInstance = NULL;
243 }
244
245 void 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
258 void wxMenuBarManager::SetMainMenuBar(wxMenuBar* menubar)
259 {
260     m_mainMenuBar = menubar;
261     if(m_mainMenuBarInstalled)
262         InstallMainMenu();
263 }
264
265 void 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
278 void 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
363 void wxMenuBarManager::WindowDidResignKey(NSNotification *notification)
364 {
365 }
366
367 void wxMenuBarManager::WindowDidBecomeMain(NSNotification *notification)
368 {
369 }
370
371 void wxMenuBarManager::WindowDidResignMain(NSNotification *notification)
372 {
373 }
374
375 void wxMenuBarManager::WindowWillClose(NSNotification *notification)
376 {
377 }
378 #endif // 0
379
380 void 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
388 void 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