X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c49245f8baabd69afd7302c43474d3bdd247fb89..7ef53f2dc59c8593285dfeb0365416fe437a037f:/src/msw/textctrl.cpp diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index 3ba5f18f0e..e48ba50b35 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: textctrl.cpp +// Name: msw/textctrl.cpp // Purpose: wxTextCtrl // Author: Julian Smart // Modified by: @@ -33,11 +33,12 @@ #include "wx/settings.h" #include "wx/brush.h" #include "wx/utils.h" + #include "wx/intl.h" #include "wx/log.h" + #include "wx/app.h" #endif #if wxUSE_CLIPBOARD - #include "wx/app.h" #include "wx/clipbrd.h" #endif @@ -57,11 +58,45 @@ # include #endif -#if wxUSE_RICHEDIT && !defined(__GNUWIN32__) +#if wxUSE_RICHEDIT && !defined(__GNUWIN32_OLD__) #include #endif -#if !USE_SHARED_LIBRARY +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +#if wxUSE_RICHEDIT + +// this module initializes RichEdit DLL if needed +class wxRichEditModule : public wxModule +{ +public: + virtual bool OnInit(); + virtual void OnExit(); + + // get the version currently loaded, -1 if none + static int GetLoadedVersion() { return ms_verRichEdit; } + + // load the richedit DLL of at least of required version + static bool Load(int version = 1); + +private: + // the handle to richedit DLL and the version of the DLL loaded + static HINSTANCE ms_hRichEdit; + + // the DLL version loaded or -1 if none + static int ms_verRichEdit; + + DECLARE_DYNAMIC_CLASS(wxRichEditModule) +}; + +HINSTANCE wxRichEditModule::ms_hRichEdit = (HINSTANCE)NULL; +int wxRichEditModule::ms_verRichEdit = -1; + +IMPLEMENT_DYNAMIC_CLASS(wxRichEditModule, wxModule) + +#endif // wxUSE_RICHEDIT // ---------------------------------------------------------------------------- // event tables and other macros @@ -86,7 +121,6 @@ BEGIN_EVENT_TABLE(wxTextCtrl, wxControl) EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo) END_EVENT_TABLE() -#endif // USE_SHARED_LIBRARY // ============================================================================ // implementation @@ -112,39 +146,42 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, const wxString& name) { // base initialization - if ( !CreateBase(parent, id, pos, size, style, name) ) + if ( !CreateBase(parent, id, pos, size, style, validator, name) ) return FALSE; - SetValidator(validator); if ( parent ) parent->AddChild(this); - // set colours - SetupColours(); - // translate wxWin style flags to MSW ones, checking for consistency while // doing it long msStyle = ES_LEFT | WS_VISIBLE | WS_CHILD | WS_TABSTOP; if ( m_windowStyle & wxTE_MULTILINE ) { wxASSERT_MSG( !(m_windowStyle & wxTE_PROCESS_ENTER), - _T("wxTE_PROCESS_ENTER style is ignored for multiline " - "text controls (they always process it)") ); + wxT("wxTE_PROCESS_ENTER style is ignored for multiline " + "text controls (they always process it)") ); - msStyle |= ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL; + msStyle |= ES_MULTILINE | ES_WANTRETURN; + if ((m_windowStyle & wxTE_NO_VSCROLL) == 0) + msStyle |= WS_VSCROLL; m_windowStyle |= wxTE_PROCESS_ENTER; } else msStyle |= ES_AUTOHSCROLL; + if (m_windowStyle & wxHSCROLL) + msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL); + if (m_windowStyle & wxTE_READONLY) msStyle |= ES_READONLY; - if (m_windowStyle & wxHSCROLL) - msStyle |= (WS_HSCROLL | ES_AUTOHSCROLL); if (m_windowStyle & wxTE_PASSWORD) // hidden input msStyle |= ES_PASSWORD; + if (m_windowStyle & wxTE_AUTO_SCROLL) + msStyle |= ES_AUTOHSCROLL; + + // we always want the characters and the arrows m_lDlgCode = DLGC_WANTCHARS | DLGC_WANTARROWS; @@ -161,18 +198,61 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, m_lDlgCode |= DLGC_WANTTAB; // do create the control - either an EDIT or RICHEDIT - const wxChar *windowClass = _T("EDIT"); + wxString windowClass = wxT("EDIT"); #if wxUSE_RICHEDIT if ( m_windowStyle & wxTE_RICH ) { - msStyle |= ES_AUTOVSCROLL; - m_isRich = TRUE; - windowClass = _T("RICHEDIT"); + static bool s_errorGiven = FALSE; // MT-FIXME + + // only give the error msg once if the DLL can't be loaded + if ( !s_errorGiven ) + { + // first try to load the RichEdit DLL (will do nothing if already + // done) + if ( !wxRichEditModule::Load() ) + { + wxLogError(_("Impossible to create a rich edit control, " + "using simple text control instead. Please " + "reinstall riched32.dll")); + + s_errorGiven = TRUE; + } + } + + if ( s_errorGiven ) + { + m_isRich = FALSE; + } + else + { + msStyle |= ES_AUTOVSCROLL; + m_isRich = TRUE; + + int ver = wxRichEditModule::GetLoadedVersion(); + if ( ver == 1 ) + { + windowClass = wxT("RICHEDIT"); + } + else + { +#ifndef RICHEDIT_CLASS + wxString RICHEDIT_CLASS; + RICHEDIT_CLASS.Printf(_T("RichEdit%d0"), ver); +#if wxUSE_UNICODE + RICHEDIT_CLASS += _T('W'); +#else // ANSI + RICHEDIT_CLASS += _T('A'); +#endif // Unicode/ANSI +#endif // !RICHEDIT_CLASS + + windowClass = RICHEDIT_CLASS; + } + } } else m_isRich = FALSE; -#endif +#endif // wxUSE_RICHEDIT bool want3D; WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); @@ -186,7 +266,7 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, // might be -1 in which case we should use the default values (and // SetSize called below takes care of it) m_hWnd = (WXHWND)::CreateWindowEx(exStyle, - windowClass, + windowClass.c_str(), NULL, msStyle, 0, 0, 0, 0, @@ -195,7 +275,7 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, wxGetInstance(), NULL); - wxCHECK_MSG( m_hWnd, FALSE, _T("Failed to create text ctrl") ); + wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create text ctrl") ); #if wxUSE_CTL3D if ( want3D ) @@ -235,6 +315,9 @@ bool wxTextCtrl::Create(wxWindow *parent, wxWindowID id, } #endif + // set colours + SetupColours(); + SetSize(pos.x, pos.y, size.x, size.y); return TRUE; @@ -254,7 +337,7 @@ void wxTextCtrl::AdoptAttributesFromHWND() GetClassName(hWnd, buf, WXSIZEOF(buf)); - if ( wxStricmp(buf, _T("EDIT")) == 0 ) + if ( wxStricmp(buf, wxT("EDIT")) == 0 ) m_isRich = FALSE; else m_isRich = TRUE; @@ -272,8 +355,13 @@ void wxTextCtrl::AdoptAttributesFromHWND() void wxTextCtrl::SetupColours() { - // FIXME why is bg colour not inherited from parent? - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); + wxColour bkgndColour; + if (IsEditable() || (m_windowStyle & wxTE_MULTILINE)) + bkgndColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW); + else + bkgndColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE); + + SetBackgroundColour(bkgndColour); SetForegroundColour(GetParent()->GetForegroundColour()); } @@ -283,16 +371,60 @@ void wxTextCtrl::SetupColours() wxString wxTextCtrl::GetValue() const { - return wxGetWindowText(GetHWND()); + // we can't use wxGetWindowText() (i.e. WM_GETTEXT internally) for + // retrieving more than 64Kb under Win9x +#if wxUSE_RICHEDIT + if ( m_isRich ) + { + int len = GetWindowTextLength(GetHwnd()) + 1; + + wxString str; + wxChar *p = str.GetWriteBuf(len); + + TEXTRANGE textRange; + textRange.chrg.cpMin = 0; + textRange.chrg.cpMax = -1; + textRange.lpstrText = p; + + (void)SendMessage(GetHwnd(), EM_GETTEXTRANGE, 0, (LPARAM)&textRange); + + // believe it or not, but EM_GETTEXTRANGE uses just CR ('\r') for the + // newlines which is neither Unix nor Windows style (Win95 with + // riched20.dll shows this behaviour) - convert it to something + // reasonable + for ( ; *p; p++ ) + { + if ( *p == _T('\r') ) + *p = _T('\n'); + } + + str.UngetWriteBuf(); + + return str; + } +#endif // wxUSE_RICHEDIT + + // WM_GETTEXT uses standard DOS CR+LF (\r\n) convention - convert to the + // same one as above for consitency + wxString str = wxGetWindowText(GetHWND()); + + return wxTextFile::Translate(str, wxTextFileType_Unix); } void wxTextCtrl::SetValue(const wxString& value) { - wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos); + // if the text is long enough, it's faster to just set it instead of first + // 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()) ) + { + wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Dos); - SetWindowText(GetHwnd(), valueDos); + SetWindowText(GetHwnd(), valueDos.c_str()); - AdjustSpaceLimit(); + AdjustSpaceLimit(); + } } void wxTextCtrl::WriteText(const wxString& value) @@ -312,7 +444,7 @@ void wxTextCtrl::AppendText(const wxString& text) void wxTextCtrl::Clear() { - SetWindowText(GetHwnd(), _T("")); + SetWindowText(GetHwnd(), wxT("")); } // ---------------------------------------------------------------------------- @@ -391,8 +523,16 @@ bool wxTextCtrl::CanPaste() const void wxTextCtrl::SetEditable(bool editable) { + bool isEditable = IsEditable(); + HWND hWnd = GetHwnd(); SendMessage(hWnd, EM_SETREADONLY, (WPARAM)!editable, (LPARAM)0L); + + if (editable != isEditable) + { + SetupColours(); + Refresh(); + } } void wxTextCtrl::SetInsertionPoint(long pos) @@ -418,7 +558,7 @@ void wxTextCtrl::SetInsertionPoint(long pos) SendMessage(hWnd, EM_SETSEL, 0, MAKELPARAM(pos, pos)); #endif // Win32/16 - static const char *nothing = ""; + static const wxChar *nothing = _T(""); SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)nothing); } @@ -671,7 +811,16 @@ int wxTextCtrl::GetLineLength(long lineNo) const wxString wxTextCtrl::GetLineText(long lineNo) const { + // TODO this should probably be optimized by using GetWriteBuf() + size_t len = (size_t)GetLineLength(lineNo) + 1; + if ( len < sizeof(WORD) ) + { + // there must be at least enough place for the length WORD in the + // buffer + len += sizeof(WORD); + } + char *buf = (char *)malloc(len); *(WORD *)buf = len; int noChars = (int)SendMessage(GetHwnd(), EM_GETLINE, lineNo, (LPARAM)buf); @@ -734,30 +883,6 @@ void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event) } } -WXHBRUSH wxTextCtrl::OnCtlColor(WXHDC pDC, WXHWND pWnd, WXUINT nCtlColor, - WXUINT message, WXWPARAM wParam, - WXLPARAM lParam) -{ -#if wxUSE_CTL3D - if ( m_useCtl3D ) - { - HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam); - return (WXHBRUSH) hbrush; - } -#endif - - HDC hdc = (HDC)pDC; - SetBkMode(hdc, GetParent()->GetTransparentBackground() ? TRANSPARENT - : OPAQUE); - - ::SetBkColor(hdc, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue())); - ::SetTextColor(hdc, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue())); - - wxBrush *backgroundBrush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID); - - return (WXHBRUSH) backgroundBrush->GetResourceHandle(); -} - void wxTextCtrl::OnChar(wxKeyEvent& event) { switch ( event.KeyCode() ) @@ -766,7 +891,7 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) if ( !(m_windowStyle & wxTE_MULTILINE) ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId); - event.SetEventObject( this ); + InitCommandEvent(event); if ( GetEventHandler()->ProcessEvent(event) ) return; } @@ -779,30 +904,19 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) // ourselves the fact that we got here means that the user code // decided to skip processing of this TAB - probably to let it // do its default job. - // - // NB: Notice that Ctrl-Tab is handled elsewhere and Alt-Tab is - // handled by Windows { wxNavigationKeyEvent eventNav; eventNav.SetDirection(!event.ShiftDown()); - eventNav.SetWindowChange(FALSE); + eventNav.SetWindowChange(event.ControlDown()); eventNav.SetEventObject(this); - if ( GetEventHandler()->ProcessEvent(eventNav) ) + if ( GetParent()->GetEventHandler()->ProcessEvent(eventNav) ) return; } break; - - default: - event.Skip(); - return; } - // don't just call event.Skip() because this will cause TABs and ENTERs - // be passed upwards and we don't always want this - instead process it - // right here - - // FIXME + // no, we didn't process it event.Skip(); } @@ -824,22 +938,20 @@ bool wxTextCtrl::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) case EN_CHANGE: { wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_windowId); - wxString val(GetValue()); - if ( !val.IsNull() ) - event.m_commandString = WXSTRINGCAST val; - event.SetEventObject( this ); + InitCommandEvent(event); + event.SetString(GetValue()); ProcessCommand(event); } break; - case EN_ERRSPACE: + case EN_MAXTEXT: // the text size limit has been hit - increase it AdjustSpaceLimit(); break; // the other notification messages are not processed case EN_UPDATE: - case EN_MAXTEXT: + case EN_ERRSPACE: case EN_HSCROLL: case EN_VSCROLL: default: @@ -855,20 +967,30 @@ void wxTextCtrl::AdjustSpaceLimit() #ifndef __WIN16__ unsigned int len = ::GetWindowTextLength(GetHwnd()), limit = ::SendMessage(GetHwnd(), EM_GETLIMITTEXT, 0, 0); - if ( len > limit ) + if ( len >= limit ) { limit = len + 0x8000; // 32Kb #if wxUSE_RICHEDIT - if ( m_isRich || limit > 0xffff ) -#else - if ( limit > 0xffff ) -#endif - ::SendMessage(GetHwnd(), EM_LIMITTEXT, 0, limit); + if ( m_isRich ) + { + // as a nice side effect, this also allows passing limit > 64Kb + ::SendMessage(GetHwnd(), EM_EXLIMITTEXT, 0, limit); + } else +#endif // wxUSE_RICHEDIT + { + if ( limit > 0xffff ) + { + // this will set it to a platform-dependent maximum (much more + // than 64Kb under NT) + limit = 0; + } + ::SendMessage(GetHwnd(), EM_LIMITTEXT, limit, 0); + } } -#endif +#endif // !Win16 } bool wxTextCtrl::AcceptsFocus() const @@ -877,7 +999,7 @@ bool wxTextCtrl::AcceptsFocus() const return IsEditable() && wxControl::AcceptsFocus(); } -wxSize wxTextCtrl::DoGetBestSize() +wxSize wxTextCtrl::DoGetBestSize() const { int cx, cy; wxGetCharSize(GetHWND(), &cx, &cy, &GetFont()); @@ -898,27 +1020,27 @@ wxSize wxTextCtrl::DoGetBestSize() // standard handlers for standard edit menu events // ---------------------------------------------------------------------------- -void wxTextCtrl::OnCut(wxCommandEvent& event) +void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event)) { Cut(); } -void wxTextCtrl::OnCopy(wxCommandEvent& event) +void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event)) { Copy(); } -void wxTextCtrl::OnPaste(wxCommandEvent& event) +void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event)) { Paste(); } -void wxTextCtrl::OnUndo(wxCommandEvent& event) +void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event)) { Undo(); } -void wxTextCtrl::OnRedo(wxCommandEvent& event) +void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) { Redo(); } @@ -948,3 +1070,68 @@ void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) event.Enable( CanRedo() ); } +// ---------------------------------------------------------------------------- +// wxRichEditModule +// ---------------------------------------------------------------------------- + +#if wxUSE_RICHEDIT + +bool wxRichEditModule::OnInit() +{ + // don't do anything - we will load it when needed + return TRUE; +} + +void wxRichEditModule::OnExit() +{ + if ( ms_hRichEdit ) + { + FreeLibrary(ms_hRichEdit); + } +} + +/* static */ +bool wxRichEditModule::Load(int version) +{ + wxCHECK_MSG( version >= 1 && version <= 3, FALSE, + _T("incorrect richedit control version requested") ); + + if ( version <= ms_verRichEdit ) + { + // we've already got this or better + return TRUE; + } + + if ( ms_hRichEdit ) + { + ::FreeLibrary(ms_hRichEdit); + } + + // always try load riched20.dll first - like this we won't have to reload + // it later if we're first asked for RE 1 and then for RE 2 or 3 + wxString dllname = _T("riched20.dll"); + ms_hRichEdit = ::LoadLibrary(dllname); + ms_verRichEdit = 2; // no way to tell if it's 2 or 3, assume 2 + + if ( !ms_hRichEdit && (version == 1) ) + { + // fall back to RE 1 + dllname = _T("riched32.dll"); + ms_hRichEdit = ::LoadLibrary(dllname); + ms_verRichEdit = 1; + } + + if ( !ms_hRichEdit ) + { + wxLogSysError(_("Could not load Rich Edit DLL '%s'"), dllname.c_str()); + + ms_verRichEdit = -1; + + return FALSE; + } + + return TRUE; +} + +#endif // wxUSE_RICHEDIT +