]> git.saurik.com Git - wxWidgets.git/blob - src/common/appbase.cpp
'i' is already used in this scope so make it clearer.
[wxWidgets.git] / src / common / appbase.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: common/base/appbase.cpp
3 // Purpose: implements wxAppConsole class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 19.06.2003 (extracted from common/appcmn.cpp)
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/intl.h"
30 #include "wx/list.h"
31 #include "wx/log.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/utils.h"
35 #include "wx/apptrait.h"
36 #include "wx/cmdline.h"
37 #include "wx/confbase.h"
38 #include "wx/filename.h"
39 #include "wx/msgout.h"
40 #include "wx/tokenzr.h"
41
42 #if !defined(__WXMSW__) || defined(__WXMICROWIN__)
43 #include <signal.h> // for SIGTRAP used by wxTrap()
44 #endif //Win/Unix
45
46 #if defined(__WXMSW__)
47 #include "wx/msw/wrapwin.h" // includes windows.h for MessageBox()
48 #endif
49
50 #if wxUSE_FONTMAP
51 #include "wx/fontmap.h"
52 #endif // wxUSE_FONTMAP
53
54 #if defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
55 // For MacTypes.h for Debugger function
56 #include <CoreFoundation/CFBase.h>
57 #endif
58
59 #if defined(__WXMAC__)
60 #ifdef __DARWIN__
61 #include <CoreServices/CoreServices.h>
62 #else
63 #include "wx/mac/private.h" // includes mac headers
64 #endif
65 #endif // __WXMAC__
66
67 #ifdef __WXDEBUG__
68 #if wxUSE_STACKWALKER
69 #include "wx/stackwalk.h"
70 #ifdef __WXMSW__
71 #include "wx/msw/debughlp.h"
72 #endif
73 #endif // wxUSE_STACKWALKER
74 #endif // __WXDEBUG__
75
76 // wxABI_VERSION can be defined when compiling applications but it should be
77 // left undefined when compiling the library itself, it is then set to its
78 // default value in version.h
79 #if wxABI_VERSION != wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + 99
80 #error "wxABI_VERSION should not be defined when compiling the library"
81 #endif
82
83 // ----------------------------------------------------------------------------
84 // private functions prototypes
85 // ----------------------------------------------------------------------------
86
87 #ifdef __WXDEBUG__
88 // really just show the assert dialog
89 static bool DoShowAssertDialog(const wxString& msg);
90
91 // prepare for showing the assert dialog, use the given traits or
92 // DoShowAssertDialog() as last fallback to really show it
93 static
94 void ShowAssertDialog(const wxChar *szFile,
95 int nLine,
96 const wxChar *szCond,
97 const wxChar *szMsg,
98 wxAppTraits *traits = NULL);
99
100 // turn on the trace masks specified in the env variable WXTRACE
101 static void LINKAGEMODE SetTraceMasks();
102 #endif // __WXDEBUG__
103
104 // ----------------------------------------------------------------------------
105 // global vars
106 // ----------------------------------------------------------------------------
107
108 wxAppConsole *wxAppConsole::ms_appInstance = NULL;
109
110 wxAppInitializerFunction wxAppConsole::ms_appInitFn = NULL;
111
112 // ============================================================================
113 // wxAppConsole implementation
114 // ============================================================================
115
116 // ----------------------------------------------------------------------------
117 // ctor/dtor
118 // ----------------------------------------------------------------------------
119
120 wxAppConsole::wxAppConsole()
121 {
122 m_traits = NULL;
123
124 ms_appInstance = this;
125
126 #ifdef __WXDEBUG__
127 SetTraceMasks();
128 #if wxUSE_UNICODE
129 // In unicode mode the SetTraceMasks call can cause an apptraits to be
130 // created, but since we are still in the constructor the wrong kind will
131 // be created for GUI apps. Destroy it so it can be created again later.
132 delete m_traits;
133 m_traits = NULL;
134 #endif
135 #endif
136 }
137
138 wxAppConsole::~wxAppConsole()
139 {
140 delete m_traits;
141 }
142
143 // ----------------------------------------------------------------------------
144 // initilization/cleanup
145 // ----------------------------------------------------------------------------
146
147 bool wxAppConsole::Initialize(int& argc, wxChar **argv)
148 {
149 // remember the command line arguments
150 this->argc = argc;
151 this->argv = argv;
152
153 #ifndef __WXPALMOS__
154 if ( m_appName.empty() && argv )
155 {
156 // the application name is, by default, the name of its executable file
157 wxFileName::SplitPath(argv[0], NULL, &m_appName, NULL);
158 }
159 #endif
160
161 return true;
162 }
163
164 void wxAppConsole::CleanUp()
165 {
166 }
167
168 // ----------------------------------------------------------------------------
169 // OnXXX() callbacks
170 // ----------------------------------------------------------------------------
171
172 bool wxAppConsole::OnInit()
173 {
174 #if wxUSE_CMDLINE_PARSER
175 wxCmdLineParser parser(argc, argv);
176
177 OnInitCmdLine(parser);
178
179 bool cont;
180 switch ( parser.Parse(false /* don't show usage */) )
181 {
182 case -1:
183 cont = OnCmdLineHelp(parser);
184 break;
185
186 case 0:
187 cont = OnCmdLineParsed(parser);
188 break;
189
190 default:
191 cont = OnCmdLineError(parser);
192 break;
193 }
194
195 if ( !cont )
196 return false;
197 #endif // wxUSE_CMDLINE_PARSER
198
199 return true;
200 }
201
202 int wxAppConsole::OnExit()
203 {
204 #if wxUSE_CONFIG
205 // delete the config object if any (don't use Get() here, but Set()
206 // because Get() could create a new config object)
207 delete wxConfigBase::Set((wxConfigBase *) NULL);
208 #endif // wxUSE_CONFIG
209
210 // use Set(NULL) and not Get() to avoid creating a message output object on
211 // demand when we just want to delete it
212 delete wxMessageOutput::Set(NULL);
213
214 return 0;
215 }
216
217 void wxAppConsole::Exit()
218 {
219 exit(-1);
220 }
221
222 // ----------------------------------------------------------------------------
223 // traits stuff
224 // ----------------------------------------------------------------------------
225
226 wxAppTraits *wxAppConsole::CreateTraits()
227 {
228 return new wxConsoleAppTraits;
229 }
230
231 wxAppTraits *wxAppConsole::GetTraits()
232 {
233 // FIXME-MT: protect this with a CS?
234 if ( !m_traits )
235 {
236 m_traits = CreateTraits();
237
238 wxASSERT_MSG( m_traits, _T("wxApp::CreateTraits() failed?") );
239 }
240
241 return m_traits;
242 }
243
244 // we must implement CreateXXX() in wxApp itself for backwards compatibility
245 #if WXWIN_COMPATIBILITY_2_4
246
247 #if wxUSE_LOG
248
249 wxLog *wxAppConsole::CreateLogTarget()
250 {
251 wxAppTraits *traits = GetTraits();
252 return traits ? traits->CreateLogTarget() : NULL;
253 }
254
255 #endif // wxUSE_LOG
256
257 wxMessageOutput *wxAppConsole::CreateMessageOutput()
258 {
259 wxAppTraits *traits = GetTraits();
260 return traits ? traits->CreateMessageOutput() : NULL;
261 }
262
263 #endif // WXWIN_COMPATIBILITY_2_4
264
265 // ----------------------------------------------------------------------------
266 // event processing
267 // ----------------------------------------------------------------------------
268
269 void wxAppConsole::ProcessPendingEvents()
270 {
271 #if wxUSE_THREADS
272 if ( !wxPendingEventsLocker )
273 return;
274 #endif
275
276 // ensure that we're the only thread to modify the pending events list
277 wxENTER_CRIT_SECT( *wxPendingEventsLocker );
278
279 if ( !wxPendingEvents )
280 {
281 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
282 return;
283 }
284
285 // iterate until the list becomes empty
286 wxList::compatibility_iterator node = wxPendingEvents->GetFirst();
287 while (node)
288 {
289 wxEvtHandler *handler = (wxEvtHandler *)node->GetData();
290 wxPendingEvents->Erase(node);
291
292 // In ProcessPendingEvents(), new handlers might be add
293 // and we can safely leave the critical section here.
294 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
295
296 handler->ProcessPendingEvents();
297
298 wxENTER_CRIT_SECT( *wxPendingEventsLocker );
299
300 node = wxPendingEvents->GetFirst();
301 }
302
303 wxLEAVE_CRIT_SECT( *wxPendingEventsLocker );
304 }
305
306 int wxAppConsole::FilterEvent(wxEvent& WXUNUSED(event))
307 {
308 // process the events normally by default
309 return -1;
310 }
311
312 // ----------------------------------------------------------------------------
313 // exception handling
314 // ----------------------------------------------------------------------------
315
316 #if wxUSE_EXCEPTIONS
317
318 void
319 wxAppConsole::HandleEvent(wxEvtHandler *handler,
320 wxEventFunction func,
321 wxEvent& event) const
322 {
323 // by default, simply call the handler
324 (handler->*func)(event);
325 }
326
327 bool
328 wxAppConsole::OnExceptionInMainLoop()
329 {
330 throw;
331
332 // some compilers are too stupid to know that we never return after throw
333 #if defined(__DMC__) || (defined(_MSC_VER) && _MSC_VER < 1200)
334 return false;
335 #endif
336 }
337
338 #endif // wxUSE_EXCEPTIONS
339
340 // ----------------------------------------------------------------------------
341 // cmd line parsing
342 // ----------------------------------------------------------------------------
343
344 #if wxUSE_CMDLINE_PARSER
345
346 #define OPTION_VERBOSE _T("verbose")
347
348 void wxAppConsole::OnInitCmdLine(wxCmdLineParser& parser)
349 {
350 // the standard command line options
351 static const wxCmdLineEntryDesc cmdLineDesc[] =
352 {
353 {
354 wxCMD_LINE_SWITCH,
355 _T("h"),
356 _T("help"),
357 gettext_noop("show this help message"),
358 wxCMD_LINE_VAL_NONE,
359 wxCMD_LINE_OPTION_HELP
360 },
361
362 #if wxUSE_LOG
363 {
364 wxCMD_LINE_SWITCH,
365 wxEmptyString,
366 OPTION_VERBOSE,
367 gettext_noop("generate verbose log messages"),
368 wxCMD_LINE_VAL_NONE,
369 0x0
370 },
371 #endif // wxUSE_LOG
372
373 // terminator
374 {
375 wxCMD_LINE_NONE,
376 wxEmptyString,
377 wxEmptyString,
378 wxEmptyString,
379 wxCMD_LINE_VAL_NONE,
380 0x0
381 }
382 };
383
384 parser.SetDesc(cmdLineDesc);
385 }
386
387 bool wxAppConsole::OnCmdLineParsed(wxCmdLineParser& parser)
388 {
389 #if wxUSE_LOG
390 if ( parser.Found(OPTION_VERBOSE) )
391 {
392 wxLog::SetVerbose(true);
393 }
394 #else
395 wxUnusedVar(parser);
396 #endif // wxUSE_LOG
397
398 return true;
399 }
400
401 bool wxAppConsole::OnCmdLineHelp(wxCmdLineParser& parser)
402 {
403 parser.Usage();
404
405 return false;
406 }
407
408 bool wxAppConsole::OnCmdLineError(wxCmdLineParser& parser)
409 {
410 parser.Usage();
411
412 return false;
413 }
414
415 #endif // wxUSE_CMDLINE_PARSER
416
417 // ----------------------------------------------------------------------------
418 // debugging support
419 // ----------------------------------------------------------------------------
420
421 /* static */
422 bool wxAppConsole::CheckBuildOptions(const char *optionsSignature,
423 const char *componentName)
424 {
425 #if 0 // can't use wxLogTrace, not up and running yet
426 printf("checking build options object '%s' (ptr %p) in '%s'\n",
427 optionsSignature, optionsSignature, componentName);
428 #endif
429
430 if ( strcmp(optionsSignature, WX_BUILD_OPTIONS_SIGNATURE) != 0 )
431 {
432 wxString lib = wxString::FromAscii(WX_BUILD_OPTIONS_SIGNATURE);
433 wxString prog = wxString::FromAscii(optionsSignature);
434 wxString progName = wxString::FromAscii(componentName);
435 wxString msg;
436
437 msg.Printf(_T("Mismatch between the program and library build versions detected.\nThe library used %s,\nand %s used %s."),
438 lib.c_str(), progName.c_str(), prog.c_str());
439
440 wxLogFatalError(msg.c_str());
441
442 // normally wxLogFatalError doesn't return
443 return false;
444 }
445 #undef wxCMP
446
447 return true;
448 }
449
450 #ifdef __WXDEBUG__
451
452 void wxAppConsole::OnAssert(const wxChar *file,
453 int line,
454 const wxChar *cond,
455 const wxChar *msg)
456 {
457 ShowAssertDialog(file, line, cond, msg, GetTraits());
458 }
459
460 #endif // __WXDEBUG__
461
462 #if WXWIN_COMPATIBILITY_2_4
463
464 bool wxAppConsole::CheckBuildOptions(const wxBuildOptions& buildOptions)
465 {
466 return CheckBuildOptions(buildOptions.m_signature, "your program");
467 }
468
469 #endif
470
471 // ============================================================================
472 // other classes implementations
473 // ============================================================================
474
475 // ----------------------------------------------------------------------------
476 // wxConsoleAppTraitsBase
477 // ----------------------------------------------------------------------------
478
479 #if wxUSE_LOG
480
481 wxLog *wxConsoleAppTraitsBase::CreateLogTarget()
482 {
483 return new wxLogStderr;
484 }
485
486 #endif // wxUSE_LOG
487
488 wxMessageOutput *wxConsoleAppTraitsBase::CreateMessageOutput()
489 {
490 return new wxMessageOutputStderr;
491 }
492
493 #if wxUSE_FONTMAP
494
495 wxFontMapper *wxConsoleAppTraitsBase::CreateFontMapper()
496 {
497 return (wxFontMapper *)new wxFontMapperBase;
498 }
499
500 #endif // wxUSE_FONTMAP
501
502 wxRendererNative *wxConsoleAppTraitsBase::CreateRenderer()
503 {
504 // console applications don't use renderers
505 return NULL;
506 }
507
508 #ifdef __WXDEBUG__
509 bool wxConsoleAppTraitsBase::ShowAssertDialog(const wxString& msg)
510 {
511 return wxAppTraitsBase::ShowAssertDialog(msg);
512 }
513 #endif
514
515 bool wxConsoleAppTraitsBase::HasStderr()
516 {
517 // console applications always have stderr, even under Mac/Windows
518 return true;
519 }
520
521 void wxConsoleAppTraitsBase::ScheduleForDestroy(wxObject *object)
522 {
523 delete object;
524 }
525
526 void wxConsoleAppTraitsBase::RemoveFromPendingDelete(wxObject * WXUNUSED(object))
527 {
528 // nothing to do
529 }
530
531 #if wxUSE_SOCKETS
532 GSocketGUIFunctionsTable* wxConsoleAppTraitsBase::GetSocketGUIFunctionsTable()
533 {
534 return NULL;
535 }
536 #endif
537
538 // ----------------------------------------------------------------------------
539 // wxAppTraits
540 // ----------------------------------------------------------------------------
541
542 #ifdef __WXDEBUG__
543
544 bool wxAppTraitsBase::ShowAssertDialog(const wxString& msg)
545 {
546 return DoShowAssertDialog(msg);
547 }
548
549 #endif // __WXDEBUG__
550
551 // ============================================================================
552 // global functions implementation
553 // ============================================================================
554
555 void wxExit()
556 {
557 if ( wxTheApp )
558 {
559 wxTheApp->Exit();
560 }
561 else
562 {
563 // what else can we do?
564 exit(-1);
565 }
566 }
567
568 void wxWakeUpIdle()
569 {
570 if ( wxTheApp )
571 {
572 wxTheApp->WakeUpIdle();
573 }
574 //else: do nothing, what can we do?
575 }
576
577 #ifdef __WXDEBUG__
578
579 // wxASSERT() helper
580 bool wxAssertIsEqual(int x, int y)
581 {
582 return x == y;
583 }
584
585 // break into the debugger
586 void wxTrap()
587 {
588 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
589 DebugBreak();
590 #elif defined(__WXMAC__) && !defined(__DARWIN__)
591 #if __powerc
592 Debugger();
593 #else
594 SysBreak();
595 #endif
596 #elif defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
597 Debugger();
598 #elif defined(__UNIX__)
599 raise(SIGTRAP);
600 #else
601 // TODO
602 #endif // Win/Unix
603 }
604
605 void wxAssert(int cond,
606 const wxChar *szFile,
607 int nLine,
608 const wxChar *szCond,
609 const wxChar *szMsg)
610 {
611 if ( !cond )
612 wxOnAssert(szFile, nLine, szCond, szMsg);
613 }
614
615 // this function is called when an assert fails
616 void wxOnAssert(const wxChar *szFile,
617 int nLine,
618 const wxChar *szCond,
619 const wxChar *szMsg)
620 {
621 // FIXME MT-unsafe
622 static bool s_bInAssert = false;
623
624 if ( s_bInAssert )
625 {
626 // He-e-e-e-elp!! we're trapped in endless loop
627 wxTrap();
628
629 s_bInAssert = false;
630
631 return;
632 }
633
634 s_bInAssert = true;
635
636 if ( !wxTheApp )
637 {
638 // by default, show the assert dialog box -- we can't customize this
639 // behaviour
640 ShowAssertDialog(szFile, nLine, szCond, szMsg);
641 }
642 else
643 {
644 // let the app process it as it wants
645 wxTheApp->OnAssert(szFile, nLine, szCond, szMsg);
646 }
647
648 s_bInAssert = false;
649 }
650
651 #endif // __WXDEBUG__
652
653 // ============================================================================
654 // private functions implementation
655 // ============================================================================
656
657 #ifdef __WXDEBUG__
658
659 static void LINKAGEMODE SetTraceMasks()
660 {
661 #if wxUSE_LOG
662 wxString mask;
663 if ( wxGetEnv(wxT("WXTRACE"), &mask) )
664 {
665 wxStringTokenizer tkn(mask, wxT(",;:"));
666 while ( tkn.HasMoreTokens() )
667 wxLog::AddTraceMask(tkn.GetNextToken());
668 }
669 #endif // wxUSE_LOG
670 }
671
672 bool DoShowAssertDialog(const wxString& msg)
673 {
674 // under MSW we can show the dialog even in the console mode
675 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
676 wxString msgDlg(msg);
677
678 // this message is intentionally not translated -- it is for
679 // developpers only
680 msgDlg += wxT("\nDo you want to stop the program?\n")
681 wxT("You can also choose [Cancel] to suppress ")
682 wxT("further warnings.");
683
684 switch ( ::MessageBox(NULL, msgDlg, _T("wxWidgets Debug Alert"),
685 MB_YESNOCANCEL | MB_ICONSTOP ) )
686 {
687 case IDYES:
688 wxTrap();
689 break;
690
691 case IDCANCEL:
692 // stop the asserts
693 return true;
694
695 //case IDNO: nothing to do
696 }
697 #else // !__WXMSW__
698 wxFprintf(stderr, wxT("%s\n"), msg.c_str());
699 fflush(stderr);
700
701 // TODO: ask the user to enter "Y" or "N" on the console?
702 wxTrap();
703 #endif // __WXMSW__/!__WXMSW__
704
705 // continue with the asserts
706 return false;
707 }
708
709 #if wxUSE_STACKWALKER
710 static wxString GetAssertStackTrace()
711 {
712 wxString stackTrace;
713
714 class StackDump : public wxStackWalker
715 {
716 public:
717 StackDump() { }
718
719 const wxString& GetStackTrace() const { return m_stackTrace; }
720
721 protected:
722 virtual void OnStackFrame(const wxStackFrame& frame)
723 {
724 m_stackTrace << wxString::Format(_T("[%02d] "), frame.GetLevel());
725
726 wxString name = frame.GetName();
727 if ( !name.empty() )
728 {
729 m_stackTrace << wxString::Format(_T("%-40s"), name.c_str());
730 }
731 else
732 {
733 m_stackTrace << wxString::Format
734 (
735 _T("0x%08lx"),
736 (unsigned long)frame.GetAddress()
737 );
738 }
739
740 if ( frame.HasSourceLocation() )
741 {
742 m_stackTrace << _T('\t')
743 << frame.GetFileName()
744 << _T(':')
745 << frame.GetLine();
746 }
747
748 m_stackTrace << _T('\n');
749 }
750
751 private:
752 wxString m_stackTrace;
753 };
754
755 StackDump dump;
756 dump.Walk(5); // don't show OnAssert() call itself
757 stackTrace = dump.GetStackTrace();
758
759 // don't show more than maxLines or we could get a dialog too tall to be
760 // shown on screen: 20 should be ok everywhere as even with 15 pixel high
761 // characters it is still only 300 pixels...
762 static const int maxLines = 20;
763 const int count = stackTrace.Freq(wxT('\n'));
764 for ( int i = 0; i < count - maxLines; i++ )
765 stackTrace = stackTrace.BeforeLast(wxT('\n'));
766
767 return stackTrace;
768 }
769 #endif // wxUSE_STACKWALKER
770
771 // show the assert modal dialog
772 static
773 void ShowAssertDialog(const wxChar *szFile,
774 int nLine,
775 const wxChar *szCond,
776 const wxChar *szMsg,
777 wxAppTraits *traits)
778 {
779 // this variable can be set to true to suppress "assert failure" messages
780 static bool s_bNoAsserts = false;
781
782 wxString msg;
783 msg.reserve(2048);
784
785 // make life easier for people using VC++ IDE by using this format: like
786 // this, clicking on the message will take us immediately to the place of
787 // the failed assert
788 msg.Printf(wxT("%s(%d): assert \"%s\" failed"), szFile, nLine, szCond);
789
790 if ( szMsg )
791 {
792 msg << _T(": ") << szMsg;
793 }
794 else // no message given
795 {
796 msg << _T('.');
797 }
798
799 #if wxUSE_STACKWALKER
800 const wxString stackTrace = GetAssertStackTrace();
801 if ( !stackTrace.empty() )
802 {
803 msg << _T("\n\nCall stack:\n") << stackTrace;
804 }
805 #endif // wxUSE_STACKWALKER
806
807 #if wxUSE_THREADS
808 // if we are not in the main thread, output the assert directly and trap
809 // since dialogs cannot be displayed
810 if ( !wxThread::IsMain() )
811 {
812 msg += wxT(" [in child thread]");
813
814 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
815 msg << wxT("\r\n");
816 OutputDebugString(msg );
817 #else
818 // send to stderr
819 wxFprintf(stderr, wxT("%s\n"), msg.c_str());
820 fflush(stderr);
821 #endif
822 // He-e-e-e-elp!! we're asserting in a child thread
823 wxTrap();
824 }
825 else
826 #endif // wxUSE_THREADS
827
828 if ( !s_bNoAsserts )
829 {
830 // send it to the normal log destination
831 wxLogDebug(_T("%s"), msg.c_str());
832
833 if ( traits )
834 {
835 // delegate showing assert dialog (if possible) to that class
836 s_bNoAsserts = traits->ShowAssertDialog(msg);
837 }
838 else // no traits object
839 {
840 // fall back to the function of last resort
841 s_bNoAsserts = DoShowAssertDialog(msg);
842 }
843 }
844 }
845
846 #endif // __WXDEBUG__
847