adding missing autorelease pool, since this can be called from anywhere, fixes #13449
[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 bool wxGUIEventLoop::Dispatch()
147 {
148     if ( !wxTheApp )
149         return false;
150
151     wxMacAutoreleasePool autoreleasepool;
152
153     if(NSEvent *event = [NSApp
154                 nextEventMatchingMask:NSAnyEventMask
155                 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
156                 inMode:NSDefaultRunLoopMode
157                 dequeue: YES])
158     {
159         WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
160         WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
161
162         if (wxTheApp)
163             wxTheApp->MacSetCurrentEvent(event, NULL);
164         m_sleepTime = 0.0;
165         [NSApp sendEvent: event];
166
167         if (wxTheApp)
168             wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
169     }
170     else
171     {
172         if (wxTheApp)
173             wxTheApp->ProcessPendingEvents();
174         
175         if ( wxTheApp->ProcessIdle() )
176             m_sleepTime = 0.0 ;
177         else
178         {
179             m_sleepTime = 1.0;
180 #if wxUSE_THREADS
181             wxMutexGuiLeave();
182             wxMilliSleep(20);
183             wxMutexGuiEnter();
184 #endif
185         }
186     }
187
188     return true;
189 }
190
191 #endif
192
193 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
194 {
195     wxMacAutoreleasePool autoreleasepool;
196
197     if ( m_modalSession )
198     {
199         NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
200         
201         switch (response) 
202         {
203             case NSRunContinuesResponse:
204             {
205                 if ( [[NSApplication sharedApplication]
206                         nextEventMatchingMask: NSAnyEventMask
207                         untilDate: nil
208                         inMode: NSDefaultRunLoopMode
209                         dequeue: NO] != nil )
210                     return 1;
211                 
212                 return -1;
213             }
214                 
215             case NSRunStoppedResponse:
216             case NSRunAbortedResponse:
217                 return -1;
218             default:
219                 wxFAIL_MSG("unknown response code");
220                 break;
221         }
222         return -1;
223     }
224     else 
225     {        
226         NSEvent *event = [NSApp
227                     nextEventMatchingMask:NSAnyEventMask
228                     untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
229                     inMode:NSDefaultRunLoopMode
230                     dequeue: YES];
231         
232         if ( event == nil )
233             return -1;
234
235         [NSApp sendEvent: event];
236
237         return 1;
238     }
239 }
240
241 void wxGUIEventLoop::DoRun()
242 {
243     wxMacAutoreleasePool autoreleasepool;
244     [NSApp run];
245 }
246
247 void wxGUIEventLoop::DoStop()
248 {
249     // only calling stop: is not enough when called from a runloop-observer,
250     // therefore add a dummy event, to make sure the runloop gets another round
251     [NSApp stop:0];
252     WakeUp();
253 }
254
255 void wxGUIEventLoop::WakeUp()
256 {
257     wxMacAutoreleasePool autoreleasepool;
258     NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined 
259                                         location:NSMakePoint(0.0, 0.0) 
260                                    modifierFlags:0 
261                                        timestamp:0 
262                                     windowNumber:0 
263                                          context:nil
264                                          subtype:0 data1:0 data2:0]; 
265     [NSApp postEvent:event atStart:FALSE];
266 }
267
268 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
269 {
270     NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
271     return [nsloop getCFRunLoop];
272 }
273
274
275 // TODO move into a evtloop_osx.cpp
276
277 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
278 {
279     m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
280     wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
281     m_modalNativeWindow = m_modalWindow->GetWXWindow();
282 }
283
284 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
285 {
286     m_modalWindow = NULL;
287     wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
288     m_modalNativeWindow = modalNativeWindow;
289 }
290
291 // END move into a evtloop_osx.cpp
292
293 void wxModalEventLoop::DoRun()
294 {
295     wxMacAutoreleasePool pool;
296
297     // If the app hasn't started, flush the event queue
298     // If we don't do this, the Dock doesn't get the message that
299     // the app has started so will refuse to activate it.
300     [NSApplication sharedApplication];
301     if (![NSApp isRunning])
302     {
303         while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
304         {
305             [NSApp sendEvent:event];
306         }
307     }
308     
309     [NSApp runModalForWindow:m_modalNativeWindow];
310 }
311
312 void wxModalEventLoop::DoStop()
313 {
314     [NSApp stopModal];
315 }
316
317 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
318 {
319     WXWindow nsnow = nil;
320
321     if ( m_modalNestedLevel > 0 )
322     {
323         wxASSERT_MSG( m_modalWindow == modalWindow, "Nested Modal Sessions must be based on same window");
324         m_modalNestedLevel++;
325         return;
326     }
327     
328     m_modalWindow = modalWindow;
329     m_modalNestedLevel = 1;
330     
331     if ( modalWindow )
332     {
333         // we must show now, otherwise beginModalSessionForWindow does it but it
334         // also would do a centering of the window before overriding all our position
335         if ( !modalWindow->IsShownOnScreen() )
336             modalWindow->Show();
337         
338         wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
339         wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
340         nsnow = now ? now->GetWXWindow() : nil;
341     }
342     else
343     {
344         NSRect r = NSMakeRect(10, 10, 0, 0);
345         nsnow = [NSPanel alloc];
346         [nsnow initWithContentRect:r
347                                styleMask:NSBorderlessWindowMask
348                                  backing:NSBackingStoreBuffered
349                                    defer:YES
350          ];
351         [nsnow orderOut:nil];
352         m_dummyWindow = nsnow;
353     }
354     m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
355     wxASSERT_MSG(m_modalSession != NULL, "modal session couldn't be started");
356 }
357
358 void wxGUIEventLoop::EndModalSession()
359 {
360     wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
361     
362     wxASSERT_MSG(m_modalNestedLevel > 0, "incorrect modal nesting level");
363     
364     if ( --m_modalNestedLevel == 0 )
365     {
366         [NSApp endModalSession:(NSModalSession)m_modalSession];
367         m_modalSession = nil;
368         if ( m_dummyWindow )
369         {
370             [m_dummyWindow release];
371             m_dummyWindow = nil;
372         }
373     }
374 }
375
376 //
377 // 
378 //
379
380 wxWindowDisabler::wxWindowDisabler(bool disable)
381 {
382     m_modalEventLoop = NULL;
383     m_disabled = disable;
384     if ( disable )
385         DoDisable();
386 }
387
388 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
389 {
390     m_disabled = true;
391     DoDisable(winToSkip);
392 }
393
394 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
395 {    
396     // remember the top level windows which were already disabled, so that we
397     // don't reenable them later
398     m_winDisabled = NULL;
399     
400     wxWindowList::compatibility_iterator node;
401     for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
402     {
403         wxWindow *winTop = node->GetData();
404         if ( winTop == winToSkip )
405             continue;
406         
407         // we don't need to disable the hidden or already disabled windows
408         if ( winTop->IsEnabled() && winTop->IsShown() )
409         {
410             winTop->Disable();
411         }
412         else
413         {
414             if ( !m_winDisabled )
415             {
416                 m_winDisabled = new wxWindowList;
417             }
418             
419             m_winDisabled->Append(winTop);
420         }
421     }
422     
423     m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
424     m_modalEventLoop->BeginModalSession(winToSkip);
425 }
426
427 wxWindowDisabler::~wxWindowDisabler()
428 {
429     if ( !m_disabled )
430         return;
431     
432     m_modalEventLoop->EndModalSession();
433     
434     wxWindowList::compatibility_iterator node;
435     for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
436     {
437         wxWindow *winTop = node->GetData();
438         if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
439         {
440             winTop->Enable();
441         }
442         //else: had been already disabled, don't reenable
443     }
444     
445     delete m_winDisabled;
446 }
447