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);