X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/50c6e05f3de1558af0c0afee70604fd329f913df..ba75967c9c0656ec0bcc342878cd9cdd27857ea9:/src/msw/textctrl.cpp diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index 0a8e54ea59..c2417d2cc4 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -82,7 +82,7 @@ // dummy value used for m_dropTarget, different from any valid pointer value // (which are all even under Windows) and NULL static wxDropTarget * - wxRICHTEXT_DEFAULT_DROPTARGET = wx_reinterpret_cast(wxDropTarget *, 1); + wxRICHTEXT_DEFAULT_DROPTARGET = reinterpret_cast(1); #endif // wxUSE_DRAG_AND_DROP && wxUSE_RICHEDIT @@ -147,7 +147,7 @@ public: : m_count(count) { wxASSERT_MSG( m_count == -1 || m_count == -2, - _T("wrong initial m_updatesCount value") ); + wxT("wrong initial m_updatesCount value") ); if (m_count != -2) m_count = 0; @@ -169,7 +169,7 @@ public: private: int& m_count; - DECLARE_NO_COPY_CLASS(UpdatesCountFilter) + wxDECLARE_NO_COPY_CLASS(UpdatesCountFilter); }; // ---------------------------------------------------------------------------- @@ -248,6 +248,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase) BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase) EVT_CHAR(wxTextCtrl::OnChar) + EVT_KEY_DOWN(wxTextCtrl::OnKeyDown) EVT_DROP_FILES(wxTextCtrl::OnDropFiles) #if wxUSE_RICHEDIT @@ -423,22 +424,24 @@ bool wxTextCtrl::MSWCreateText(const wxString& value, } #endif +#if wxUSE_INKEDIT if (!IsInkEdit()) +#endif // wxUSE_INKEDIT { if ( m_verRichEdit == 2 ) { if ( wxRichEditModule::Load(wxRichEditModule::Version_41) ) { // yes, class name for version 4.1 really is 5.0 - windowClass = _T("RICHEDIT50W"); + windowClass = wxT("RICHEDIT50W"); } else if ( wxRichEditModule::Load(wxRichEditModule::Version_2or3) ) { - windowClass = _T("RichEdit20") + windowClass = wxT("RichEdit20") #if wxUSE_UNICODE - _T("W"); + wxT("W"); #else // ANSI - _T("A"); + wxT("A"); #endif // Unicode/ANSI } else // failed to load msftedit.dll and riched20.dll @@ -451,7 +454,7 @@ bool wxTextCtrl::MSWCreateText(const wxString& value, { if ( wxRichEditModule::Load(wxRichEditModule::Version_1) ) { - windowClass = _T("RICHEDIT"); + windowClass = wxT("RICHEDIT"); } else // failed to load any richedit control DLL { @@ -483,9 +486,19 @@ bool wxTextCtrl::MSWCreateText(const wxString& value, valueWin = value; } + // suppress events sent during control creation: we're called either from + // the ctor and then we shouldn't generate any events for compatibility + // with the other ports, or from SetWindowStyleFlag() and then we shouldn't + // generate the events because our text doesn't really change, the fact + // that we (sometimes) need to recreate the control is just an + // implementation detail + m_updatesCount = -2; + if ( !MSWCreateControl(windowClass.wx_str(), msStyle, pos, size, valueWin) ) return false; + m_updatesCount = -1; + #if wxUSE_RICHEDIT if (IsRich()) { @@ -531,7 +544,20 @@ bool wxTextCtrl::MSWCreateText(const wxString& value, ::SendMessage(GetHwnd(), EM_SETEVENTMASK, 0, mask); } + else #endif // wxUSE_RICHEDIT + if ( HasFlag(wxTE_MULTILINE) && HasFlag(wxTE_READONLY) ) + { + // non-rich read-only multiline controls have grey background by + // default under MSW but this is not always appropriate, so forcefully + // reset the background colour to normal default + // + // this is not ideal but, after a long discussion on wx-dev (see + // http://thread.gmane.org/gmane.comp.lib.wxwidgets.devel/116360/) it + // was finally deemed to be the best behaviour by default (and ideally + // we'd have a way to change this, see #11521) + SetBackgroundColour(GetClassDefaultAttributes().colBg); + } #ifndef __WXWINCE__ // Without this, if we pass the size in the constructor and then don't change it, @@ -556,16 +582,16 @@ void wxTextCtrl::AdoptAttributesFromHWND() #if wxUSE_RICHEDIT wxString classname = wxGetWindowClass(GetHWND()); - if ( classname.IsSameAs(_T("EDIT"), false /* no case */) ) + if ( classname.IsSameAs(wxT("EDIT"), false /* no case */) ) { m_verRichEdit = 0; } else // rich edit? { wxChar c; - if ( wxSscanf(classname, _T("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 ) + if ( wxSscanf(classname, wxT("RichEdit%d0%c"), &m_verRichEdit, &c) != 2 ) { - wxLogDebug(_T("Unknown edit control '%s'."), classname.c_str()); + wxLogDebug(wxT("Unknown edit control '%s'."), classname.c_str()); m_verRichEdit = 0; } @@ -784,10 +810,12 @@ wxString wxTextCtrl::GetRange(long from, long to) const encoding = font.GetEncoding(); } +#if wxUSE_INTL if ( encoding == wxFONTENCODING_SYSTEM ) { encoding = wxLocale::GetSystemEncoding(); } +#endif // wxUSE_INTL if ( encoding == wxFONTENCODING_SYSTEM ) { @@ -828,8 +856,8 @@ wxString wxTextCtrl::GetRange(long from, long to) const // style - convert it to something reasonable for ( ; *p; p++ ) { - if ( *p == _T('\r') ) - *p = _T('\n'); + if ( *p == wxT('\r') ) + *p = wxT('\n'); } } } @@ -845,8 +873,9 @@ wxString wxTextCtrl::GetRange(long from, long to) const else #endif // wxUSE_RICHEDIT { - // retrieve all text - str = wxGetWindowText(GetHWND()); + // retrieve all text: wxTextEntry method works even for multiline + // controls and must be used for single line ones to account for hints + str = wxTextEntry::GetValue(); // need only a range? if ( from < to ) @@ -869,7 +898,7 @@ void wxTextCtrl::DoSetValue(const wxString& value, int flags) // comparing it with the old one (chances are that it will be different // anyhow, this comparison is there to avoid flicker for small single-line // edit controls mostly) - if ( (value.length() > 0x400) || (value != GetValue()) ) + if ( (value.length() > 0x400) || (value != DoGetValue()) ) { DoWriteText(value, flags /* doesn't include SelectionOnly here */); @@ -899,7 +928,7 @@ void wxTextCtrl::DoSetValue(const wxString& value, int flags) // TODO: using memcpy() would improve performance a lot for big amounts of text DWORD CALLBACK -wxRichEditStreamIn(DWORD dwCookie, BYTE *buf, LONG cb, LONG *pcb) +wxRichEditStreamIn(DWORD_PTR dwCookie, BYTE *buf, LONG cb, LONG *pcb) { *pcb = 0; @@ -974,21 +1003,19 @@ wxTextCtrl::StreamIn(const wxString& value, const size_t len = conv.MB2WC(NULL, value.mb_str(), value.length()); -#if wxUSE_WCHAR_T - wxWCharBuffer wchBuf(len); + if (len == wxCONV_FAILED) + return false; + + wxWCharBuffer wchBuf(len); // allocates one extra character 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.mb_str(), value.length()); + conv.MB2WC(wpc, value.mb_str(), len + 1); #endif // wxUSE_UNICODE_MSLU // finally, stream it in the control EDITSTREAM eds; wxZeroMemory(eds); - eds.dwCookie = (DWORD)&wpc; + eds.dwCookie = (DWORD_PTR)&wpc; // the cast below is needed for broken (very) old mingw32 headers eds.pfnCallback = (EDITSTREAMCALLBACK)wxRichEditStreamIn; @@ -1004,17 +1031,13 @@ wxTextCtrl::StreamIn(const wxString& value, // It's okay for EN_UPDATE to not be sent if the selection is empty and // the text is empty, otherwise warn the programmer about it. wxASSERT_MSG( ucf.GotUpdate() || ( !HasSelection() && value.empty() ), - _T("EM_STREAMIN didn't send EN_UPDATE?") ); + wxT("EM_STREAMIN didn't send EN_UPDATE?") ); if ( eds.dwError ) { - wxLogLastError(_T("EM_STREAMIN")); + wxLogLastError(wxT("EM_STREAMIN")); } -#if !wxUSE_WCHAR_T - free(wchBuf); -#endif // !wxUSE_WCHAR_T - return true; } @@ -1027,13 +1050,8 @@ wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const 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; @@ -1054,7 +1072,7 @@ wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const if ( eds.dwError ) { - wxLogLastError(_T("EM_STREAMOUT")); + wxLogLastError(wxT("EM_STREAMOUT")); } else // streamed out ok { @@ -1066,16 +1084,13 @@ wxTextCtrl::StreamOut(wxFontEncoding encoding, bool selectionOnly) const // conversion but what else can we do) wxCSConv conv(encoding); size_t lenNeeded = conv.WC2MB(NULL, wchBuf, 0); - if ( lenNeeded++ ) + + if ( lenNeeded != wxCONV_FAILED && lenNeeded++ ) { conv.WC2MB(wxStringBuffer(out, lenNeeded), wchBuf, lenNeeded); } } -#if !wxUSE_WCHAR_T - free(wchBuf); -#endif // !wxUSE_WCHAR_T - return out; } @@ -1178,9 +1193,7 @@ void wxTextCtrl::AppendText(const wxString& text) // don't do this if we're frozen, saves some time if ( !IsFrozen() && IsMultiLine() && GetRichVersion() > 1 ) { - // setting the caret to the end and showing it simply doesn't work for - // RichEdit 2.0 -- force it to still do what we want - ::SendMessage(GetHwnd(), EM_LINESCROLL, 0, GetNumberOfLines()); + ::SendMessage(GetHwnd(), WM_VSCROLL, SB_BOTTOM, NULL); } #endif // wxUSE_RICHEDIT } @@ -1299,6 +1312,13 @@ void wxTextCtrl::DoSetSelection(long from, long to, int flags) #if wxUSE_RICHEDIT if ( IsRich() ) { + // if from and to are both -1, it means (in wxWidgets) that all text + // should be selected, translate this into Windows convention + if ( (from == -1) && (to == -1) ) + { + from = 0; + } + CHARRANGE range; range.cpMin = from; range.cpMax = to; @@ -1599,24 +1619,24 @@ wxString wxTextCtrl::GetLineText(long lineNo) const { // 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') ) + if ( buf[len - 2] == wxT('\r') && buf[len - 1] == wxT('\n') ) { // richedit 1.0 uses "\r\n" as line terminator, so remove "\r" // here and "\n" below - buf[len - 2] = _T('\n'); + buf[len - 2] = wxT('\n'); len--; } - else if ( buf[len - 1] == _T('\r') ) + else if ( buf[len - 1] == wxT('\r') ) { // richedit 2.0+ uses only "\r", replace it with "\n" - buf[len - 1] = _T('\n'); + buf[len - 1] = wxT('\n'); } } #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') ) + if ( buf[len - 1] == wxT('\n') ) { len--; } @@ -1737,7 +1757,7 @@ bool wxTextCtrl::MSWShouldPreProcessMessage(WXMSG* msg) switch ( ctrl + shift ) { default: - wxFAIL_MSG( _T("how many modifiers have we got?") ); + wxFAIL_MSG( wxT("how many modifiers have we got?") ); // fall through case 0: @@ -1833,6 +1853,35 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) event.Skip(); } +void wxTextCtrl::OnKeyDown(wxKeyEvent& event) +{ + // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages + // when Ctrl-V, X or C is pressed and this prevents wxClipboardTextEvent + // from working. So we work around it by intercepting these shortcuts + // ourselves and emitting clipboard events (which richedit will handle, + // so everything works as before, including pasting of rich text): + if ( event.GetModifiers() == wxMOD_CONTROL && IsRich() ) + { + switch ( event.GetKeyCode() ) + { + case 'C': + Copy(); + return; + case 'X': + Cut(); + return; + case 'V': + Paste(); + return; + default: + break; + } + } + + // no, we didn't process it + event.Skip(); +} + WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { WXLRESULT lRc = wxTextCtrlBase::MSWWindowProc(nMsg, wParam, lParam); @@ -1869,7 +1918,7 @@ WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara else // !editable { // NB: use "=", not "|=" as the base class version returns - // the same flags is this state as usual (i.e. + // the same flags in the disabled state as usual (i.e. // including DLGC_WANTMESSAGE). This is strange (how // does it work in the native Win32 apps?) but for now // live with it. @@ -1878,12 +1927,20 @@ WXLRESULT wxTextCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara } break; - case WM_CUT: - case WM_COPY: - case WM_PASTE: - if ( HandleClipboardEvent(nMsg) ) - lRc = 0; - break; +#if wxUSE_MENUS + case WM_SETCURSOR: + // rich text controls seem to have a bug and don't change the + // cursor to the standard arrow one from the I-beam cursor usually + // used by them even when a popup menu is shown (this works fine + // for plain EDIT controls though), so explicitly work around this + if ( IsRich() ) + { + extern wxMenu *wxCurrentPopupMenu; + if ( wxCurrentPopupMenu && + wxCurrentPopupMenu->GetInvokingWindow() == this ) + ::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR)); + } +#endif // wxUSE_MENUS } return lRc; @@ -1907,7 +1964,7 @@ bool wxTextCtrl::SendUpdateEvent() return false; default: - wxFAIL_MSG( _T("unexpected wxTextCtrl::m_updatesCount value") ); + wxFAIL_MSG( wxT("unexpected wxTextCtrl::m_updatesCount value") ); // fall through case -1: @@ -1928,17 +1985,6 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) { switch ( param ) { - case EN_SETFOCUS: - case EN_KILLFOCUS: - { - wxFocusEvent event(param == EN_KILLFOCUS ? wxEVT_KILL_FOCUS - : wxEVT_SET_FOCUS, - m_windowId); - event.SetEventObject(this); - HandleWindowEvent(event); - } - break; - case EN_CHANGE: SendUpdateEvent(); break; @@ -1954,7 +2000,9 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) } break; - // the other edit notification messages are not processed + // the other edit notification messages are not processed (or, in + // the case of EN_{SET/KILL}FOCUS were already handled at WM_SET/ + // KILLFOCUS level) default: return false; } @@ -2067,15 +2115,12 @@ void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event)) { - long from, to; - GetSelection(& from, & to); - if (from != -1 && to != -1) - Remove(from, to); + RemoveSelection(); } void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event)) { - SetSelection(-1, -1); + SelectAll(); } void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event) @@ -2105,14 +2150,12 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event) { - long from, to; - GetSelection(& from, & to); - event.Enable(from != -1 && to != -1 && from != to && IsEditable()) ; + event.Enable( HasSelection() && IsEditable() ); } void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event) { - event.Enable(GetLastPosition() > 0); + event.Enable( !IsEmpty() ); } void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event) @@ -2152,23 +2195,6 @@ void wxTextCtrl::OnSetFocus(wxFocusEvent& event) event.Skip(); } -// ---------------------------------------------------------------------------- -// Default colors for MSW text control -// -// Set default background color to the native white instead of -// the default wxSYS_COLOUR_BTNFACE (is triggered with wxNullColour). -// ---------------------------------------------------------------------------- - -wxVisualAttributes wxTextCtrl::GetDefaultAttributes() const -{ - wxVisualAttributes attrs; - attrs.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - attrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - attrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //white - - return attrs; -} - // the rest of the file only deals with the rich edit controls #if wxUSE_RICHEDIT @@ -2332,6 +2358,26 @@ bool wxTextCtrl::SetForegroundColour(const wxColour& colour) return true; } +bool wxTextCtrl::SetFont(const wxFont& font) +{ + if ( !wxTextCtrlBase::SetFont(font) ) + return false; + + if ( IsRich() ) + { + // Using WM_SETFONT doesn't work reliably with rich edit controls: as + // an example, if we set a fixed width font for a richedit 4.1 control, + // it's used for the ASCII characters but inserting any non-ASCII ones + // switches the font to a proportional one, whether it's done + // programmatically or not. So just use EM_SETCHARFORMAT for this too. + wxTextAttr attr; + attr.SetFont(font); + SetDefaultStyle(attr); + } + + return true; +} + // ---------------------------------------------------------------------------- // styling support for rich edit controls // ---------------------------------------------------------------------------- @@ -2420,7 +2466,7 @@ bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) cf.yHeight = 20*font.GetPointSize(); // 1 pt = 20 twips cf.bCharSet = lf.lfCharSet; cf.bPitchAndFamily = lf.lfPitchAndFamily; - wxStrncpy( cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName) ); + wxStrlcpy(cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName)); // also deal with underline/italic/bold attributes: note that we must // always set CFM_ITALIC &c bits in dwMask, even if we don't set the @@ -2462,7 +2508,7 @@ bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) SCF_SELECTION, (LPARAM)&cf) != 0; if ( !ok ) { - wxLogDebug(_T("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed")); + wxLogDebug(wxT("SendMessage(EM_SETCHARFORMAT, SCF_SELECTION) failed")); } // now do the paragraph formatting @@ -2547,7 +2593,7 @@ bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) 0, (LPARAM) &pf) != 0; if ( !ok ) { - wxLogDebug(_T("SendMessage(EM_SETPARAFORMAT, 0) failed")); + wxLogDebug(wxT("SendMessage(EM_SETPARAFORMAT, 0) failed")); } } @@ -2766,11 +2812,11 @@ bool wxRichEditModule::Load(Version version) return true; } - static const wxChar *dllnames[] = + static const wxChar *const dllnames[] = { - _T("riched32"), - _T("riched20"), - _T("msftedit"), + wxT("riched32"), + wxT("riched20"), + wxT("msftedit"), }; wxCOMPILE_TIME_ASSERT( WXSIZEOF(dllnames) == Version_Max, @@ -2792,10 +2838,8 @@ bool wxRichEditModule::Load(Version version) // load the InkEdit library bool wxRichEditModule::LoadInkEdit() { - static wxDynamicLibrary ms_inkEditLib; - static bool ms_inkEditLibLoadAttemped; if (ms_inkEditLibLoadAttemped) - ms_inkEditLib.IsLoaded(); + return ms_inkEditLib.IsLoaded(); ms_inkEditLibLoadAttemped = true;