]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/app.mm
2.5.3 - cleanups, fixes, etc. etc. -
[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 RN: We used to use nil as the untilDate in previous versions since nil
76 is a shorter and more concise way of specifying an infinite amount of
77 time than [NSDate distantPast]. However, Apple neglects to mention in
78 their documentation that nil is not handled correctly in OSX 10.2
79 (and possibly lower) and when the call is reached the system comes to
80 a screeching halt, therefore we need to specify [NSDate distantPast]
81 explicitly so that wxCocoa will work correctly in OSX 10.2.
82 */
83
84 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
85 {
86 // Get the same events except don't block
87 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
88 // If we got one, simply return it
89 if(event)
90 return event;
91 // No events, try doing some idle stuff
92 if(sg_needIdle /*&& !wxTheApp->IsInAssert()*/ && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
93 {
94 sg_needIdle = false;
95 wxLogDebug(wxT("Processing idle events"));
96 while(wxTheApp->ProcessIdle())
97 {
98 // Get the same events except don't block
99 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast]/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
100 // If we got one, simply return it
101 if(event)
102 return event;
103 // we didn't get one, do some idle work
104 wxLogDebug(wxT("Looping idle events"));
105 }
106 // No more idle work requested, block
107 wxLogDebug(wxT("Finished idle processing"));
108 }
109 else
110 wxLogDebug(wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
111 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
112 }
113
114 - (void)sendEvent: (NSEvent*)anEvent
115 {
116 wxLogDebug(wxT("SendEvent"));
117 sg_needIdle = true;
118 [super sendEvent: anEvent];
119 }
120
121 @end // wxPoserNSApplication
122
123 // ========================================================================
124 // wxNSApplicationDelegate
125 // ========================================================================
126 @interface wxNSApplicationDelegate : NSObject
127 {
128 }
129
130 // Delegate methods
131 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
132 - (void)applicationWillBecomeActive:(NSNotification *)notification;
133 - (void)applicationDidBecomeActive:(NSNotification *)notification;
134 - (void)applicationWillResignActive:(NSNotification *)notification;
135 - (void)applicationDidResignActive:(NSNotification *)notification;
136 @end // interface wxNSApplicationDelegate : NSObject
137
138 @implementation wxNSApplicationDelegate : NSObject
139
140 // NOTE: Terminate means that the event loop does NOT return and thus
141 // cleanup code doesn't properly execute. Furthermore, wxWindows has its
142 // own exit on frame delete mechanism.
143 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
144 {
145 return NO;
146 }
147
148 - (void)applicationWillBecomeActive:(NSNotification *)notification
149 {
150 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
151 }
152
153 - (void)applicationDidBecomeActive:(NSNotification *)notification
154 {
155 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
156 }
157
158 - (void)applicationWillResignActive:(NSNotification *)notification
159 {
160 wxTheApp->CocoaDelegate_applicationWillResignActive();
161 }
162
163 - (void)applicationDidResignActive:(NSNotification *)notification
164 {
165 wxTheApp->CocoaDelegate_applicationDidResignActive();
166 }
167
168 @end // implementation wxNSApplicationDelegate : NSObject
169
170 // ========================================================================
171 // wxApp
172 // ========================================================================
173
174 // ----------------------------------------------------------------------------
175 // wxApp Static member initialization
176 // ----------------------------------------------------------------------------
177 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
178 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
179 EVT_IDLE(wxAppBase::OnIdle)
180 // EVT_END_SESSION(wxApp::OnEndSession)
181 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
182 END_EVENT_TABLE()
183
184 // ----------------------------------------------------------------------------
185 // wxApp initialization/cleanup
186 // ----------------------------------------------------------------------------
187 bool wxApp::Initialize(int& argc, wxChar **argv)
188 {
189 wxAutoNSAutoreleasePool pool;
190 m_cocoaMainThread = [NSThread currentThread];
191 // Mac OS X passes a process serial number command line argument when
192 // the application is launched from the Finder. This argument must be
193 // removed from the command line arguments before being handled by the
194 // application (otherwise applications would need to handle it)
195 if ( argc > 1 )
196 {
197 static const wxChar *ARG_PSN = _T("-psn_");
198 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
199 {
200 // remove this argument
201 --argc;
202 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
203 }
204 }
205
206 // Posing must be completed before any instances of the Objective-C
207 // classes being posed as are created.
208 wxPoseAsInitializer::InitializePosers();
209
210 return wxAppBase::Initialize(argc, argv);
211 }
212
213 void wxApp::CleanUp()
214 {
215 wxAutoNSAutoreleasePool pool;
216
217 wxDC::CocoaShutdownTextSystem();
218 wxMenuBarManager::DestroyInstance();
219
220 [m_cocoaApp setDelegate:nil];
221 [m_cocoaAppDelegate release];
222 m_cocoaAppDelegate = NULL;
223
224 wxAppBase::CleanUp();
225 }
226
227 // ----------------------------------------------------------------------------
228 // wxApp creation
229 // ----------------------------------------------------------------------------
230 wxApp::wxApp()
231 {
232 m_topWindow = NULL;
233
234 #if WXWIN_COMPATIBILITY_2_2
235 m_wantDebugOutput = TRUE;
236 #endif
237 #ifdef __WXDEBUG__
238 m_isInAssert = FALSE;
239 #endif // __WXDEBUG__
240
241 argc = 0;
242 argv = NULL;
243 m_cocoaApp = NULL;
244 m_cocoaAppDelegate = NULL;
245 }
246
247 void wxApp::CocoaDelegate_applicationWillBecomeActive()
248 {
249 }
250
251 void wxApp::CocoaDelegate_applicationDidBecomeActive()
252 {
253 }
254
255 void wxApp::CocoaDelegate_applicationWillResignActive()
256 {
257 wxTopLevelWindowCocoa::DeactivatePendingWindow();
258 }
259
260 void wxApp::CocoaDelegate_applicationDidResignActive()
261 {
262 }
263
264 bool wxApp::OnInitGui()
265 {
266 wxAutoNSAutoreleasePool pool;
267 if(!wxAppBase::OnInitGui())
268 return FALSE;
269
270 // Create the app using the sharedApplication method
271 m_cocoaApp = [NSApplication sharedApplication];
272 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
273 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
274
275 wxMenuBarManager::CreateInstance();
276
277 wxDC::CocoaInitializeTextSystem();
278 // [ m_cocoaApp setDelegate:m_cocoaApp ];
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(NSEvent *event = [GetNSApplication()
329 nextEventMatchingMask:NSAnyEventMask
330 untilDate:[NSDate distantPast]
331 inMode:NSDefaultRunLoopMode
332 dequeue: YES])
333 {
334 [GetNSApplication() sendEvent: event];
335 }
336
337 #if wxUSE_LOG
338 // let the logs be flashed again
339 wxLog::Resume();
340 #endif // wxUSE_LOG
341
342 s_inYield = false;
343
344 return true;
345 }
346
347 void wxApp::WakeUpIdle()
348 {
349 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
350 location:NSZeroPoint modifierFlags:NSAnyEventMask
351 timestamp:0 windowNumber:0 context:nil
352 subtype:0 data1:0 data2:0] atStart:NO];
353 }
354
355 #ifdef __WXDEBUG__
356 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
357 {
358 m_isInAssert = TRUE;
359 wxAppBase::OnAssert(file, line, cond, msg);
360 m_isInAssert = FALSE;
361 }
362 #endif // __WXDEBUG__
363