Merge branch 'msw-richedit-paste'
Fix pasting long strings into wxTextCtrl in wxMSW.
This commit is contained in:
@@ -60,6 +60,8 @@ public:
|
||||
|
||||
virtual void GetSelection(long *from, long *to) const wxOVERRIDE;
|
||||
|
||||
virtual void Paste() wxOVERRIDE;
|
||||
|
||||
virtual void Redo() 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.
|
||||
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;
|
||||
|
||||
bool m_isNativeCaretShown;
|
||||
|
@@ -995,6 +995,16 @@ void MyTextCtrl::OnKeyUp(wxKeyEvent& event)
|
||||
|
||||
void MyTextCtrl::OnKeyDown(wxKeyEvent& event)
|
||||
{
|
||||
if ( ms_logKey )
|
||||
LogKeyEvent( "Key down", event);
|
||||
|
||||
event.Skip();
|
||||
|
||||
// Only handle bare function keys below, notably let Alt-Fn perform their
|
||||
// usual default functions as intercepting them is annoying.
|
||||
if ( event.GetModifiers() != 0 )
|
||||
return;
|
||||
|
||||
switch ( event.GetKeyCode() )
|
||||
{
|
||||
case WXK_F1:
|
||||
@@ -1088,11 +1098,6 @@ void MyTextCtrl::OnKeyDown(wxKeyEvent& event)
|
||||
wxLogMessage("Control marked as non modified");
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ms_logKey )
|
||||
LogKeyEvent( "Key down", event);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@@ -47,6 +47,7 @@
|
||||
|
||||
#if wxUSE_CLIPBOARD
|
||||
#include "wx/clipbrd.h"
|
||||
#include "wx/dataobj.h"
|
||||
#endif
|
||||
|
||||
#include "wx/textfile.h"
|
||||
@@ -2154,21 +2155,37 @@ void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
|
||||
// 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() )
|
||||
if ( IsRich() )
|
||||
{
|
||||
switch ( event.GetKeyCode() )
|
||||
if ( event.GetModifiers() == wxMOD_CONTROL )
|
||||
{
|
||||
case 'C':
|
||||
Copy();
|
||||
return;
|
||||
case 'X':
|
||||
Cut();
|
||||
return;
|
||||
case 'V':
|
||||
Paste();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
switch ( event.GetKeyCode() )
|
||||
{
|
||||
case 'C':
|
||||
case WXK_INSERT:
|
||||
Copy();
|
||||
return;
|
||||
case 'X':
|
||||
Cut();
|
||||
return;
|
||||
case 'V':
|
||||
Paste();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( event.GetModifiers() == wxMOD_SHIFT )
|
||||
{
|
||||
switch ( event.GetKeyCode() )
|
||||
{
|
||||
case WXK_INSERT:
|
||||
Paste();
|
||||
return;
|
||||
case WXK_DELETE:
|
||||
Cut();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2197,6 +2214,24 @@ void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
|
||||
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
|
||||
wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
||||
WXUINT nMsg,
|
||||
@@ -2301,6 +2336,12 @@ wxTextCtrl::MSWHandleMessage(WXLRESULT *rc,
|
||||
::SetCursor(GetHcursorOf(*wxSTANDARD_CURSOR));
|
||||
}
|
||||
#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;
|
||||
@@ -2439,6 +2480,48 @@ bool wxTextCtrl::AdjustSpaceLimit()
|
||||
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
|
||||
{
|
||||
// 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/uiaction.h"
|
||||
|
||||
#if wxUSE_CLIPBOARD
|
||||
#include "wx/clipbrd.h"
|
||||
#include "wx/dataobj.h"
|
||||
#endif // wxUSE_CLIPBOARD
|
||||
|
||||
#ifdef __WXGTK__
|
||||
#include "wx/stopwatch.h"
|
||||
#endif
|
||||
@@ -1341,4 +1346,74 @@ TEST_CASE("wxTextCtrl::GetBestSize", "[wxTextCtrl][best-size]")
|
||||
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
|
||||
|
Reference in New Issue
Block a user