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 
 365              : wxLogStream(new ostream(pTextCtrl
)) 
 369 wxLogTextCtrl::~wxLogTextCtrl() 
 374 // ---------------------------------------------------------------------------- 
 375 // wxLogGui implementation 
 376 // ---------------------------------------------------------------------------- 
 378 #ifndef   WX_TEST_MINIMAL 
 385 void wxLogGui::Flush() 
 387   if ( !m_bHasMessages 
) 
 392   // concatenate all strings (but not too many to not overfill the msg box) 
 395        nMsgCount 
= m_aMessages
.Count(); 
 397   // start from the most recent message 
 398   for ( uint n 
= nMsgCount
; n 
> 0; n
-- ) { 
 399     // for Windows strings longer than this value are wrapped (NT 4.0) 
 400     const uint nMsgLineWidth 
= 156; 
 402     nLines 
+= (m_aMessages
[n 
- 1].Len() + nMsgLineWidth 
- 1) / nMsgLineWidth
; 
 404     if ( nLines 
> 25 )  // don't put too many lines in message box 
 407     str 
<< m_aMessages
[n 
- 1] << "\n"; 
 411     wxMessageBox(str
, _("Error"), wxOK 
| wxICON_EXCLAMATION
); 
 414     wxMessageBox(str
, _("Information"), wxOK 
| wxICON_INFORMATION
); 
 417   // no undisplayed messages whatsoever 
 423 // the default behaviour is to discard all informational messages if there 
 424 // are any errors/warnings. 
 425 void wxLogGui::DoLog(wxLogLevel level
, const char *szString
) 
 432           m_aMessages
.Add(szString
); 
 433           m_bHasMessages 
= TRUE
; 
 439         // find the top window and set it's status text if it has any 
 440         wxWindow 
*pWin 
= wxTheApp
->GetTopWindow(); 
 441         if ( pWin 
!= NULL 
&& pWin
->IsKindOf(CLASSINFO(wxFrame
)) ) { 
 442           wxFrame 
*pFrame 
= (wxFrame 
*)pWin
; 
 443           pFrame
->SetStatusText(szString
); 
 452           OutputDebugString(szString
); 
 453           OutputDebugString("\n\r"); 
 455           // send them to stderr 
 456           fprintf(stderr
, "%s: %s\n", 
 457                   level 
== wxLOG_Trace 
? _("Trace") : _("Debug"), szString
); 
 463     case wxLOG_FatalError
: 
 464       // show this one immediately 
 465       wxMessageBox(szString
, _("Fatal error"), wxICON_HAND
); 
 470       // discard earlier informational messages if this is the 1st error 
 473         m_bHasMessages 
= TRUE
; 
 477       m_aMessages
.Add(szString
); 
 481       wxFAIL_MSG("unknown log level in wxLogGui::DoLog"); 
 485 // ---------------------------------------------------------------------------- 
 486 // wxLogWindow implementation 
 487 // ---------------------------------------------------------------------------- 
 490 class wxLogFrame 
: public wxFrame
 
 494   wxLogFrame(const char *szTitle
); 
 497   void OnClose(wxCommandEvent
& event
); 
 498   void OnCloseWindow(wxCloseEvent
& event
); 
 499   void OnSave (wxCommandEvent
& event
); 
 500   void OnClear(wxCommandEvent
& event
); 
 503   wxTextCtrl 
*TextCtrl() const { return m_pTextCtrl
; } 
 513   wxTextCtrl 
*m_pTextCtrl
; 
 515   DECLARE_EVENT_TABLE() 
 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
) 
 524   EVT_CLOSE(wxLogFrame::OnCloseWindow
) 
 527 wxLogFrame::wxLogFrame(const char *szTitle
) 
 528           : wxFrame(NULL
, -1, szTitle
) 
 530   // we don't want to be a top-level frame because it would prevent the 
 531   // application termination when all other frames are closed 
 532   wxTopLevelWindows
.DeleteObject(this); 
 534   // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating 
 535   //    a rich edit control instead of a normal one we want 
 536   m_pTextCtrl 
= new wxTextCtrl(this, -1, wxEmptyString
, wxDefaultPosition
, 
 543   m_pTextCtrl->SetEditable(FALSE); 
 544   m_pTextCtrl->SetRichEdit(FALSE); 
 548   wxMenuBar 
*pMenuBar 
= new wxMenuBar
; 
 549   wxMenu 
*pMenu 
= new wxMenu
; 
 550   pMenu
->Append(Menu_Save
,  "&Save..."); 
 551   pMenu
->Append(Menu_Clear
, "C&lear"); 
 552   pMenu
->AppendSeparator(); 
 553   pMenu
->Append(Menu_Close
, "&Close"); 
 554   pMenuBar
->Append(pMenu
, "&Log"); 
 555   SetMenuBar(pMenuBar
); 
 557   // @@ what about status bar? needed (for menu prompts)? 
 560 void wxLogFrame::OnClose(wxCommandEvent
& WXUNUSED(event
)) 
 562   // just hide the window 
 566 void wxLogFrame::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
)) 
 568   // just hide the window 
 572 void wxLogFrame::OnSave(wxCommandEvent
& WXUNUSED(event
)) 
 576   const char *szFileName 
