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 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;
|
||||||
|
@@ -995,6 +995,16 @@ void MyTextCtrl::OnKeyUp(wxKeyEvent& event)
|
|||||||
|
|
||||||
void MyTextCtrl::OnKeyDown(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() )
|
switch ( event.GetKeyCode() )
|
||||||
{
|
{
|
||||||
case WXK_F1:
|
case WXK_F1:
|
||||||
@@ -1088,11 +1098,6 @@ void MyTextCtrl::OnKeyDown(wxKeyEvent& event)
|
|||||||
wxLogMessage("Control marked as non modified");
|
wxLogMessage("Control marked as non modified");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ms_logKey )
|
|
||||||
LogKeyEvent( "Key down", event);
|
|
||||||
|
|
||||||
event.Skip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
@@ -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"
|
||||||
@@ -2154,21 +2155,37 @@ void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
|
|||||||
// from working. So we work around it by intercepting these shortcuts
|
// from working. So we work around it by intercepting these shortcuts
|
||||||
// ourselves and emitting clipboard events (which richedit will handle,
|
// ourselves and emitting clipboard events (which richedit will handle,
|
||||||
// so everything works as before, including pasting of rich text):
|
// 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':
|
switch ( event.GetKeyCode() )
|
||||||
Copy();
|
{
|
||||||
return;
|
case 'C':
|
||||||
case 'X':
|
case WXK_INSERT:
|
||||||
Cut();
|
Copy();
|
||||||
return;
|
return;
|
||||||
case 'V':
|
case 'X':
|
||||||
Paste();
|
Cut();
|
||||||
return;
|
return;
|
||||||
default:
|
case 'V':
|
||||||
break;
|
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();
|
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,
|
||||||
@@ -2301,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;
|
||||||
@@ -2439,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