1. Yet another unit test for new stirng stuff
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        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 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14     #include "wx/defs.h"
15     #include "wx/app.h"
16     #include "wx/dc.h"
17     #include "wx/intl.h"
18     #include "wx/log.h"
19 #endif
20
21 #include "wx/module.h"
22
23 #include "wx/cocoa/ObjcPose.h"
24 #include "wx/cocoa/autorelease.h"
25 #include "wx/cocoa/mbarman.h"
26 #include "wx/cocoa/NSApplication.h"
27
28 #if wxUSE_WX_RESOURCES
29 #  include "wx/resource.h"
30 #endif
31
32 #import <AppKit/NSApplication.h>
33 #import <Foundation/NSRunLoop.h>
34 #import <Foundation/NSThread.h>
35 #import <AppKit/NSEvent.h>
36 #import <Foundation/NSString.h>
37 #import <Foundation/NSNotification.h>
38 #import <AppKit/NSCell.h>
39
40 // ========================================================================
41 // wxPoseAsInitializer
42 // ========================================================================
43 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
44
45 static bool sg_needIdle = true;
46
47 // ========================================================================
48 // wxPoserNSApplication
49 // ========================================================================
50 @interface wxPoserNSApplication : NSApplication
51 {
52 }
53
54 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
55 - (void)sendEvent: (NSEvent*)anEvent;
56 @end // wxPoserNSApplication
57
58 WX_IMPLEMENT_POSER(wxPoserNSApplication);
59
60 @implementation wxPoserNSApplication : NSApplication
61
62 /* NOTE: The old method of idle event handling added the handler using the
63     [NSRunLoop -performSelector:target:argument:order:modes] which caused
64     the invocation to occur at the begining of [NSApplication
65     -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:].  However,
66     the code would be scheduled for invocation with every iteration of
67     the event loop.  This new method simply overrides the method.  The
68     same caveats apply.  In particular, by the time the event loop has
69     called this method, it usually expects to receive an event.  If you
70     plan on stopping the event loop, it is wise to send an event through
71     the queue to ensure this method will return.
72     See wxEventLoop::Exit() for more information.
73
74  Ê ÊRN: We used to use nil as the untilDate in previous versions since nil
75 Ê  Êis a shorter and more concise way of specifying an infinite amount of
76 ÊÊ Êtime than [NSDate distantPast]. ÊHowever, Apple neglects to mention in
77 Ê  Êtheir documentation that nil is not handled correctly in OSX 10.2
78 Ê  Ê(and possibly lower) and when the call is reached the system comes to
79 Ê  Êa screeching halt, therefore we need to specify [NSDate distantPast]
80 Ê  Êexplicitly so that wxCocoa will work correctly in OSX 10.2.
81 */
82    
83 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
84 {
85     // Get the same events except don't block
86     NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
87     // If we got one, simply return it
88     if(event)
89         return event;
90     // No events, try doing some idle stuff
91     if(sg_needIdle
92 #ifdef __WXDEBUG__
93         && !wxTheApp->IsInAssert()
94 #endif
95         && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
96     {
97         sg_needIdle = false;
98         wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
99         while(wxTheApp->ProcessIdle())
100         {
101             // Get the same events except don't block
102             NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
103             // If we got one, simply return it
104             if(event)
105                 return event;
106             // we didn't get one, do some idle work
107             wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
108         }
109         // No more idle work requested, block
110         wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
111     }
112     else
113         wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
114     return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
115 }
116
117 - (void)sendEvent: (NSEvent*)anEvent
118 {
119     wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
120     sg_needIdle = true;
121     [super sendEvent: anEvent];
122 }
123
124 @end // wxPoserNSApplication
125
126 // ========================================================================
127 // wxNSApplicationDelegate
128 // ========================================================================
129 @implementation wxNSApplicationDelegate : NSObject
130
131 // NOTE: Terminate means that the event loop does NOT return and thus
132 // cleanup code doesn't properly execute.  Furthermore, wxWidgets has its
133 // own exit on frame delete mechanism.
134 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
135 {
136     return NO;
137 }
138
139 - (void)applicationWillBecomeActive:(NSNotification *)notification
140 {
141     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
142 }
143
144 - (void)applicationDidBecomeActive:(NSNotification *)notification
145 {
146     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
147 }
148
149 - (void)applicationWillResignActive:(NSNotification *)notification
150 {
151     wxTheApp->CocoaDelegate_applicationWillResignActive();
152 }
153
154 - (void)applicationDidResignActive:(NSNotification *)notification
155 {
156     wxTheApp->CocoaDelegate_applicationDidResignActive();
157 }
158
159 - (void)controlTintChanged:(NSNotification *)notification
160 {
161     wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
162 }
163
164 @end // implementation wxNSApplicationDelegate : NSObject
165
166 // ========================================================================
167 // wxApp
168 // ========================================================================
169
170 // ----------------------------------------------------------------------------
171 // wxApp Static member initialization
172 // ----------------------------------------------------------------------------
173 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
174 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
175     EVT_IDLE(wxAppBase::OnIdle)
176 //    EVT_END_SESSION(wxApp::OnEndSession)
177 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
178 END_EVENT_TABLE()
179
180 // ----------------------------------------------------------------------------
181 // wxApp initialization/cleanup
182 // ----------------------------------------------------------------------------
183 bool wxApp::Initialize(int& argc, wxChar **argv)
184 {
185     wxAutoNSAutoreleasePool pool;
186     m_cocoaMainThread = [NSThread currentThread];
187     // Mac OS X passes a process serial number command line argument when
188     // the application is launched from the Finder. This argument must be
189     // removed from the command line arguments before being handled by the
190     // application (otherwise applications would need to handle it)
191     if ( argc > 1 )
192     {
193         static const wxChar *ARG_PSN = _T("-psn_");
194         if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
195         {
196             // remove this argument
197             --argc;
198             memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
199         }
200     }
201
202     // Posing must be completed before any instances of the Objective-C
203     // classes being posed as are created.
204     wxPoseAsInitializer::InitializePosers();
205
206     return wxAppBase::Initialize(argc, argv);
207 }
208
209 void wxApp::CleanUp()
210 {
211     wxAutoNSAutoreleasePool pool;
212
213     wxDC::CocoaShutdownTextSystem();
214     wxMenuBarManager::DestroyInstance();
215
216     [m_cocoaApp setDelegate:nil];
217     [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
218         name:NSControlTintDidChangeNotification object:nil];
219     [m_cocoaAppDelegate release];
220     m_cocoaAppDelegate = NULL;
221
222     wxAppBase::CleanUp();
223 }
224
225 // ----------------------------------------------------------------------------
226 // wxApp creation
227 // ----------------------------------------------------------------------------
228 wxApp::wxApp()
229 {
230     m_topWindow = NULL;
231
232 #if WXWIN_COMPATIBILITY_2_2
233     m_wantDebugOutput = TRUE;
234 #endif
235 #ifdef __WXDEBUG__
236     m_isInAssert = FALSE;
237 #endif // __WXDEBUG__
238
239     argc = 0;
240     argv = NULL;
241     m_cocoaApp = NULL;
242     m_cocoaAppDelegate = NULL;
243 }
244
245 void wxApp::CocoaDelegate_applicationWillBecomeActive()
246 {
247 }
248
249 void wxApp::CocoaDelegate_applicationDidBecomeActive()
250 {
251 }
252
253 void wxApp::CocoaDelegate_applicationWillResignActive()
254 {
255     wxTopLevelWindowCocoa::DeactivatePendingWindow();
256 }
257
258 void wxApp::CocoaDelegate_applicationDidResignActive()
259 {
260 }
261
262 bool wxApp::OnInitGui()
263 {
264     wxAutoNSAutoreleasePool pool;
265     if(!wxAppBase::OnInitGui())
266         return FALSE;
267
268     // Create the app using the sharedApplication method
269     m_cocoaApp = [NSApplication sharedApplication];
270     m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
271     [m_cocoaApp setDelegate:m_cocoaAppDelegate];
272     [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
273         selector:@selector(controlTintChanged:)
274         name:NSControlTintDidChangeNotification object:nil];
275
276     wxMenuBarManager::CreateInstance();
277
278     wxDC::CocoaInitializeTextSystem();
279     return TRUE;
280 }
281
282 bool wxApp::CallOnInit()
283 {
284 //    wxAutoNSAutoreleasePool pool;
285     return OnInit();
286 }
287
288 bool wxApp::OnInit()
289 {
290     if(!wxAppBase::OnInit())
291         return FALSE;
292
293     return TRUE;
294 }
295
296 void wxApp::Exit()
297 {
298     wxApp::CleanUp();
299
300     wxAppConsole::Exit();
301 }
302
303 // Yield to other processes
304 bool wxApp::Yield(bool onlyIfNeeded)
305 {
306     // MT-FIXME
307     static bool s_inYield = false;
308
309 #if wxUSE_LOG
310     // disable log flushing from here because a call to wxYield() shouldn't
311     // normally result in message boxes popping up &c
312     wxLog::Suspend();
313 #endif // wxUSE_LOG
314
315     if (s_inYield)
316     {
317         if ( !onlyIfNeeded )
318         {
319             wxFAIL_MSG( wxT("wxYield called recursively" ) );
320         }
321
322         return false;
323     }
324
325     s_inYield = true;
326
327     // Run the event loop until it is out of events
328     while(1)
329     {
330         wxAutoNSAutoreleasePool pool;
331         NSEvent *event = [GetNSApplication()
332                 nextEventMatchingMask:NSAnyEventMask
333                 untilDate:[NSDate distantPast]
334                 inMode:NSDefaultRunLoopMode
335                 dequeue: YES];
336         if(!event)
337             break;
338         [GetNSApplication() sendEvent: event];
339     }
340
341 #if wxUSE_LOG
342     // let the logs be flashed again
343     wxLog::Resume();
344 #endif // wxUSE_LOG
345
346     s_inYield = false;
347
348     return true;
349 }
350
351 void wxApp::WakeUpIdle()
352 {
353     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
354             location:NSZeroPoint modifierFlags:NSAnyEventMask
355             timestamp:0 windowNumber:0 context:nil
356             subtype:0 data1:0 data2:0] atStart:NO];
357 }
358
359 #ifdef __WXDEBUG__
360 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
361 {
362     m_isInAssert = TRUE;
363     wxAppBase::OnAssert(file, line, cond, msg);
364     m_isInAssert = FALSE;
365 }
366 #endif // __WXDEBUG__
367