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