1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Assorted wxLogXXX functions, and wxLog (sink for logs) 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows license 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20   #pragma implementation "log.h" 
  23 // For compilers that support precompilation, includes "wx.h". 
  24 #include "wx/wxprec.h" 
  33   #include  <wx/string.h> 
  37   #include  <wx/generic/msgdlgg.h> 
  38   #include  <wx/filedlg.h> 
  39   #include  <wx/textctrl.h> 
  43 #include  <wx/textfile.h> 
  47 // other standard headers 
  58 // ---------------------------------------------------------------------------- 
  59 // non member functions 
  60 // ---------------------------------------------------------------------------- 
  62 // define this to enable wrapping of log messages 
  63 //#define LOG_PRETTY_WRAP 
  65 #ifdef  LOG_PRETTY_WRAP 
  66   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
  69 // ============================================================================ 
  71 // ============================================================================ 
  73 // ---------------------------------------------------------------------------- 
  74 // implementation of Log functions 
  76 // NB: unfortunately we need all these distinct functions, we can't make them 
  77 //     macros and not all compilers inline vararg functions. 
  78 // ---------------------------------------------------------------------------- 
  80 // log functions can't allocate memory (LogError("out of memory...") should 
  81 // work!), so we use a static buffer for all log messages 
  82 #define LOG_BUFFER_SIZE   (4096) 
  84 // static buffer for error messages (@@@ MT-unsafe) 
  85 static char s_szBuf
