From 4c711c8002f301f78e552ef808a5cbc5da6383b5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 20 Feb 2014 00:31:35 +0000 Subject: [PATCH] Fix wxTextCtrl contents corruption with long strings in wxMSW. wxMSW automatically extended wxTextCtrl length limit beyond the tiny standard 32KB when it was exceeded, but part of the text being appended into the control was lost when doing it. Fix this by retrying insertion after extending the limit. Closes #15980. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_3_0_BRANCH@75936 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/msw/textctrl.cpp | 55 +++++++++++++++++++++++++++++++-- tests/controls/textctrltest.cpp | 32 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index d2e448cc05..af37e0d7b6 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -173,6 +173,20 @@ private: wxDECLARE_NO_COPY_CLASS(UpdatesCountFilter); }; +namespace +{ + +// The length of the text being currently inserted into the control. +// +// This is used to pass information from DoWriteText() to AdjustSpaceLimit() +// and is global as text can only be inserted into one text control at a time +// by a single thread and this operation can only be done from the main thread +// (and we don't want to waste space in every wxTextCtrl object for this field +// unnecessarily). +int gs_lenOfInsertedText = 0; + +} // anonymous namespace + // ---------------------------------------------------------------------------- // event tables and other macros // ---------------------------------------------------------------------------- @@ -1137,10 +1151,33 @@ void wxTextCtrl::DoWriteText(const wxString& value, int flags) UpdatesCountFilter ucf(m_updatesCount); + // Remember the length of the text we're inserting so that + // AdjustSpaceLimit() could adjust the limit to be big enough for it: + // and also signal us whether it did it by resetting it to 0. + gs_lenOfInsertedText = valueDos.length(); + ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT, // EM_REPLACESEL takes 1 to indicate the operation should be redoable selectionOnly ? 1 : 0, wxMSW_CONV_LPARAM(valueDos)); + if ( !gs_lenOfInsertedText ) + { + // Text size limit has been hit and added text has been truncated. + // But the max length has been increased by the EN_MAXTEXT message + // handler, which also reset gs_lenOfInsertedText to 0), so we + // should be able to set it successfully now if we try again. + if ( selectionOnly ) + Undo(); + + ::SendMessage(GetHwnd(), selectionOnly ? EM_REPLACESEL : WM_SETTEXT, + selectionOnly ? 1 : 0, wxMSW_CONV_LPARAM(valueDos)); + } + else + { + // EN_MAXTEXT handler wasn't called, so reset the flag ourselves. + gs_lenOfInsertedText = 0; + } + if ( !ucf.GotUpdate() && (flags & SetValue_SendEvent) ) { SendUpdateEvent(); @@ -2119,8 +2156,22 @@ bool wxTextCtrl::AdjustSpaceLimit() unsigned int len = ::GetWindowTextLength(GetHwnd()); if ( len >= limit ) { - // increment in 32Kb chunks - SetMaxLength(len + 0x8000); + // We need to increase the size of the buffer and to avoid increasing + // it too many times make sure that we make it at least big enough to + // fit all the text we are currently inserting into the control. + unsigned long increaseBy = gs_lenOfInsertedText; + + // Don't let it affect any future unrelated calls to this function. + gs_lenOfInsertedText = 0; + + // But also increase it by at least 32KB chunks -- again, to avoid + // doing it too often -- and round it up to 32KB in any case. + if ( increaseBy < 0x8000 ) + increaseBy = 0x8000; + else + increaseBy = (increaseBy + 0x7fff) & ~0x7fff; + + SetMaxLength(len + increaseBy); } // we changed the limit diff --git a/tests/controls/textctrltest.cpp b/tests/controls/textctrltest.cpp index 044d662725..a527aba580 100644 --- a/tests/controls/textctrltest.cpp +++ b/tests/controls/textctrltest.cpp @@ -85,6 +85,7 @@ private: CPPUNIT_TEST( FontStyle ); CPPUNIT_TEST( Lines ); CPPUNIT_TEST( LogTextCtrl ); + CPPUNIT_TEST( LongText ); CPPUNIT_TEST( PositionToCoords ); CPPUNIT_TEST( PositionToCoordsRich ); CPPUNIT_TEST( PositionToCoordsRich2 ); @@ -106,6 +107,7 @@ private: void FontStyle(); void Lines(); void LogTextCtrl(); + void LongText(); void PositionToCoords(); void PositionToCoordsRich(); void PositionToCoordsRich2(); @@ -521,6 +523,36 @@ void TextCtrlTestCase::LogTextCtrl() CPPUNIT_ASSERT(!m_text->IsEmpty()); } +void TextCtrlTestCase::LongText() +{ + delete m_text; + CreateText(wxTE_MULTILINE|wxTE_DONTWRAP); + + // Pattern for the line. + wxChar linePattern[100+1]; + for (int i = 0; i < WXSIZEOF(linePattern) - 1; i++) + { + linePattern[i] = wxChar('0' + i % 10); + } + linePattern[WXSIZEOF(linePattern) - 1] = wxChar('\0'); + + // Fill the control. + const int numLines = 1000; + m_text->SetMaxLength(15000); + for (int i = 0; i < numLines; i++) + { + m_text->AppendText(wxString::Format(wxT("[%3d] %s\n"), i, linePattern)); + } + + // Check the content. + for (int i = 0; i < numLines; i++) + { + wxString pattern = wxString::Format(wxT("[%3d] %s"), i, linePattern); + wxString line = m_text->GetLineText(i); + CPPUNIT_ASSERT_EQUAL( line, pattern ); + } +} + void TextCtrlTestCase::PositionToCoords() { DoPositionToCoordsTestWithStyle(0);