From a59901f8f1bc6d738ee1079b14c8d77c8aa83665 Mon Sep 17 00:00:00 2001 From: Andreas Falkenhahn Date: Mon, 24 Aug 2020 20:41:44 +0200 Subject: [PATCH] Fix bug in CanUndo() returning true after wxTextCtrl creation For wxMSW text controls with wxTE_RICH2 style, calling SetFont() counts as an undoable operation, resulting in CanUndo() returning true even if no "real" changes have been made yet. Fix this by resetting the undo stack after creating the control using ITextDocument::Undo(). Unfortunately this interface is not available in MinGW-32, so this fix can't be used with it. Closes https://github.com/wxWidgets/wxWidgets/pull/2010 Closes #17524. --- src/msw/textctrl.cpp | 53 +++++++++++++++++++++++++++++++++ tests/controls/textctrltest.cpp | 31 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index 985664755c..16bb3cceff 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -65,7 +65,21 @@ #if wxUSE_RICHEDIT #include #include + + // MinGW32 doesn't have tom.h and doesn't define the interfaces and the + // constants we need, so we can't use ITextDocument::Undo() with it. All + // the other compilers do have this header. + #ifndef __MINGW32_TOOLCHAIN__ + #define wxHAS_TOM_H + #endif + + #ifdef wxHAS_TOM_H + #include + #endif + #include "wx/msw/ole/oleutils.h" + + #include "wx/msw/private/comptr.h" #endif // wxUSE_RICHEDIT #if wxUSE_INKEDIT @@ -129,6 +143,17 @@ DEFINE_GUID(wxIID_IRichEditOleCallback, 0x00020d03, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); } // anonymous namespace + +#ifdef wxHAS_TOM_H + +// This one is not defined in the standard libraries at all and MSDN just says +// to define it explicitly, so we do it for IID_XXX constant itself and not our +// own wxIID_XXX. +DEFINE_GUID(IID_ITextDocument, + 0x8cc497c0, 0xa1df, 0x11ce, 0x80, 0x98, 0x00, 0xaa, 0x00, 0x47, 0xbe, 0x5d); + +#endif // wxHAS_TOM_H + #endif // wxUSE_OLE // ---------------------------------------------------------------------------- @@ -668,6 +693,34 @@ bool wxTextCtrl::MSWCreateText(const wxString& value, ::SendMessage(GetHwnd(), EM_SETMARGINS, wParam, lParam); } +#if wxUSE_RICHEDIT && defined(wxHAS_TOM_H) + // For RichEdit >= 4, SetFont(), called above from MSWCreateControl(), uses + // EM_SETCHARFORMAT which affects the undo buffer, meaning that CanUndo() + // for a newly created control returns true, which is unexpected. To avoid + // this, we explicitly use Undo(tomFalse) here to clear the undo buffer. + // And since Undo(tomFalse) also disables the undo buffer, we need to + // enable it again immediately after clearing by calling Undo(tomTrue). + if ( GetRichVersion() >= 4 ) + { + wxCOMPtr pRichEditOle; + if ( SendMessage(GetHwnd(), EM_GETOLEINTERFACE, + 0, (LPARAM)&pRichEditOle) && pRichEditOle ) + { + wxCOMPtr pDoc; + HRESULT hr = pRichEditOle->QueryInterface + ( + wxIID_PPV_ARGS(ITextDocument, &pDoc) + ); + if ( SUCCEEDED(hr) ) + { + hr = pDoc->Undo(tomFalse, NULL); + if ( SUCCEEDED(hr) ) + pDoc->Undo(tomTrue, NULL); + } + } + } +#endif // wxUSE_RICHEDIT && wxHAS_TOM_H + return true; } diff --git a/tests/controls/textctrltest.cpp b/tests/controls/textctrltest.cpp index e07c218d67..d30c620d0a 100644 --- a/tests/controls/textctrltest.cpp +++ b/tests/controls/textctrltest.cpp @@ -1432,4 +1432,35 @@ TEST_CASE("wxTextCtrl::EventsOnCreate", "[wxTextCtrl][event]") CHECK( updated.GetCount() == 1 ); } +TEST_CASE("wxTextCtrl::InitialCanUndo", "[wxTextCtrl][undo]") +{ + wxWindow* const parent = wxTheApp->GetTopWindow(); + + const long styles[] = { 0, wxTE_RICH, wxTE_RICH2 }; + + for ( size_t n = 0; n < WXSIZEOF(styles); n++ ) + { + const long style = styles[n]; + +#ifdef __MINGW32_TOOLCHAIN__ + if ( style == wxTE_RICH2 ) + { + // We can't call ITextDocument::Undo() in wxMSW code when using + // MinGW32, so this test would always fail with it. + WARN("Skipping test known to fail with MinGW-32."); + } + continue; +#endif // __MINGW32_TOOLCHAIN__ + + INFO("wxTextCtrl with style " << style); + + wxScopedPtr text(new wxTextCtrl(parent, wxID_ANY, "", + wxDefaultPosition, + wxDefaultSize, + style)); + + CHECK( !text->CanUndo() ); + } +} + #endif //wxUSE_TEXTCTRL