Don't call wxMenuBarManager::CocoaInternalIdle(). It is about to be removed.
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        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:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14     #include "wx/defs.h"
15     #include "wx/app.h"
16     #include "wx/frame.h"
17     #include "wx/dialog.h"
18     #include "wx/dc.h"
19     #include "wx/intl.h"
20     #include "wx/log.h"
21 #endif
22
23 #include "wx/module.h"
24
25 #include "wx/cocoa/ObjcPose.h"
26 #include "wx/cocoa/autorelease.h"
27 #include "wx/cocoa/mbarman.h"
28
29 #if wxUSE_WX_RESOURCES
30 #  include "wx/resource.h"
31 #endif
32
33 #import <AppKit/NSApplication.h>
34 #import <Foundation/NSRunLoop.h>
35 #import <Foundation/NSArray.h>
36 #import <Foundation/NSAutoreleasePool.h>
37 #import <Foundation/NSThread.h>
38 #import <AppKit/NSEvent.h>
39
40 // ========================================================================
41 // wxPoseAsInitializer
42 // ========================================================================
43 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
44
45 // ========================================================================
46 // wxPoserNSApplication
47 // ========================================================================
48 @interface wxPoserNSApplication : NSApplication
49 {
50 }
51
52 - (void)sendEvent: (NSEvent*)anEvent;
53 @end // wxPoserNSApplication
54
55 WX_IMPLEMENT_POSER(wxPoserNSApplication);
56
57 @implementation wxPoserNSApplication : NSApplication
58
59 - (void)sendEvent: (NSEvent*)anEvent
60 {
61     wxLogDebug("SendEvent");
62     wxTheApp->CocoaInstallRequestedIdleHandler();
63     [super sendEvent: anEvent];
64 }
65
66 @end // wxPoserNSApplication
67
68 // ========================================================================
69 // wxNSApplicationDelegate
70 // ========================================================================
71 @interface wxNSApplicationDelegate : NSObject
72 {
73 }
74
75 - (void)doIdle: (id)data;
76 // Delegate methods
77 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
78 - (void)applicationWillBecomeActive:(NSNotification *)notification;
79 - (void)applicationDidBecomeActive:(NSNotification *)notification;
80 - (void)applicationWillResignActive:(NSNotification *)notification;
81 - (void)applicationDidResignActive:(NSNotification *)notification;
82 @end // interface wxNSApplicationDelegate : NSObject
83
84 @implementation wxNSApplicationDelegate : NSObject
85
86 - (void)doIdle: (id)data
87 {
88     wxASSERT(wxTheApp);
89     wxASSERT(wxMenuBarManager::GetInstance());
90     wxLogDebug("doIdle called");
91 #ifdef __WXDEBUG__
92     if(wxTheApp->IsInAssert())
93     {
94         wxLogDebug("Idle events ignored durring assertion dialog");
95     }
96     else
97 #endif
98     {
99         NSRunLoop *rl = [NSRunLoop currentRunLoop];
100         // runMode: beforeDate returns YES if something was done
101         while(wxTheApp->ProcessIdle()) // FIXME: AND NO EVENTS ARE PENDING
102         {
103             wxLogDebug("Looping for idle events");
104             #if 1
105             if( [rl runMode:[rl currentMode] beforeDate:[NSDate distantPast]])
106             {
107                 wxLogDebug("Found actual work to do");
108                 break;
109             }
110             #endif
111         }
112     }
113     wxLogDebug("Idle processing complete, requesting next idle event");
114     // Add ourself back into the run loop (on next event) if necessary
115     wxTheApp->CocoaRequestIdle();
116 }
117
118 // NOTE: Terminate means that the event loop does NOT return and thus
119 // cleanup code doesn't properly execute.  Furthermore, wxWindows has its
120 // own exit on frame delete mechanism.
121 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
122 {
123     return NO;
124 }
125
126 - (void)applicationWillBecomeActive:(NSNotification *)notification
127 {
128     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
129 }
130
131 - (void)applicationDidBecomeActive:(NSNotification *)notification
132 {
133     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
134 }
135
136 - (void)applicationWillResignActive:(NSNotification *)notification
137 {
138     wxTheApp->CocoaDelegate_applicationWillResignActive();
139 }
140
141 - (void)applicationDidResignActive:(NSNotification *)notification
142 {
143     wxTheApp->CocoaDelegate_applicationDidResignActive();
144 }
145
146 @end // implementation wxNSApplicationDelegate : NSObject
147
148 // ========================================================================
149 // wxApp
150 // ========================================================================
151
152 // ----------------------------------------------------------------------------
153 // wxApp Static member initialization
154 // ----------------------------------------------------------------------------
155 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
156 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
157     EVT_IDLE(wxAppBase::OnIdle)
158 //    EVT_END_SESSION(wxApp::OnEndSession)
159 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
160 END_EVENT_TABLE()
161
162 // ----------------------------------------------------------------------------
163 // wxApp initialization/cleanup
164 // ----------------------------------------------------------------------------
165 bool wxApp::Initialize(int& argc, wxChar **argv)
166 {
167     wxAutoNSAutoreleasePool pool;
168     m_cocoaMainThread = [NSThread currentThread];
169     // Mac OS X passes a process serial number command line argument when
170     // the application is launched from the Finder. This argument must be
171     // removed from the command line arguments before being handled by the
172     // application (otherwise applications would need to handle it)
173     if ( argc > 1 )
174     {
175         static const wxChar *ARG_PSN = _T("-psn_");
176         if ( wxStrncmp(argv[1], ARG_PSN, strlen(ARG_PSN)) == 0 )
177         {
178             // remove this argument
179             --argc;
180             memmove(argv + 1, argv + 2, argc * sizeof(char *));
181         }
182     }
183
184     // Posing must be completed before any instances of the Objective-C
185     // classes being posed as are created.
186     wxPoseAsInitializer::InitializePosers();
187
188     return wxAppBase::Initialize(argc, argv);
189 }
190
191 void wxApp::CleanUp()
192 {
193     wxAutoNSAutoreleasePool pool;
194
195     wxDC::CocoaShutdownTextSystem();
196     wxMenuBarManager::DestroyInstance();
197
198     [m_cocoaApp setDelegate:nil];
199     [m_cocoaAppDelegate release];
200     m_cocoaAppDelegate = NULL;
201
202     wxAppBase::CleanUp();
203 }
204
205 // ----------------------------------------------------------------------------
206 // wxApp creation
207 // ----------------------------------------------------------------------------
208 wxApp::wxApp()
209 {
210     m_topWindow = NULL;
211
212     m_isIdle = true;
213 #if WXWIN_COMPATIBILITY_2_2
214     m_wantDebugOutput = TRUE;
215 #endif
216 #ifdef __WXDEBUG__
217     m_isInAssert = FALSE;
218 #endif // __WXDEBUG__
219
220     argc = 0;
221     argv = NULL;
222     m_cocoaApp = NULL;
223     m_cocoaAppDelegate = NULL;
224 }
225
226 void wxApp::CocoaInstallIdleHandler()
227 {
228     // If we're not the main thread, don't install the idle handler
229     if(m_cocoaMainThread != [NSThread currentThread])
230     {
231         wxLogDebug("Attempt to install idle handler from secondary thread");
232         return;
233     }
234     // If we're supposed to be stopping, don't add more idle events
235     if(![m_cocoaApp isRunning])
236         return;
237     wxLogDebug("wxApp::CocoaInstallIdleHandler");
238     m_isIdle = false;
239     // Call doIdle for EVERYTHING dammit
240 // We'd need Foundation/NSConnection.h for this next constant, do we need it?
241     [[ NSRunLoop currentRunLoop ] performSelector:@selector(doIdle:) target:m_cocoaAppDelegate argument:NULL order:0 modes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, /* NSConnectionReplyRunLoopMode,*/ NSModalPanelRunLoopMode, /**/NSEventTrackingRunLoopMode,/**/ nil] ];
242     /* Notes:
243     In the Mac OS X implementation of Cocoa, the above method schedules
244     doIdle: to be called from *within* [NSApplication
245     -nextEventMatchingMask:untilDate:inMode:dequeue:].  That is, no
246     NSEvent object is generated and control does not return from that
247     method.  In fact, control will only return from that method for the
248     usual reasons (e.g. a real event is received or the untilDate is reached).
249     This has implications when trying to stop the event loop and return to
250     its caller.  See wxEventLoop::Exit
251     */
252 }
253
254 void wxApp::CocoaDelegate_applicationWillBecomeActive()
255 {
256 }
257
258 void wxApp::CocoaDelegate_applicationDidBecomeActive()
259 {
260 }
261
262 void wxApp::CocoaDelegate_applicationWillResignActive()
263 {
264 }
265
266 void wxApp::CocoaDelegate_applicationDidResignActive()
267 {
268 }
269
270 bool wxApp::OnInitGui()
271 {
272     wxAutoNSAutoreleasePool pool;
273     if(!wxAppBase::OnInitGui())
274         return FALSE;
275
276     // Create the app using the sharedApplication method
277     m_cocoaApp = [NSApplication sharedApplication];
278     m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
279     [m_cocoaApp setDelegate:m_cocoaAppDelegate];
280
281     wxMenuBarManager::CreateInstance();
282
283     wxDC::CocoaInitializeTextSystem();
284 //    [ m_cocoaApp setDelegate:m_cocoaApp ];
285     #if 0
286     wxLogDebug("Just for kicks");
287     [ m_cocoaAppDelegate performSelector:@selector(doIdle:) withObject:NULL ];
288     wxLogDebug("okay.. done now");
289     #endif
290     return TRUE;
291 }
292
293 bool wxApp::CallOnInit()
294 {
295 //    wxAutoNSAutoreleasePool pool;
296     return OnInit();
297 }
298
299 bool wxApp::OnInit()
300 {
301     if(!wxAppBase::OnInit())
302         return FALSE;
303
304     return TRUE;
305 }
306
307 void wxApp::Exit()
308 {
309     wxApp::CleanUp();
310
311     wxAppConsole::Exit();
312 }
313
314 // Yield to other processes
315 bool wxApp::Yield(bool onlyIfNeeded)
316 {
317     // MT-FIXME
318     static bool s_inYield = false;
319
320 #if wxUSE_LOG
321     // disable log flushing from here because a call to wxYield() shouldn't
322     // normally result in message boxes popping up &c
323     wxLog::Suspend();
324 #endif // wxUSE_LOG
325
326     if (s_inYield)
327     {
328         if ( !onlyIfNeeded )
329         {
330             wxFAIL_MSG( wxT("wxYield called recursively" ) );
331         }
332
333         return false;
334     }
335
336     s_inYield = true;
337
338     // Run the event loop until it is out of events
339     while(NSEvent *event = [GetNSApplication()
340                 nextEventMatchingMask:NSAnyEventMask
341                 untilDate:[NSDate distantPast]
342                 inMode:NSDefaultRunLoopMode
343                 dequeue: YES])
344     {
345         [GetNSApplication() sendEvent: event];
346     }
347
348 #if wxUSE_LOG
349     // let the logs be flashed again
350     wxLog::Resume();
351 #endif // wxUSE_LOG
352
353     s_inYield = false;
354
355     return true;
356 }
357
358 #ifdef __WXDEBUG__
359 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
360 {
361     m_isInAssert = TRUE;
362     wxAppBase::OnAssert(file, line, cond, msg);
363     m_isInAssert = FALSE;
364 }
365 #endif // __WXDEBUG__
366