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