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