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