]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/cocoa/app.mm
don't pass 0 time_t to Borland localtime(), it crashes (bug 1704438); also check...
[wxWidgets.git] / src / cocoa / app.mm
... / ...
CommitLineData
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 #include "wx/module.h"
21#endif
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#import <AppKit/NSApplication.h>
29#import <Foundation/NSRunLoop.h>
30#import <Foundation/NSThread.h>
31#import <AppKit/NSEvent.h>
32#import <Foundation/NSString.h>
33#import <Foundation/NSNotification.h>
34#import <AppKit/NSCell.h>
35
36// ========================================================================
37// wxPoseAsInitializer
38// ========================================================================
39wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
40
41static bool sg_needIdle = true;
42
43// ========================================================================
44// wxPoserNSApplication
45// ========================================================================
46@interface wxPoserNSApplication : NSApplication
47{
48}
49
50- (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
51- (void)sendEvent: (NSEvent*)anEvent;
52@end // wxPoserNSApplication
53
54WX_IMPLEMENT_POSER(wxPoserNSApplication);
55
56@implementation wxPoserNSApplication : NSApplication
57
58/* NOTE: The old method of idle event handling added the handler using the
59 [NSRunLoop -performSelector:target:argument:order:modes] which caused
60 the invocation to occur at the begining of [NSApplication
61 -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:]. However,
62 the code would be scheduled for invocation with every iteration of
63 the event loop. This new method simply overrides the method. The
64 same caveats apply. In particular, by the time the event loop has
65 called this method, it usually expects to receive an event. If you
66 plan on stopping the event loop, it is wise to send an event through
67 the queue to ensure this method will return.
68 See wxEventLoop::Exit() for more information.
69
70 This overridden method calls the superclass method with an untilDate
71 parameter that indicates nil should be returned if there are no pending
72 events. That is, nextEventMatchingMask: should not wait for an event.
73 If nil is returned then idle event processing occurs until the user
74 does not request anymore idle events or until a real event comes through.
75
76 RN: Even though Apple documentation states that nil can be passed in place
77 of [NSDate distantPast] in the untilDate parameter, this causes Jaguar (10.2)
78 to get stuck in some kind of loop deep within nextEventMatchingMask:, thus we
79 need to explicitly pass [NSDate distantPast] instead.
80*/
81
82- (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
83{
84 // Get the same events except don't block
85 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
86 // If we got one, simply return it
87 if(event)
88 return event;
89 // No events, try doing some idle stuff
90 if(sg_needIdle
91#ifdef __WXDEBUG__
92 && !wxTheApp->IsInAssert()
93#endif
94 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
95 {
96 sg_needIdle = false;
97 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
98 while(wxTheApp->ProcessIdle())
99 {
100 // Get the same events except don't block
101 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
102 // If we got one, simply return it
103 if(event)
104 return event;
105 // we didn't get one, do some idle work
106 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
107 }
108 // No more idle work requested, block
109 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
110 }
111 else
112 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
113 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
114}
115
116- (void)sendEvent: (NSEvent*)anEvent
117{
118 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
119 sg_needIdle = true;
120 [super sendEvent: anEvent];
121}
122
123@end // wxPoserNSApplication
124
125// ========================================================================
126// wxNSApplicationDelegate
127// ========================================================================
128@implementation wxNSApplicationDelegate : NSObject
129
130// NOTE: Terminate means that the event loop does NOT return and thus
131// cleanup code doesn't properly execute. Furthermore, wxWidgets has its
132// own exit on frame delete mechanism.
133- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
134{
135 return NO;
136}
137
138- (void)applicationWillBecomeActive:(NSNotification *)notification
139{
140 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
141}
142
143- (void)applicationDidBecomeActive:(NSNotification *)notification
144{
145 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
146}
147
148- (void)applicationWillResignActive:(NSNotification *)notification
149{
150 wxTheApp->CocoaDelegate_applicationWillResignActive();
151}
152
153- (void)applicationDidResignActive:(NSNotification *)notification
154{
155 wxTheApp->CocoaDelegate_applicationDidResignActive();
156}
157
158- (void)controlTintChanged:(NSNotification *)notification
159{
160 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
161}
162
163@end // implementation wxNSApplicationDelegate : NSObject
164
165// ========================================================================
166// wxApp
167// ========================================================================
168
169// ----------------------------------------------------------------------------
170// wxApp Static member initialization
171// ----------------------------------------------------------------------------
172IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
173BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
174 EVT_IDLE(wxAppBase::OnIdle)
175// EVT_END_SESSION(wxApp::OnEndSession)
176// EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
177END_EVENT_TABLE()
178
179// ----------------------------------------------------------------------------
180// wxApp initialization/cleanup
181// ----------------------------------------------------------------------------
182bool wxApp::Initialize(int& argc, wxChar **argv)
183{
184 wxAutoNSAutoreleasePool pool;
185 m_cocoaMainThread = [NSThread currentThread];
186 // Mac OS X passes a process serial number command line argument when
187 // the application is launched from the Finder. This argument must be
188 // removed from the command line arguments before being handled by the
189 // application (otherwise applications would need to handle it)
190 if ( argc > 1 )
191 {
192 static const wxChar *ARG_PSN = _T("-psn_");
193 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
194 {
195 // remove this argument
196 --argc;
197 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
198 }
199 }
200
201 // Posing must be completed before any instances of the Objective-C
202 // classes being posed as are created.
203 wxPoseAsInitializer::InitializePosers();
204
205 return wxAppBase::Initialize(argc, argv);
206}
207
208void wxApp::CleanUp()
209{
210 wxAutoNSAutoreleasePool pool;
211
212 wxDC::CocoaShutdownTextSystem();
213 wxMenuBarManager::DestroyInstance();
214
215 [m_cocoaApp setDelegate:nil];
216 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
217 name:NSControlTintDidChangeNotification object:nil];
218 [m_cocoaAppDelegate release];
219 m_cocoaAppDelegate = NULL;
220
221 wxAppBase::CleanUp();
222}
223
224// ----------------------------------------------------------------------------
225// wxApp creation
226// ----------------------------------------------------------------------------
227wxApp::wxApp()
228{
229 m_topWindow = NULL;
230
231#ifdef __WXDEBUG__
232 m_isInAssert = false;
233#endif // __WXDEBUG__
234
235 argc = 0;
236 argv = NULL;
237 m_cocoaApp = NULL;
238 m_cocoaAppDelegate = NULL;
239}
240
241void wxApp::CocoaDelegate_applicationWillBecomeActive()
242{
243}
244
245void wxApp::CocoaDelegate_applicationDidBecomeActive()
246{
247}
248
249void wxApp::CocoaDelegate_applicationWillResignActive()
250{
251 wxTopLevelWindowCocoa::DeactivatePendingWindow();
252}
253
254void wxApp::CocoaDelegate_applicationDidResignActive()
255{
256}
257
258bool wxApp::OnInitGui()
259{
260 wxAutoNSAutoreleasePool pool;
261 if(!wxAppBase::OnInitGui())
262 return false;
263
264 // Create the app using the sharedApplication method
265 m_cocoaApp = [NSApplication sharedApplication];
266 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
267 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
268 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
269 selector:@selector(controlTintChanged:)
270 name:NSControlTintDidChangeNotification object:nil];
271
272 wxMenuBarManager::CreateInstance();
273
274 wxDC::CocoaInitializeTextSystem();
275 return true;
276}
277
278bool wxApp::CallOnInit()
279{
280// wxAutoNSAutoreleasePool pool;
281 return OnInit();
282}
283
284bool wxApp::OnInit()
285{
286 if(!wxAppBase::OnInit())
287 return false;
288
289 return true;
290}
291
292void wxApp::Exit()
293{
294 wxApp::CleanUp();
295
296 wxAppConsole::Exit();
297}
298
299// Yield to other processes
300bool 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(1)
325 {
326 wxAutoNSAutoreleasePool pool;
327 NSEvent *event = [GetNSApplication()
328 nextEventMatchingMask:NSAnyEventMask
329 untilDate:[NSDate distantPast]
330 inMode:NSDefaultRunLoopMode
331 dequeue: YES];
332 if(!event)
333 break;
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
347void 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__
356void 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__