]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/app.mm
f8f064934f73bb861a6af0ffce3bc8e72fdf0c55
[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
75 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
76 {
77 // Get the same events except don't block
78 NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
79 // If we got one, simply return it
80 if(event)
81 return event;
82 // No events, try doing some idle stuff
83 if(sg_needIdle
84 #ifdef __WXDEBUG__
85 && !wxTheApp->IsInAssert()
86 #endif
87 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
88 {
89 sg_needIdle = false;
90 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
91 while(wxTheApp->ProcessIdle())
92 {
93 // Get the same events except don't block
94 NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
95 // If we got one, simply return it
96 if(event)
97 return event;
98 // we didn't get one, do some idle work
99 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
100 }
101 // No more idle work requested, block
102 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
103 }
104 else
105 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
106 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
107 }
108
109 - (void)sendEvent: (NSEvent*)anEvent
110 {
111 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
112 sg_needIdle = true;
113 [super sendEvent: anEvent];
114 }
115
116 @end // wxPoserNSApplication
117
118 // ========================================================================
119 // wxNSApplicationDelegate
120 // ========================================================================
121 @implementation wxNSApplicationDelegate : NSObject
122
123 // NOTE: Terminate means that the event loop does NOT return and thus
124 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
125 // own exit on frame delete mechanism.
126 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
127 {
128 return NO;
129 }
130
131 - (void)applicationWillBecomeActive:(NSNotification *)notification
132 {
133 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
134 }
135
136 - (void)applicationDidBecomeActive:(NSNotification *)notification
137 {
138 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
139 }
140
141 - (void)applicationWillResignActive:(NSNotification *)notification
142 {
143 wxTheApp->CocoaDelegate_applicationWillResignActive();
144 }
145
146 - (void)applicationDidResignActive:(NSNotification *)notification
147 {
148 wxTheApp->CocoaDelegate_applicationDidResignActive();
149 }
150
151 - (void)controlTintChanged:(NSNotification *)notification
152 {
153 wxLogDebug("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate");
154 }
155
156 @end // implementation wxNSApplicationDelegate : NSObject
157
158 // ========================================================================
159 // wxApp
160 // ========================================================================
161
162 // ----------------------------------------------------------------------------
163 // wxApp Static member initialization
164 // ----------------------------------------------------------------------------
165 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
166 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
167 EVT_IDLE(wxAppBase::OnIdle)
168 // EVT_END_SESSION(wxApp::OnEndSession)
169 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
170 END_EVENT_TABLE()
171
172 // ----------------------------------------------------------------------------
173 // wxApp initialization/cleanup
174 // ----------------------------------------------------------------------------
175 bool wxApp::Initialize(int& argc, wxChar **argv)
176 {
177 wxAutoNSAutoreleasePool pool;
178 m_cocoaMainThread = [NSThread currentThread];
179 // Mac OS X passes a process serial number command line argument when
180 // the application is launched from the Finder. This argument must be
181 // removed from the command line arguments before being handled by the
182 // application (otherwise applications would need to handle it)
183 if ( argc > 1 )
184 {
185 static const wxChar *ARG_PSN = _T("-psn_");
186 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
187 {
188 // remove this argument
189 --argc;
190 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
191 }
192 }
193
194 // Posing must be completed before any instances of the Objective-C
195 // classes being posed as are created.
196 wxPoseAsInitializer::InitializePosers();
197
198 return wxAppBase::Initialize(argc, argv);
199 }
200
201 void wxApp::CleanUp()
202 {
203 wxAutoNSAutoreleasePool pool;
204
205 wxDC::CocoaShutdownTextSystem();
206 wxMenuBarManager::DestroyInstance();
207
208 [m_cocoaApp setDelegate:nil];
209 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
210 name:NSControlTintDidChangeNotification object:nil];
211 [m_cocoaAppDelegate release];
212 m_cocoaAppDelegate = NULL;
213
214 wxAppBase::CleanUp();
215 }
216
217 // ----------------------------------------------------------------------------
218 // wxApp creation
219 // ----------------------------------------------------------------------------
220 wxApp::wxApp()
221 {
222 m_topWindow = NULL;
223
224 #if WXWIN_COMPATIBILITY_2_2
225 m_wantDebugOutput = TRUE;
226 #endif
227 #ifdef __WXDEBUG__
228 m_isInAssert = FALSE;
229 #endif // __WXDEBUG__
230
231 argc = 0;
232 argv = NULL;
233 m_cocoaApp = NULL;
234 m_cocoaAppDelegate = NULL;
235 }
236
237 void wxApp::CocoaDelegate_applicationWillBecomeActive()
238 {
239 }
240
241 void wxApp::CocoaDelegate_applicationDidBecomeActive()
242 {
243 }
244
245 void wxApp::CocoaDelegate_applicationWillResignActive()
246 {
247 wxTopLevelWindowCocoa::DeactivatePendingWindow();
248 }
249
250 void wxApp::CocoaDelegate_applicationDidResignActive()
251 {
252 }
253
254 bool wxApp::OnInitGui()
255 {
256 wxAutoNSAutoreleasePool pool;
257 if(!wxAppBase::OnInitGui())
258 return FALSE;
259
260 // Create the app using the sharedApplication method
261 m_cocoaApp = [NSApplication sharedApplication];
262 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
263 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
264 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
265 selector:@selector(controlTintChanged:)
266 name:NSControlTintDidChangeNotification object:nil];
267
268 wxMenuBarManager::CreateInstance();
269
270 wxDC::CocoaInitializeTextSystem();
271 return TRUE;
272 }
273
274 bool wxApp::CallOnInit()
275 {
276 // wxAutoNSAutoreleasePool pool;
277 return OnInit();
278 }
279
280 bool wxApp::OnInit()
281 {
282 if(!wxAppBase::OnInit())
283 return FALSE;
284
285 return TRUE;
286 }
287
288 void wxApp::Exit()
289 {
290 wxApp::CleanUp();
291
292 wxAppConsole::Exit();
293 }
294
295 // Yield to other processes
296 bool wxApp::Yield(bool onlyIfNeeded)
297 {
298 // MT-FIXME
299 static bool s_inYield = false;
300
301 #if wxUSE_LOG
302 // disable log flushing from here because a call to wxYield() shouldn't
303 // normally result in message boxes popping up &c
304 wxLog::Suspend();
305 #endif // wxUSE_LOG
306
307 if (s_inYield)
308 {
309 if ( !onlyIfNeeded )
310 {
311 wxFAIL_MSG( wxT("wxYield called recursively" ) );
312 }
313
314 return false;
315 }
316
317 s_inYield = true;
318
319 // Run the event loop until it is out of events
320 while(1)
321 {
322 wxAutoNSAutoreleasePool pool;
323 NSEvent *event = [GetNSApplication()
324 nextEventMatchingMask:NSAnyEventMask
325 untilDate:nil /* ==[NSDate distantPast] */
326 inMode:NSDefaultRunLoopMode
327 dequeue: YES];
328 if(!event)
329 break;
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