* Add public wxApp::sm_isEmbedded flag like on wxMac. Default initialization
[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 // RCS-ID:      $Id$
8 // Copyright:   (c) David Elliott
9 //              Software 2000 Ltd.
10 // Licence:     wxWidgets licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 #include "wx/wxprec.h"
14
15 #include "wx/app.h"
16
17 #ifndef WX_PRECOMP
18     #include "wx/dc.h"
19     #include "wx/intl.h"
20     #include "wx/log.h"
21     #include "wx/module.h"
22 #endif
23
24 #include "wx/cocoa/ObjcRef.h"
25 #include "wx/cocoa/autorelease.h"
26 #include "wx/cocoa/mbarman.h"
27 #include "wx/cocoa/NSApplication.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 = [[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
57 // ========================================================================
58 // wxNSApplicationObserver
59 // ========================================================================
60 @implementation wxNSApplicationObserver : NSObject
61
62 - (void)applicationWillBecomeActive:(NSNotification *)notification
63 {
64     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
65 }
66
67 - (void)applicationDidBecomeActive:(NSNotification *)notification
68 {
69     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
70 }
71
72 - (void)applicationWillResignActive:(NSNotification *)notification
73 {
74     wxTheApp->CocoaDelegate_applicationWillResignActive();
75 }
76
77 - (void)applicationDidResignActive:(NSNotification *)notification
78 {
79     wxTheApp->CocoaDelegate_applicationDidResignActive();
80 }
81
82 - (void)applicationWillUpdate:(NSNotification *)notification;
83 {
84     wxTheApp->CocoaDelegate_applicationWillUpdate();
85 }
86
87 - (void)controlTintChanged:(NSNotification *)notification
88 {
89     wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
90 }
91
92 @end // implementation wxNSApplicationObserver : NSObject
93
94 // ========================================================================
95 // wxApp
96 // ========================================================================
97
98 // ----------------------------------------------------------------------------
99 // wxApp Static member initialization
100 // ----------------------------------------------------------------------------
101 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
102 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
103     EVT_IDLE(wxAppBase::OnIdle)
104 //    EVT_END_SESSION(wxApp::OnEndSession)
105 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
106 END_EVENT_TABLE()
107
108 // ----------------------------------------------------------------------------
109 // wxApp initialization/cleanup
110 // ----------------------------------------------------------------------------
111 bool wxApp::Initialize(int& argc, wxChar **argv)
112 {
113     wxAutoNSAutoreleasePool pool;
114     m_cocoaMainThread = [NSThread currentThread];
115     // Mac OS X passes a process serial number command line argument when
116     // the application is launched from the Finder. This argument must be
117     // removed from the command line arguments before being handled by the
118     // application (otherwise applications would need to handle it)
119     if ( argc > 1 )
120     {
121         static const wxChar *ARG_PSN = _T("-psn_");
122         if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
123         {
124             // remove this argument
125             --argc;
126             memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
127         }
128     }
129
130     return wxAppBase::Initialize(argc, argv);
131 }
132
133 void wxApp::CleanUp()
134 {
135     wxAutoNSAutoreleasePool pool;
136
137     wxDC::CocoaShutdownTextSystem();
138     wxMenuBarManager::DestroyInstance();
139
140     [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
141     if(!sm_isEmbedded)
142     {
143         [m_cocoaApp setDelegate:nil];
144         [m_cocoaAppDelegate release];
145         m_cocoaAppDelegate = NULL;
146     }
147
148     wxAppBase::CleanUp();
149 }
150
151 // ----------------------------------------------------------------------------
152 // wxApp creation
153 // ----------------------------------------------------------------------------
154 wxApp::wxApp()
155 {
156     m_topWindow = NULL;
157
158 #ifdef __WXDEBUG__
159     m_isInAssert = false;
160 #endif // __WXDEBUG__
161
162     argc = 0;
163     argv = NULL;
164     m_cocoaApp = NULL;
165     m_cocoaAppDelegate = NULL;
166 }
167
168 void wxApp::CocoaDelegate_applicationWillBecomeActive()
169 {
170 }
171
172 void wxApp::CocoaDelegate_applicationDidBecomeActive()
173 {
174 }
175
176 void wxApp::CocoaDelegate_applicationWillResignActive()
177 {
178     wxTopLevelWindowCocoa::DeactivatePendingWindow();
179 }
180
181 void wxApp::CocoaDelegate_applicationDidResignActive()
182 {
183 }
184
185 bool wxApp::OnInitGui()
186 {
187     wxAutoNSAutoreleasePool pool;
188     if(!wxAppBase::OnInitGui())
189         return false;
190
191     // Create the app using the sharedApplication method
192     m_cocoaApp = [NSApplication sharedApplication];
193
194     if(!sm_isEmbedded)
195     {
196         // Enable response to application delegate messages
197         m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
198         [m_cocoaApp setDelegate:m_cocoaAppDelegate];
199     }
200
201     // Enable response to "delegate" messages on the notification observer
202     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
203         selector:@selector(applicationWillBecomeActive:)
204         name:NSApplicationWillBecomeActiveNotification object:nil];
205
206     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
207         selector:@selector(applicationDidBecomeActive:)
208         name:NSApplicationDidBecomeActiveNotification object:nil];
209
210     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
211         selector:@selector(applicationWillResignActive:)
212         name:NSApplicationWillResignActiveNotification object:nil];
213
214     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
215         selector:@selector(applicationDidResignActive:)
216         name:NSApplicationDidResignActiveNotification object:nil];
217
218     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
219         selector:@selector(applicationWillUpdate:)
220         name:NSApplicationWillUpdateNotification object:nil];
221
222     // Enable response to system notifications
223     [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
224         selector:@selector(controlTintChanged:)
225         name:NSControlTintDidChangeNotification object:nil];
226
227     if(!sm_isEmbedded)
228         wxMenuBarManager::CreateInstance();
229
230     wxDC::CocoaInitializeTextSystem();
231     return true;
232 }
233
234 wxApp::~wxApp()
235 {
236     if(m_cfRunLoopIdleObserver != NULL)
237     {
238         // Invalidate the observer which also removes it from the run loop.
239         CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
240         // Release the ref as we don't need it anymore.
241         m_cfRunLoopIdleObserver.reset();
242     }
243 }
244
245 bool wxApp::CallOnInit()
246 {
247 //    wxAutoNSAutoreleasePool pool;
248     return OnInit();
249 }
250
251 bool wxApp::OnInit()
252 {
253     if(!wxAppBase::OnInit())
254         return false;
255
256     return true;
257 }
258
259 void wxApp::Exit()
260 {
261     wxApp::CleanUp();
262
263     wxAppConsole::Exit();
264 }
265
266 // Yield to other processes
267 bool wxApp::Yield(bool onlyIfNeeded)
268 {
269     // MT-FIXME
270     static bool s_inYield = false;
271
272 #if wxUSE_LOG
273     // disable log flushing from here because a call to wxYield() shouldn't
274     // normally result in message boxes popping up &c
275     wxLog::Suspend();
276 #endif // wxUSE_LOG
277
278     if (s_inYield)
279     {
280         if ( !onlyIfNeeded )
281         {
282             wxFAIL_MSG( wxT("wxYield called recursively" ) );
283         }
284
285         return false;
286     }
287
288     s_inYield = true;
289
290     // Run the event loop until it is out of events
291     while(1)
292     {
293         wxAutoNSAutoreleasePool pool;
294         NSEvent *event = [GetNSApplication()
295                 nextEventMatchingMask:NSAnyEventMask
296                 untilDate:[NSDate distantPast]
297                 inMode:NSDefaultRunLoopMode
298                 dequeue: YES];
299         if(!event)
300             break;
301         [GetNSApplication() sendEvent: event];
302     }
303
304 #if wxUSE_LOG
305     // let the logs be flashed again
306     wxLog::Resume();
307 #endif // wxUSE_LOG
308
309     s_inYield = false;
310
311     return true;
312 }
313
314 void wxApp::WakeUpIdle()
315 {
316     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
317             location:NSZeroPoint modifierFlags:NSAnyEventMask
318             timestamp:0 windowNumber:0 context:nil
319             subtype:0 data1:0 data2:0] atStart:NO];
320 }
321
322 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
323 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
324 {
325     static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
326 }
327
328 #if 0
329 static int sg_cApplicationWillUpdate = 0;
330 #endif
331
332 void wxApp::CocoaDelegate_applicationWillUpdate()
333 {
334     wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
335
336 //    CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
337     CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
338     wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
339
340     if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
341     {
342         CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
343         m_cfRunLoopIdleObserver.reset();
344     }
345 #if 0
346     ++sg_cApplicationWillUpdate;
347 #endif
348     if(m_cfRunLoopIdleObserver == NULL)
349     {
350         // Enable idle event handling
351         CFRunLoopObserverContext observerContext =
352         {   0
353         ,   this
354         ,   NULL
355         ,   NULL
356         ,   NULL
357         };
358         m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
359         m_cfObservedRunLoopMode = cfRunLoopMode;
360         CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
361     }
362 }
363
364 static inline bool FakeNeedMoreIdle()
365 {
366 #if 0
367 // Return true on every 10th call.
368     static int idleCount = 0;
369     return ++idleCount % 10;
370 #else
371     return false;
372 #endif
373 }
374
375 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
376 {
377     // Ensure that the app knows we've been invalidated
378     m_cfRunLoopIdleObserver.reset();
379 #if 0
380     wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
381     sg_cApplicationWillUpdate = 0;
382 #else
383     wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
384 #endif
385     if( ProcessIdle() || FakeNeedMoreIdle() )
386     {
387         wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
388         [NSApp setWindowsNeedUpdate:YES];
389     }
390     else
391     {
392         wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
393     }
394 }
395
396 #ifdef __WXDEBUG__
397 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
398 {
399     m_isInAssert = true;
400     wxAppBase::OnAssert(file, line, cond, msg);
401     m_isInAssert = false;
402 }
403 #endif // __WXDEBUG__