just in case we have an erroneous character on the server as well at the end of this...
[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 }