Unicode build fixes
[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:     wxWindows 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/frame.h"
17     #include "wx/dialog.h"
18     #include "wx/dc.h"
19     #include "wx/intl.h"
20     #include "wx/log.h"
21 #endif
22
23 #include "wx/module.h"
24
25 #include "wx/cocoa/ObjcPose.h"
26 #include "wx/cocoa/autorelease.h"
27 #include "wx/cocoa/mbarman.h"
28
29 #if wxUSE_WX_RESOURCES
30 #  include "wx/resource.h"
31 #endif
32
33 #import <AppKit/NSApplication.h>
34 #import <Foundation/NSRunLoop.h>
35 #import <Foundation/NSArray.h>
36 #import <Foundation/NSAutoreleasePool.h>
37 #import <Foundation/NSThread.h>
38 #import <AppKit/NSEvent.h>
39 #import <Foundation/NSString.h>
40
41 // ========================================================================
42 // wxPoseAsInitializer
43 // ========================================================================
44 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
45
46 static bool sg_needIdle = true;
47
48 // ========================================================================
49 // wxPoserNSApplication
50 // ========================================================================
51 @interface wxPoserNSApplication : NSApplication
52 {
53 }
54
55 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
56 - (void)sendEvent: (NSEvent*)anEvent;
57 @end // wxPoserNSApplication
58
59 WX_IMPLEMENT_POSER(wxPoserNSApplication);
60
61 @implementation wxPoserNSApplication : NSApplication
62
63 /* NOTE: The old method of idle event handling added the handler using the
64     [NSRunLoop -performSelector:target:argument:order:modes] which caused
65     the invocation to occur at the begining of [NSApplication
66     -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:].  However,
67     the code would be scheduled for invocation with every iteration of
68     the event loop.  This new method simply overrides the method.  The
69     same caveats apply.  In particular, by the time the event loop has
70     called this method, it usually expects to receive an event.  If you
71     plan on stopping the event loop, it is wise to send an event through
72     the queue to ensure this method will return.
73     See wxEventLoop::Exit() for more information.
74 */
75    
76 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
77 {
78     // Get the same events except don't block
79     NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
80     // If we got one, simply return it
81     if(event)
82         return event;
83     // No events, try doing some idle stuff
84     if(sg_needIdle
85 #ifdef __WXDEBUG__
86         && !wxTheApp->IsInAssert()
87 #endif
88         && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
89     {
90         sg_needIdle = false;
91         wxLogDebug(wxT("Processing idle events"));
92         while(wxTheApp->ProcessIdle())
93         {
94             // Get the same events except don't block
95             NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
96             // If we got one, simply return it
97             if(event)
98                 return event;
99             // we didn't get one, do some idle work
100             wxLogDebug(wxT("Looping idle events"));
101         }
102         // No more idle work requested, block
103         wxLogDebug(wxT("Finished idle processing"));
104     }
105     else
106         wxLogDebug(wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
107     return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
108 }
109
110 - (void)sendEvent: (NSEvent*)anEvent
111 {
112     wxLogDebug(wxT("SendEvent"));
113     sg_needIdle = true;
114     [super sendEvent: anEvent];
115 }
116
117 @end // wxPoserNSApplication
118
119 // ========================================================================
120 // wxNSApplicationDelegate
121 // ========================================================================
122 @interface wxNSApplicationDelegate : NSObject
123 {
124 }
125
126 // Delegate methods
127 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
128 - (void)applicationWillBecomeActive:(NSNotification *)notification;
129 - (void)applicationDidBecomeActive:(NSNotification *)notification;
130 - (void)applicationWillResignActive:(NSNotification *)notification;
131 - (void)applicationDidResignActive:(NSNotification *)notification;
132 @end // interface wxNSApplicationDelegate : NSObject
133
134 @implementation wxNSApplicationDelegate : NSObject
135
136 // NOTE: Terminate means that the event loop does NOT return and thus
137 // cleanup code doesn't properly execute.  Furthermore, wxWindows has its
138 // own exit on frame delete mechanism.
139 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
140 {
141     return NO;
142 }
143
144 - (void)applicationWillBecomeActive:(NSNotification *)notification
145 {
146     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
147 }
148
149 - (void)applicationDidBecomeActive:(NSNotification *)notification
150 {
151     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
152 }
153
154 - (void)applicationWillResignActive:(NSNotification *)notification
155 {
156     wxTheApp->CocoaDelegate_applicationWillResignActive();
157 }
158
159 - (void)applicationDidResignActive:(NSNotification *)notification
160 {
161     wxTheApp->CocoaDelegate_applicationDidResignActive();
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     [m_cocoaAppDelegate release];
218     m_cocoaAppDelegate = NULL;
219
220     wxAppBase::CleanUp();
221 }
222
223 // ----------------------------------------------------------------------------
224 // wxApp creation
225 // ----------------------------------------------------------------------------
226 wxApp::wxApp()
227 {
228     m_topWindow = NULL;
229
230 #if WXWIN_COMPATIBILITY_2_2
231     m_wantDebugOutput = TRUE;
232 #endif
233 #ifdef __WXDEBUG__
234     m_isInAssert = FALSE;
235 #endif // __WXDEBUG__
236
237     argc = 0;
238     argv = NULL;
239     m_cocoaApp = NULL;
240     m_cocoaAppDelegate = NULL;
241 }
242
243 void wxApp::CocoaDelegate_applicationWillBecomeActive()
244 {
245 }
246
247 void wxApp::CocoaDelegate_applicationDidBecomeActive()
248 {
249 }
250
251 void wxApp::CocoaDelegate_applicationWillResignActive()
252 {
253     wxTopLevelWindowCocoa::DeactivatePendingWindow();
254 }
255
256 void wxApp::CocoaDelegate_applicationDidResignActive()
257 {
258 }
259
260 bool wxApp::OnInitGui()
261 {
262     wxAutoNSAutoreleasePool pool;
263     if(!wxAppBase::OnInitGui())
264         return FALSE;
265
266     // Create the app using the sharedApplication method
267     m_cocoaApp = [NSApplication sharedApplication];
268     m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
269     [m_cocoaApp setDelegate:m_cocoaAppDelegate];
270
271     wxMenuBarManager::CreateInstance();
272
273     wxDC::CocoaInitializeTextSystem();
274 //    [ m_cocoaApp setDelegate:m_cocoaApp ];
275     return TRUE;
276 }
277
278 bool wxApp::CallOnInit()
279 {
280 //    wxAutoNSAutoreleasePool pool;
281     return OnInit();
282 }
283
284 bool wxApp::OnInit()
285 {
286     if(!wxAppBase::OnInit())
287         return FALSE;
288
289     return TRUE;
290 }
291
292 void wxApp::Exit()
293 {
294     wxApp::CleanUp();
295
296     wxAppConsole::Exit();
297 }
298
299 // Yield to other processes
300 bool wxApp::Yield(bool onlyIfNeeded)
301 {
302     // MT-FIXME
303     static bool s_inYield = false;
304
305 #if wxUSE_LOG
306     // disable log flushing from here because a call to wxYield() shouldn't
307     // normally result in message boxes popping up &c
308     wxLog::Suspend();
309 #endif // wxUSE_LOG
310
311     if (s_inYield)
312     {
313         if ( !onlyIfNeeded )
314         {
315             wxFAIL_MSG( wxT("wxYield called recursively" ) );
316         }
317
318         return false;
319     }
320
321     s_inYield = true;
322
323     // Run the event loop until it is out of events
324     while(NSEvent *event = [GetNSApplication()
325                 nextEventMatchingMask:NSAnyEventMask
326                 untilDate:[NSDate distantPast]
327                 inMode:NSDefaultRunLoopMode
328                 dequeue: YES])
329     {
330         [GetNSApplication() sendEvent: event];
331     }
332
333 #if wxUSE_LOG
334     // let the logs be flashed again
335     wxLog::Resume();
336 #endif // wxUSE_LOG
337
338     s_inYield = false;
339
340     return true;
341 }
342
343 void wxApp::WakeUpIdle()
344 {
345     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
346             location:NSZeroPoint modifierFlags:NSAnyEventMask
347             timestamp:0 windowNumber:0 context:nil
348             subtype:0 data1:0 data2:0] atStart:NO];
349 }
350
351 #ifdef __WXDEBUG__
352 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
353 {
354     m_isInAssert = TRUE;
355     wxAppBase::OnAssert(file, line, cond, msg);
356     m_isInAssert = FALSE;
357 }
358 #endif // __WXDEBUG__
359