(bad) fix for asserts and OnIdle events
[wxWidgets.git] / src / gtk / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "app.h"
12 #endif
13
14 #include "wx/app.h"
15 #include "wx/gdicmn.h"
16 #include "wx/utils.h"
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #include "wx/memory.h"
20 #include "wx/font.h"
21 #include "wx/settings.h"
22 #include "wx/dialog.h"
23
24 #if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26 #endif
27
28 #include "wx/module.h"
29 #include "wx/image.h"
30
31 #if wxUSE_THREADS
32 #include "wx/thread.h"
33 #endif
34
35 #include <unistd.h>
36
37 #include <glib.h>
38 #include <gdk/gdk.h>
39 #include <gtk/gtk.h>
40
41 #include "wx/gtk/win_gtk.h"
42
43 //-----------------------------------------------------------------------------
44 // global data
45 //-----------------------------------------------------------------------------
46
47 wxApp *wxTheApp = (wxApp *) NULL;
48 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50 extern bool g_isIdle;
51
52 bool g_mainThreadLocked = FALSE;
53 gint g_pendingTag = 0;
54
55 static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
56
57 //-----------------------------------------------------------------------------
58 // local functions
59 //-----------------------------------------------------------------------------
60
61 /* forward declaration */
62 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
63 gint wxapp_pending_callback( gpointer WXUNUSED(data) );
64 void wxapp_install_idle_handler();
65
66 #if wxUSE_THREADS
67 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
68 #endif
69
70 //-----------------------------------------------------------------------------
71 // wxExit
72 //-----------------------------------------------------------------------------
73
74 void wxExit()
75 {
76 gtk_main_quit();
77 }
78
79 //-----------------------------------------------------------------------------
80 // wxYield
81 //-----------------------------------------------------------------------------
82
83 static bool gs_inYield = FALSE;
84
85 bool wxYield()
86 {
87 #if wxUSE_THREADS
88 if ( !wxThread::IsMain() )
89 {
90 // can't call gtk_main_iteration() from other threads like this
91 return TRUE;
92 }
93 #endif // wxUSE_THREADS
94
95 #ifdef __WXDEBUG__
96 if (gs_inYield)
97 wxFAIL_MSG( wxT("wxYield called recursively" ) );
98 #endif
99
100 gs_inYield = TRUE;
101
102 if (!g_isIdle)
103 {
104 // We need to remove idle callbacks or the loop will
105 // never finish.
106 gtk_idle_remove( wxTheApp->m_idleTag );
107 wxTheApp->m_idleTag = 0;
108 g_isIdle = TRUE;
109 }
110
111 // disable log flushing from here because a call to wxYield() shouldn't
112 // normally result in message boxes popping up &c
113 wxLog::Suspend();
114
115 while (gtk_events_pending())
116 gtk_main_iteration();
117
118 /* it's necessary to call ProcessIdle() to update the frames sizes which
119 might have been changed (it also will update other things set from
120 OnUpdateUI() which is a nice (and desired) side effect) */
121 while (wxTheApp->ProcessIdle()) { }
122
123 // let the logs be flashed again
124 wxLog::Resume();
125
126 gs_inYield = FALSE;
127
128 return TRUE;
129 }
130
131 //-----------------------------------------------------------------------------
132 // wxYieldIfNeeded
133 // Like wxYield, but fails silently if the yield is recursive.
134 //-----------------------------------------------------------------------------
135
136 bool wxYieldIfNeeded()
137 {
138 if (gs_inYield)
139 return FALSE;
140
141 return wxYield();
142 }
143
144 //-----------------------------------------------------------------------------
145 // wxWakeUpIdle
146 //-----------------------------------------------------------------------------
147
148 void wxWakeUpIdle()
149 {
150 #if wxUSE_THREADS
151 if (!wxThread::IsMain())
152 wxMutexGuiEnter();
153 #endif
154
155 if (g_isIdle)
156 wxapp_install_idle_handler();
157
158 #if wxUSE_THREADS
159 if (!wxThread::IsMain())
160 wxMutexGuiLeave();
161 #endif
162 }
163
164 //-----------------------------------------------------------------------------
165 // local functions
166 //-----------------------------------------------------------------------------
167
168 gint wxapp_pending_callback( gpointer WXUNUSED(data) )
169 {
170 if (!wxTheApp) return TRUE;
171
172 // when getting called from GDK's time-out handler
173 // we are no longer within GDK's grab on the GUI
174 // thread so we must lock it here ourselves
175 gdk_threads_enter();
176
177 // Sent idle event to all who request them
178 wxTheApp->ProcessPendingEvents();
179
180 g_pendingTag = 0;
181
182 /* flush the logged messages if any */
183 #if wxUSE_LOG
184 wxLog::FlushActive();
185 #endif // wxUSE_LOG
186
187 // Release lock again
188 gdk_threads_leave();
189
190 // Return FALSE to indicate that no more idle events are
191 // to be sent (single shot instead of continuous stream)
192 return FALSE;
193 }
194
195 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
196 {
197 if (!wxTheApp)
198 return TRUE;
199
200 #ifdef __WXDEBUG__
201 // don't generate the idle events while the assert modal dialog is shown,
202 // this completely confuses the apps which don't expect to be reentered
203 // from some safely-looking functions
204 if ( wxTheApp->IsInAssert() )
205 {
206 return TRUE;
207 }
208 #endif // __WXDEBUG__
209
210 // when getting called from GDK's time-out handler
211 // we are no longer within GDK's grab on the GUI
212 // thread so we must lock it here ourselves
213 gdk_threads_enter();
214
215 /* Indicate that we are now in idle mode - even so deeply
216 in idle mode that we don't get any idle events anymore.
217 this is like wxMSW where an idle event is sent only
218 once each time after the event queue has been completely
219 emptied */
220 g_isIdle = TRUE;
221 wxTheApp->m_idleTag = 0;
222
223 // Sent idle event to all who request them as long as they do
224 while (wxTheApp->ProcessIdle())
225 ;
226
227 // Release lock again
228 gdk_threads_leave();
229
230 // Return FALSE to indicate that no more idle events are
231 // to be sent (single shot instead of continuous stream)
232 return FALSE;
233 }
234
235 void wxapp_install_idle_handler()
236 {
237 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
238
239 g_isIdle = FALSE;
240
241 if (g_pendingTag == 0)
242 g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
243
244 /* This routine gets called by all event handlers
245 indicating that the idle is over. It may also
246 get called from other thread for sending events
247 to the main thread (and processing these in
248 idle time). Very low priority. */
249
250 wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
251 }
252
253 #if wxUSE_THREADS
254
255 static int g_threadUninstallLevel = 0;
256
257 void wxapp_install_thread_wakeup()
258 {
259 g_threadUninstallLevel++;
260
261 if (g_threadUninstallLevel != 1) return;
262
263 if (wxTheApp->m_wakeUpTimerTag) return;
264
265 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
266 }
267
268 void wxapp_uninstall_thread_wakeup()
269 {
270 g_threadUninstallLevel--;
271
272 if (g_threadUninstallLevel != 0) return;
273
274 if (!wxTheApp->m_wakeUpTimerTag) return;
275
276 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
277 wxTheApp->m_wakeUpTimerTag = 0;
278 }
279
280 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
281 {
282 // when getting called from GDK's time-out handler
283 // we are no longer within GDK's grab on the GUI
284 // thread so we must lock it here ourselves
285 gdk_threads_enter();
286
287 wxapp_uninstall_thread_wakeup();
288
289 // unblock other threads wishing to do some GUI things
290 wxMutexGuiLeave();
291
292 g_mainThreadLocked = TRUE;
293
294 // wake up other threads
295 wxUsleep( 1 );
296
297 // block other thread again
298 wxMutexGuiEnter();
299
300 g_mainThreadLocked = FALSE;
301
302 wxapp_install_thread_wakeup();
303
304 // release lock again
305 gdk_threads_leave();
306
307 return TRUE;
308 }
309
310 #endif // wxUSE_THREADS
311
312 //-----------------------------------------------------------------------------
313 // wxApp
314 //-----------------------------------------------------------------------------
315
316 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
317
318 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
319 EVT_IDLE(wxApp::OnIdle)
320 END_EVENT_TABLE()
321
322 wxApp::wxApp()
323 {
324 m_initialized = FALSE;
325 #ifdef __WXDEBUG__
326 m_isInAssert = FALSE;
327 #endif // __WXDEBUG__
328
329 m_idleTag = 0;
330 wxapp_install_idle_handler();
331
332 #if wxUSE_THREADS
333 m_wakeUpTimerTag = 0;
334 wxapp_install_thread_wakeup();
335 #endif
336
337 m_colorCube = (unsigned char*) NULL;
338 }
339
340 wxApp::~wxApp()
341 {
342 if (m_idleTag) gtk_idle_remove( m_idleTag );
343
344 #if wxUSE_THREADS
345 wxapp_uninstall_thread_wakeup();
346 #endif
347
348 if (m_colorCube) free(m_colorCube);
349 }
350
351 bool wxApp::OnInitGui()
352 {
353 if ( !wxAppBase::OnInitGui() )
354 return FALSE;
355
356 GdkVisual *visual = gdk_visual_get_system();
357
358 /* on some machines, the default visual is just 256 colours, so
359 we make sure we get the best. this can sometimes be wasteful,
360 of course, but what do these guys pay $30.000 for? */
361
362 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
363 (m_useBestVisual))
364 {
365 #ifdef __WXGTK20__
366 /* seems gtk_widget_set_default_visual no longer exists? */
367 GdkVisual* vis = gtk_widget_get_default_visual();
368 #else
369 GdkVisual* vis = gdk_visual_get_best();
370 gtk_widget_set_default_visual( vis );
371 #endif
372
373 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
374 gtk_widget_set_default_colormap( colormap );
375
376 visual = vis;
377 }
378
379 /* Nothing to do for 15, 16, 24, 32 bit displays */
380 if (visual->depth > 8) return TRUE;
381
382 /* initialize color cube for 8-bit color reduction dithering */
383
384 GdkColormap *cmap = gtk_widget_get_default_colormap();
385
386 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
387
388 for (int r = 0; r < 32; r++)
389 {
390 for (int g = 0; g < 32; g++)
391 {
392 for (int b = 0; b < 32; b++)
393 {
394 int rr = (r << 3) | (r >> 2);
395 int gg = (g << 3) | (g >> 2);
396 int bb = (b << 3) | (b >> 2);
397
398 int index = -1;
399
400 GdkColor *colors = cmap->colors;
401 if (colors)
402 {
403 int max = 3 * 65536;
404
405 for (int i = 0; i < cmap->size; i++)
406 {
407 int rdiff = ((rr << 8) - colors[i].red);
408 int gdiff = ((gg << 8) - colors[i].green);
409 int bdiff = ((bb << 8) - colors[i].blue);
410 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
411 if (sum < max)
412 {
413 index = i; max = sum;
414 }
415 }
416 }
417 else
418 {
419 #if (GTK_MINOR_VERSION > 0)
420 /* assume 8-bit true or static colors. this really
421 exists. */
422 GdkVisual* vis = gdk_colormap_get_visual( cmap );
423 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
424 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
425 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
426 #else
427 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
428 #endif
429 }
430 m_colorCube[ (r*1024) + (g*32) + b ] = index;
431 }
432 }
433 }
434
435 return TRUE;
436 }
437
438 bool wxApp::ProcessIdle()
439 {
440 wxIdleEvent event;
441 event.SetEventObject( this );
442 ProcessEvent( event );
443
444 return event.MoreRequested();
445 }
446
447 void wxApp::OnIdle( wxIdleEvent &event )
448 {
449 static bool s_inOnIdle = FALSE;
450
451 /* Avoid recursion (via ProcessEvent default case) */
452 if (s_inOnIdle)
453 return;
454
455 s_inOnIdle = TRUE;
456
457 /* Resend in the main thread events which have been prepared in other
458 threads */
459 ProcessPendingEvents();
460
461 /* 'Garbage' collection of windows deleted with Close(). */
462 DeletePendingObjects();
463
464 /* Send OnIdle events to all windows */
465 bool needMore = SendIdleEvents();
466
467 if (needMore)
468 event.RequestMore(TRUE);
469
470 s_inOnIdle = FALSE;
471 }
472
473 bool wxApp::SendIdleEvents()
474 {
475 bool needMore = FALSE;
476
477 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
478 while (node)
479 {
480 wxWindow* win = node->GetData();
481 if (SendIdleEvents(win))
482 needMore = TRUE;
483 node = node->GetNext();
484 }
485
486 return needMore;
487 }
488
489 bool wxApp::SendIdleEvents( wxWindow* win )
490 {
491 bool needMore = FALSE;
492
493 wxIdleEvent event;
494 event.SetEventObject(win);
495
496 win->GetEventHandler()->ProcessEvent(event);
497
498 win->OnInternalIdle();
499
500 if (event.MoreRequested())
501 needMore = TRUE;
502
503 wxNode* node = win->GetChildren().First();
504 while (node)
505 {
506 wxWindow* win = (wxWindow*) node->Data();
507 if (SendIdleEvents(win))
508 needMore = TRUE;
509
510 node = node->Next();
511 }
512 return needMore ;
513 }
514
515 int wxApp::MainLoop()
516 {
517 gtk_main();
518 return 0;
519 }
520
521 void wxApp::ExitMainLoop()
522 {
523 if (gtk_main_level() > 0)
524 gtk_main_quit();
525 }
526
527 bool wxApp::Initialized()
528 {
529 return m_initialized;
530 }
531
532 bool wxApp::Pending()
533 {
534 return (gtk_events_pending() > 0);
535 }
536
537 void wxApp::Dispatch()
538 {
539 gtk_main_iteration();
540 }
541
542 void wxApp::DeletePendingObjects()
543 {
544 wxNode *node = wxPendingDelete.First();
545 while (node)
546 {
547 wxObject *obj = (wxObject *)node->Data();
548
549 delete obj;
550
551 if (wxPendingDelete.Find(obj))
552 delete node;
553
554 node = wxPendingDelete.First();
555 }
556 }
557
558 bool wxApp::Initialize()
559 {
560 wxBuffer = new wxChar[BUFSIZ + 512];
561
562 wxClassInfo::InitializeClasses();
563
564 wxSystemSettings::Init();
565
566 #if wxUSE_INTL
567 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
568 #endif
569
570 // GL: I'm annoyed ... I don't know where to put this and I don't want to
571 // create a module for that as it's part of the core.
572 #if wxUSE_THREADS
573 wxPendingEvents = new wxList();
574 wxPendingEventsLocker = new wxCriticalSection();
575 #endif
576
577 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
578 wxTheColourDatabase->Initialize();
579
580 wxInitializeStockLists();
581 wxInitializeStockObjects();
582
583 #if wxUSE_WX_RESOURCES
584 wxInitializeResourceSystem();
585 #endif
586
587 wxModule::RegisterModules();
588 if (!wxModule::InitializeModules()) return FALSE;
589
590 return TRUE;
591 }
592
593 void wxApp::CleanUp()
594 {
595 wxModule::CleanUpModules();
596
597 #if wxUSE_WX_RESOURCES
598 wxCleanUpResourceSystem();
599 #endif
600
601 if (wxTheColourDatabase)
602 delete wxTheColourDatabase;
603
604 wxTheColourDatabase = (wxColourDatabase*) NULL;
605
606 wxDeleteStockObjects();
607
608 wxDeleteStockLists();
609
610 delete wxTheApp;
611 wxTheApp = (wxApp*) NULL;
612
613 // GL: I'm annoyed ... I don't know where to put this and I don't want to
614 // create a module for that as it's part of the core.
615 #if wxUSE_THREADS
616 delete wxPendingEvents;
617 delete wxPendingEventsLocker;
618 #endif
619
620 wxSystemSettings::Done();
621
622 delete[] wxBuffer;
623
624 wxClassInfo::CleanUpClasses();
625
626 // check for memory leaks
627 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
628 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
629 {
630 wxLogDebug(wxT("There were memory leaks.\n"));
631 wxDebugContext::Dump();
632 wxDebugContext::PrintStatistics();
633 }
634 #endif // Debug
635
636 #if wxUSE_LOG
637 // do this as the very last thing because everything else can log messages
638 wxLog::DontCreateOnDemand();
639
640 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
641 if (oldLog)
642 delete oldLog;
643 #endif // wxUSE_LOG
644 }
645
646 //-----------------------------------------------------------------------------
647 // Access to the root window global
648 //-----------------------------------------------------------------------------
649
650 GtkWidget* wxGetRootWindow()
651 {
652 if (gs_RootWindow == NULL) {
653 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
654 gtk_widget_realize( gs_RootWindow );
655 }
656 return gs_RootWindow;
657 }
658
659 //-----------------------------------------------------------------------------
660 // wxEntry
661 //-----------------------------------------------------------------------------
662
663
664 int wxEntryStart( int argc, char *argv[] )
665 {
666 #if wxUSE_THREADS
667 /* GTK 1.2 up to version 1.2.3 has broken threads */
668 if ((gtk_major_version == 1) &&
669 (gtk_minor_version == 2) &&
670 (gtk_micro_version < 4))
671 {
672 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
673 }
674 else
675 {
676 g_thread_init(NULL);
677 }
678 #endif
679
680 gtk_set_locale();
681
682 // We should have the wxUSE_WCHAR_T test on the _outside_
683 #if wxUSE_WCHAR_T
684 #if defined(__WXGTK20__)
685 // gtk+ 2.0 supports Unicode through UTF-8 strings
686 wxConvCurrent = &wxConvUTF8;
687 #else
688 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
689 #endif
690 #else
691 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
692 #endif
693
694 gdk_threads_enter();
695
696 gtk_init( &argc, &argv );
697
698 wxSetDetectableAutoRepeat( TRUE );
699
700 if (!wxApp::Initialize())
701 {
702 gdk_threads_leave();
703 return -1;
704 }
705
706 return 0;
707 }
708
709
710 int wxEntryInitGui()
711 {
712 int retValue = 0;
713
714 if ( !wxTheApp->OnInitGui() )
715 retValue = -1;
716
717 wxGetRootWindow();
718
719 return retValue;
720 }
721
722
723 void wxEntryCleanup()
724 {
725 #if wxUSE_LOG
726 // flush the logged messages if any
727 wxLog *log = wxLog::GetActiveTarget();
728 if (log != NULL && log->HasPendingMessages())
729 log->Flush();
730
731 // continuing to use user defined log target is unsafe from now on because
732 // some resources may be already unavailable, so replace it by something
733 // more safe
734 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
735 if ( oldlog )
736 delete oldlog;
737 #endif // wxUSE_LOG
738
739 wxApp::CleanUp();
740
741 gdk_threads_leave();
742 }
743
744
745
746 int wxEntry( int argc, char *argv[] )
747 {
748 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
749 // This seems to be necessary since there are 'rogue'
750 // objects present at this point (perhaps global objects?)
751 // Setting a checkpoint will ignore them as far as the
752 // memory checking facility is concerned.
753 // Of course you may argue that memory allocated in globals should be
754 // checked, but this is a reasonable compromise.
755 wxDebugContext::SetCheckpoint();
756 #endif
757 int err = wxEntryStart(argc, argv);
758 if (err)
759 return err;
760
761 if (!wxTheApp)
762 {
763 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
764 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
765
766 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
767
768 wxObject *test_app = app_ini();
769
770 wxTheApp = (wxApp*) test_app;
771 }
772
773 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
774
775 wxTheApp->argc = argc;
776 #if wxUSE_UNICODE
777 wxTheApp->argv = new wxChar*[argc+1];
778 int mb_argc = 0;
779 while (mb_argc < argc)
780 {
781 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
782 mb_argc++;
783 }
784 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
785 #else
786 wxTheApp->argv = argv;
787 #endif
788
789 wxString name(wxFileNameFromPath(argv[0]));
790 wxStripExtension( name );
791 wxTheApp->SetAppName( name );
792
793 int retValue;
794 retValue = wxEntryInitGui();
795
796 // Here frames insert themselves automatically into wxTopLevelWindows by
797 // getting created in OnInit().
798 if ( retValue == 0 )
799 {
800 if ( !wxTheApp->OnInit() )
801 retValue = -1;
802 }
803
804 if ( retValue == 0 )
805 {
806 /* delete pending toplevel windows (typically a single
807 dialog) so that, if there isn't any left, we don't
808 call OnRun() */
809 wxTheApp->DeletePendingObjects();
810
811 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
812
813 if (wxTheApp->Initialized())
814 {
815 wxTheApp->OnRun();
816
817 wxWindow *topWindow = wxTheApp->GetTopWindow();
818 if (topWindow)
819 {
820 /* Forcibly delete the window. */
821 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
822 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
823 {
824 topWindow->Close( TRUE );
825 wxTheApp->DeletePendingObjects();
826 }
827 else
828 {
829 delete topWindow;
830 wxTheApp->SetTopWindow( (wxWindow*) NULL );
831 }
832 }
833
834 retValue = wxTheApp->OnExit();
835 }
836 }
837
838 wxEntryCleanup();
839
840 return retValue;
841 }
842
843 #include "wx/gtk/info.xpm"
844 #include "wx/gtk/error.xpm"
845 #include "wx/gtk/question.xpm"
846 #include "wx/gtk/warning.xpm"
847
848 wxIcon
849 wxApp::GetStdIcon(int which) const
850 {
851 switch(which)
852 {
853 case wxICON_INFORMATION:
854 return wxIcon(info_xpm);
855
856 case wxICON_QUESTION:
857 return wxIcon(question_xpm);
858
859 case wxICON_EXCLAMATION:
860 return wxIcon(warning_xpm);
861
862 default:
863 wxFAIL_MSG(wxT("requested non existent standard icon"));
864 // still fall through
865
866 case wxICON_HAND:
867 return wxIcon(error_xpm);
868 }
869 }
870
871 #ifdef __WXDEBUG__
872
873 void wxApp::OnAssert(const wxChar *file, int line, const wxChar *msg)
874 {
875 m_isInAssert = TRUE;
876
877 wxAppBase::OnAssert(file, line, msg);
878
879 m_isInAssert = FALSE;
880 }
881
882 #endif // __WXDEBUG__
883