X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/199fbd7093daca4902bb42c9791574ad91d12565..f0f4301209cc58eb5562be289f49bf0fb9e7d6b8:/src/msw/textctrl.cpp diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index 9951c9ba36..7dd6a84fcf 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -13,7 +13,7 @@ // declarations // ============================================================================ -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "textctrl.h" #endif @@ -73,16 +73,6 @@ #endif // wxUSE_RICHEDIT -// ---------------------------------------------------------------------------- -// private functions -// ---------------------------------------------------------------------------- - -#if wxUSE_RICHEDIT - -DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb); - -#endif // wxUSE_RICHEDIT - // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- @@ -116,7 +106,75 @@ IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule) // event tables and other macros // ---------------------------------------------------------------------------- +#if wxUSE_EXTENDED_RTTI +WX_DEFINE_FLAGS( wxTextCtrlStyle ) + +wxBEGIN_FLAGS( wxTextCtrlStyle ) + // new style border flags, we put them first to + // use them for streaming out + wxFLAGS_MEMBER(wxBORDER_SIMPLE) + wxFLAGS_MEMBER(wxBORDER_SUNKEN) + wxFLAGS_MEMBER(wxBORDER_DOUBLE) + wxFLAGS_MEMBER(wxBORDER_RAISED) + wxFLAGS_MEMBER(wxBORDER_STATIC) + wxFLAGS_MEMBER(wxBORDER_NONE) + + // old style border flags + wxFLAGS_MEMBER(wxSIMPLE_BORDER) + wxFLAGS_MEMBER(wxSUNKEN_BORDER) + wxFLAGS_MEMBER(wxDOUBLE_BORDER) + wxFLAGS_MEMBER(wxRAISED_BORDER) + wxFLAGS_MEMBER(wxSTATIC_BORDER) + wxFLAGS_MEMBER(wxBORDER) + + // standard window styles + wxFLAGS_MEMBER(wxTAB_TRAVERSAL) + wxFLAGS_MEMBER(wxCLIP_CHILDREN) + wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) + wxFLAGS_MEMBER(wxWANTS_CHARS) + wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) + wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) + wxFLAGS_MEMBER(wxVSCROLL) + wxFLAGS_MEMBER(wxHSCROLL) + + wxFLAGS_MEMBER(wxTE_PROCESS_ENTER) + wxFLAGS_MEMBER(wxTE_PROCESS_TAB) + wxFLAGS_MEMBER(wxTE_MULTILINE) + wxFLAGS_MEMBER(wxTE_PASSWORD) + wxFLAGS_MEMBER(wxTE_READONLY) + wxFLAGS_MEMBER(wxHSCROLL) + wxFLAGS_MEMBER(wxTE_RICH) + wxFLAGS_MEMBER(wxTE_RICH2) + wxFLAGS_MEMBER(wxTE_AUTO_URL) + wxFLAGS_MEMBER(wxTE_NOHIDESEL) + wxFLAGS_MEMBER(wxTE_LEFT) + wxFLAGS_MEMBER(wxTE_CENTRE) + wxFLAGS_MEMBER(wxTE_RIGHT) + wxFLAGS_MEMBER(wxTE_DONTWRAP) + wxFLAGS_MEMBER(wxTE_LINEWRAP) + wxFLAGS_MEMBER(wxTE_WORDWRAP) + +wxEND_FLAGS( wxTextCtrlStyle ) + +IMPLEMENT_DYNAMIC_CLASS_XTI(wxTextCtrl, wxControl,"wx/textctrl.h") + +wxBEGIN_PROPERTIES_TABLE(wxTextCtrl) + wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) + wxEVENT_PROPERTY( TextEnter , wxEVT_COMMAND_TEXT_ENTER , wxCommandEvent ) + + wxPROPERTY( Font , wxFont , SetFont , GetFont ,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) + wxPROPERTY( Value , wxString , SetValue, GetValue, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) + wxPROPERTY_FLAGS( WindowStyle , wxTextCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style +wxEND_PROPERTIES_TABLE() + +wxBEGIN_HANDLERS_TABLE(wxTextCtrl) +wxEND_HANDLERS_TABLE() + +wxCONSTRUCTOR_6( wxTextCtrl , wxWindow* , Parent , wxWindowID , Id , wxString , Value , wxPoint , Position , wxSize , Size , long , WindowStyle) +#else IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl) +#endif + BEGIN_EVENT_TABLE(wxTextCtrl, wxControl) EVT_CHAR(wxTextCtrl::OnChar) @@ -396,11 +454,12 @@ WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const msStyle |= ES_AUTOHSCROLL; } - // styles which we add depending on the specified wxWindows styles - if ( style & wxHSCROLL ) + // note that wxTE_DONTWRAP is the same as wxHSCROLL so if we have a horz + // scrollbar, there is no wrapping -- which makes sense + if ( style & wxTE_DONTWRAP ) { // automatically scroll the control horizontally as necessary - msStyle |= WS_HSCROLL;// | ES_AUTOHSCROLL; + msStyle |= WS_HSCROLL; } if ( style & wxTE_READONLY ) @@ -418,7 +477,7 @@ WXDWORD wxTextCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const else if ( style & wxTE_RIGHT ) msStyle |= ES_RIGHT; else - msStyle |= ES_LEFT; // ES_LEFT if 0 as well but for consistency... + msStyle |= ES_LEFT; // ES_LEFT is 0 as well but for consistency... return msStyle; } @@ -468,6 +527,52 @@ wxString wxTextCtrl::GetRange(long from, long to) const int len = GetWindowTextLength(GetHwnd()); if ( len > from ) { + if ( to == -1 ) + to = len; + +#if !wxUSE_UNICODE + // we must use EM_STREAMOUT if we don't want to lose all characters + // not representable in the current character set (EM_GETTEXTRANGE + // simply replaces them with question marks...) + if ( GetRichVersion() > 1 ) + { + // we must have some encoding, otherwise any 8bit chars in the + // control are simply *lost* (replaced by '?') + wxFontEncoding encoding = wxFONTENCODING_SYSTEM; + + wxFont font = m_defaultStyle.GetFont(); + if ( !font.Ok() ) + font = GetFont(); + + if ( font.Ok() ) + { + encoding = font.GetEncoding(); + } + + if ( encoding == wxFONTENCODING_SYSTEM ) + { + encoding = wxLocale::GetSystemEncoding(); + } + + if ( encoding == wxFONTENCODING_SYSTEM ) + { + encoding = wxFONTENCODING_ISO8859_1; + } + + str = StreamOut(encoding); + + if ( !str.empty() ) + { + // we have to manually extract the required part, luckily + // this is easy in this case as EOL characters in str are + // just LFs because we remove CRs in wxRichEditStreamOut + str = str.Mid(from, to - from); + } + } + + // StreamOut() wasn't used or failed, try to do it in normal way + if ( str.empty() ) +#endif // !wxUSE_UNICODE { // alloc one extra WORD as needed by the control wxStringBuffer tmp(str, ++len); @@ -475,7 +580,7 @@ wxString wxTextCtrl::GetRange(long from, long to) const TEXTRANGE textRange; textRange.chrg.cpMin = from; - textRange.chrg.cpMax = to == -1 ? len : to; + textRange.chrg.cpMax = to; textRange.lpstrText = p; (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE, @@ -532,6 +637,9 @@ void wxTextCtrl::SetValue(const wxString& value) if ( (value.length() > 0x400) || (value != GetValue()) ) { DoWriteText(value, FALSE /* not selection only */); + + // for compatibility, don't move the cursor when doing SetValue() + SetInsertionPoint(0); } else // same text { @@ -544,19 +652,21 @@ void wxTextCtrl::SetValue(const wxString& value) // mark the control as being not dirty - we changed its text, not the // user DiscardEdits(); - - // for compatibility, don't move the cursor when doing SetValue() - SetInsertionPoint(0); } #if wxUSE_RICHEDIT && (!wxUSE_UNICODE || wxUSE_UNICODE_MSLU) -DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb) +// 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 = *(const wchar_t **)dwCookie; + const wchar_t *wpc = *ppws; while ( cb && *wpc ) { *wbuf++ = *wpc++; @@ -565,56 +675,77 @@ DWORD CALLBACK wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb) (*pcb) += sizeof(wchar_t); } - *(const wchar_t **)dwCookie = wpc; + *ppws = wpc; return 0; } -// from utils.cpp -extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding); - -#if wxUSE_UNICODE_MSLU -bool wxTextCtrl::StreamIn(const wxString& value, - wxFontEncoding WXUNUSED(encoding), - bool selectionOnly) +// helper struct used to pass parameters from wxTextCtrl to wxRichEditStreamOut +struct wxStreamOutData { - const wchar_t *wpc = value.c_str(); -#else // !wxUSE_UNICODE_MSLU -bool wxTextCtrl::StreamIn(const wxString& value, - wxFontEncoding encoding, - bool selectionOnly) + wchar_t *wpc; + size_t len; +}; + +DWORD CALLBACK +wxRichEditStreamOut(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb) { - // 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 + *pcb = 0; - // first get the Windows code page for this encoding - long codepage = wxEncodingToCodepage(encoding); - if ( codepage == -1 ) + wxStreamOutData *data = (wxStreamOutData *)dwCookie; + + const wchar_t *wbuf = (const wchar_t *)buf; + wchar_t *wpc = data->wpc; + while ( cb ) { - // unknown encoding - return FALSE; + 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); } - // next translate to Unicode using this code page - int len = ::MultiByteToWideChar(codepage, 0, value, -1, NULL, 0); + 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 - if ( !::MultiByteToWideChar(codepage, 0, value, -1, - (wchar_t *)(const wchar_t *)wchBuf, len) ) - { - wxLogLastError(_T("MultiByteToWideChar")); - } - - // finally, stream it in the control - const wchar_t *wpc = wchBuf; + conv.MB2WC(wpc, value, value.length()); #endif // wxUSE_UNICODE_MSLU + // finally, stream it in the control EDITSTREAM eds; wxZeroMemory(eds); eds.dwCookie = (DWORD)&wpc; @@ -647,6 +778,69 @@ bool wxTextCtrl::StreamIn(const wxString& value, 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) @@ -698,6 +892,10 @@ void wxTextCtrl::DoWriteText(const wxString& value, bool selectionOnly) 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); } } @@ -1067,13 +1265,7 @@ void wxTextCtrl::Replace(long from, long to, const wxString& value) // Set selection and remove it DoSetSelection(from, to, FALSE /* don't scroll caret into view */); - SendMessage(GetHwnd(), EM_REPLACESEL, -#ifdef __WIN32__ - TRUE, -#else - FALSE, -#endif - (LPARAM)value.c_str()); + DoWriteText(value, TRUE /* selection only */); } void wxTextCtrl::Remove(long from, long to) @@ -1099,7 +1291,11 @@ bool wxTextCtrl::IsModified() const return SendMessage(GetHwnd(), EM_GETMODIFY, 0, 0) != 0; } -// Makes 'unmodified' +void wxTextCtrl::MarkDirty() +{ + SendMessage(GetHwnd(), EM_SETMODIFY, TRUE, 0L); +} + void wxTextCtrl::DiscardEdits() { SendMessage(GetHwnd(), EM_SETMODIFY, FALSE, 0L); @@ -1208,8 +1404,28 @@ wxString wxTextCtrl::GetLineText(long lineNo) const wxChar *buf = tmp; *(WORD *)buf = (WORD)len; - len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, - lineNo, (LPARAM)buf); + len = (size_t)::SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf); + +#if wxUSE_RICHEDIT + if ( IsRich() ) + { + // remove the '\r' returned by the rich edit control, the user code + // should never see it + if ( buf[len - 2] == _T('\r') && buf[len - 1] == _T('\n') ) + { + buf[len - 2] = _T('\n'); + len--; + } + } +#endif // wxUSE_RICHEDIT + + // remove the '\n' at the end, if any (this is how this function is + // supposed to work according to the docs) + if ( buf[len - 1] == _T('\n') ) + { + len--; + } + buf[len] = 0; tmp.SetLength(len); } @@ -1849,7 +2065,7 @@ bool wxTextCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } return TRUE; } - + // not processed, leave it to the base class return wxTextCtrlBase::MSWOnNotify(idCtrl, lParam, result); }