]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/app.mm
use wxDIB class instead of duplicating DDB -> DIB conversion code once again (aaaargh...
[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