X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5de76427c87d8289f5c343cef16e9375056fc49f..eebb848a8516e14cb6c23e1c7b9db56af375733f:/src/common/log.cpp diff --git a/src/common/log.cpp b/src/common/log.cpp index b3bc58fbdb..03267bf2d3 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -29,12 +29,13 @@ // wxWindows #ifndef WX_PRECOMP + #include #include #include #include #include - - #include + #include + #include #include #include #endif //WX_PRECOMP @@ -51,6 +52,8 @@ #ifdef __WXMSW__ #include + // Redefines OutputDebugString if necessary + #include "wx/msw/private.h" #else //Unix #include #endif //Win/Unix @@ -66,6 +69,14 @@ static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz); #endif +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +// we use a global variable to store the frame pointer for wxLogStatus - bad, +// but it's he easiest way +static wxFrame *gs_pFrame; + // ============================================================================ // implementation // ============================================================================ @@ -117,6 +128,24 @@ IMPLEMENT_LOG_FUNCTION(Message) IMPLEMENT_LOG_FUNCTION(Info) IMPLEMENT_LOG_FUNCTION(Status) +// accepts an additional argument which tells to which frame the output should +// be directed +void wxLogStatus(wxFrame *pFrame, const char *szFormat, ...) +{ + wxLog *pLog = wxLog::GetActiveTarget(); + if ( pLog != NULL ) { + va_list argptr; + va_start(argptr, szFormat); + vsprintf(s_szBuf, szFormat, argptr); + va_end(argptr); + + wxASSERT( gs_pFrame == NULL ); // should be reset! + gs_pFrame = pFrame; + wxLog::OnLog(wxLOG_Status, s_szBuf); + gs_pFrame = (wxFrame *) NULL; + } +} + // same as info, but only if 'verbose' mode is on void wxLogVerbose(const char *szFormat, ...) { @@ -153,7 +182,7 @@ void wxLogVerbose(const char *szFormat, ...) // we check that all of mask bits are set in the current mask, so // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something // if both bits are set. - if ( pLog != NULL && (pLog->GetTraceMask() & mask == mask) ) { + if ( pLog != NULL && ((pLog->GetTraceMask() & mask) == mask) ) { va_list argptr; va_start(argptr, szFormat); vsprintf(s_szBuf, szFormat, argptr); @@ -223,14 +252,18 @@ wxLog *wxLog::GetActiveTarget() if ( !s_bInGetActiveTarget ) { s_bInGetActiveTarget = TRUE; - #ifdef WX_TEST_MINIMAL + #ifdef wxUSE_NOGUI ms_pLogger = new wxLogStderr; #else // ask the application to create a log target for us if ( wxTheApp != NULL ) ms_pLogger = wxTheApp->CreateLogTarget(); + else + ms_pLogger = new wxLogStderr; #endif + s_bInGetActiveTarget = FALSE; + // do nothing if it fails - what can we do? } } @@ -240,20 +273,25 @@ wxLog *wxLog::GetActiveTarget() wxLog *wxLog::SetActiveTarget(wxLog *pLogger) { - // flush the old messages before changing - if ( ms_pLogger != NULL ) + if ( ms_pLogger != NULL ) { + // flush the old messages before changing because otherwise they might + // get lost later if this target is not restored ms_pLogger->Flush(); + } wxLog *pOldLogger = ms_pLogger; ms_pLogger = pLogger; + return pOldLogger; } -void wxLog::DoLog(wxLogLevel level, const char *szString) +wxString wxLog::TimeStamp() const { wxString str; - // prepend a timestamp if not disabled +/* Let's disable TimeStamp and see if anyone complains. + * If not, we'll remove it, since it's probably unlikely + * to ever be used. -- JACS 22/11/98 if ( !IsEmpty(m_szTimeFormat) ) { char szBuf[128]; time_t timeNow; @@ -265,6 +303,15 @@ void wxLog::DoLog(wxLogLevel level, const char *szString) strftime(szBuf, WXSIZEOF(szBuf), m_szTimeFormat, ptmNow); str = szBuf; } +*/ + + return str; +} + +void wxLog::DoLog(wxLogLevel level, const char *szString) +{ + // prepend a timestamp if not disabled + wxString str = TimeStamp(); switch ( level ) { case wxLOG_FatalError: @@ -283,8 +330,8 @@ void wxLog::DoLog(wxLogLevel level, const char *szString) break; case wxLOG_Info: - if ( GetVerbose() ) case wxLOG_Message: + if ( GetVerbose() ) DoLogString(str + szString); // fall through @@ -295,25 +342,23 @@ void wxLog::DoLog(wxLogLevel level, const char *szString) case wxLOG_Trace: case wxLOG_Debug: #ifdef __WXDEBUG__ - #ifdef __WIN32__ - // in addition to normal logging, also send the string to debugger - // (don't prepend "Debug" here: it will go to debug window anyhow) - ::OutputDebugString(str + szString + "\n\r"); - #endif //Win32 - DoLogString(str << (level == wxLOG_Trace ? _("Trace") : _("Debug")) - << ": " << szString); + // DoLogString(str << (level == wxLOG_Trace ? _("Trace") : _("Debug")) + // << ": " << szString); + // JACS: we don't really want to prefix with 'Debug'. It's just extra + // verbiage. + DoLogString(szString); #endif break; default: - wxFAIL_MSG("unknown log level in wxLog::DoLog"); + wxFAIL_MSG(_("unknown log level in wxLog::DoLog")); } } void wxLog::DoLogString(const char *WXUNUSED(szString)) { - wxFAIL_MSG("DoLogString must be overrided if it's called."); + wxFAIL_MSG(_("DoLogString must be overrided if it's called.")); } void wxLog::Flush() @@ -344,6 +389,7 @@ void wxLogStderr::DoLogString(const char *szString) // wxLogStream implementation // ---------------------------------------------------------------------------- +#if wxUSE_STD_IOSTREAM wxLogStream::wxLogStream(ostream *ostr) { if ( ostr == NULL ) @@ -356,14 +402,17 @@ void wxLogStream::DoLogString(const char *szString) { (*m_ostr) << szString << endl << flush; } +#endif + +#ifndef wxUSE_NOGUI // ---------------------------------------------------------------------------- // wxLogTextCtrl implementation // ---------------------------------------------------------------------------- -wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl) -// @@@ TODO: in wxGTK wxTextCtrl doesn't derive from streambuf -// Also, in DLL mode in wxMSW, can't use it. +#if wxUSE_STD_IOSTREAM +wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl) +// DLL mode in wxMSW, can't use it. #if defined(NO_TEXT_WINDOW_STREAM) #else : wxLogStream(new ostream(pTextCtrl)) @@ -375,13 +424,12 @@ wxLogTextCtrl::~wxLogTextCtrl() { delete m_ostr; } +#endif // ---------------------------------------------------------------------------- // wxLogGui implementation // ---------------------------------------------------------------------------- -#ifndef WX_TEST_MINIMAL - wxLogGui::wxLogGui() { m_bErrors = FALSE; @@ -392,17 +440,20 @@ void wxLogGui::Flush() if ( !m_bHasMessages ) return; + // do it right now to block any new calls to Flush() while we're here + m_bHasMessages = FALSE; + // @@@ ugly... // concatenate all strings (but not too many to not overfill the msg box) wxString str; - uint nLines = 0, + size_t nLines = 0, nMsgCount = m_aMessages.Count(); // start from the most recent message - for ( uint n = nMsgCount; n > 0; n-- ) { + for ( size_t n = nMsgCount; n > 0; n-- ) { // for Windows strings longer than this value are wrapped (NT 4.0) - const uint nMsgLineWidth = 156; + const size_t nMsgLineWidth = 156; nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth; @@ -420,7 +471,6 @@ void wxLogGui::Flush() } // no undisplayed messages whatsoever - m_bHasMessages = m_bErrors = FALSE; m_aMessages.Empty(); } @@ -442,26 +492,42 @@ void wxLogGui::DoLog(wxLogLevel level, const char *szString) case wxLOG_Status: { // find the top window and set it's status text if it has any - wxWindow *pWin = wxTheApp->GetTopWindow(); - if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) { - wxFrame *pFrame = (wxFrame *)pWin; - pFrame->SetStatusText(szString); + wxFrame *pFrame = gs_pFrame; + if ( pFrame == NULL ) { + wxWindow *pWin = wxTheApp->GetTopWindow(); + if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) { + pFrame = (wxFrame *)pWin; + } } + + if ( pFrame != NULL ) + pFrame->SetStatusText(szString); } break; case wxLOG_Trace: case wxLOG_Debug: #ifdef __WXDEBUG__ - #ifdef __WIN32__ - OutputDebugString(szString); - OutputDebugString("\n\r"); - #else //!WIN32 - // send them to stderr - fprintf(stderr, "%s: %s\n", - level == wxLOG_Trace ? _("Trace") : _("Debug"), szString); - fflush(stderr); - #endif // WIN32 + { + wxString strTime = TimeStamp(); + + #ifdef __WXMSW__ + // don't prepend debug/trace here: it goes to the debug window + // anyhow, but do put a timestamp + OutputDebugString(strTime + szString + "\n\r"); + #else + // send them to stderr + /* + fprintf(stderr, "%s %s: %s\n", + strTime.c_str(), + level == wxLOG_Trace ? _("Trace") : _("Debug"), + szString); + */ + fprintf(stderr, "%s\n", + szString); + fflush(stderr); + #endif + } #endif break; @@ -483,20 +549,22 @@ void wxLogGui::DoLog(wxLogLevel level, const char *szString) break; default: - wxFAIL_MSG("unknown log level in wxLogGui::DoLog"); + wxFAIL_MSG(_("unknown log level in wxLogGui::DoLog")); } } // ---------------------------------------------------------------------------- -// wxLogWindow implementation +// wxLogWindow and wxLogFrame implementation // ---------------------------------------------------------------------------- // log frame class +// --------------- class wxLogFrame : public wxFrame { public: - // ctor - wxLogFrame(const char *szTitle); + // ctor & dtor + wxLogFrame(wxFrame *pParent, wxLogWindow *log, const char *szTitle); + virtual ~wxLogFrame(); // menu callbacks void OnClose(wxCommandEvent& event); @@ -504,6 +572,8 @@ public: void OnSave (wxCommandEvent& event); void OnClear(wxCommandEvent& event); + void OnIdle(wxIdleEvent&); + // accessors wxTextCtrl *TextCtrl() const { return m_pTextCtrl; } @@ -515,7 +585,11 @@ private: Menu_Clear }; - wxTextCtrl *m_pTextCtrl; + // instead of closing just hide the window to be able to Show() it later + void DoClose() { Show(FALSE); } + + wxTextCtrl *m_pTextCtrl; + wxLogWindow *m_log; DECLARE_EVENT_TABLE() }; @@ -529,49 +603,44 @@ BEGIN_EVENT_TABLE(wxLogFrame, wxFrame) EVT_CLOSE(wxLogFrame::OnCloseWindow) END_EVENT_TABLE() -wxLogFrame::wxLogFrame(const char *szTitle) - : wxFrame(NULL, -1, szTitle) +wxLogFrame::wxLogFrame(wxFrame *pParent, wxLogWindow *log, const char *szTitle) + : wxFrame(pParent, -1, szTitle) { - // we don't want to be a top-level frame because it would prevent the - // application termination when all other frames are closed - wxTopLevelWindows.DeleteObject(this); + m_log = log; // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating - // a rich edit control instead of a normal one we want + // a rich edit control instead of a normal one we want in wxMSW m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxSIMPLE_BORDER | + //wxSIMPLE_BORDER | wxTE_MULTILINE | wxHSCROLL | wxTE_READONLY); - /* - m_pTextCtrl->SetEditable(FALSE); - m_pTextCtrl->SetRichEdit(FALSE); - */ // create menu wxMenuBar *pMenuBar = new wxMenuBar; wxMenu *pMenu = new wxMenu; - pMenu->Append(Menu_Save, "&Save..."); - pMenu->Append(Menu_Clear, "C&lear"); + pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file")); + pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents")); pMenu->AppendSeparator(); - pMenu->Append(Menu_Close, "&Close"); - pMenuBar->Append(pMenu, "&Log"); + pMenu->Append(Menu_Close, _("&Close"), _("Close this window")); + pMenuBar->Append(pMenu, _("&Log")); SetMenuBar(pMenuBar); - // @@ what about status bar? needed (for menu prompts)? + // status bar for menu prompts + CreateStatusBar(); + + m_log->OnFrameCreate(this); } void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { - // just hide the window - Show(FALSE); + DoClose(); } void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event)) { - // just hide the window - Show(FALSE); + DoClose(); } void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event)) @@ -587,13 +656,13 @@ void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event)) // open file // --------- wxFile file; - bool bOk = FALSE; + bool bOk = FALSE; if ( wxFile::Exists(szFileName) ) { bool bAppend = FALSE; wxString strMsg; strMsg.Printf(_("Append log to file '%s' " "(choosing [No] will overwrite it)?"), szFileName); - switch ( wxMessageBox(strMsg, "Question", wxYES_NO | wxCANCEL) ) { + switch ( wxMessageBox(strMsg, _("Question"), wxYES_NO | wxCANCEL) ) { case wxYES: bAppend = TRUE; break; @@ -606,7 +675,7 @@ void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event)) return; default: - wxFAIL_MSG("invalid message box return value"); + wxFAIL_MSG(_("invalid message box return value")); } if ( bAppend ) { @@ -622,22 +691,26 @@ void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event)) // retrieve text and save it // ------------------------- -#ifdef __WXGTK__ - // @@@@ TODO: no GetNumberOfLines and GetLineText in wxGTK yet - wxLogError("Sorry, this function is not implemented under GTK"); -#else int nLines = m_pTextCtrl->GetNumberOfLines(); for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) { - bOk = file.Write(m_pTextCtrl->GetLineText(nLine) + wxTextFile::GetEOL()); + bOk = file.Write(m_pTextCtrl->GetLineText(nLine) + +// we're not going to pull in the whole wxTextFile if all we need is this... +#if wxUSE_TEXTFILE + wxTextFile::GetEOL() +#else // !wxUSE_TEXTFILE + '\n' +#endif // wxUSE_TEXTFILE + ); } -#endif //GTK if ( bOk ) bOk = file.Close(); if ( !bOk ) { wxLogError(_("Can't save log contents to file.")); - return; + } + else { + wxLogStatus(this, _("Log saved to the file '%s'."), szFileName); } } @@ -646,10 +719,22 @@ void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event)) m_pTextCtrl->Clear(); } -wxLogWindow::wxLogWindow(const char *szTitle, bool bShow) +wxLogFrame::~wxLogFrame() { - m_pOldLog = wxLog::GetActiveTarget(); - m_pLogFrame = new wxLogFrame(szTitle); + m_log->OnFrameDelete(this); +} + +// wxLogWindow +// ----------- +wxLogWindow::wxLogWindow(wxFrame *pParent, + const char *szTitle, + bool bShow, + bool bDoPass) +{ + m_bPassMessages = bDoPass; + + m_pLogFrame = new wxLogFrame(pParent, this, szTitle); + m_pOldLog = wxLog::SetActiveTarget(this); if ( bShow ) m_pLogFrame->Show(TRUE); @@ -660,15 +745,18 @@ void wxLogWindow::Show(bool bShow) m_pLogFrame->Show(bShow); } -wxFrame *wxLogWindow::GetFrame() const +void wxLogWindow::Flush() { - return m_pLogFrame; + if ( m_pOldLog != NULL ) + m_pOldLog->Flush(); + + m_bHasMessages = FALSE; } void wxLogWindow::DoLog(wxLogLevel level, const char *szString) { // first let the previous logger show it - if ( m_pOldLog != NULL ) { + if ( m_pOldLog != NULL && m_bPassMessages ) { // @@@ why can't we access protected wxLog method from here (we derive // from wxLog)? gcc gives "DoLog is protected in this context", what // does this mean? Anyhow, the cast is harmless and let's us do what @@ -676,8 +764,32 @@ void wxLogWindow::DoLog(wxLogLevel level, const char *szString) ((wxLogWindow *)m_pOldLog)->DoLog(level, szString); } - // and this will format it nicely and call our DoLogString() - wxLog::DoLog(level, szString); + if ( m_pLogFrame ) { + switch ( level ) { + case wxLOG_Status: + // by default, these messages are ignored by wxLog, so process + // them ourselves + { + wxString str = TimeStamp(); + str << _("Status: ") << szString; + DoLogString(str); + } + break; + + // don't put trace messages in the text window for 2 reasons: + // 1) there are too many of them + // 2) they may provoke other trace messages thus sending a program + // into an infinite loop + case wxLOG_Trace: + break; + + default: + // and this will format it nicely and call our DoLogString() + wxLog::DoLog(level, szString); + } + } + + m_bHasMessages = TRUE; } void wxLogWindow::DoLogString(const char *szString) @@ -694,16 +806,32 @@ void wxLogWindow::DoLogString(const char *szString) pText->WriteText(szString); pText->WriteText("\n"); // "\n" ok here (_not_ "\r\n") - // ensure that the line can be seen - // @@@ TODO + // TODO ensure that the line can be seen +} + +wxFrame *wxLogWindow::GetFrame() const +{ + return m_pLogFrame; +} + +void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame)) +{ +} + +void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame)) +{ + m_pLogFrame = (wxLogFrame *)NULL; } wxLogWindow::~wxLogWindow() { - m_pLogFrame->Close(TRUE); + delete m_pOldLog; + + // may be NULL if log frame already auto destroyed itself + delete m_pLogFrame; } -#endif //WX_TEST_MINIMAL +#endif //wxUSE_NOGUI // ============================================================================ // Global functions/variables @@ -712,7 +840,8 @@ wxLogWindow::~wxLogWindow() // ---------------------------------------------------------------------------- // static variables // ---------------------------------------------------------------------------- -wxLog *wxLog::ms_pLogger = NULL; +wxLog *wxLog::ms_pLogger = (wxLog *) NULL; +bool wxLog::ms_doLog = TRUE; bool wxLog::ms_bAutoCreate = TRUE; wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0; @@ -824,6 +953,14 @@ void Trap() { #ifdef __WXMSW__ DebugBreak(); + #elif defined(__WXSTUBS__) + // TODO + #elif defined(__WXMAC__) + #if __powerc + Debugger(); + #else + SysBreak(); + #endif #else // Unix raise(SIGTRAP); #endif // Win/Unix @@ -839,12 +976,23 @@ void wxOnAssert(const char *szFile, int nLine, const char *szMsg) if ( s_bInAssert ) { // He-e-e-e-elp!! we're trapped in endless loop Trap(); + + return; } s_bInAssert = TRUE; char szBuf[LOG_BUFFER_SIZE]; + + // make life easier for people using VC++ IDE: clicking on the message will + // take us immediately to the place of the failed assert +#ifdef __VISUALC__ + sprintf(szBuf, _("%s(%d): assert failed"), szFile, nLine); +#else // !VC++ + // make the error message more clear for all the others sprintf(szBuf, _("Assert failed in file %s at line %d"), szFile, nLine); +#endif // VC/!VC + if ( szMsg != NULL ) { strcat(szBuf, ": "); strcat(szBuf, szMsg); @@ -857,22 +1005,26 @@ void wxOnAssert(const char *szFile, int nLine, const char *szMsg) // send it to the normal log destination wxLogDebug(szBuf); - strcat(szBuf, _("\nDo you want to stop the program?" - "\nYou can also choose [Cancel] to suppress " - "further warnings.")); + #if wxUSE_NOGUI + Trap(); + #else + strcat(szBuf, _("\nDo you want to stop the program?" + "\nYou can also choose [Cancel] to suppress " + "further warnings.")); - switch ( wxMessageBox(szBuf, _("Debug"), - wxYES_NO | wxCANCEL | wxICON_STOP ) ) { - case wxYES: - Trap(); - break; + switch ( wxMessageBox(szBuf, _("Debug"), + wxYES_NO | wxCANCEL | wxICON_STOP ) ) { + case wxYES: + Trap(); + break; - case wxCANCEL: - s_bNoAsserts = TRUE; - break; + case wxCANCEL: + s_bNoAsserts = TRUE; + break; - //case wxNO: nothing to do - } + //case wxNO: nothing to do + } + #endif // USE_NOGUI } s_bInAssert = FALSE;