Fix pasting long strings in wxTextCtrl under MSW
Adjust the length limit before pasting to ensure that all text on clipboard will be successfully pasted, instead of only pasting the part of it which fits. Add a unit test checking that this works. Closes #4646.
This commit is contained in:
@@ -60,6 +60,8 @@ public:
|
|||||||
|
|
||||||
virtual void GetSelection(long *from, long *to) const wxOVERRIDE;
|
virtual void GetSelection(long *from, long *to) const wxOVERRIDE;
|
||||||
|
|
||||||
|
virtual void Paste() wxOVERRIDE;
|
||||||
|
|
||||||
virtual void Redo() wxOVERRIDE;
|
virtual void Redo() wxOVERRIDE;
|
||||||
virtual bool CanRedo() const wxOVERRIDE;
|
virtual bool CanRedo() const wxOVERRIDE;
|
||||||
|
|
||||||
@@ -292,6 +294,11 @@ private:
|
|||||||
// false if we hit the limit set by SetMaxLength() and so didn't change it.
|
// false if we hit the limit set by SetMaxLength() and so didn't change it.
|
||||||
bool AdjustSpaceLimit();
|
bool AdjustSpaceLimit();
|
||||||
|
|
||||||
|
// Called before pasting to ensure that the limit is at big enough to allow
|
||||||
|
// pasting the entire text on the clipboard.
|
||||||
|
void AdjustMaxLengthBeforePaste();
|
||||||
|
|
||||||
|
|
||||||
wxMenu* m_privateContextMenu;
|
wxMenu* m_privateContextMenu;
|
||||||
|
|
||||||
bool m_isNativeCaretShown;
|
bool m_isNativeCaretShown;
|
||||||
|
@@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
#if wxUSE_CLIPBOARD
|
#if wxUSE_CLIPBOARD
|
||||||
#include "wx/clipbrd.h"
|
#include "wx/clipbrd.h"
|
||||||
|
#include "wx/dataobj.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/textfile.h"
|
#include "wx/textfile.h"
|
||||||
@@ -2213,6 +2214,24 @@ void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
|
|||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxTextCtrl::Paste()
|
||||||
|
{
|
||||||
|
// Before pasting, check that the pasted text will fit, unless an explicit
|
||||||
|
// maximum length was set, to avoid only pasting some part of it.
|
||||||
|
//
|
||||||
|
// Note that rich text controls do not send WM_PASTE, so we can't do it in
|
||||||
|
// response to it, but we could handle EN_PROTECTED (after requesting it by
|
||||||
|
// specifying ENM_PROTECTED in EM_SETEVENTMASK argument) and check for the
|
||||||
|
// message being WM_PASTE there, but this doesn't seem to be better than
|
||||||
|
// the simpler approach used here.
|
||||||
|
if ( IsRich() )
|
||||||
|
{
|
||||||
|
AdjustMaxLengthBeforePaste();
|
||||||
|
}
|
||||||
|
|
||||||
|
wxTextCtrlBase::Paste();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
||||||
WXUINT nMsg,
|
WXUINT nMsg,
|
||||||
@@ -2317,6 +2336,12 @@ wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
|||||||
::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
|
::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
|
||||||
}
|
}
|
||||||
#endif // wxUSE_MENUS
|
#endif // wxUSE_MENUS
|
||||||
|
|
||||||
|
case WM_PASTE:
|
||||||
|
// Note that we get this message for plain EDIT controls only, rich
|
||||||
|
// controls are dealt with in our own Paste().
|
||||||
|
AdjustMaxLengthBeforePaste();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
@@ -2455,6 +2480,48 @@ bool wxTextCtrl::AdjustSpaceLimit()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxTextCtrl::AdjustMaxLengthBeforePaste()
|
||||||
|
{
|
||||||
|
#if wxUSE_CLIPBOARD
|
||||||
|
// We only need to do this for multi line controls, single lines should
|
||||||
|
// never receive more text than fits into them by default anyhow.
|
||||||
|
if ( IsSingleLine() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Also don't override an explicitly set limit.
|
||||||
|
unsigned int limit;
|
||||||
|
if ( HasSpaceLimit(&limit) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Otherwise check if we have enough space for clipboard data. We only do
|
||||||
|
// it for plain text because this is all we know how to handle here.
|
||||||
|
wxClipboardLocker lock;
|
||||||
|
wxTextDataObject textData;
|
||||||
|
if ( !wxTheClipboard->GetData(textData) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Unfortunately we can't just get the length directly because we need to
|
||||||
|
// convert EOLs, otherwise our calculation of the required length could be
|
||||||
|
// way off when there are many lines.
|
||||||
|
const unsigned long lenPasted =
|
||||||
|
wxTextFile::Translate(textData.GetText(), wxTextFileType_Dos).length();
|
||||||
|
|
||||||
|
long from, to;
|
||||||
|
GetSelection(&from, &to);
|
||||||
|
const unsigned long lenSel = to - from;
|
||||||
|
|
||||||
|
const unsigned long lenCurrent = GetLastPosition();
|
||||||
|
|
||||||
|
// We need enough space for all the current text and all the new
|
||||||
|
// text, but the selection will be replaced.
|
||||||
|
const unsigned long lenNeeded = lenCurrent - lenSel + lenPasted;
|
||||||
|
if ( lenNeeded >= limit )
|
||||||
|
{
|
||||||
|
SetMaxLength(lenNeeded);
|
||||||
|
}
|
||||||
|
#endif // wxUSE_CLIPBOARD
|
||||||
|
}
|
||||||
|
|
||||||
bool wxTextCtrl::AcceptsFocusFromKeyboard() const
|
bool wxTextCtrl::AcceptsFocusFromKeyboard() const
|
||||||
{
|
{
|
||||||
// we don't want focus if we can't be edited unless we're a multiline
|
// we don't want focus if we can't be edited unless we're a multiline
|
||||||
|
@@ -27,6 +27,11 @@
|
|||||||
#include "wx/scopeguard.h"
|
#include "wx/scopeguard.h"
|
||||||
#include "wx/uiaction.h"
|
#include "wx/uiaction.h"
|
||||||
|
|
||||||
|
#if wxUSE_CLIPBOARD
|
||||||
|
#include "wx/clipbrd.h"
|
||||||
|
#include "wx/dataobj.h"
|
||||||
|
#endif // wxUSE_CLIPBOARD
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
#ifdef __WXGTK__
|
||||||
#include "wx/stopwatch.h"
|
#include "wx/stopwatch.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -1341,4 +1346,74 @@ TEST_CASE("wxTextCtrl::GetBestSize", "[wxTextCtrl][best-size]")
|
|||||||
CHECK( sizeVeryLong.y == sizeLong.y );
|
CHECK( sizeVeryLong.y == sizeLong.y );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if wxUSE_CLIPBOARD
|
||||||
|
|
||||||
|
TEST_CASE("wxTextCtrl::LongPaste", "[wxTextCtrl][clipboard][paste]")
|
||||||
|
{
|
||||||
|
long style = 0;
|
||||||
|
|
||||||
|
SECTION("Plain")
|
||||||
|
{
|
||||||
|
style = wxTE_MULTILINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wxTE_RICH[2] style only makes any different under MSW, so don't bother
|
||||||
|
// testing it under the other platforms.
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
SECTION("Rich")
|
||||||
|
{
|
||||||
|
style = wxTE_MULTILINE | wxTE_RICH;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Rich v2")
|
||||||
|
{
|
||||||
|
style = wxTE_MULTILINE | wxTE_RICH2;
|
||||||
|
}
|
||||||
|
#endif // __WXMSW__
|
||||||
|
|
||||||
|
if ( !style )
|
||||||
|
{
|
||||||
|
// This can happen when explicitly selecting just a single section to
|
||||||
|
// execute -- this code still runs even if the corresponding section is
|
||||||
|
// skipped, so we have to explicitly skip it too in this case.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxScopedPtr<wxTextCtrl>
|
||||||
|
text(new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY, wxString(),
|
||||||
|
wxDefaultPosition, wxDefaultSize, style));
|
||||||
|
|
||||||
|
// This could actually be much higher, but it makes the test proportionally
|
||||||
|
// slower, so use a relatively small (but still requiring more space than
|
||||||
|
// the default maximum length under MSW) number here.
|
||||||
|
const int NUM_LINES = 10000;
|
||||||
|
|
||||||
|
{
|
||||||
|
wxClipboardLocker lock;
|
||||||
|
|
||||||
|
// Build a longish string.
|
||||||
|
wxString s;
|
||||||
|
s.reserve(NUM_LINES*5 + 10);
|
||||||
|
for ( int n = 0; n < NUM_LINES; ++n )
|
||||||
|
{
|
||||||
|
s += wxString::Format("%04d\n", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "THE END";
|
||||||
|
|
||||||
|
wxTheClipboard->AddData(new wxTextDataObject(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
text->ChangeValue("THE BEGINNING\n");
|
||||||
|
text->SetInsertionPointEnd();
|
||||||
|
text->Paste();
|
||||||
|
|
||||||
|
const int numLines = text->GetNumberOfLines();
|
||||||
|
|
||||||
|
CHECK( numLines == NUM_LINES + 2 );
|
||||||
|
CHECK( text->GetLineText(numLines - 1) == "THE END" );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // wxUSE_CLIPBOARD
|
||||||
|
|
||||||
#endif //wxUSE_TEXTCTRL
|
#endif //wxUSE_TEXTCTRL
|
||||||
|
Reference in New Issue
Block a user