]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/app.mm
moved IMPLEMENT_DYNAMIC_CLASS(wxGDIObject,wxObject) line to gdicmn.cpp so that we...
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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
14 #include "wx/app.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/dc.h"
18 #include "wx/intl.h"
19 #include "wx/log.h"
20 #endif
21
22 #include "wx/module.h"
23
24 #include "wx/cocoa/ObjcPose.h"
25 #include "wx/cocoa/autorelease.h"
26 #include "wx/cocoa/mbarman.h"
27 #include "wx/cocoa/NSApplication.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/NSThread.h>
36 #import <AppKit/NSEvent.h>
37 #import <Foundation/NSString.h>
38 #import <Foundation/NSNotification.h>
39 #import <AppKit/NSCell.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 This overridden method calls the superclass method with an untilDate
76 parameter that indicates nil should be returned if there are no pending
77 events. That is, nextEventMatchingMask: should not wait for an event.
78 If nil is returned then idle event processing occurs until the user
79 does not request anymore idle events or until a real event comes through.
80
81 RN: Even though Apple documentation states that nil can be passed in place
82 of [NSDate distantPast] in the untilDate parameter, this causes Jaguar (10.2)
83 to get stuck in some kind of loop deep within nextEventMatchingMask:, thus we
84 need to explicitly pass [NSDate distantPast] instead.
85 */
86
87 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
88 {
89 // Get the same events except don't block
90 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
91 // If we got one, simply return it
92 if(event)
93 return event;
94 // No events, try doing some idle stuff
95 if(sg_needIdle
96 #ifdef __WXDEBUG__
97 && !wxTheApp->IsInAssert()
98 #endif
99 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
100 {
101 sg_needIdle = false;
102 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
103 while(wxTheApp->ProcessIdle())
104 {
105 // Get the same events except don't block
106 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
107 // If we got one, simply return it
108 if(event)
109 return event;
110 // we didn't get one, do some idle work
111 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
112 }
113 // No more idle work requested, block
114 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
115 }
116 else
117 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
118 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
119 }
120
121 - (void)sendEvent: (NSEvent*)anEvent
122 {
123 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
124 sg_needIdle = true;
125 [super sendEvent: anEvent];
126 }
127
128 @end // wxPoserNSApplication
129
130 // ========================================================================
131 // wxNSApplicationDelegate
132 // ========================================================================
133 @implementation wxNSApplicationDelegate : NSObject
134
135 // NOTE: Terminate means that the event loop does NOT return and thus
136 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
137 // own exit on frame delete mechanism.
138 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
139 {
140 return NO;
141 }
142
143 - (void)applicationWillBecomeActive:(NSNotification *)notification
144 {
145 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
146 }
147
148 - (void)applicationDidBecomeActive:(NSNotification *)notification
149 {
150 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
151 }
152
153 - (void)applicationWillResignActive:(NSNotification *)notification
154 {
155 wxTheApp->CocoaDelegate_applicationWillResignActive();
156 }
157
158 - (void)applicationDidResignActive:(NSNotification *)notification
159 {
160 wxTheApp->CocoaDelegate_applicationDidResignActive();
161 }
162
163 - (void)controlTintChanged:(NSNotification *)notification
164 {
165 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
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 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
222 name:NSControlTintDidChangeNotification object:nil];
223 [m_cocoaAppDelegate release];
224 m_cocoaAppDelegate = NULL;
225
226 wxAppBase::CleanUp();
227 }
228
229 // ----------------------------------------------------------------------------
230 // wxApp creation
231 // ----------------------------------------------------------------------------
232 wxApp::wxApp()
233 {
234 m_topWindow = NULL;
235
236 #ifdef __WXDEBUG__
237 m_isInAssert = false;
238 #endif // __WXDEBUG__
239
240 argc = 0;
241 argv = NULL;
242 m_cocoaApp = NULL;
243 m_cocoaAppDelegate = NULL;
244 }
245
246 void wxApp::CocoaDelegate_applicationWillBecomeActive()
247 {
248 }
249
250 void wxApp::CocoaDelegate_applicationDidBecomeActive()
251 {
252 }
253
254 void wxApp::CocoaDelegate_applicationWillResignActive()
255 {
256 wxTopLevelWindowCocoa::DeactivatePendingWindow();
257 }
258
259 void wxApp::CocoaDelegate_applicationDidResignActive()
260 {
261 }
262
263 bool wxApp::OnInitGui()
264 {
265 wxAutoNSAutoreleasePool pool;
266 if(!wxAppBase::OnInitGui())
267 return false;
268
269 // Create the app using the sharedApplication method
270 m_cocoaApp = [NSApplication sharedApplication];
271 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
272 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
273 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
274 selector:@selector(controlTintChanged:)
275 name:NSControlTintDidChangeNotification object:nil];
276
277 wxMenuBarManager::CreateInstance();
278
279 wxDC::CocoaInitializeTextSystem();
280 return true;
281 }
282
283 bool wxApp::CallOnInit()
284 {
285 // wxAutoNSAutoreleasePool pool;
286 return OnInit();
287 }
288
289 bool wxApp::OnInit()
290 {
291 if(!wxAppBase::OnInit())
292 return false;
293
294 return true;
295 }
296
297 void wxApp::Exit()
298 {
299 wxApp::CleanUp();
300
301 wxAppConsole::Exit();
302 }
303
304 // Yield to other processes
305 bool wxApp::Yield(bool onlyIfNeeded)
306 {
307 // MT-FIXME
308 static bool s_inYield = false;
309
310 #if wxUSE_LOG
311 // disable log flushing from here because a call to wxYield() shouldn't
312 // normally result in message boxes popping up &c
313 wxLog::Suspend();
314 #endif // wxUSE_LOG
315
316 if (s_inYield)
317 {
318 if ( !onlyIfNeeded )
319 {
320 wxFAIL_MSG( wxT("wxYield called recursively" ) );
321 }
322
323 return false;
324 }
325
326 s_inYield = true;
327
328 // Run the event loop until it is out of events
329 while(1)
330 {
331 wxAutoNSAutoreleasePool pool;
332 NSEvent *event = [GetNSApplication()
333 nextEventMatchingMask:NSAnyEventMask
334 untilDate:[NSDate distantPast]
335 inMode:NSDefaultRunLoopMode
336 dequeue: YES];
337 if(!event)
338 break;
339 [GetNSApplication() sendEvent: event];
340 }
341
342 #if wxUSE_LOG
343 // let the logs be flashed again
344 wxLog::Resume();
345 #endif // wxUSE_LOG
346
347 s_inYield = false;
348
349 return true;
350 }
351
352 void wxApp::WakeUpIdle()
353 {
354 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
355 location:NSZeroPoint modifierFlags:NSAnyEventMask
356 timestamp:0 windowNumber:0 context:nil
357 subtype:0 data1:0 data2:0] atStart:NO];
358 }
359
360 #ifdef __WXDEBUG__
361 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
362 {
363 m_isInAssert = true;
364 wxAppBase::OnAssert(file, line, cond, msg);
365 m_isInAssert = false;
366 }
367 #endif // __WXDEBUG__