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