= wxSaveFileSelector("log", "txt", "log.txt"); 
 577   if ( szFileName 
== NULL 
) { 
 586   if ( wxFile::Exists(szFileName
) ) { 
 587     bool bAppend 
= FALSE
; 
 589     strMsg
.Printf(_("Append log to file '%s' " 
 590                     "(choosing [No] will overwrite it)?"), szFileName
); 
 591     switch ( wxMessageBox(strMsg
, "Question", wxYES_NO 
| wxCANCEL
) ) { 
 604         wxFAIL_MSG("invalid message box return value"); 
 608       bOk 
= file
.Open(szFileName
, wxFile::write_append
); 
 611       bOk 
= file
.Create(szFileName
, TRUE 
/* overwrite */); 
 615     bOk 
= file
.Create(szFileName
); 
 618   // retrieve text and save it 
 619   // ------------------------- 
 621   // @@@@ TODO: no GetNumberOfLines and GetLineText in wxGTK yet 
 622   wxLogError("Sorry, this function is not implemented under GTK"); 
 624   int nLines 
= m_pTextCtrl
->GetNumberOfLines(); 
 625   for ( int nLine 
= 0; bOk 
&& nLine 
< nLines
; nLine
++ ) { 
 626     bOk 
= file
.Write(m_pTextCtrl
->GetLineText(nLine
) + wxTextFile::GetEOL()); 
 634     wxLogError(_("Can't save log contents to file.")); 
 639 void wxLogFrame::OnClear(wxCommandEvent
& WXUNUSED(event
)) 
 641   m_pTextCtrl
->Clear(); 
 644 wxLogWindow::wxLogWindow(const char *szTitle
, bool bShow
) 
 646   m_pOldLog 
= wxLog::GetActiveTarget(); 
 647   m_pLogFrame 
= new wxLogFrame(szTitle
); 
 650     m_pLogFrame
->Show(TRUE
); 
 653 void wxLogWindow::Show(bool bShow
) 
 655   m_pLogFrame
->Show(bShow
); 
 658 wxFrame 
*wxLogWindow::GetFrame() const 
 663 void wxLogWindow::DoLog(wxLogLevel level
, const char *szString
) 
 665   // first let the previous logger show it 
 666   if ( m_pOldLog 
!= NULL 
) { 
 667     // @@@ why can't we access protected wxLog method from here (we derive 
 668     // from wxLog)? gcc gives "DoLog is protected in this context", what 
 669     // does this mean? Anyhow, the cast is harmless and let's us do what 
 671     ((wxLogWindow 
*)m_pOldLog
)->DoLog(level
, szString
); 
 674   // and this will format it nicely and call our DoLogString() 
 675   wxLog::DoLog(level
, szString
); 
 678 void wxLogWindow::DoLogString(const char *szString
) 
 680   // put the text into our window 
 681   wxTextCtrl 
*pText 
= m_pLogFrame
->TextCtrl(); 
 683   // remove selection (WriteText is in fact ReplaceSelection) 
 685     long nLen 
= pText
->GetLastPosition(); 
 686     pText
->SetSelection(nLen
, nLen
); 
 689   pText
->WriteText(szString
); 
 690   pText
->WriteText("\n"); // "\n" ok here (_not_ "\r\n") 
 692   // ensure that the line can be seen 
 696 wxLogWindow::~wxLogWindow() 
 698   m_pLogFrame
->Close(TRUE
); 
 701 #endif  //WX_TEST_MINIMAL 
 703 // ============================================================================ 
 704 // Global functions/variables 
 705 // ============================================================================ 
 707 // ---------------------------------------------------------------------------- 
 709 // ---------------------------------------------------------------------------- 
 710 wxLog      
*wxLog::ms_pLogger      
= NULL
; 
 711 bool        wxLog::ms_bAutoCreate  
= TRUE
; 
 712 wxTraceMask 
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 714 // ---------------------------------------------------------------------------- 
 715 // stdout error logging helper 
 716 // ---------------------------------------------------------------------------- 
 718 // helper function: wraps the message and justifies it under given position 
 719 // (looks more pretty on the terminal). Also adds newline at the end. 
 721 // @@ this is now disabled until I find a portable way of determining the 
 722 //    terminal window size (ok, I found it but does anybody really cares?) 
 723 #ifdef    LOG_PRETTY_WRAP 
 724 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 726   size_t nMax 
= 80; // @@@@ 
 727   size_t nStart 
= strlen(pszPrefix
); 
 731   while ( *psz 
!= '\0' ) { 
 732     for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 736     if ( *psz 
!= '\0' ) { 
 738       for ( n 
= 0; n 
< nStart
; n
++ ) 
 741       // as we wrapped, squeeze all white space 
 742       while ( isspace(*psz
) ) 
 749 #endif  //LOG_PRETTY_WRAP 
 751 // ---------------------------------------------------------------------------- 
 752 // error code/error message retrieval functions 
 753 // ---------------------------------------------------------------------------- 
 755 // get error code from syste 
 756 unsigned long wxSysErrorCode() 
 760       return ::GetLastError(); 
 762       // @@@@ what to do on Windows 3.1? 
 770 // get error message from system 
 771 const char *wxSysErrorMsg(unsigned long nErrCode
) 
 774     nErrCode 
= wxSysErrorCode(); 
 778       static char s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 780       // get error message from system 
 782       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 784                     MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 788       // copy it to our buffer and free memory 
 789       strncpy(s_szBuf
, (const char *)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1); 
 790       s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = '\0'; 
 793       // returned string is capitalized and ended with '\r\n' - bad 
 794       s_szBuf
[0] = (char)wxToLower(s_szBuf
[0]); 
 795       size_t len 
= strlen(s_szBuf
); 
 798         if ( s_szBuf
[len 
- 2] == '\r' ) 
 799           s_szBuf
[len 
- 2] = '\0'; 
 808     return strerror(nErrCode
); 
 812 // ---------------------------------------------------------------------------- 
 814 // ---------------------------------------------------------------------------- 
 827 // this function is called when an assert fails 
 828 void wxOnAssert(const char *szFile
, int nLine
, const char *szMsg
) 
 830   // this variable can be set to true to suppress "assert failure" messages 
 831   static bool s_bNoAsserts 
= FALSE
; 
 832   static bool s_bInAssert 
= FALSE
; 
 835     // He-e-e-e-elp!! we're trapped in endless loop 
 841   char szBuf
[LOG_BUFFER_SIZE
]; 
 842   sprintf(szBuf
, _("Assert failed in file %s at line %d"), szFile
, nLine
); 
 843   if ( szMsg 
!= NULL 
) { 
 845     strcat(szBuf
, szMsg
); 
 851   if ( !s_bNoAsserts 
) { 
 852     // send it to the normal log destination 
 855     strcat(szBuf
, _("\nDo you want to stop the program?" 
 856                     "\nYou can also choose [Cancel] to suppress " 
 857                     "further warnings.")); 
 859     switch ( wxMessageBox(szBuf
, _("Debug"), 
 860                           wxYES_NO 
| wxCANCEL 
| wxICON_STOP 
) ) { 
 869       //case wxNO: nothing to do