]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/evtloop.mm
avoid shortcut ampersands in button text
[wxWidgets.git] / src / osx / cocoa / evtloop.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/evtloop.mm
3 // Purpose: implementation of wxEventLoop for OS X
4 // Author: Vadim Zeitlin, Stefan Csomor
5 // Modified by:
6 // Created: 2006-01-12
7 // RCS-ID: $Id$
8 // Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/evtloop.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/nonownedwnd.h"
32 #endif // WX_PRECOMP
33
34 #include "wx/log.h"
35
36 #include "wx/osx/private.h"
37
38 // ============================================================================
39 // wxEventLoop implementation
40 // ============================================================================
41
42 #if 0
43
44 // in case we want to integrate this
45
46 static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
47 {
48 // the masking system doesn't really help, as only the lowlevel UI events
49 // are split in a useful way, all others are way to broad
50
51 if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
52 return NSAnyEventMask;
53
54 NSUInteger mask = 0;
55
56 if ( cat | wxEVT_CATEGORY_USER_INPUT )
57 {
58 mask |=
59 NSLeftMouseDownMask |
60 NSLeftMouseUpMask |
61 NSRightMouseDownMask |
62 NSRightMouseUpMask |
63 NSMouseMovedMask |
64 NSLeftMouseDraggedMask |
65 NSRightMouseDraggedMask |
66 NSMouseEnteredMask |
67 NSMouseExitedMask |
68 NSScrollWheelMask |
69 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
70 NSTabletPointMask |
71 NSTabletProximityMask |
72 #endif
73 NSOtherMouseDownMask |
74 NSOtherMouseUpMask |
75 NSOtherMouseDraggedMask |
76
77 NSKeyDownMask |
78 NSKeyUpMask |
79 NSFlagsChangedMask |
80 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
81 NSEventMaskGesture |
82 NSEventMaskMagnify |
83 NSEventMaskSwipe |
84 NSEventMaskRotate |
85 NSEventMaskBeginGesture |
86 NSEventMaskEndGesture |
87 #endif
88 0;
89 }
90
91 if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
92 {
93 mask |=
94 NSAppKitDefinedMask |
95 NSSystemDefinedMask |
96 NSApplicationDefinedMask |
97 NSPeriodicMask |
98 NSCursorUpdateMask;
99 }
100
101 return mask;
102 }
103
104 #endif
105
106 wxGUIEventLoop::wxGUIEventLoop()
107 {
108 m_modalSession = nil;
109 m_dummyWindow = nil;
110 m_modalNestedLevel = 0;
111 m_modalWindow = NULL;
112 }
113
114 wxGUIEventLoop::~wxGUIEventLoop()
115 {
116 wxASSERT( m_modalSession == nil );
117 wxASSERT( m_dummyWindow == nil );
118 wxASSERT( m_modalNestedLevel == 0 );
119 }
120
121 //-----------------------------------------------------------------------------
122 // events dispatch and loop handling
123 //-----------------------------------------------------------------------------
124
125 #if 0
126
127 bool wxGUIEventLoop::Pending() const
128 {
129 #if 0
130 // this code doesn't reliably detect pending events
131 // so better return true and have the dispatch deal with it
132 // as otherwise we end up in a tight loop when idle events are responded
133 // to by RequestMore(true)
134 wxMacAutoreleasePool autoreleasepool;
135
136 return [[NSApplication sharedApplication]
137 nextEventMatchingMask: NSAnyEventMask
138 untilDate: nil
139 inMode: NSDefaultRunLoopMode
140 dequeue: NO] != nil;
141 #else
142 return true;
143 #endif
144 }
145
146
147 bool wxGUIEventLoop::Dispatch()
148 {
149 if ( !wxTheApp )
150 return false;
151
152 wxMacAutoreleasePool autoreleasepool;
153
154 if(NSEvent *event = [NSApp
155 nextEventMatchingMask:NSAnyEventMask
156 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
157 inMode:NSDefaultRunLoopMode
158 dequeue: YES])
159 {
160 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
161 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
162
163 if (wxTheApp)
164 wxTheApp->MacSetCurrentEvent(event, NULL);
165 m_sleepTime = 0.0;
166 [NSApp sendEvent: event];
167
168 if (wxTheApp)
169 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
170 }
171 else
172 {
173 if (wxTheApp)
174 wxTheApp->ProcessPendingEvents();
175
176 if ( wxTheApp->ProcessIdle() )
177 m_sleepTime = 0.0 ;
178 else
179 {
180 m_sleepTime = 1.0;
181 #if wxUSE_THREADS
182 wxMutexGuiLeave();
183 wxMilliSleep(20);
184 wxMutexGuiEnter();
185 #endif
186 }
187 }
188
189 return true;
190 }
191
192 #endif
193
194 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
195 {
196 wxMacAutoreleasePool autoreleasepool;
197
198 if ( m_modalSession )
199 {
200 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
201
202 switch (response)
203 {
204 case NSRunContinuesResponse:
205 {
206 if ( [[NSApplication sharedApplication]
207 nextEventMatchingMask: NSAnyEventMask
208 untilDate: nil
209 inMode: NSDefaultRunLoopMode
210 dequeue: NO] != nil )
211 return 1;
212
213 return -1;
214 }
215
216 case NSRunStoppedResponse:
217 case NSRunAbortedResponse:
218 return -1;
219 default:
220 wxFAIL_MSG("unknown response code");
221 break;
222 }
223 return -1;
224 }
225 else
226 {
227 NSEvent *event = [NSApp
228 nextEventMatchingMask:NSAnyEventMask
229 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
230 inMode:NSDefaultRunLoopMode
231 dequeue: YES];
232
233 if ( event == nil )
234 return -1;
235
236 [NSApp sendEvent: event];
237
238 return 1;
239 }
240 }
241
242 void wxGUIEventLoop::DoRun()
243 {
244 wxMacAutoreleasePool autoreleasepool;
245 [NSApp run];
246 }
247
248 void wxGUIEventLoop::DoStop()
249 {
250 // only calling stop: is not enough when called from a runloop-observer,
251 // therefore add a dummy event, to make sure the runloop gets another round
252 [NSApp stop:0];
253 WakeUp();
254 }
255
256 void wxGUIEventLoop::WakeUp()
257 {
258 // NSEvent* cevent = [NSApp currentEvent];
259 NSString* mode = [[NSRunLoop mainRunLoop] currentMode];
260
261 // when already in a mouse event handler, don't add higher level event
262 // if ( cevent != nil && [cevent type] <= NSMouseMoved && )
263 if ( [NSEventTrackingRunLoopMode isEqualToString:mode] )
264 {
265 // NSLog(@"event for wakeup %@ in mode %@",cevent,mode);
266 wxCFEventLoop::WakeUp();
267 }
268 else
269 {
270 wxMacAutoreleasePool autoreleasepool;
271 NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
272 location:NSMakePoint(0.0, 0.0)
273 modifierFlags:0
274 timestamp:0
275 windowNumber:0
276 context:nil
277 subtype:0 data1:0 data2:0];
278 [NSApp postEvent:event atStart:FALSE];
279 }
280 }
281
282 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
283 {
284 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
285 return [nsloop getCFRunLoop];
286 }
287
288
289 // TODO move into a evtloop_osx.cpp
290
291 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
292 {
293 m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
294 wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
295 m_modalNativeWindow = m_modalWindow->GetWXWindow();
296 }
297
298 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
299 {
300 m_modalWindow = NULL;
301 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
302 m_modalNativeWindow = modalNativeWindow;
303 }
304
305 // END move into a evtloop_osx.cpp
306
307 void wxModalEventLoop::DoRun()
308 {
309 wxMacAutoreleasePool pool;
310
311 // If the app hasn't started, flush the event queue
312 // If we don't do this, the Dock doesn't get the message that
313 // the app has started so will refuse to activate it.
314 [NSApplication sharedApplication];
315 if (![NSApp isRunning])
316 {
317 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
318 {
319 [NSApp sendEvent:event];
320 }
321 }
322
323 [NSApp runModalForWindow:m_modalNativeWindow];
324 }
325
326 void wxModalEventLoop::DoStop()
327 {
328 [NSApp abortModal];
329 }
330
331 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
332 {
333 WXWindow nsnow = nil;
334
335 if ( m_modalNestedLevel > 0 )
336 {
337 wxASSERT_MSG( m_modalWindow == modalWindow, "Nested Modal Sessions must be based on same window");
338 m_modalNestedLevel++;
339 return;
340 }
341
342 m_modalWindow = modalWindow;
343 m_modalNestedLevel = 1;
344
345 if ( modalWindow )
346 {
347 // we must show now, otherwise beginModalSessionForWindow does it but it
348 // also would do a centering of the window before overriding all our position
349 if ( !modalWindow->IsShownOnScreen() )
350 modalWindow->Show();
351
352 wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
353 wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
354 nsnow = now ? now->GetWXWindow() : nil;
355 }
356 else
357 {
358 NSRect r = NSMakeRect(10, 10, 0, 0);
359 nsnow = [NSPanel alloc];
360 [nsnow initWithContentRect:r
361 styleMask:NSBorderlessWindowMask
362 backing:NSBackingStoreBuffered
363 defer:YES
364 ];
365 [nsnow orderOut:nil];
366 m_dummyWindow = nsnow;
367 }
368 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
369 wxASSERT_MSG(m_modalSession != NULL, "modal session couldn't be started");
370 }
371
372 void wxGUIEventLoop::EndModalSession()
373 {
374 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
375
376 wxASSERT_MSG(m_modalNestedLevel > 0, "incorrect modal nesting level");
377
378 if ( --m_modalNestedLevel == 0 )
379 {
380 [NSApp endModalSession:(NSModalSession)m_modalSession];
381 m_modalSession = nil;
382 if ( m_dummyWindow )
383 {
384 [m_dummyWindow release];
385 m_dummyWindow = nil;
386 }
387 }
388 }
389
390 //
391 //
392 //
393
394 wxWindowDisabler::wxWindowDisabler(bool disable)
395 {
396 m_modalEventLoop = NULL;
397 m_disabled = disable;
398 if ( disable )
399 DoDisable();
400 }
401
402 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
403 {
404 m_disabled = true;
405 DoDisable(winToSkip);
406 }
407
408 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
409 {
410 // remember the top level windows which were already disabled, so that we
411 // don't reenable them later
412 m_winDisabled = NULL;
413
414 wxWindowList::compatibility_iterator node;
415 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
416 {
417 wxWindow *winTop = node->GetData();
418 if ( winTop == winToSkip )
419 continue;
420
421 // we don't need to disable the hidden or already disabled windows
422 if ( winTop->IsEnabled() && winTop->IsShown() )
423 {
424 winTop->Disable();
425 }
426 else
427 {
428 if ( !m_winDisabled )
429 {
430 m_winDisabled = new wxWindowList;
431 }
432
433 m_winDisabled->Append(winTop);
434 }
435 }
436
437 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
438 if (m_modalEventLoop)
439 m_modalEventLoop->BeginModalSession(winToSkip);
440 }
441
442 wxWindowDisabler::~wxWindowDisabler()
443 {
444 if ( !m_disabled )
445 return;
446
447 if (m_modalEventLoop)
448 m_modalEventLoop->EndModalSession();
449
450 wxWindowList::compatibility_iterator node;
451 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
452 {
453 wxWindow *winTop = node->GetData();
454 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
455 {
456 winTop->Enable();
457 }
458 //else: had been already disabled, don't reenable
459 }
460
461 delete m_winDisabled;
462 }