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