[LOG_BUFFER_SIZE
]; 
  87 // generic log function 
  88 void wxLogGeneric(wxLogLevel level
, const char *szFormat
, ...) 
  90   if ( wxLog::GetActiveTarget() != NULL 
) { 
  92     va_start(argptr
, szFormat
); 
  93     vsprintf(s_szBuf
, szFormat
, argptr
); 
  96     wxLog::OnLog(level
, s_szBuf
); 
 100 #define IMPLEMENT_LOG_FUNCTION(level)                             \ 
 101   void wxLog##level(const char *szFormat, ...)                    \ 
 103     if ( wxLog::GetActiveTarget() != NULL ) {                     \ 
 105       va_start(argptr, szFormat);                                 \ 
 106       vsprintf(s_szBuf, szFormat, argptr);                        \ 
 109       wxLog::OnLog(wxLOG_##level, s_szBuf);                       \ 
 113 IMPLEMENT_LOG_FUNCTION(FatalError
) 
 114 IMPLEMENT_LOG_FUNCTION(Error
) 
 115 IMPLEMENT_LOG_FUNCTION(Warning
) 
 116 IMPLEMENT_LOG_FUNCTION(Message
) 
 117 IMPLEMENT_LOG_FUNCTION(Info
) 
 118 IMPLEMENT_LOG_FUNCTION(Status
) 
 120 // same as info, but only if 'verbose' mode is on 
 121 void wxLogVerbose(const char *szFormat
, ...) 
 123   wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 124   if ( pLog 
!= NULL 
&& pLog
->GetVerbose() ) { 
 126     va_start(argptr
, szFormat
); 
 127     vsprintf(s_szBuf
, szFormat
, argptr
); 
 130     wxLog::OnLog(wxLOG_Info
, s_szBuf
); 
 136 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                       \ 
 137   void wxLog##level(const char *szFormat, ...)                    \ 
 139     if ( wxLog::GetActiveTarget() != NULL ) {                     \ 
 141       va_start(argptr, szFormat);                                 \ 
 142       vsprintf(s_szBuf, szFormat, argptr);                        \ 
 145       wxLog::OnLog(wxLOG_##level, s_szBuf);                       \ 
 149   void wxLogTrace(wxTraceMask mask
, const char *szFormat
, ...) 
 151     wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 153     // we check that all of mask bits are set in the current mask, so 
 154     // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something 
 155     // if both bits are set. 
 156     if ( pLog 
!= NULL 
&& (pLog
->GetTraceMask() & mask 
== mask
) ) { 
 158       va_start(argptr
, szFormat
); 
 159       vsprintf(s_szBuf
, szFormat
, argptr
); 
 162       wxLog::OnLog(wxLOG_Trace
, s_szBuf
); 
 167   #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) 
 170 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug
) 
 171 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace
) 
 173 // wxLogSysError: one uses the last error code, for other  you must give it 
 176 // common part of both wxLogSysError 
 177 void wxLogSysErrorHelper(long lErrCode
) 
 179   char szErrMsg
[LOG_BUFFER_SIZE 
/ 2]; 
 180   sprintf(szErrMsg
, _(" (error %ld: %s)"), lErrCode
, wxSysErrorMsg(lErrCode
)); 
 181   strncat(s_szBuf
, szErrMsg
, WXSIZEOF(s_szBuf
) - strlen(s_szBuf
)); 
 183   wxLog::OnLog(wxLOG_Error
, s_szBuf
); 
 186 void WXDLLEXPORT 
wxLogSysError(const char *szFormat
, ...) 
 189   va_start(argptr
, szFormat
); 
 190   vsprintf(s_szBuf
, szFormat
, argptr
); 
 193   wxLogSysErrorHelper(wxSysErrorCode()); 
 196 void WXDLLEXPORT 
wxLogSysError(long lErrCode
, const char *szFormat
, ...) 
 199   va_start(argptr
, szFormat
); 
 200   vsprintf(s_szBuf
, szFormat
, argptr
); 
 203   wxLogSysErrorHelper(lErrCode
); 
 206 // ---------------------------------------------------------------------------- 
 207 // wxLog class implementation 
 208 // ---------------------------------------------------------------------------- 
 212   m_bHasMessages 
= FALSE
; 
 214   m_szTimeFormat 
= "[%d/%b/%y %H:%M:%S] "; 
 217 wxLog 
*wxLog::GetActiveTarget() 
 219   if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 220     // prevent infinite recursion if someone calls wxLogXXX() from 
 221     // wxApp::CreateLogTarget() 
 222     static bool s_bInGetActiveTarget 
= FALSE
; 
 223     if ( !s_bInGetActiveTarget 
) { 
 224       s_bInGetActiveTarget 
= TRUE
; 
 226       #ifdef  WX_TEST_MINIMAL 
 227         ms_pLogger 
= new wxLogStderr
; 
 229         // ask the application to create a log target for us 
 230         if ( wxTheApp 
!= NULL 
) 
 231           ms_pLogger 
= wxTheApp
->CreateLogTarget(); 
 234       // do nothing if it fails - what can we do? 
 241 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 243   // flush the old messages before changing 
 244   if ( ms_pLogger 
!= NULL 
) 
 247   wxLog 
*pOldLogger 
= ms_pLogger
; 
 248   ms_pLogger 
= pLogger
; 
 252 void wxLog::DoLog(wxLogLevel level
, const char *szString
) 
 256   // prepend a timestamp if not disabled 
 257   if ( !IsEmpty(m_szTimeFormat
) ) { 
 263     ptmNow 
= localtime(&timeNow
); 
 265     strftime(szBuf
, WXSIZEOF(szBuf
), m_szTimeFormat
, ptmNow
); 
 270     case wxLOG_FatalError
: 
 271       DoLogString(str 
<< _("Fatal error: ") << szString
); 
 272       DoLogString(_("Program aborted.")); 
 278       DoLogString(str 
<< _("Error: ") << szString
); 
 282       DoLogString(str 
<< _("Warning: ") << szString
); 
 288         DoLogString(str 
+ szString
); 
 299           // in addition to normal logging, also send the string to debugger 
 300           // (don't prepend "Debug" here: it will go to debug window anyhow) 
 301           ::OutputDebugString(str 
+ szString 
+ "\n\r"); 
 303         DoLogString(str 
<< (level 
== wxLOG_Trace 
? _("Trace") : _("Debug")) 
 304                         << ": " << szString
); 
 310       wxFAIL_MSG("unknown log level in wxLog::DoLog"); 
 314 void wxLog::DoLogString(const char *WXUNUSED(szString
)) 
 316   wxFAIL_MSG("DoLogString must be overrided if it's called."); 
 324 // ---------------------------------------------------------------------------- 
 325 // wxLogStderr class implementation 
 326 // ---------------------------------------------------------------------------- 
 328 wxLogStderr::wxLogStderr(FILE *fp
) 
 336 void wxLogStderr::DoLogString(const char *szString
) 
 338   fputs(szString
, m_fp
); 
 343 // ---------------------------------------------------------------------------- 
 344 // wxLogStream implementation 
 345 // ---------------------------------------------------------------------------- 
 347 wxLogStream::wxLogStream(ostream 
*ostr
) 
 355 void wxLogStream::DoLogString(const char *szString
) 
 357   (*m_ostr
) << szString 
<< endl 
<< flush
; 
 360 // ---------------------------------------------------------------------------- 
 361 // wxLogTextCtrl implementation 
 362 // ---------------------------------------------------------------------------- 
 363 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl 
*pTextCtrl
) 
 364 // @@@ TODO: in wxGTK wxTextCtrl doesn't derive from streambuf 
 366 // Also, in DLL mode in wxMSW, can't use it. 
 367 #if defined(NO_TEXT_WINDOW_STREAM) 
 369              : wxLogStream(new ostream(pTextCtrl
)) 
 374 wxLogTextCtrl::~wxLogTextCtrl() 
 379 // ---------------------------------------------------------------------------- 
 380 // wxLogGui implementation 
 381 // ---------------------------------------------------------------------------- 
 383 #ifndef   WX_TEST_MINIMAL 
 390 void wxLogGui::Flush() 
 392   if ( !m_bHasMessages 
) 
 397   // concatenate all strings (but not too many to not overfill the msg box) 
 400        nMsgCount 
= m_aMessages
.Count(); 
 402   // start from the most recent message 
 403   for ( uint n 
= nMsgCount
; n 
> 0; n
-- ) { 
 404     // for Windows strings longer than this value are wrapped (NT 4.0) 
 405     const uint nMsgLineWidth 
= 156; 
 407     nLines 
+= (m_aMessages
[n 
- 1].Len() + nMsgLineWidth 
- 1) / nMsgLineWidth
; 
 409     if ( nLines 
> 25 )  // don't put too many lines in message box 
 412     str 
<< m_aMessages
[n 
- 1] << "\n"; 
 416     wxMessageBox(str
, _("Error"), wxOK 
| wxICON_EXCLAMATION
); 
 419     wxMessageBox(str
, _("Information"), wxOK 
| wxICON_INFORMATION
); 
 422   // no undisplayed messages whatsoever 
 428 // the default behaviour is to discard all informational messages if there 
 429 // are any errors/warnings. 
 430 void wxLogGui::DoLog(wxLogLevel level
, const char *szString
) 
 437           m_aMessages
.Add(szString
); 
 438           m_bHasMessages 
= TRUE
; 
 444         // find the top window and set it's status text if it has any 
 445         wxWindow 
*pWin 
= wxTheApp
->GetTopWindow(); 
 446         if ( pWin 
!= NULL 
&& pWin
->IsKindOf(CLASSINFO(wxFrame
)) ) { 
 447           wxFrame 
*pFrame 
= (wxFrame 
*)pWin
; 
 448           pFrame
->SetStatusText(szString
); 
 457           OutputDebugString(szString
); 
 458           OutputDebugString("\n\r"); 
 460           // send them to stderr 
 461           fprintf(stderr
, "%s: %s\n", 
 462                   level 
== wxLOG_Trace 
? _("Trace") : _("Debug"), szString
); 
 468     case wxLOG_FatalError
: 
 469       // show this one immediately 
 470       wxMessageBox(szString
, _("Fatal error"), wxICON_HAND
); 
 475       // discard earlier informational messages if this is the 1st error 
 478         m_bHasMessages 
= TRUE
; 
 482       m_aMessages
.Add(szString
); 
 486       wxFAIL_MSG("unknown log level in wxLogGui::DoLog"); 
 490 // ---------------------------------------------------------------------------- 
 491 // wxLogWindow implementation 
 492 // ---------------------------------------------------------------------------- 
 495 class wxLogFrame 
: public wxFrame
 
 499   wxLogFrame(const char *szTitle
); 
 502   void OnClose(wxCommandEvent
& event
); 
 503   void OnCloseWindow(wxCloseEvent
& event
); 
 504   void OnSave (wxCommandEvent
& event
); 
 505   void OnClear(wxCommandEvent
& event
); 
 508   wxTextCtrl 
*TextCtrl() const { return m_pTextCtrl
; } 
 518   wxTextCtrl 
*m_pTextCtrl
; 
 520   DECLARE_EVENT_TABLE() 
 523 BEGIN_EVENT_TABLE(wxLogFrame
, wxFrame
) 
 524   // wxLogWindow menu events 
 525   EVT_MENU(Menu_Close
, wxLogFrame::OnClose
) 
 526   EVT_MENU(Menu_Save
,  wxLogFrame::OnSave
) 
 527   EVT_MENU(Menu_Clear
, wxLogFrame::OnClear
) 
 529   EVT_CLOSE(wxLogFrame::OnCloseWindow
) 
 532 wxLogFrame::wxLogFrame(const char *szTitle
) 
 533           : wxFrame(NULL
, -1, szTitle
) 
 535   // we don't want to be a top-level frame because it would prevent the 
 536   // application termination when all other frames are closed 
 537   wxTopLevelWindows
.DeleteObject(this); 
 539   // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating 
 540   //    a rich edit control instead of a normal one we want 
 541   m_pTextCtrl 
= new wxTextCtrl(this, -1, wxEmptyString
, wxDefaultPosition
, 
 548   m_pTextCtrl->SetEditable(FALSE); 
 549   m_pTextCtrl->SetRichEdit(FALSE); 
 553   wxMenuBar 
*pMenuBar 
= new wxMenuBar
; 
 554   wxMenu 
*pMenu 
= new wxMenu
; 
 555   pMenu
->Append(Menu_Save
,  "&Save..."); 
 556   pMenu
->Append(Menu_Clear
, "C&lear"); 
 557   pMenu
->AppendSeparator(); 
 558   pMenu
->Append(Menu_Close
, "&Close"); 
 559   pMenuBar
->Append(pMenu
, "&Log"); 
 560   SetMenuBar(pMenuBar
); 
 562   // @@ what about status bar? needed (for menu prompts)? 
 565 void wxLogFrame::OnClose(wxCommandEvent
& WXUNUSED(event
)) 
 567   // just hide the window 
 571 void wxLogFrame::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
)) 
 573   // just hide the window 
 577 void wxLogFrame::OnSave(wxCommandEvent
& WXUNUSED(event
)) 
 581   const char *szFileName 
= wxSaveFileSelector("log", "txt", "log.txt"); 
 582   if ( szFileName 
== NULL 
) { 
 591   if ( wxFile::Exists(szFileName
) ) { 
 592     bool bAppend 
= FALSE
; 
 594     strMsg
.Printf(_("Append log to file '%s' " 
 595                     "(choosing [No] will overwrite it)?"), szFileName
); 
 596     switch ( wxMessageBox(strMsg
, "Question", wxYES_NO 
| wxCANCEL
) ) { 
 609         wxFAIL_MSG("invalid message box return value"); 
 613       bOk 
= file
.Open(szFileName
, wxFile::write_append
); 
 616       bOk 
= file
.Create(szFileName
, TRUE 
/* overwrite */); 
 620     bOk 
= file
.Create(szFileName
); 
 623   // retrieve text and save it 
 624   // ------------------------- 
 626   // @@@@ TODO: no GetNumberOfLines and GetLineText in wxGTK yet 
 627   wxLogError("Sorry, this function is not implemented under GTK"); 
 629   int nLines 
= m_pTextCtrl
->GetNumberOfLines(); 
 630   for ( int nLine 
= 0; bOk 
&& nLine 
< nLines
; nLine
++ ) { 
 631     bOk 
= file
.Write(m_pTextCtrl
->GetLineText(nLine
) + wxTextFile::GetEOL()); 
 639     wxLogError(_("Can't save log contents to file.")); 
 644 void wxLogFrame::OnClear(wxCommandEvent
& WXUNUSED(event
)) 
 646   m_pTextCtrl
->Clear(); 
 649 wxLogWindow::wxLogWindow(const char *szTitle
, bool bShow
, bool bDoPass
) 
 651   m_bPassMessages 
= bDoPass
; 
 653   m_pLogFrame 
= new wxLogFrame(szTitle
); 
 654   m_pOldLog 
= wxLog::SetActiveTarget(this); 
 657     m_pLogFrame
->Show(TRUE
); 
 660 void wxLogWindow::Show(bool bShow
) 
 662   m_pLogFrame
->Show(bShow
); 
 665 wxFrame 
*wxLogWindow::GetFrame() const 
 670 void wxLogWindow::DoLog(wxLogLevel level
, const char *szString
) 
 672   // first let the previous logger show it 
 673   if ( m_pOldLog 
!= NULL 
&& m_bPassMessages 
) { 
 674     // @@@ why can't we access protected wxLog method from here (we derive 
 675     // from wxLog)? gcc gives "DoLog is protected in this context", what 
 676     // does this mean? Anyhow, the cast is harmless and let's us do what 
 678     ((wxLogWindow 
*)m_pOldLog
)->DoLog(level
, szString
); 
 681   // and this will format it nicely and call our DoLogString() 
 682   wxLog::DoLog(level
, szString
); 
 685 void wxLogWindow::DoLogString(const char *szString
) 
 687   // put the text into our window 
 688   wxTextCtrl 
*pText 
= m_pLogFrame
->TextCtrl(); 
 690   // remove selection (WriteText is in fact ReplaceSelection) 
 692     long nLen 
= pText
->GetLastPosition(); 
 693     pText
->SetSelection(nLen
, nLen
); 
 696   pText
->WriteText(szString
); 
 697   pText
->WriteText("\n"); // "\n" ok here (_not_ "\r\n") 
 699   // ensure that the line can be seen 
 703 wxLogWindow::~wxLogWindow() 
 705   m_pLogFrame
->Close(TRUE
); 
 708 #endif  //WX_TEST_MINIMAL 
 710 // ============================================================================ 
 711 // Global functions/variables 
 712 // ============================================================================ 
 714 // ---------------------------------------------------------------------------- 
 716 // ---------------------------------------------------------------------------- 
 717 wxLog      
*wxLog::ms_pLogger      
= NULL
; 
 718 bool        wxLog::ms_bAutoCreate  
= TRUE
; 
 719 wxTraceMask 
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 721 // ---------------------------------------------------------------------------- 
 722 // stdout error logging helper 
 723 // ---------------------------------------------------------------------------- 
 725 // helper function: wraps the message and justifies it under given position 
 726 // (looks more pretty on the terminal). Also adds newline at the end. 
 728 // @@ this is now disabled until I find a portable way of determining the 
 729 //    terminal window size (ok, I found it but does anybody really cares?) 
 730 #ifdef    LOG_PRETTY_WRAP 
 731 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 733   size_t nMax 
= 80; // @@@@ 
 734   size_t nStart 
= strlen(pszPrefix
); 
 738   while ( *psz 
!= '\0' ) { 
 739     for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 743     if ( *psz 
!= '\0' ) { 
 745       for ( n 
= 0; n 
< nStart
; n
++ ) 
 748       // as we wrapped, squeeze all white space 
 749       while ( isspace(*psz
) ) 
 756 #endif  //LOG_PRETTY_WRAP 
 758 // ---------------------------------------------------------------------------- 
 759 // error code/error message retrieval functions 
 760 // ---------------------------------------------------------------------------- 
 762 // get error code from syste 
 763 unsigned long wxSysErrorCode() 
 767       return ::GetLastError(); 
 769       // @@@@ what to do on Windows 3.1? 
 777 // get error message from system 
 778 const char *wxSysErrorMsg(unsigned long nErrCode
) 
 781     nErrCode 
= wxSysErrorCode(); 
 785       static char s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 787       // get error message from system 
 789       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 791                     MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 795       // copy it to our buffer and free memory 
 796       strncpy(s_szBuf
, (const char *)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1); 
 797       s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = '\0'; 
 800       // returned string is capitalized and ended with '\r\n' - bad 
 801       s_szBuf
[0] = (char)wxToLower(s_szBuf
[0]); 
 802       size_t len 
= strlen(s_szBuf
); 
 805         if ( s_szBuf
[len 
- 2] == '\r' ) 
 806           s_szBuf
[len 
- 2] = '\0'; 
 815     return strerror(nErrCode
); 
 819 // ---------------------------------------------------------------------------- 
 821 // ---------------------------------------------------------------------------- 
 834 // this function is called when an assert fails 
 835 void wxOnAssert(const char *szFile
, int nLine
, const char *szMsg
) 
 837   // this variable can be set to true to suppress "assert failure" messages 
 838   static bool s_bNoAsserts 
= FALSE
; 
 839   static bool s_bInAssert 
= FALSE
; 
 842     // He-e-e-e-elp!! we're trapped in endless loop 
 848   char szBuf
[LOG_BUFFER_SIZE
]; 
 849   sprintf(szBuf
, _("Assert failed in file %s at line %d"), szFile
, nLine
); 
 850   if ( szMsg 
!= NULL 
) { 
 852     strcat(szBuf
, szMsg
); 
 858   if ( !s_bNoAsserts 
) { 
 859     // send it to the normal log destination 
 862     strcat(szBuf
, _("\nDo you want to stop the program?" 
 863                     "\nYou can also choose [Cancel] to suppress " 
 864                     "further warnings.")); 
 866     switch ( wxMessageBox(szBuf
, _("Debug"), 
 867                           wxYES_NO 
| wxCANCEL 
| wxICON_STOP 
) ) { 
 876       //case wxNO: nothing to do