removed obsolete wxUSE_WX_RESOURCES check
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/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
14 #include "wx/app.h"
15
16 #ifndef WX_PRECOMP
17     #include "wx/dc.h"
18     #include "wx/intl.h"
19     #include "wx/log.h"
20     #include "wx/module.h"
21 #endif
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 #import <AppKit/NSApplication.h>
29 #import <Foundation/NSRunLoop.h>
30 #import <Foundation/NSThread.h>
31 #import <AppKit/NSEvent.h>
32 #import <Foundation/NSString.h>
33 #import <Foundation/NSNotification.h>
34 #import <AppKit/NSCell.h>
35
36 // ========================================================================
37 // wxPoseAsInitializer
38 // ========================================================================
39 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
40
41 static bool sg_needIdle = true;
42
43 // ========================================================================
44 // wxPoserNSApplication
45 // ========================================================================
46 @interface wxPoserNSApplication : NSApplication
47 {
48 }
49
50 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
51 - (void)sendEvent: (NSEvent*)anEvent;
52 @end // wxPoserNSApplication
53
54 WX_IMPLEMENT_POSER(wxPoserNSApplication);
55
56 @implementation wxPoserNSApplication : NSApplication
57
58 /* NOTE: The old method of idle event handling added the handler using the
59     [NSRunLoop -performSelector:target:argument:order:modes] which caused
60     the invocation to occur at the begining of [NSApplication
61     -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:].  However,
62     the code would be scheduled for invocation with every iteration of
63     the event loop.  This new method simply overrides the method.  The
64     same caveats apply.  In particular, by the time the event loop has
65     called this method, it usually expects to receive an event.  If you
66     plan on stopping the event loop, it is wise to send an event through
67     the queue to ensure this method will return.
68     See wxEventLoop::Exit() for more information.
69
70     This overridden method calls the superclass method with an untilDate
71     parameter that indicates nil should be returned if there are no pending
72     events.  That is, nextEventMatchingMask: should not wait for an event.
73     If nil is returned then idle event processing occurs until the user
74     does not request anymore idle events or until a real event comes through.
75
76     RN: Even though Apple documentation states that nil can be passed in place
77     of [NSDate distantPast] in the untilDate parameter, this causes Jaguar (10.2)
78     to get stuck in some kind of loop deep within nextEventMatchingMask:, thus we
79     need to explicitly pass [NSDate distantPast] instead.
80 */
81
82 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
83 {
84     // Get the same events except don't block
85     NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
86     // If we got one, simply return it
87     if(event)
88         return event;
89     // No events, try doing some idle stuff
90     if(sg_needIdle
91 #ifdef __WXDEBUG__
92         && !wxTheApp->IsInAssert()
93 #endif
94         && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
95     {
96         sg_needIdle = false;
97         wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
98         while(wxTheApp->ProcessIdle())
99         {
100             // Get the same events except don't block
101             NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
102             // If we got one, simply return it
103             if(event)
104                 return event;
105             // we didn't get one, do some idle work
106             wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
107         }
108         // No more idle work requested, block
109         wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
110     }
111     else
112         wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
113     return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
114 }
115
116 - (void)sendEvent: (NSEvent*)anEvent
117 {
118     wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
119     sg_needIdle = true;
120     [super sendEvent: anEvent];
121 }
122
123 @end // wxPoserNSApplication
124
125 // ========================================================================
126 // wxNSApplicationDelegate
127 // ========================================================================
128 @implementation wxNSApplicationDelegate : NSObject
129
130 // NOTE: Terminate means that the event loop does NOT return and thus
131 // cleanup code doesn't properly execute.  Furthermore, wxWidgets has its
132 // own exit on frame delete mechanism.
133 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
134 {
135     return NO;
136 }
137
138 - (void)applicationWillBecomeActive:(NSNotification *)notification
139 {
140     wxTheApp->CocoaDelegate_applicationWillBecomeActive();
141 }
142
143 - (void)applicationDidBecomeActive:(NSNotification *)notification
144 {
145     wxTheApp->CocoaDelegate_applicationDidBecomeActive();
146 }
147
148 - (void)applicationWillResignActive:(NSNotification *)notification
149 {
150     wxTheApp->CocoaDelegate_applicationWillResignActive();
151 }
152
153 - (void)applicationDidResignActive:(NSNotification *)notification
154 {
155     wxTheApp->CocoaDelegate_applicationDidResignActive();
156 }
157
158 - (void)controlTintChanged:(NSNotification *)notification
159 {
160     wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
161 }
162
163 @end // implementation wxNSApplicationDelegate : NSObject
164
165 // ========================================================================
166 // wxApp
167 // ========================================================================
168
169 // ----------------------------------------------------------------------------
170 // wxApp Static member initialization
171 // ----------------------------------------------------------------------------
172 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
173 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
174     EVT_IDLE(wxAppBase::OnIdle)
175 //    EVT_END_SESSION(wxApp::OnEndSession)
176 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
177 END_EVENT_TABLE()
178
179 // ----------------------------------------------------------------------------
180 // wxApp initialization/cleanup
181 // ----------------------------------------------------------------------------
182 bool wxApp::Initialize(int& argc, wxChar **argv)
183 {
184     wxAutoNSAutoreleasePool pool;
185     m_cocoaMainThread = [NSThread currentThread];
186     // Mac OS X passes a process serial number command line argument when
187     // the application is launched from the Finder. This argument must be
188     // removed from the command line arguments before being handled by the
189     // application (otherwise applications would need to handle it)
190     if ( argc > 1 )
191     {
192         static const wxChar *ARG_PSN = _T("-psn_");
193         if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
194         {
195             // remove this argument
196             --argc;
197             memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
198         }
199     }
200
201     // Posing must be completed before any instances of the Objective-C
202     // classes being posed as are created.
203     wxPoseAsInitializer::InitializePosers();
204
205     return wxAppBase::Initialize(argc, argv);
206 }
207
208 void wxApp::CleanUp()
209 {
210     wxAutoNSAutoreleasePool pool;
211
212     wxDC::CocoaShutdownTextSystem();
213     wxMenuBarManager::DestroyInstance();
214
215     [m_cocoaApp setDelegate:nil];
216     [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
217         name:NSControlTintDidChangeNotification object:nil];
218     [m_cocoaAppDelegate release];
219     m_cocoaAppDelegate = NULL;
220
221     wxAppBase::CleanUp();
222 }
223
224 // ----------------------------------------------------------------------------
225 // wxApp creation
226 // ----------------------------------------------------------------------------
227 wxApp::wxApp()
228 {
229     m_topWindow = NULL;
230
231 #ifdef __WXDEBUG__
232     m_isInAssert = false;
233 #endif // __WXDEBUG__
234
235     argc = 0;
236     argv = NULL;
237     m_cocoaApp = NULL;
238     m_cocoaAppDelegate = NULL;
239 }
240
241 void wxApp::CocoaDelegate_applicationWillBecomeActive()
242 {
243 }
244
245 void wxApp::CocoaDelegate_applicationDidBecomeActive()
246 {
247 }
248
249 void wxApp::CocoaDelegate_applicationWillResignActive()
250 {
251     wxTopLevelWindowCocoa::DeactivatePendingWindow();
252 }
253
254 void wxApp::CocoaDelegate_applicationDidResignActive()
255 {
256 }
257
258 bool wxApp::OnInitGui()
259 {
260     wxAutoNSAutoreleasePool pool;
261     if(!wxAppBase::OnInitGui())
262         return false;
263
264     // Create the app using the sharedApplication method
265     m_cocoaApp = [NSApplication sharedApplication];
266     m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
267     [m_cocoaApp setDelegate:m_cocoaAppDelegate];
268     [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
269         selector:@selector(controlTintChanged:)
270         name:NSControlTintDidChangeNotification object:nil];
271
272     wxMenuBarManager::CreateInstance();
273
274     wxDC::CocoaInitializeTextSystem();
275     return true;
276 }
277
278 bool wxApp::CallOnInit()
279 {
280 //    wxAutoNSAutoreleasePool pool;
281     return OnInit();
282 }
283
284 bool wxApp::OnInit()
285 {
286     if(!wxAppBase::OnInit())
287         return false;
288
289     return true;
290 }
291
292 void wxApp::Exit()
293 {
294     wxApp::CleanUp();
295
296     wxAppConsole::Exit();
297 }
298
299 // Yield to other processes
300 bool wxApp::Yield(bool onlyIfNeeded)
301 {
302     // MT-FIXME
303     static bool s_inYield = false;
304
305 #if wxUSE_LOG
306     // disable log flushing from here because a call to wxYield() shouldn't
307     // normally result in message boxes popping up &c
308     wxLog::Suspend();
309 #endif // wxUSE_LOG
310
311     if (s_inYield)
312     {
313         if ( !onlyIfNeeded )
314         {
315             wxFAIL_MSG( wxT("wxYield called recursively" ) );
316         }
317
318         return false;
319     }
320
321     s_inYield = true;
322
323     // Run the event loop until it is out of events
324     while(1)
325     {
326         wxAutoNSAutoreleasePool pool;
327         NSEvent *event = [GetNSApplication()
328                 nextEventMatchingMask:NSAnyEventMask
329                 untilDate:[NSDate distantPast]
330                 inMode:NSDefaultRunLoopMode
331                 dequeue: YES];
332         if(!event)
333             break;
334         [GetNSApplication() sendEvent: event];
335     }
336
337 #if wxUSE_LOG
338     // let the logs be flashed again
339     wxLog::Resume();
340 #endif // wxUSE_LOG
341
342     s_inYield = false;
343
344     return true;
345 }
346
347 void wxApp::WakeUpIdle()
348 {
349     [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
350             location:NSZeroPoint modifierFlags:NSAnyEventMask
351             timestamp:0 windowNumber:0 context:nil
352             subtype:0 data1:0 data2:0] atStart:NO];
353 }
354
355 #ifdef __WXDEBUG__
356 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
357 {
358     m_isInAssert = true;
359     wxAppBase::OnAssert(file, line, cond, msg);
360     m_isInAssert = false;
361 }
362 #endif // __WXDEBUG__