Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/app.mm
3 // Purpose:     wxApp
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2002/11/27
7 // Copyright:   (c) David Elliott
8 //              Software 2000 Ltd.
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #include "wx/app.h"
15
16 #ifndef WX_PRECOMP
17     #include "wx/intl.h"
18     #include "wx/log.h"
19     #include "wx/module.h"
20 #endif
21
22 #include "wx/cocoa/ObjcRef.h"
23 #include "wx/cocoa/autorelease.h"
24 #include "wx/cocoa/mbarman.h"
25 #include "wx/cocoa/NSApplication.h"
26
27 #include "wx/cocoa/dc.h"
28
29 #import <AppKit/NSApplication.h>
30 #import <Foundation/NSRunLoop.h>
31 #import <Foundation/NSThread.h>
32 #import <AppKit/NSEvent.h>
33 #import <Foundation/NSString.h>
34 #import <Foundation/NSNotification.h>
35 #import <AppKit/NSCell.h>
36
37 bool      wxApp::sm_isEmbedded = false; // Normally we're not a plugin
38
39 // wxNSApplicationObserver singleton.
40 static wxObjcAutoRefFromAlloc<wxNSApplicationObserver*> sg_cocoaAppObserver = [[WX_GET_OBJC_CLASS(wxNSApplicationObserver) alloc] init];
41
42 // ========================================================================
43 // wxNSApplicationDelegate
44 // ========================================================================
45 @implementation wxNSApplicationDelegate : NSObject
46
47 // NOTE: Terminate means that the event loop does NOT return and thus
48 // cleanup code doesn't properly execute.  Furthermore, wxWidgets has its
49 // own exit on frame delete mechanism.
50 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
51 {
52     return NO;
53 }
54
55 @end // implementation wxNSApplicationDelegate : NSObject
56 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationDelegate,NSObject)
57
58 // ========================================================================
59 // wxNSApplicationObserver
60 // ========================================================================
61 @implementation wxNSApplicationObserver : NSObject
62
63 - (void)applicationWillBecomeActive:(NSNotification *)notification
64 {
65     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
66 }
67
68 - (void)applicationDidBecomeActive:(NSNotification *)notification
69 {
70     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
71 }
72
73 - (void)applicationWillResignActive:(NSNotification *)notification
74 {
75     wxTheApp->CocoaDelegate_applicationWillResignActive();
76 }
77
78 - (void)applicationDidResignActive:(NSNotification *)notification
79 {
80     wxTheApp->CocoaDelegate_applicationDidResignActive();
81 }
82
83 - (void)applicationWillUpdate:(NSNotification *)notification;
84 {
85     wxTheApp->CocoaDelegate_applicationWillUpdate();
86 }
87
88 - (void)controlTintChanged:(NSNotification *)notification
89 {
90     wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
91 }
92
93 @end // implementation wxNSApplicationObserver : NSObject
94 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationObserver,NSObject)
95
96 // ========================================================================
97 // wxApp
98 // ========================================================================
99
100 // ----------------------------------------------------------------------------
101 // wxApp Static member initialization
102 // ----------------------------------------------------------------------------
103 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
104
105 // ----------------------------------------------------------------------------
106 // wxApp initialization/cleanup
107 // ----------------------------------------------------------------------------
108 bool wxApp::Initialize(int& argc, wxChar **argv)
109 {
110     wxAutoNSAutoreleasePool pool;
111     m_cocoaMainThread = [NSThread currentThread];
112     // Mac OS X passes a process serial number command line argument when
113     // the application is launched from the Finder. This argument must be
114     // removed from the command line arguments before being handled by the
115     // application (otherwise applications would need to handle it)
116     if ( argc > 1 )
117     {
118         static const wxChar *ARG_PSN = wxT("-psn_");
119         if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
120         {
121             // remove this argument
122             --argc;
123             memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
124         }
125     }
126
127     /*
128         Cocoa supports -Key value options which set the user defaults key "Key"
129         to the value "value"  Some of them are very handy for debugging like
130         -NSShowAllViews YES.  Cocoa picks these up from the real argv so
131         our removal of them from the wx copy of it does not affect Cocoa's
132         ability to see them.
133
134         We basically just assume that any "-NS" option and its following
135         argument needs to be removed from argv.  We hope that user code does
136         not expect to see -NS options and indeed it's probably a safe bet
137         since most user code accepting options is probably using the
138         double-dash GNU-style syntax.
139      */
140     for(int i=1; i < argc; ++i)
141     {
142         static const wxChar *ARG_NS = wxT("-NS");
143         static const int ARG_NS_LEN = wxStrlen(ARG_NS);
144         if( wxStrncmp(argv[i], ARG_NS, ARG_NS_LEN) == 0 )
145         {
146             // Only eat this option if it has an argument
147             if( (i + 1) < argc )
148             {
149                 argc -= 2;
150                 memmove(argv + i, argv + i + 2, argc * sizeof(wxChar*));
151                 // drop back one position so the next run through the loop
152                 // reprocesses the argument at our current index.
153                 --i;
154             }
155         }
156     }
157
158     return wxAppBase::Initialize(argc, argv);
159 }
160
161 void wxApp::CleanUp()
162 {
163     wxAutoNSAutoreleasePool pool;
164
165     wxCocoaDCImpl::CocoaShutdownTextSystem();
166     wxMenuBarManager::DestroyInstance();
167
168     [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
169     if(!sm_isEmbedded)
170     {
171         [m_cocoaApp setDelegate:nil];
172         [m_cocoaAppDelegate release];
173         m_cocoaAppDelegate = NULL;
174     }
175
176     wxAppBase::CleanUp();
177 }
178
179 // ----------------------------------------------------------------------------
180 // wxApp creation
181 // ----------------------------------------------------------------------------
182 wxApp::wxApp()
183 {
184     m_topWindow = NULL;
185
186     argc = 0;
187 #if !wxUSE_UNICODE
188     argv = NULL;
189 #endif
190     m_cocoaApp = NULL;
191     m_cocoaAppDelegate = NULL;
192 }
193
194 void wxApp::CocoaDelegate_applicationWillBecomeActive()
195 {
196 }
197
198 void wxApp::CocoaDelegate_applicationDidBecomeActive()
199 {
200 }
201
202 void wxApp::CocoaDelegate_applicationWillResignActive()
203 {
204     wxTopLevelWindowCocoa::DeactivatePendingWindow();
205 }
206
207 void wxApp::CocoaDelegate_applicationDidResignActive()
208 {
209 }
210
211 bool wxApp::OnInitGui()
212 {
213     wxAutoNSAutoreleasePool pool;
214     if(!wxAppBase::OnInitGui())
215         return false;
216
217     // Create the app using the sharedApplication method
218     m_cocoaApp = [NSApplication sharedApplication];
219
220     if(!sm_isEmbedded)
221     {
222         // Enable response to application delegate messages
223         m_cocoaAppDelegate = [[WX_GET_OBJC_CLASS(wxNSApplicationDelegate) alloc] init];
224         [m_cocoaApp setDelegate:m_cocoaAppDelegate];
225     }
226
227     // Enable response to "delegate" messages on the notification observer
228     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
229         selector:@selector(applicationWillBecomeActive:)
230         name:NSApplicationWillBecomeActiveNotification object:nil];
231
232     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
233         selector:@selector(applicationDidBecomeActive:)
234         name:NSApplicationDidBecomeActiveNotification object:nil];
235
236     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
237         selector:@selector(applicationWillResignActive:)
238         name:NSApplicationWillResignActiveNotification object:nil];
239
240     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
241         selector:@selector(applicationDidResignActive:)
242         name:NSApplicationDidResignActiveNotification object:nil];
243
244     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
245         selector:@selector(applicationWillUpdate:)
246         name:NSApplicationWillUpdateNotification object:nil];
247
248     // Enable response to system notifications
249     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
250         selector:@selector(controlTintChanged:)
251         name:NSControlTintDidChangeNotification object:nil];
252
253     if(!sm_isEmbedded)
254         wxMenuBarManager::CreateInstance();
255
256     wxCocoaDCImpl::CocoaInitializeTextSystem();
257     return true;
258 }
259
260 wxApp::~wxApp()
261 {
262     if(m_cfRunLoopIdleObserver != NULL)
263     {
264         // Invalidate the observer which also removes it from the run loop.
265         CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
266         // Release the ref as we don't need it anymore.
267         m_cfRunLoopIdleObserver.reset();
268     }
269 }
270
271 bool wxApp::CallOnInit()
272 {
273 //    wxAutoNSAutoreleasePool pool;
274     return OnInit();
275 }
276
277 bool wxApp::OnInit()
278 {
279     if(!wxAppBase::OnInit())
280         return false;
281
282     return true;
283 }
284
285 void wxApp::Exit()
286 {
287     wxApp::CleanUp();
288
289     wxAppConsole::Exit();
290 }
291
292 void wxApp::WakeUpIdle()
293 {
294     /*  When called from the main thread the NSAutoreleasePool managed by
295         the [NSApplication run] method would ordinarily be in place and so
296         one would think a pool here would be unnecessary.
297
298         However, when called from a different thread there is usually no
299         NSAutoreleasePool in place because wxThread has no knowledge of
300         wxCocoa.  The pool here is generally only ever going to contain
301         the NSEvent we create with the factory method.  As soon as we add
302         it to the main event queue with postEvent:atStart: it is retained
303         and so safe for our pool to release.
304      */
305     wxAutoNSAutoreleasePool pool;
306     /*  NOTE: This is a little heavy handed.  What this does is cause an
307         AppKit NSEvent to be added to NSApplication's queue (which is always
308         on the main thread).  This will cause the main thread runloop to
309         exit which returns control to nextEventMatchingMask which returns
310         the event which is then sent with sendEvent: and essentially dropped
311         since it's not for a window (windowNumber 0) and NSApplication
312         certainly doesn't understand it.
313
314         With the exception of wxEventLoop::Exit which uses us to cause the
315         runloop to exit and return to the NSApplication event loop, most
316         callers only need wx idle to happen, or more specifically only really
317         need to ensure that ProcessPendingEvents is called which is currently
318         done without exiting the runloop.
319
320         Be careful if you decide to change the implementation of this method
321         as wxEventLoop::Exit depends on the current behaviour.
322      */
323     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
324             location:NSZeroPoint modifierFlags:NSAnyEventMask
325             timestamp:0 windowNumber:0 context:nil
326             subtype:0 data1:0 data2:0] atStart:NO];
327 }
328
329 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
330 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
331 {
332     static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
333 }
334
335 #if 0
336 static int sg_cApplicationWillUpdate = 0;
337 #endif
338
339 /*!
340     Invoked from the applicationWillUpdate notification observer.  See the
341     NSApplication documentation for the official statement on when this
342     will be called.  Since it can be hard to understand for a Cocoa newbie
343     I'll try to explain it here as it relates to wxCocoa.
344
345     Basically, we get called from within nextEventMatchingMask if and only
346     if any user code told the application to send the update notification
347     (sort of like a request for idle events).  However, unlike wx idle events,
348     this notification is sent quite often, nearly every time through the loop
349     because nearly every control tells the application to send it.
350
351     Because wx idle events are only supposed to be sent when the event loop
352     is about to block we instead schedule a function to be called just
353     before the run loop waits and send the idle events from there.
354
355     It also has the desirable effect of only sending the wx idle events when
356     the event loop is actually going to block.  If the event loop is being
357     pumped manualy (e.g. like a PeekMessage) then the kCFRunLoopBeforeWaiting
358     observer never fires.  Our Yield() method depends on this because sending
359     idle events from within Yield would be bad.
360
361     Normally you might think that we could just set the observer up once and
362     leave it attached.  However, this is problematic because our run loop
363     observer calls user code (the idle handlers) which can actually display
364     modal dialogs.  Displaying a modal dialog causes reentry of the event
365     loop, usually in a different run loop mode than the main loop (e.g. in
366     modal-dialog mode instead of default mode).  Because we only register the
367     observer with the run loop mode at the time of this call, it won't be
368     called from a modal loop.
369
370     We want it to be called and thus we need a new observer.
371  */
372 void wxApp::CocoaDelegate_applicationWillUpdate()
373 {
374     wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
375
376 //    CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
377     CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
378     wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
379
380     /*  If we have an observer and that observer is for the wrong run loop
381         mode then invalidate it and release it.
382      */
383     if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
384     {
385         CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
386         m_cfRunLoopIdleObserver.reset();
387     }
388 #if 0
389     ++sg_cApplicationWillUpdate;
390 #endif
391     /*  This will be true either on the first call or when the above code has
392         invalidated and released the exisiting observer.
393      */
394     if(m_cfRunLoopIdleObserver == NULL)
395     {
396         // Enable idle event handling
397         CFRunLoopObserverContext observerContext =
398         {   0
399         ,   this
400         ,   NULL
401         ,   NULL
402         ,   NULL
403         };
404         /*  NOTE: I can't recall why we don't just let the observer repeat
405             instead of invalidating itself each time it fires thus requiring
406             it to be recreated for each shot but there was if I remember
407             some good (but very obscure) reason for it.
408
409             On the other hand, I could be wrong so don't take that as gospel.
410          */
411         m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
412         m_cfObservedRunLoopMode = cfRunLoopMode;
413         CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
414     }
415 }
416
417 static inline bool FakeNeedMoreIdle()
418 {
419 #if 0
420 // Return true on every 10th call.
421     static int idleCount = 0;
422     return ++idleCount % 10;
423 #else
424     return false;
425 #endif
426 }
427
428 /*!
429     Called by CFRunLoop just before waiting.  This is the appropriate time to
430     send idle events.  Unlike other ports, we don't peek the queue for events
431     and stop idling if there is one.  Instead, if the user requests more idle
432     events we tell Cocoa to send us an applicationWillUpdate notification
433     which will cause our observer of that notification to tell CFRunLoop to
434     call us before waiting which will cause us to be fired again but only
435     after exhausting the event queue.
436
437     The reason we do it this way is that peeking for an event causes CFRunLoop
438     to reenter and fire off its timers, observers, and sources which we're
439     better off avoiding.  Doing it this way, we basically let CFRunLoop do the
440     work of peeking for the next event which is much nicer.
441  */
442 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
443 {
444     // Ensure that CocoaDelegate_applicationWillUpdate will recreate us.
445     // We've already been invalidated by CFRunLoop because we are one-shot.
446     m_cfRunLoopIdleObserver.reset();
447 #if 0
448     wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
449     sg_cApplicationWillUpdate = 0;
450 #else
451     wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
452 #endif
453     if( ProcessIdle() || FakeNeedMoreIdle() )
454     {
455         wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
456         [NSApp setWindowsNeedUpdate:YES];
457     }
458     else
459     {
460         wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
461     }
462 }
463
464 /*  A note about Cocoa's event loops vs. run loops:
465
466     It's important to understand that Cocoa has a two-level event loop.  The
467     outer level is run by NSApplication and can only ever happen on the main
468     thread. The nextEventMatchingMask:untilDate:inMode:dequeue: method returns
469     the next event which is then given to sendEvent: to send it.  These
470     methods are defined in NSApplication and are thus part of AppKit.
471
472     Events (NSEvent) are only sent due to actual user actions like clicking
473     the mouse or moving the mouse or pressing a key and so on.  There are no
474     paint events; there are no timer events; there are no socket events; there
475     are no idle events.
476
477     All of those types of "events" have nothing to do with the GUI at all.
478     That is why Cocoa's AppKit doesn't implement them.  Instead, they are
479     implemented in Foundation's NSRunLoop which on OS X uses CFRunLoop
480     to do the actual work.
481
482     How NSApplication uses NSRunLoop is rather interesting.  Basically, it
483     interacts with NSRunLoop only from within the nextEventMatchingMask
484     method.  It passes its inMode: argument almost directly to NSRunLoop
485     and thus CFRunLoop.  The run loop then runs (e.g. loops) until it
486     is told to exit.  The run loop calls the callout functions directly.
487     From within those callout functions the run loop is considered to
488     be running.  Presumably, the AppKit installs a run loop source to
489     receive messages from the window server over the mach port (like a
490     socket).  For some messages (e.g. need to paint) the AppKit will
491     call application code like drawRect: without exiting the run loop.
492     For other messages (ones that can be encapsulated in an NSEvent)
493     the AppKit tells the run loop to exit which returns control to
494     the nextEventMatchingMask method which then returns the NSEvent
495     object.  It's important to note that once the runloop has exited
496     it is no longer considered running and thus if you ask it which
497     mode it is running in it will return nil.
498
499     When manually pumping the event loop care should be taken to
500     tell it to run in the correct mode.  For instance, if you are
501     using it to run a modal dialog then you want to run it in
502     the modal panel run loop mode.  AppKit presumably has sources
503     or timers or observers that specifically don't listen on this
504     mode.  Another interesting mode is the connection reply mode.
505     This allows Cocoa to wait for a response from a distributed
506     objects message without firing off user code that may result
507     in a DO call being made thus recursing.  So basically, the
508     mode is a way for Cocoa to attempt to avoid run loop recursion
509     but to allow it under certain circumstances.
510  */
511