]> git.saurik.com Git - wxWidgets.git/blob - src/common/log.cpp
083eca45237f4b02540c6ac61df294450491b90b
[wxWidgets.git] / src / common / log.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: log.cpp
3 // Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 29/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 #ifdef __GNUG__
20 #pragma implementation "log.h"
21 #endif
22
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 // wxWindows
31 #ifndef WX_PRECOMP
32 #include <wx/app.h>
33 #include <wx/string.h>
34 #include <wx/intl.h>
35
36 #include <wx/generic/msgdlgg.h>
37 #include <wx/filedlg.h>
38 #include <wx/textctrl.h>
39 #endif //WX_PRECOMP
40
41 #include <wx/file.h>
42 #include <wx/textfile.h>
43 #include <wx/log.h>
44
45 // other standard headers
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <time.h>
49
50 // _WINDOWS_ is defined when windows.h is included,
51 // __WINDOWS__ is defined for MS Windows compilation
52 #if defined(__WINDOWS__) && !defined(_WINDOWS_)
53 #include <windows.h>
54 #endif //windows.h
55
56 // ----------------------------------------------------------------------------
57 // non member functions
58 // ----------------------------------------------------------------------------
59
60 // define this to enable wrapping of log messages
61 //#define LOG_PRETTY_WRAP
62
63 #ifdef LOG_PRETTY_WRAP
64 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
65 #endif
66
67 // ============================================================================
68 // implementation
69 // ============================================================================
70
71 // ----------------------------------------------------------------------------
72 // implementation of Log functions
73 //
74 // NB: unfortunately we need all these distinct functions, we can't make them
75 // macros and not all compilers inline vararg functions.
76 // ----------------------------------------------------------------------------
77
78 // log functions can't allocate memory (LogError("out of memory...") should
79 // work!), so we use a static buffer for all log messages
80 #define LOG_BUFFER_SIZE (4096)
81
82 // static buffer for error messages (@@@ MT-unsafe)
83 static char s_szBuf[LOG_BUFFER_SIZE];
84
85 // generic log function
86 void wxLogGeneric(wxLogLevel level, wxTString strFormat, ...)
87 {
88 if ( wxLog::GetActiveTarget() != NULL ) {
89 va_list argptr;
90 va_start(argptr, strFormat);
91 vsprintf(s_szBuf, strFormat, argptr);
92 va_end(argptr);
93
94 wxLog::OnLog(level, s_szBuf);
95 }
96 }
97
98 #define IMPLEMENT_LOG_FUNCTION(level) \
99 void wxLog##level(wxTString strFormat, ...) \
100 { \
101 if ( wxLog::GetActiveTarget() != NULL ) { \
102 va_list argptr; \
103 va_start(argptr, strFormat); \
104 vsprintf(s_szBuf, strFormat, argptr); \
105 va_end(argptr); \
106 \
107 wxLog::OnLog(wxLOG_##level, s_szBuf); \
108 } \
109 }
110
111 IMPLEMENT_LOG_FUNCTION(FatalError)
112 IMPLEMENT_LOG_FUNCTION(Error)
113 IMPLEMENT_LOG_FUNCTION(Warning)
114 IMPLEMENT_LOG_FUNCTION(Message)
115 IMPLEMENT_LOG_FUNCTION(Info)
116 IMPLEMENT_LOG_FUNCTION(Status)
117
118 // same as info, but only if 'verbose' mode is on
119 void wxLogVerbose(wxTString strFormat, ...)
120 {
121 wxLog *pLog = wxLog::GetActiveTarget();
122 if ( pLog != NULL && pLog->GetVerbose() ) {
123 va_list argptr;
124 va_start(argptr, strFormat);
125 vsprintf(s_szBuf, strFormat, argptr);
126 va_end(argptr);
127
128 wxLog::OnLog(wxLOG_Info, s_szBuf);
129 }
130 }
131
132 // debug functions
133 #ifdef __DEBUG__
134 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) \
135 void wxLog##level(const char *szFormat, ...) \
136 { \
137 if ( wxLog::GetActiveTarget() != NULL ) { \
138 va_list argptr; \
139 va_start(argptr, szFormat); \
140 vsprintf(s_szBuf, szFormat, argptr); \
141 va_end(argptr); \
142 \
143 wxLog::OnLog(wxLOG_##level, s_szBuf); \
144 } \
145 }
146
147 void wxLogTrace(wxTraceMask mask, const char *szFormat, ...)
148 {
149 wxLog *pLog = wxLog::GetActiveTarget();
150
151 // we check that all of mask bits are set in the current mask, so
152 // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something
153 // if both bits are set.
154 if ( pLog != NULL && (pLog->GetTraceMask() & mask == mask) ) {
155 va_list argptr;
156 va_start(argptr, szFormat);
157 vsprintf(s_szBuf, szFormat, argptr);
158 va_end(argptr);
159
160 wxLog::OnLog(wxLOG_Trace, s_szBuf);
161 }
162 }
163
164 #else // release
165 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)
166 #endif
167
168 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug)
169 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace)
170
171 // wxLogSysError: one uses the last error code, for other you must give it
172 // explicitly
173
174 // common part of both wxLogSysError
175 void wxLogSysErrorHelper(long lErrCode)
176 {
177 char szErrMsg[LOG_BUFFER_SIZE / 2];
178 sprintf(szErrMsg, _(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode));
179 strncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - strlen(s_szBuf));
180
181 wxLog::OnLog(wxLOG_Error, s_szBuf);
182 }
183
184 void WXDLLEXPORT wxLogSysError(wxTString strFormat, ...)
185 {
186 va_list argptr;
187 va_start(argptr, strFormat);
188 vsprintf(s_szBuf, strFormat, argptr);
189 va_end(argptr);
190
191 wxLogSysErrorHelper(wxSysErrorCode());
192 }
193
194 void WXDLLEXPORT wxLogSysError(long lErrCode, wxTString strFormat, ...)
195 {
196 va_list argptr;
197 va_start(argptr, strFormat);
198 vsprintf(s_szBuf, strFormat, argptr);
199 va_end(argptr);
200
201 wxLogSysErrorHelper(lErrCode);
202 }
203
204 // ----------------------------------------------------------------------------
205 // wxLog class implementation
206 // ----------------------------------------------------------------------------
207
208 wxLog::wxLog()
209 {
210 m_bHasMessages = FALSE;
211 m_bVerbose = FALSE;
212 m_szTimeFormat = "[%d/%b/%y %H:%M:%S] ";
213 m_ulTraceMask = (wxTraceMask)-1; // set all bits
214 }
215
216 wxLog *wxLog::GetActiveTarget()
217 {
218 if ( !ms_bInitialized ) {
219 // prevent infinite recursion if someone calls wxLogXXX() from
220 // wxApp::CreateLogTarget()
221 ms_bInitialized = TRUE;
222
223 #ifdef WX_TEST_MINIMAL
224 ms_pLogger = new wxLogStderr;
225 #else
226 // ask the application to create a log target for us
227 ms_pLogger = wxTheApp->CreateLogTarget();
228 #endif
229
230 // do nothing if it fails - what can we do?
231 }
232
233 return ms_pLogger;
234 }
235
236 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
237 {
238 // flush the old messages before changing
239 if ( ms_pLogger != NULL )
240 ms_pLogger->Flush();
241
242 ms_bInitialized = TRUE;
243
244 wxLog *pOldLogger = ms_pLogger;
245 ms_pLogger = pLogger;
246 return pOldLogger;
247 }
248
249 void wxLog::DoLog(wxLogLevel level, const char *szString)
250 {
251 wxString str;
252
253 // prepend a timestamp if not disabled
254 if ( !IsEmpty(m_szTimeFormat) ) {
255 char szBuf[128];
256 time_t timeNow;
257 struct tm *ptmNow;
258
259 time(&timeNow);
260 ptmNow = localtime(&timeNow);
261
262 strftime(szBuf, WXSIZEOF(szBuf), m_szTimeFormat, ptmNow);
263 str = szBuf;
264 }
265
266 switch ( level ) {
267 case wxLOG_FatalError:
268 DoLogString(str << _("Fatal error: ") << szString);
269 DoLogString(_("Program aborted."));
270 Flush();
271 abort();
272 break;
273
274 case wxLOG_Error:
275 DoLogString(str << _("Error: ") << szString);
276 break;
277
278 case wxLOG_Warning:
279 DoLogString(str << _("Warning: ") << szString);
280 break;
281
282 case wxLOG_Info:
283 if ( GetVerbose() )
284 case wxLOG_Message:
285 DoLogString(str + szString);
286 // fall through
287
288 case wxLOG_Status:
289 // nothing to do
290 break;
291
292 case wxLOG_Trace:
293 case wxLOG_Debug:
294 #ifdef __DEBUG__
295 #ifdef __WIN32__
296 // in addition to normal logging, also send the string to debugger
297 // (don't prepend "Debug" here: it will go to debug window anyhow)
298 ::OutputDebugString(str + szString + "\n\r");
299 #endif //Win32
300 DoLogString(str << (level == wxLOG_Trace ? "Trace" : "Debug")
301 << ": " << szString);
302 #endif
303
304 break;
305
306 default:
307 wxFAIL_MSG("unknown log level in wxLog::DoLog");
308 }
309 }
310
311 void wxLog::DoLogString(const char *szString)
312 {
313 wxFAIL_MSG("DoLogString must be overrided if it's called.");
314 }
315
316 void wxLog::Flush()
317 {
318 // do nothing
319 }
320
321 // ----------------------------------------------------------------------------
322 // wxLogStderr class implementation
323 // ----------------------------------------------------------------------------
324
325 wxLogStderr::wxLogStderr(FILE *fp)
326 {
327 if ( fp == NULL )
328 m_fp = stderr;
329 else
330 m_fp = fp;
331 }
332
333 void wxLogStderr::DoLogString(const char *szString)
334 {
335 fputs(szString, m_fp);
336 fputc('\n', m_fp);
337 fflush(m_fp);
338 }
339
340 // ----------------------------------------------------------------------------
341 // wxLogStream implementation
342 // ----------------------------------------------------------------------------
343
344 wxLogStream::wxLogStream(ostream *ostr)
345 {
346 if ( ostr == NULL )
347 m_ostr = &cerr;
348 else
349 m_ostr = ostr;
350 }
351
352 void wxLogStream::DoLogString(const char *szString)
353 {
354 (*m_ostr) << szString << endl << flush;
355 }
356
357 // ----------------------------------------------------------------------------
358 // wxLogTextCtrl implementation
359 // ----------------------------------------------------------------------------
360 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
361 // @@@ TODO: in wxGTK wxTextCtrl doesn't derive from streambuf
362 #ifndef __GTK__
363 : wxLogStream(new ostream(pTextCtrl))
364 #endif //GTK
365 {
366 }
367
368 wxLogTextCtrl::~wxLogTextCtrl()
369 {
370 #ifndef __GTK__
371 delete m_ostr;
372 #endif //GTK
373 }
374
375 // ----------------------------------------------------------------------------
376 // wxLogGui implementation
377 // ----------------------------------------------------------------------------
378
379 #ifndef WX_TEST_MINIMAL
380
381 wxLogGui::wxLogGui()
382 {
383 m_bErrors = FALSE;
384 }
385
386 void wxLogGui::Flush()
387 {
388 if ( !m_bHasMessages )
389 return;
390
391 // @@@ ugly...
392
393 // concatenate all strings (but not too many to not overfill the msg box)
394 wxString str;
395 uint nLines = 0,
396 nMsgCount = m_aMessages.Count();
397
398 // start from the most recent message
399 for ( uint n = nMsgCount; n > 0; n-- ) {
400 // for Windows strings longer than this value are wrapped (NT 4.0)
401 const uint nMsgLineWidth = 156;
402
403 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
404
405 if ( nLines > 25 ) // don't put too many lines in message box
406 break;
407
408 str << m_aMessages[n - 1] << "\n";
409 }
410
411 if ( m_bErrors ) {
412 wxMessageBox(str, _("Error"), wxOK | wxICON_EXCLAMATION);
413 }
414 else {
415 wxMessageBox(str, _("Information"), wxOK | wxICON_INFORMATION);
416 }
417
418 // no undisplayed messages whatsoever
419 m_bHasMessages =
420 m_bErrors = FALSE;
421 m_aMessages.Empty();
422 }
423
424 // the default behaviour is to discard all informational messages if there
425 // are any errors/warnings.
426 void wxLogGui::DoLog(wxLogLevel level, const char *szString)
427 {
428 switch ( level ) {
429 case wxLOG_Info:
430 if ( GetVerbose() )
431 case wxLOG_Message:
432 if ( !m_bErrors ) {
433 m_aMessages.Add(szString);
434 m_bHasMessages = TRUE;
435 }
436 break;
437
438 case wxLOG_Status:
439 {
440 // find the top window and set it's status text if it has any
441 wxWindow *pWin = wxTheApp->GetTopWindow();
442 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
443 wxFrame *pFrame = (wxFrame *)pWin;
444 pFrame->SetStatusText(szString);
445 }
446 }
447 break;
448
449 case wxLOG_Trace:
450 case wxLOG_Debug:
451 #ifdef __DEBUG__
452 #ifdef __WIN32__
453 OutputDebugString(szString);
454 OutputDebugString("\n\r");
455 #else //!WIN32
456 // send them to stderr
457 fprintf(stderr, level == wxLOG_Trace ? "Trace: %s\n"
458 : "Debug: %s\n", szString);
459 fflush(stderr);
460 #endif // WIN32
461 #endif
462 break;
463
464 case wxLOG_FatalError:
465 // show this one immediately
466 wxMessageBox(szString, "Fatal error", wxICON_HAND);
467 break;
468
469 case wxLOG_Error:
470 case wxLOG_Warning:
471 // discard earlier informational messages if this is the 1st error
472 if ( !m_bErrors ) {
473 m_aMessages.Empty();
474 m_bHasMessages = TRUE;
475 m_bErrors = TRUE;
476 }
477
478 m_aMessages.Add(szString);
479 break;
480
481 default:
482 wxFAIL_MSG("unknown log level in wxLogGui::DoLog");
483 }
484 }
485
486 // ----------------------------------------------------------------------------
487 // wxLogWindow implementation
488 // ----------------------------------------------------------------------------
489
490 // log frame class
491 class wxLogFrame : public wxFrame
492 {
493 public:
494 // ctor
495 wxLogFrame(const char *szTitle);
496
497 // menu callbacks
498 void OnClose(wxCommandEvent& event);
499 void OnSave (wxCommandEvent& event);
500 void OnClear(wxCommandEvent& event);
501
502 // accessors
503 wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
504
505 private:
506 enum
507 {
508 Menu_Close = 100,
509 Menu_Save,
510 Menu_Clear
511 };
512
513 wxTextCtrl *m_pTextCtrl;
514
515 DECLARE_EVENT_TABLE()
516 };
517
518 BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
519 // wxLogWindow menu events
520 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
521 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
522 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
523
524 EVT_CLOSE(wxLogFrame::OnClose)
525 END_EVENT_TABLE()
526
527 wxLogFrame::wxLogFrame(const char *szTitle)
528 : wxFrame(NULL, -1, szTitle)
529 {
530 // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating
531 // a rich edit control instead of a normal one we want
532 m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
533 wxDefaultSize,
534 wxSIMPLE_BORDER |
535 wxTE_MULTILINE |
536 wxHSCROLL |
537 wxTE_READONLY);
538 /*
539 m_pTextCtrl->SetEditable(FALSE);
540 m_pTextCtrl->SetRichEdit(FALSE);
541 */
542
543 // create menu
544 wxMenuBar *pMenuBar = new wxMenuBar;
545 wxMenu *pMenu = new wxMenu;
546 pMenu->Append(Menu_Save, "&Save...");
547 pMenu->Append(Menu_Clear, "C&lear");
548 pMenu->AppendSeparator();
549 pMenu->Append(Menu_Close, "&Close");
550 pMenuBar->Append(pMenu, "&Log");
551 SetMenuBar(pMenuBar);
552
553 // @@ what about status bar? needed (for menu prompts)?
554 }
555
556 void wxLogFrame::OnClose(wxCommandEvent& event)
557 {
558 // just hide the window
559 Show(FALSE);
560 }
561
562 void wxLogFrame::OnSave(wxCommandEvent& event)
563 {
564 // get the file name
565 // -----------------
566 const char *szFileName = wxSaveFileSelector("log", "txt", "log.txt");
567 if ( szFileName == NULL ) {
568 // cancelled
569 return;
570 }
571
572 // open file
573 // ---------
574 wxFile file;
575 bool bOk;
576 if ( wxFile::Exists(szFileName) ) {
577 bool bAppend;
578 wxString strMsg;
579 strMsg.Printf(_("Append log to file '%s' "
580 "(choosing [No] will overwrite it)?"), szFileName);
581 switch ( wxMessageBox(strMsg, "Question", wxYES_NO | wxCANCEL) ) {
582 case wxYES:
583 bAppend = TRUE;
584 break;
585
586 case wxNO:
587 bAppend = FALSE;
588 break;
589
590 case wxCANCEL:
591 return;
592
593 default:
594 wxFAIL_MSG("invalid message box return value");
595 }
596
597 if ( bAppend ) {
598 bOk = file.Open(szFileName, wxFile::write_append);
599 }
600 else {
601 bOk = file.Create(szFileName, TRUE /* overwrite */);
602 }
603 }
604 else {
605 bOk = file.Create(szFileName);
606 }
607
608 // retrieve text and save it
609 // -------------------------
610
611 // @@@@ TODO: no GetNumberOfLines and GetLineText in wxGTK yet
612 #ifndef __GTK__
613 int nLines = m_pTextCtrl->GetNumberOfLines();
614 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
615 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) + wxTextFile::GetEOL());
616 }
617 #endif //GTK
618
619 if ( bOk )
620 bOk = file.Close();
621
622 if ( !bOk ) {
623 wxLogError("Can't save log contents to file.");
624 return;
625 }
626 }
627
628 void wxLogFrame::OnClear(wxCommandEvent& event)
629 {
630 m_pTextCtrl->Clear();
631 }
632
633 wxLogWindow::wxLogWindow(const wxTString& strTitle)
634 {
635 m_pOldLog = wxLog::GetActiveTarget();
636 m_pLogFrame = new wxLogFrame(strTitle);
637 }
638
639 void wxLogWindow::Show(bool bShow)
640 {
641 m_pLogFrame->Show(bShow);
642 }
643
644 void wxLogWindow::DoLog(wxLogLevel level, const char *szString)
645 {
646 // first let the previous logger show it
647 if ( m_pOldLog != NULL ) {
648 // @@@ why can't we access protected wxLog method from here (we derive
649 // from wxLog)? gcc gives "DoLog is protected in this context", what
650 // does this mean? Anyhow, the cast is harmless and let's us do what
651 // we want.
652 ((wxLogWindow *)m_pOldLog)->DoLog(level, szString);
653 }
654
655 // and this will format it nicely and call our DoLogString()
656 wxLog::DoLog(level, szString);
657 }
658
659 void wxLogWindow::DoLogString(const char *szString)
660 {
661 // put the text into our window
662 wxTextCtrl *pText = m_pLogFrame->TextCtrl();
663
664 // remove selection (WriteText is in fact ReplaceSelection)
665 long nLen = pText->GetLastPosition();
666 pText->SetSelection(nLen, nLen);
667
668 pText->WriteText(szString);
669 pText->WriteText("\n"); // "\n" ok here (_not_ "\r\n")
670
671 // ensure that the line can be seen
672 // @@@ TODO
673 }
674
675 wxLogWindow::~wxLogWindow()
676 {
677 m_pLogFrame->Close(TRUE);
678 }
679
680 #endif //WX_TEST_MINIMAL
681
682 // ============================================================================
683 // Global functions/variables
684 // ============================================================================
685
686 // ----------------------------------------------------------------------------
687 // static variables
688 // ----------------------------------------------------------------------------
689 wxLog *wxLog::ms_pLogger = NULL;
690 bool wxLog::ms_bInitialized = FALSE;
691
692 // ----------------------------------------------------------------------------
693 // stdout error logging helper
694 // ----------------------------------------------------------------------------
695
696 // helper function: wraps the message and justifies it under given position
697 // (looks more pretty on the terminal). Also adds newline at the end.
698 //
699 // @@ this is now disabled until I find a portable way of determining the
700 // terminal window size (ok, I found it but does anybody really cares?)
701 #ifdef LOG_PRETTY_WRAP
702 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
703 {
704 size_t nMax = 80; // @@@@
705 size_t nStart = strlen(pszPrefix);
706 fputs(pszPrefix, f);
707
708 size_t n;
709 while ( *psz != '\0' ) {
710 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
711 putc(*psz++, f);
712
713 // wrapped?
714 if ( *psz != '\0' ) {
715 /*putc('\n', f);*/
716 for ( n = 0; n < nStart; n++ )
717 putc(' ', f);
718
719 // as we wrapped, squeeze all white space
720 while ( isspace(*psz) )
721 psz++;
722 }
723 }
724
725 putc('\n', f);
726 }
727 #endif //LOG_PRETTY_WRAP
728
729 // ----------------------------------------------------------------------------
730 // error code/error message retrieval functions
731 // ----------------------------------------------------------------------------
732
733 // get error code from syste
734 unsigned long wxSysErrorCode()
735 {
736 #ifdef __WINDOWS__
737 #ifdef __WIN32__
738 return ::GetLastError();
739 #else //WIN16
740 // @@@@ what to do on Windows 3.1?
741 return 0;
742 #endif //WIN16/32
743 #else //Unix
744 return errno;
745 #endif //Win/Unix
746 }
747
748 // get error message from system
749 const char *wxSysErrorMsg(unsigned long nErrCode)
750 {
751 if ( nErrCode == 0 )
752 nErrCode = wxSysErrorCode();
753
754 #ifdef __WINDOWS__
755 #ifdef __WIN32__
756 static char s_szBuf[LOG_BUFFER_SIZE / 2];
757
758 // get error message from system
759 LPVOID lpMsgBuf;
760 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
761 NULL, nErrCode,
762 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
763 (LPTSTR)&lpMsgBuf,
764 0, NULL);
765
766 // copy it to our buffer and free memory
767 strncpy(s_szBuf, (const char *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
768 s_szBuf[WXSIZEOF(s_szBuf) - 1] = '\0';
769 LocalFree(lpMsgBuf);
770
771 // returned string is capitalized and ended with '\r\n' - bad
772 s_szBuf[0] = (char)tolower(s_szBuf[0]);
773 size_t len = strlen(s_szBuf);
774 if ( len > 0 ) {
775 // truncate string
776 if ( s_szBuf[len - 2] == '\r' )
777 s_szBuf[len - 2] = '\0';
778 }
779
780 return s_szBuf;
781 #else //Win16
782 // TODO @@@@
783 return NULL;
784 #endif // Win16/32
785 #else // Unix
786 return strerror(nErrCode);
787 #endif // Win/Unix
788 }
789
790 // ----------------------------------------------------------------------------
791 // debug helper
792 // ----------------------------------------------------------------------------
793
794 #ifdef __DEBUG__
795
796 // this function is called when an assert fails
797 void wxOnAssert(const char *szFile, int nLine, const char *szMsg)
798 {
799 // this variable can be set to true to suppress "assert failure" messages
800 static s_bNoAsserts = FALSE;
801
802 char szBuf[LOG_BUFFER_SIZE];
803 sprintf(szBuf, _("Assert failed in file %s at line %d"), szFile, nLine);
804 if ( szMsg != NULL ) {
805 strcat(szBuf, ": ");
806 strcat(szBuf, szMsg);
807 }
808 else {
809 strcat(szBuf, ".");
810 }
811
812 // send it to the normal log destination
813 wxLogDebug(szBuf);
814
815 #ifdef __WINDOWS__
816 if ( !s_bNoAsserts ) {
817 strcat(szBuf, _("\nDo you want to stop the program?"
818 "\nYou can also choose [Cancel] to suppress "
819 "further warnings."));
820
821 switch ( ::MessageBox(NULL, szBuf, _("Debug"),
822 MB_YESNOCANCEL | MB_ICONINFORMATION) ) {
823 case IDYES:
824 DebugBreak();
825 break;
826
827 case IDCANCEL:
828 s_bNoAsserts = TRUE;
829 break;
830 }
831 }
832 #else // !Windows
833 // @@@@ don't know how to start the debugger under generic Unix
834 s_bNoAsserts = TRUE; // suppress 'unused var' warning
835 abort();
836 #endif // Windows/!Windows
837 }
838
839 #endif //DEBUG
840