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