diff --git a/docs/changes.txt b/docs/changes.txt index 7b2980dd0c..0f5d8fd377 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -91,6 +91,7 @@ All: wxLog::SetVerbose() which now only affects wxLogVerbose(). - Add wxFileType::GetExpandedCommand() (troelsk). - Make it easier to convert to/from UTF-8-encoded std::string (ARATA Mizuki). +- Support custom conversions in wxLogStream and wxLogStderr (Catalin Raceanu). - Add support for loading dynamic lexer in wxStyledTextCtrl (New Pagodi). - Handle strings with embedded NULs in wxDataStream (Nitch). - Don't crash in wxTextFile::GetLastLine() if the file is empty (crohr). diff --git a/include/wx/log.h b/include/wx/log.h index 7f0112c04f..fa2efb5ee0 100644 --- a/include/wx/log.h +++ b/include/wx/log.h @@ -61,6 +61,7 @@ class WXDLLIMPEXP_FWD_BASE wxObject; #include "wx/dynarray.h" #include "wx/hashmap.h" +#include "wx/msgout.h" #if wxUSE_THREADS #include "wx/thread.h" @@ -716,29 +717,31 @@ private: // log everything to a "FILE *", stderr by default -class WXDLLIMPEXP_BASE wxLogStderr : public wxLog +class WXDLLIMPEXP_BASE wxLogStderr : public wxLog, + private wxMessageOutputStderr { public: // redirect log output to a FILE - wxLogStderr(FILE *fp = NULL); + wxLogStderr(FILE *fp = NULL, + const wxMBConv &conv = wxConvWhateverWorks); protected: // implement sink function virtual void DoLogText(const wxString& msg) wxOVERRIDE; - FILE *m_fp; - wxDECLARE_NO_COPY_CLASS(wxLogStderr); }; #if wxUSE_STD_IOSTREAM // log everything to an "ostream", cerr by default -class WXDLLIMPEXP_BASE wxLogStream : public wxLog +class WXDLLIMPEXP_BASE wxLogStream : public wxLog, + private wxMessageOutputWithConv { public: // redirect log output to an ostream - wxLogStream(wxSTD ostream *ostr = (wxSTD ostream *) NULL); + wxLogStream(wxSTD ostream *ostr = (wxSTD ostream *) NULL, + const wxMBConv& conv = wxConvWhateverWorks); protected: // implement sink function @@ -746,6 +749,8 @@ protected: // using ptr here to avoid including from this file wxSTD ostream *m_ostr; + + wxDECLARE_NO_COPY_CLASS(wxLogStream); }; #endif // wxUSE_STD_IOSTREAM diff --git a/include/wx/msgout.h b/include/wx/msgout.h index 006e0651c3..c687661c41 100644 --- a/include/wx/msgout.h +++ b/include/wx/msgout.h @@ -58,22 +58,51 @@ private: }; // ---------------------------------------------------------------------------- -// implementation which sends output to stderr or specified file +// helper mix-in for output targets that can use difference encodings // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_BASE wxMessageOutputStderr : public wxMessageOutput +class WXDLLIMPEXP_BASE wxMessageOutputWithConv { -public: - wxMessageOutputStderr(FILE *fp = stderr) : m_fp(fp) { } - - virtual void Output(const wxString& str) wxOVERRIDE; - protected: + explicit wxMessageOutputWithConv(const wxMBConv& conv) + : m_conv(conv.Clone()) + { + } + + ~wxMessageOutputWithConv() + { + delete m_conv; + } + // return the string with "\n" appended if it doesn't already terminate // with it (in which case it's returned unchanged) wxString AppendLineFeedIfNeeded(const wxString& str); + // Prepare the given string for output by appending a new line to it, if + // necessary, and converting it to a narrow string using our conversion + // object. + wxCharBuffer PrepareForOutput(const wxString& str); + + const wxMBConv* const m_conv; +}; + +// ---------------------------------------------------------------------------- +// implementation which sends output to stderr or specified file +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_BASE wxMessageOutputStderr : public wxMessageOutput, + protected wxMessageOutputWithConv +{ +public: + wxMessageOutputStderr(FILE *fp = stderr, + const wxMBConv &conv = wxConvWhateverWorks); + + virtual void Output(const wxString& str) wxOVERRIDE; + +protected: FILE *m_fp; + + wxDECLARE_NO_COPY_CLASS(wxMessageOutputStderr); }; // ---------------------------------------------------------------------------- diff --git a/interface/wx/log.h b/interface/wx/log.h index 8b8d59e328..a701af81af 100644 --- a/interface/wx/log.h +++ b/interface/wx/log.h @@ -756,8 +756,24 @@ public: /** Constructs a log target which sends all the log messages to the given output stream. If it is @NULL, the messages are sent to @c cerr. + The messages will be written in the encoding specified by the + given @c wxMBConv. + + The @a conv argument is only available in wxWidgets 3.1.1 and later. + + @note + In practice, it is only advisable to specify @c wxConvUTF8 as + the @a conv. + If using @c wxMBConvUTF16(), the file should be opened in + @c std::ios::binary mode. + + @warning + If a log message contains any characters that cannot be converted + to the character set given by @a conv, that message will be + silently ignored, i.e. it will not be written at all. */ - wxLogStream(std::ostream *ostr = NULL); + wxLogStream(std::ostream *ostr = NULL, + const wxMBConv &conv = wxConvWhateverWorks); }; @@ -782,8 +798,24 @@ public: /** Constructs a log target which sends all the log messages to the given @c FILE. If it is @NULL, the messages are sent to @c stderr. + The messages will be written in the encoding specified by the + given @c wxMBConv. + + The @a conv argument is only available in wxWidgets 3.1.1 and later. + + @note + In practice, it is only advisable to specify @c wxConvUTF8 as + the @a conv. + If using @c wxMBConvUTF16(), the file should be opened in + @c "wb" mode. + + @warning + If a log message contains any characters that cannot be converted + to the character set given by @a conv, that message will be + silently ignored, i.e. it will not be written at all. */ - wxLogStderr(FILE* fp = NULL); + wxLogStderr(FILE *fp = NULL, + const wxMBConv &conv = wxConvWhateverWorks); }; diff --git a/src/common/log.cpp b/src/common/log.cpp index 12efed0667..d5a59ff502 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -839,12 +839,9 @@ void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level, const wxString& msg) // wxLogStderr class implementation // ---------------------------------------------------------------------------- -wxLogStderr::wxLogStderr(FILE *fp) +wxLogStderr::wxLogStderr(FILE *fp, const wxMBConv& conv) + : wxMessageOutputStderr(fp ? fp : stderr, conv) { - if ( fp == NULL ) - m_fp = stderr; - else - m_fp = fp; } void wxLogStderr::DoLogText(const wxString& msg) @@ -852,7 +849,7 @@ void wxLogStderr::DoLogText(const wxString& msg) // First send it to stderr, even if we don't have it (e.g. in a Windows GUI // application under) it's not a problem to try to use it and it's easier // than determining whether we do have it or not. - wxMessageOutputStderr(m_fp).Output(msg); + wxMessageOutputStderr::Output(msg); // under GUI systems such as Windows or Mac, programs usually don't have // stderr at all, so show the messages also somewhere else, typically in @@ -874,7 +871,8 @@ void wxLogStderr::DoLogText(const wxString& msg) #if wxUSE_STD_IOSTREAM #include "wx/ioswrap.h" -wxLogStream::wxLogStream(wxSTD ostream *ostr) +wxLogStream::wxLogStream(wxSTD ostream *ostr, const wxMBConv& conv) + : wxMessageOutputWithConv(conv) { if ( ostr == NULL ) m_ostr = &wxSTD cerr; @@ -884,7 +882,8 @@ wxLogStream::wxLogStream(wxSTD ostream *ostr) void wxLogStream::DoLogText(const wxString& msg) { - (*m_ostr) << msg << wxSTD endl; + const wxCharBuffer& buf = PrepareForOutput(msg); + m_ostr->write(buf, buf.length()); } #endif // wxUSE_STD_IOSTREAM diff --git a/src/common/msgout.cpp b/src/common/msgout.cpp index 9003413e57..1be492f520 100644 --- a/src/common/msgout.cpp +++ b/src/common/msgout.cpp @@ -133,10 +133,10 @@ void wxMessageOutputBest::Output(const wxString& str) } // ---------------------------------------------------------------------------- -// wxMessageOutputStderr +// wxMessageOutputWithConv // ---------------------------------------------------------------------------- -wxString wxMessageOutputStderr::AppendLineFeedIfNeeded(const wxString& str) +wxString wxMessageOutputWithConv::AppendLineFeedIfNeeded(const wxString& str) { wxString strLF(str); if ( strLF.empty() || *strLF.rbegin() != '\n' ) @@ -145,11 +145,37 @@ wxString wxMessageOutputStderr::AppendLineFeedIfNeeded(const wxString& str) return strLF; } +wxCharBuffer wxMessageOutputWithConv::PrepareForOutput(const wxString& str) +{ + wxString strWithLF = AppendLineFeedIfNeeded(str); + +#if defined(__WINDOWS__) + // Determine whether the encoding is UTF-16. In that case, the file + // should have been opened in "wb" mode, and EOL conversion must be done + // here as it won't be done at stdio level. + if ( m_conv->GetMBNulLen() == 2 ) + { + strWithLF.Replace("\n", "\r\n"); + } +#endif // __WINDOWS__ + + return m_conv->cWX2MB(strWithLF.c_str()); +} + +// ---------------------------------------------------------------------------- +// wxMessageOutputStderr +// ---------------------------------------------------------------------------- + +wxMessageOutputStderr::wxMessageOutputStderr(FILE *fp, const wxMBConv& conv) + : wxMessageOutputWithConv(conv), + m_fp(fp) +{ +} + void wxMessageOutputStderr::Output(const wxString& str) { - const wxString strWithLF = AppendLineFeedIfNeeded(str); - - fprintf(m_fp, "%s", (const char*) strWithLF.mb_str(wxConvWhateverWorks)); + const wxCharBuffer& buf = PrepareForOutput(str); + fwrite(buf, buf.length(), 1, m_fp); fflush(m_fp); }