+#if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU)
+
+// TODO: using memcpy() would improve performance a lot for big amounts of text
+
+DWORD CALLBACK
+wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb)
+{
+ *pcb = 0;
+
+ const wchar_t ** const ppws = (const wchar_t **)dwCookie;
+
+ wchar_t *wbuf = (wchar_t *)buf;
+ const wchar_t *wpc = *ppws;
+ while ( cb && *wpc )
+ {
+ *wbuf++ = *wpc++;
+
+ cb -= sizeof(wchar_t);
+ (*pcb) += sizeof(wchar_t);
+ }
+
+ *ppws = wpc;
+
+ return 0;
+}
+
+// helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut
+struct wxStreamOutData
+{
+ wchar_t *wpc;
+ size_t len;
+};
+
+DWORD CALLBACK
+wxRichEditStreamOut(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb)
+{
+ *pcb = 0;
+
+ wxStreamOutData *data = (wxStreamOutData *)dwCookie;
+
+ const wchar_t *wbuf = (const wchar_t *)buf;
+ wchar_t *wpc = data->wpc;
+ while ( cb )
+ {
+ wchar_t wch = *wbuf++;
+
+ // turn "\r\n" into "\n" on the fly
+ if ( wch != L'\r' )
+ *wpc++ = wch;
+ else
+ data->len--;
+
+ cb -= sizeof(wchar_t);
+ (*pcb) += sizeof(wchar_t);
+ }
+
+ data->wpc = wpc;
+
+ return 0;
+}
+
+
+#if wxUSE_UNICODE_MSLU
+ #define UNUSED_IF_MSLU(param)
+#else
+ #define UNUSED_IF_MSLU(param) param
+#endif
+
+bool
+wxTextCtrl::StreamIn(const wxString& value,
+ wxFontEncoding UNUSED_IF_MSLU(encoding),
+ bool selectionOnly)
+{
+#if wxUSE_UNICODE_MSLU
+ const wchar_t *wpc = value.c_str();
+#else // !wxUSE_UNICODE_MSLU
+ wxCSConv conv(encoding);
+
+ const size_t len = conv.MB2WC(NULL, value, value.length());
+
+#if wxUSE_WCHAR_T
+ wxWCharBuffer wchBuf(len);
+ wchar_t *wpc = wchBuf.data();
+#else
+ wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
+ wchar_t *wpc = wchBuf;
+#endif
+
+ conv.MB2WC(wpc, value, value.length());
+#endif // wxUSE_UNICODE_MSLU
+
+ // finally, stream it in the control
+ EDITSTREAM eds;
+ wxZeroMemory(eds);
+ eds.dwCookie = (DWORD)&wpc;
+ // the cast below is needed for broken (very) old mingw32 headers
+ eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn;
+
+ // same problem as in DoWriteText(): we can get multiple events here
+ UpdatesCountFilter ucf(m_updatesCount);
+
+ ::SendMessage(GetHwnd(), EM_STREAMIN,
+ SF_TEXT |
+ SF_UNICODE |
+ (selectionOnly ? SFF_SELECTION : 0),
+ (LPARAM)&eds);
+
+ wxASSERT_MSG( ucf.GotUpdate(), _T("EM_STREAMIN didn't send EN_UPDATE?") );
+
+ if ( eds.dwError )
+ {
+ wxLogLastError(_T("EM_STREAMIN"));
+ }
+
+#if !wxUSE_WCHAR_T
+ free(wchBuf);
+#endif // !wxUSE_WCHAR_T
+
+ return true;
+}
+
+#if !wxUSE_UNICODE_MSLU
+
+wxString
+wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const
+{
+ wxString out;
+
+ const int len = GetWindowTextLength(GetHwnd());
+
+#if wxUSE_WCHAR_T
+ wxWCharBuffer wchBuf(len);
+ wchar_t *wpc = wchBuf.data();
+#else
+ wchar_t *wchBuf = (wchar_t *)malloc((len + 1)*sizeof(wchar_t));
+ wchar_t *wpc = wchBuf;
+#endif
+
+ wxStreamOutData data;
+ data.wpc = wpc;
+ data.len = len;
+
+ EDITSTREAM eds;
+ wxZeroMemory(eds);
+ eds.dwCookie = (DWORD)&data;
+ eds.pfnCallback = wxRichEditStreamOut;
+
+ ::SendMessage
+ (
+ GetHwnd(),
+ EM_STREAMOUT,
+ SF_TEXT | SF_UNICODE | (selectionOnly ? SFF_SELECTION : 0),
+ (LPARAM)&eds
+ );
+
+ if ( eds.dwError )
+ {
+ wxLogLastError(_T("EM_STREAMOUT"));
+ }
+ else // streamed out ok
+ {
+ // NUL-terminate the string because its length could have been
+ // decreased by wxRichEditStreamOut
+ *(wchBuf.data() + data.len) = L'\0';
+
+ // now convert to the given encoding (this is a possibly lossful
+ // conversion but what else can we do)
+ wxCSConv conv(encoding);
+ size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0);
+ if ( lenNeeded++ )
+ {
+ conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded);
+ }
+ }
+
+#if !wxUSE_WCHAR_T
+ free(wchBuf);
+#endif // !wxUSE_WCHAR_T
+
+ return out;
+}
+
+#endif // !wxUSE_UNICODE_MSLU
+
+#endif // wxUSE_RICHEDIT
+
+void wxTextCtrl::WriteText(const wxString& value)
+{
+ DoWriteText(value);
+}
+
+void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly)
+{
+ wxString valueDos;
+ if ( m_windowStyle & wxTE_MULTILINE )
+ valueDos = wxTextFile::Translate(value, wxTextFileType_Dos);
+ else
+ valueDos = value;
+
+#if wxUSE_RICHEDIT
+ // there are several complications with the rich edit controls here
+ bool done = false;
+ if ( IsRich() )
+ {
+ // first, ensure that the new text will be in the default style
+ if ( !m_defaultStyle.IsDefault() )
+ {
+ long start, end;
+ GetSelection(&start, &end);
+ SetStyle(start, end, m_defaultStyle);
+ }
+
+#if wxUSE_UNICODE_MSLU
+ // RichEdit doesn't have Unicode version of EM_REPLACESEL on Win9x,
+ // but EM_STREAMIN works
+ if ( wxUsingUnicowsDll() && GetRichVersion() > 1 )
+ {
+ done = StreamIn(valueDos, wxFONTENCODING_SYSTEM, selectionOnly);
+ }
+#endif // wxUSE_UNICODE_MSLU
+
+#if !wxUSE_UNICODE
+ // next check if the text we're inserting must be shown in a non
+ // default charset -- this only works for RichEdit > 1.0
+ if ( GetRichVersion() > 1 )
+ {
+ wxFont font = m_defaultStyle.GetFont();
+ if ( !font.Ok() )
+ font = GetFont();
+
+ if ( font.Ok() )
+ {
+ wxFontEncoding encoding = font.GetEncoding();
+ if ( encoding != wxFONTENCODING_SYSTEM )
+ {
+ // we have to use EM_STREAMIN to force richedit control 2.0+
+ // to show any text in the non default charset -- otherwise
+ // it thinks it knows better than we do and always shows it
+ // in the default one
+ done = StreamIn(valueDos, encoding, selectionOnly);
+ }
+ }
+ }
+#endif // !wxUSE_UNICODE
+ }
+
+ if ( !done )
+#endif // wxUSE_RICHEDIT
+ {
+ // in some cases we get 2 EN_CHANGE notifications after the SendMessage
+ // call (this happens for plain EDITs with EM_REPLACESEL and under some
+ // -- undetermined -- conditions with rich edit) and sometimes we don't
+ // get any events at all (plain EDIT with WM_SETTEXT), so ensure that
+ // we generate exactly one of them by ignoring all but the first one in
+ // SendUpdateEvent() and generating one ourselves if we hadn't got any
+ // notifications from Windows
+ UpdatesCountFilter ucf(m_updatesCount);
+
+ ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT,
+ 0, (LPARAM)valueDos.c_str());
+
+ if ( !ucf.GotUpdate() )
+ {
+ SendUpdateEvent();
+ }