revert badly screwed up commit :\
[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:     wxWidgets 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/dc.h"
17     #include "wx/intl.h"
18     #include "wx/log.h"
19 #endif
20
21 #include "wx/module.h"
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    
75 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
76 {
77     // Get the same events except don't block
78     NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
79     // If we got one, simply return it
80     if(event)
81         return event;
82     // No events, try doing some idle stuff
83     if(sg_needIdle
84 #ifdef __WXDEBUG__
85         && !wxTheApp->IsInAssert()
86 #endif
87         && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
88     {
89         sg_needIdle = false;
90         wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
91         while(wxTheApp->ProcessIdle())
92         {
93             // Get the same events except don't block
94             NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
95             // If we got one, simply return it
96             if(event)
97                 return event;
98             // we didn't get one, do some idle work
99             wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
100         }
101         // No more idle work requested, block
102         wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
103     }
104     else
105         wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
106     return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
107 }
108
109 - (void)sendEvent: (NSEvent*)anEvent
110 {
111     wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
112     sg_needIdle = true;
113     [super sendEvent: anEvent];
114 }
115
116 @end // wxPoserNSApplication
117
118 // ========================================================================
119 // wxNSApplicationDelegate
120 // ========================================================================
121 @implementation wxNSApplicationDelegate : NSObject
122
123 // NOTE: Terminate means that the event loop does NOT return and thus
124 // cleanup code doesn't properly execute.  Furthermore, wxWidgets has its
125 // own exit on frame delete mechanism.
126 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
127 {
128     return NO;
129 }
130
131 - (void)applicationWillBecomeActive:(NSNotification *)notification
132 {
133     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
134 }
135
136 - (void)applicationDidBecomeActive:(NSNotification *)notification
137 {
138     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
139 }
140
141 - (void)applicationWillResignActive:(NSNotification *)notification
142 {
143     wxTheApp->CocoaDelegate_applicationWillResignActive();
144 }
145
146 - (void)applicationDidResignActive:(NSNotification *)notification
147 {
148     wxTheApp->CocoaDelegate_applicationDidResignActive();
149 }
150
151 - (void)controlTintChanged:(NSNotification *)notification
152 {
153     wxLogDebug("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate");
154 }
155
156 @end // implementation wxNSApplicationDelegate : NSObject
157
158 // ========================================================================
159 // wxApp
160 // ========================================================================
161
162 // ----------------------------------------------------------------------------
163 // wxApp Static member initialization
164 // ----------------------------------------------------------------------------
165 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
166 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
167     EVT_IDLE(wxAppBase::OnIdle)
168 //    EVT_END_SESSION(wxApp::OnEndSession)
169 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
170 END_EVENT_TABLE()
171
172 // ----------------------------------------------------------------------------
173 // wxApp initialization/cleanup
174 // ----------------------------------------------------------------------------
175 bool wxApp::Initialize(int& argc, wxChar **argv)
176 {
177     wxAutoNSAutoreleasePool pool;
178     m_cocoaMainThread = [NSThread currentThread];
179     // Mac OS X passes a process serial number command line argument when
180     // the application is launched from the Finder. This argument must be
181     // removed from the command line arguments before being handled by the
182     // application (otherwise applications would need to handle it)
183     if ( argc > 1 )
184     {
185         static const wxChar *ARG_PSN = _T("-psn_");
186         if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
187         {
188             // remove this argument
189             --argc;
190             memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
191         }
192     }
193
194     // Posing must be completed before any instances of the Objective-C
195     // classes being posed as are created.
196     wxPoseAsInitializer::InitializePosers();
197
198     return wxAppBase::Initialize(argc, argv);
199 }
200
201 void wxApp::CleanUp()
202 {
203     wxAutoNSAutoreleasePool pool;
204
205     wxDC::CocoaShutdownTextSystem();
206     wxMenuBarManager::DestroyInstance();
207
208     [m_cocoaApp setDelegate:nil];
209     [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
210         name:NSControlTintDidChangeNotification object:nil];
211     [m_cocoaAppDelegate release];
212     m_cocoaAppDelegate = NULL;
213
214     wxAppBase::CleanUp();
215 }
216
217 // ----------------------------------------------------------------------------
218 // wxApp creation
219 // ----------------------------------------------------------------------------
220 wxApp::wxApp()
221 {
222     m_topWindow = NULL;
223
224 #if WXWIN_COMPATIBILITY_2_2
225     m_wantDebugOutput = TRUE;
226 #endif
227 #ifdef __WXDEBUG__
228     m_isInAssert = FALSE;
229 #endif // __WXDEBUG__
230
231     argc = 0;
232     argv = NULL;
233     m_cocoaApp = NULL;
234     m_cocoaAppDelegate = NULL;
235 }
236
237 void wxApp::CocoaDelegate_applicationWillBecomeActive()
238 {
239 }
240
241 void wxApp::CocoaDelegate_applicationDidBecomeActive()
242 {
243 }
244
245 void wxApp::CocoaDelegate_applicationWillResignActive()
246 {
247     wxTopLevelWindowCocoa::DeactivatePendingWindow();
248 }
249
250 void wxApp::CocoaDelegate_applicationDidResignActive()
251 {
252 }
253
254 bool wxApp::OnInitGui()
255 {
256     wxAutoNSAutoreleasePool pool;
257     if(!wxAppBase::OnInitGui())
258         return FALSE;
259
260     // Create the app using the sharedApplication method
261     m_cocoaApp = [NSApplication sharedApplication];
262     m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
263     [m_cocoaApp setDelegate:m_cocoaAppDelegate];
264     [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
265         selector:@selector(controlTintChanged:)
266         name:NSControlTintDidChangeNotification object:nil];
267
268     wxMenuBarManager::CreateInstance();
269
270     wxDC::CocoaInitializeTextSystem();
271     return TRUE;
272 }
273
274 bool wxApp::CallOnInit()
275 {
276 //    wxAutoNSAutoreleasePool pool;
277     return OnInit();
278 }
279
280 bool wxApp::OnInit()
281 {
282     if(!wxAppBase::OnInit())
283         return FALSE;
284
285     return TRUE;
286 }
287
288 void wxApp::Exit()
289 {
290     wxApp::CleanUp();
291
292     wxAppConsole::Exit();
293 }
294
295 // Yield to other processes
296 bool wxApp::Yield(bool onlyIfNeeded)
297 {
298     // MT-FIXME
299     static bool s_inYield = false;
300
301 #if wxUSE_LOG
302     // disable log flushing from here because a call to wxYield() shouldn't
303     // normally result in message boxes popping up &c
304     wxLog::Suspend();
305 #endif // wxUSE_LOG
306
307     if (s_inYield)
308     {
309         if ( !onlyIfNeeded )
310         {
311             wxFAIL_MSG( wxT("wxYield called recursively" ) );
312         }
313
314         return false;
315     }
316
317     s_inYield = true;
318
319     // Run the event loop until it is out of events
320     while(1)
321     {
322         wxAutoNSAutoreleasePool pool;
323         NSEvent *event = [GetNSApplication()
324                 nextEventMatchingMask:NSAnyEventMask
325                 untilDate:nil /* ==[NSDate distantPast] */
326                 inMode:NSDefaultRunLoopMode
327                 dequeue: YES];
328         if(!event)
329             break;
330         [GetNSApplication() sendEvent: event];
331     }
332
333 #if wxUSE_LOG
334     // let the logs be flashed again
335     wxLog::Resume();
336 #endif // wxUSE_LOG
337
338     s_inYield = false;
339
340     return true;
341 }
342
343 void wxApp::WakeUpIdle()
344 {
345     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
346             location:NSZeroPoint modifierFlags:NSAnyEventMask
347             timestamp:0 windowNumber:0 context:nil
348             subtype:0 data1:0 data2:0] atStart:NO];
349 }
350
351 #ifdef __WXDEBUG__
352 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
353 {
354     m_isInAssert = TRUE;
355     wxAppBase::OnAssert(file, line, cond, msg);
356     m_isInAssert = FALSE;
357 }
358 #endif // __WXDEBUG__
359