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