Replace dc.cpp with dc.mm and dcclient.cpp with dc.mm
[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 license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21 #ifndef WX_PRECOMP
22     #include "wx/defs.h"
23     #include "wx/app.h"
24     #include "wx/frame.h"
25     #include "wx/dialog.h"
26     #include "wx/dc.h"
27     #include "wx/intl.h"
28     #include "wx/log.h"
29     #include "wx/cocoa/ObjcPose.h"
30 #endif
31
32 #if wxUSE_WX_RESOURCES
33 #  include "wx/resource.h"
34 #endif
35
36 #import <AppKit/NSApplication.h>
37 #import <Foundation/NSRunLoop.h>
38 #import <Foundation/NSArray.h>
39
40 // ----------------------------------------------------------------------------
41 // globals
42 // ----------------------------------------------------------------------------
43
44 wxApp *wxTheApp = NULL;
45 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
46
47 @interface wxPoserNSApplication : NSApplication
48 {
49 }
50
51 - (void)doIdle: (id)data;
52 - (void)finishLaunching;
53 - (void)sendEvent: (NSEvent*)anEvent;
54 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
55 @end // wxPoserNSApplication
56
57 @implementation wxPoserNSApplication : NSApplication
58
59 - (void)doIdle: (id)data
60 {
61     wxASSERT(wxTheApp);
62     wxLogDebug("doIdle called");
63     NSRunLoop *rl = [NSRunLoop currentRunLoop];
64     // runMode: beforeDate returns YES if something was done
65     while(wxTheApp->ProcessIdle()) // FIXME: AND NO EVENTS ARE PENDING
66     {
67         wxLogDebug("Looping for idle events");
68         #if 1
69         if( [rl runMode:[rl currentMode] beforeDate:[NSDate distantPast]])
70         {
71             wxLogDebug("Found actual work to do");
72             break;
73         }
74         #endif
75     }
76     wxLogDebug("Idle processing complete, requesting next idle event");
77     // Add ourself back into the run loop (on next event) if necessary
78     wxTheApp->CocoaRequestIdle();
79 }
80
81 - (void)finishLaunching
82 {
83     wxLogDebug("finishLaunching");
84     bool initsuccess = wxTheApp->OnInit();
85     if(!initsuccess)
86         [super stop: NULL];
87
88     [super finishLaunching];
89 }
90
91 - (void)sendEvent: (NSEvent*)anEvent
92 {
93     wxLogDebug("SendEvent");
94     wxTheApp->CocoaInstallRequestedIdleHandler();
95     [super sendEvent: anEvent];
96 }
97
98 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
99 {
100     BOOL ret = wxTheApp->GetExitOnFrameDelete();
101     wxLogDebug("applicationShouldTermintaeAfterLastWindowClosed=%d",ret);
102     return ret;
103 }
104
105 @end // wxPoserNSApplication
106 WX_IMPLEMENT_POSER(wxPoserNSApplication);
107
108 // ============================================================================
109 // functions
110 // ============================================================================
111
112 //----------------------------------------------------------------------
113 // wxEntry
114 //----------------------------------------------------------------------
115
116 int WXDLLEXPORT wxEntryStart( int WXUNUSED(argc), char *WXUNUSED(argv)[] )
117 {
118     return wxApp::Initialize();
119 }
120
121 int WXDLLEXPORT wxEntryInitGui()
122 {
123     return wxTheApp->OnInitGui();
124 }
125
126 void WXDLLEXPORT wxEntryCleanup()
127 {
128     wxApp::CleanUp();
129 }
130
131 int wxEntry( int argc, char *argv[])
132 {
133     if (!wxEntryStart(argc, argv)) {
134         return 0;
135     }
136     wxLogDebug("Creating application");
137    // create the application object or ensure that one already exists
138     if (!wxTheApp)
139     {
140         // The app may have declared a global application object, but we recommend
141         // the IMPLEMENT_APP macro is used instead, which sets an initializer
142         // function for delayed, dynamic app object construction.
143         wxCHECK_MSG( wxApp::GetInitializerFunction(), 0,
144                      wxT("No initializer - use IMPLEMENT_APP macro.") );
145
146         wxTheApp = (wxApp*) (*wxApp::GetInitializerFunction()) ();
147     }
148
149     wxCHECK_MSG( wxTheApp, 0, wxT("You have to define an instance of wxApp!") );
150
151     // Mac OS X passes a process serial number command line argument when
152     // the application is launched from the Finder. This argument must be
153     // removed from the command line arguments before being handled by the
154     // application (otherwise applications would need to handle it)
155
156     if (argc > 1) {
157         char theArg[6] = "";
158         strncpy(theArg, argv[1], 5);
159
160         if (strcmp(theArg, "-psn_") == 0) {
161             // assume the argument is always the only one and remove it
162             --argc;
163         }
164     }
165
166     wxTheApp->argc = argc;
167     wxTheApp->argv = argv;
168
169     wxLogDebug("initializing gui");
170     // GUI-specific initialization, such as creating an app context.
171     wxEntryInitGui();
172
173     // Here frames insert themselves automatically
174     // into wxTopLevelWindows by getting created
175     // in OnInit().
176
177     int retValue = 0;
178
179     wxLogDebug("Time to run");
180     retValue = wxTheApp->OnRun();
181
182     wxWindow *topWindow = wxTheApp->GetTopWindow();
183     if ( topWindow )
184     {
185         // Forcibly delete the window.
186         if ( topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
187                 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
188         {
189             topWindow->Close(TRUE);
190         }
191         else
192         {
193             delete topWindow;
194             wxTheApp->SetTopWindow(NULL);
195         }
196     }
197
198     wxTheApp->OnExit();
199
200     wxEntryCleanup();
201
202     return retValue;
203 }
204
205 // ----------------------------------------------------------------------------
206 // other functions
207 // ----------------------------------------------------------------------------
208 void wxWakeUpIdle()
209 {
210     wxTheApp->CocoaRequestIdle();
211 }
212
213 void wxExit()
214 {
215     wxLogError(_("Fatal error: exiting"));
216
217     wxApp::CleanUp();
218     exit(1);
219 }
220
221 // ============================================================================
222 // wxApp implementation
223 // ============================================================================
224
225 // ----------------------------------------------------------------------------
226 // wxApp Static member initialization
227 // ----------------------------------------------------------------------------
228 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
229
230 #if !USE_SHARED_LIBRARY
231 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
232 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
233     EVT_IDLE(wxApp::OnIdle)
234 //    EVT_END_SESSION(wxApp::OnEndSession)
235 //    EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
236 END_EVENT_TABLE()
237 #endif
238
239 // ----------------------------------------------------------------------------
240 // wxApp static functions
241 // ----------------------------------------------------------------------------
242 /*static*/ bool wxApp::Initialize()
243 {
244     wxPoseAsInitializer::InitializePosers();
245     wxClassInfo::InitializeClasses();
246
247 #if wxUSE_THREADS
248     wxPendingEventsLocker = new wxCriticalSection;
249 #endif
250
251     wxTheColourDatabase = new wxColourDatabase(wxKEY_STRING);
252     wxTheColourDatabase->Initialize();
253
254     wxInitializeStockLists();
255     wxInitializeStockObjects();
256
257 #if wxUSE_WX_RESOURCES
258     wxInitializeResourceSystem();
259 #endif
260
261     wxBitmap::InitStandardHandlers();
262
263     wxModule::RegisterModules();
264     if (!wxModule::InitializeModules()) {
265         return FALSE;
266     }
267     return TRUE;
268 }
269
270 /*static*/ void wxApp::CleanUp()
271 {
272     wxModule::CleanUpModules();
273
274 #if wxUSE_WX_RESOURCES
275     wxCleanUpResourceSystem();
276 #endif
277
278     wxDeleteStockObjects() ;
279
280     // Destroy all GDI lists, etc.
281     wxDeleteStockLists();
282
283     delete wxTheColourDatabase;
284     wxTheColourDatabase = NULL;
285
286     wxBitmap::CleanUpHandlers();
287
288     delete wxPendingEvents;
289
290 #if wxUSE_THREADS
291     delete wxPendingEventsLocker;
292     // If we don't do the following, we get an apparent memory leak.
293     ((wxEvtHandler&) wxDefaultValidator).ClearEventLocker();
294 #endif
295
296     wxClassInfo::CleanUpClasses();
297
298     delete wxTheApp;
299     wxTheApp = NULL;
300
301 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
302     // At this point we want to check if there are any memory
303     // blocks that aren't part of the wxDebugContext itself,
304     // as a special case. Then when dumping we need to ignore
305     // wxDebugContext, too.
306     if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
307     {
308         wxLogDebug(wxT("There were memory leaks."));
309         wxDebugContext::Dump();
310         wxDebugContext::PrintStatistics();
311     }
312     //  wxDebugContext::SetStream(NULL, NULL);
313 #endif
314
315     wxDC::CocoaShutdownTextSystem();
316 #if wxUSE_LOG
317     // do it as the very last thing because everything else can log messages
318     delete wxLog::SetActiveTarget(NULL);
319 #endif // wxUSE_LOG
320 }
321
322 // ----------------------------------------------------------------------------
323 // wxApp creation
324 // ----------------------------------------------------------------------------
325
326 wxApp::wxApp()
327 {
328     m_topWindow = NULL;
329     wxTheApp = this;
330
331     m_isIdle = true;
332 #if WXWIN_COMPATIBILITY_2_2
333     m_wantDebugOutput = TRUE;
334 #endif
335
336     argc = 0;
337     argv = NULL;
338     m_cocoaApp = NULL;
339 }
340
341 void wxApp::CocoaInstallIdleHandler()
342 {
343     wxLogDebug("wxApp::CocoaInstallIdleHandler");
344     m_isIdle = false;
345     // Call doIdle for EVERYTHING dammit
346 // We'd need Foundation/NSConnection.h for this next constant, do we need it?
347     [[ NSRunLoop currentRunLoop ] performSelector:@selector(doIdle:) target:m_cocoaApp argument:NULL order:0 modes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, /* NSConnectionReplyRunLoopMode,*/ NSModalPanelRunLoopMode, /**/NSEventTrackingRunLoopMode,/**/ nil] ];
348 }
349
350 bool wxApp::OnInitGui()
351 {
352     if(!wxAppBase::OnInitGui())
353         return FALSE;
354
355     // Create the app using the sharedApplication method
356     m_cocoaApp = [NSApplication sharedApplication];
357     wxDC::CocoaInitializeTextSystem();
358 //    [ m_cocoaApp setDelegate:m_cocoaApp ];
359     #if 0
360     wxLogDebug("Just for kicks");
361     [ m_cocoaApp performSelector:@selector(doIdle:) withObject:NULL ];
362     wxLogDebug("okay.. done now");
363     #endif
364     return TRUE;
365 }
366
367 bool wxApp::OnInit()
368 {
369     if(!wxAppBase::OnInit())
370         return FALSE;
371
372     return TRUE;
373 }
374
375 bool wxApp::Initialized()
376 {
377   if (GetTopWindow())
378     return TRUE;
379   else
380     return FALSE;
381 }
382
383 int wxApp::MainLoop()
384 {
385     [m_cocoaApp run];
386     return 0;
387 }
388
389 // Returns TRUE if more time is needed.
390 bool wxApp::ProcessIdle()
391 {
392     wxIdleEvent event;
393     event.SetEventObject(this);
394     ProcessEvent(event);
395
396     return event.MoreRequested();
397 }
398
399 void wxApp::ExitMainLoop()
400 {
401     wxLogDebug("wxApp::ExitMailLoop m_isIdle=%d, isRunning=%d",(int)m_isIdle,(int)[m_cocoaApp isRunning]);
402 //    CocoaInstallRequestedIdleHandler();
403 //    if(m_isIdle)
404 //        [[ NSRunLoop currentRunLoop ] performSelector:@selector(doIdle:) target:m_cocoaApp argument:NULL order:0 modes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, /* NSConnectionReplyRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode,*/ nil] ];
405 // actually.. we WANT the idle event
406 // or not
407 #if 0
408     if(!m_isIdle)
409         [[ NSRunLoop currentRunLoop ] cancelPerformSelector:@selector(doIdle:) target:m_cocoaApp argument:NULL];
410 #endif
411     [m_cocoaApp terminate: m_cocoaApp];
412 }
413
414 // Is a message/event pending?
415 bool wxApp::Pending()
416 {
417     return 0;
418 }
419
420 // Dispatch a message.
421 void wxApp::Dispatch()
422 {
423 }
424
425 void wxApp::OnIdle(wxIdleEvent& event)
426 {
427     wxLogDebug("wxApp::OnIdle");
428    static bool s_inOnIdle = FALSE;
429
430     // Avoid recursion (via ProcessEvent default case)
431     if ( s_inOnIdle )
432         return;
433     s_inOnIdle = TRUE;
434
435
436     DeletePendingObjects();
437
438     // flush the logged messages if any
439     wxLog *pLog = wxLog::GetActiveTarget();
440     if ( pLog != NULL && pLog->HasPendingMessages() )
441         pLog->Flush();
442
443     // Send OnIdle events to all windows
444     bool needMore = SendIdleEvents();
445
446     if (needMore)
447       event.RequestMore(TRUE);
448
449     s_inOnIdle = FALSE;
450 }
451
452 // Send idle event to all top-level windows
453 bool wxApp::SendIdleEvents()
454 {
455     bool needMore = FALSE;
456     wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
457     while (node)
458     {
459         wxWindow* win = node->GetData();
460         if (SendIdleEvents(win))
461             needMore = TRUE;
462
463         node = node->GetNext();
464     }
465     return needMore;
466 }
467
468 // Send idle event to window and all subwindows
469 bool wxApp::SendIdleEvents(wxWindow* win)
470 {
471 //    wxLogDebug("SendIdleEvents win=%p",win);
472     bool needMore = FALSE;
473
474     wxIdleEvent event;
475     event.SetEventObject(win);
476     win->ProcessEvent(event);
477
478     if (event.MoreRequested())
479         needMore = TRUE;
480
481     wxWindowList::Node* node = win->GetChildren().GetFirst();
482     while (node)
483     {
484 //        wxLogDebug("child=%p",node->Data());
485         wxWindow* win = node->GetData();
486         if (SendIdleEvents(win))
487             needMore = TRUE;
488
489         node = node->GetNext();
490     }
491     return needMore;
492 }
493
494 // Yield to other processes
495
496 bool wxApp::Yield(bool onlyIfNeeded)
497 {
498     // MT-FIXME
499     static bool s_inYield = false;
500
501 #if wxUSE_LOG
502     // disable log flushing from here because a call to wxYield() shouldn't
503     // normally result in message boxes popping up &c
504     wxLog::Suspend();
505 #endif // wxUSE_LOG
506
507     if (s_inYield)
508     {
509         if ( !onlyIfNeeded )
510         {
511             wxFAIL_MSG( wxT("wxYield called recursively" ) );
512         }
513
514         return false;
515     }
516
517     s_inYield = true;
518
519     wxLogDebug("WARNING: SUPPOSED to have yielded!");
520     // FIXME: Do something!
521
522 #if wxUSE_LOG
523     // let the logs be flashed again
524     wxLog::Resume();
525 #endif // wxUSE_LOG
526
527     s_inYield = false;
528
529     return true;
530 }
531
532 void wxApp::DeletePendingObjects()
533 {
534     wxNode *node = wxPendingDelete.GetFirst();
535     while (node)
536     {
537         wxObject *obj = (wxObject *)node->GetData();
538
539         delete obj;
540
541         if (wxPendingDelete.Find(obj))
542             delete node;
543
544         node = wxPendingDelete.GetFirst();
545     }
546 }
547
548 // platform specifics
549