From aa4d51d57945cefac0d1b33324f9a2d82795fe43 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 16:31:28 +0100 Subject: [PATCH 01/12] Fix font creation using font flags in Cairo wxGraphicsContext Don't crash trying to use an uninitialized font in GetPartialTextExtents() later if the font was created using CreateFont(sizeInPixels, facename, flags) overload. Closes #18021. --- src/generic/graphicc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/generic/graphicc.cpp b/src/generic/graphicc.cpp index 69c407acd3..bb30529579 100644 --- a/src/generic/graphicc.cpp +++ b/src/generic/graphicc.cpp @@ -1013,6 +1013,10 @@ wxCairoFontData::wxCairoFontData(wxGraphicsRenderer* renderer, int flags, const wxColour& col) : wxGraphicsObjectRefData(renderer) +#ifdef __WXGTK__ + , m_wxfont(wxFontInfo(wxSize(sizeInPixels, sizeInPixels)) + .AllFlags(flags).FaceName(facename)) +#endif { InitColour(col); From 5e429702bfa1dd62ffcd26031f5d155e3c3870a8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 10 Dec 2017 00:03:39 +0100 Subject: [PATCH 02/12] Fix fatal bug in wxXLocale initialization Ensure that m_locale is always initialized to null pointer, as it could remain not initialized at all if information for wxLanguage could be found, but its locale name was empty, which resulted in a crash in dtor when the wxXLocale object was destroyed as freelocale() was called with an invalid pointer. --- src/common/xlocale.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/common/xlocale.cpp b/src/common/xlocale.cpp index 02c9d2ca2d..e8095a1132 100644 --- a/src/common/xlocale.cpp +++ b/src/common/xlocale.cpp @@ -90,12 +90,10 @@ wxXLocale& wxXLocale::GetCLocale() #if wxUSE_INTL wxXLocale::wxXLocale(wxLanguage lang) { + m_locale = NULL; + const wxLanguageInfo * const info = wxLocale::GetLanguageInfo(lang); - if ( !info ) - { - m_locale = NULL; - } - else + if ( info ) { Init(info->GetLocaleName().c_str()); } From 17041075b40f39c4e1cabc7b5155e2506fa65ed5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 10 Dec 2017 00:10:38 +0100 Subject: [PATCH 03/12] Fix wxLanguageInfo::GetLocaleName() This function could (and did) return completely wrong results because the value of the pointer returned by setlocale() could be (and was) changed by the subsequent call to setlocale() with different arguments. Fix the problem by copying the returned value into wxString immediately, without any intervening setlocale() calls. --- src/common/intl.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 1fc2b8d989..96dd2389d6 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -208,12 +208,17 @@ wxString wxLanguageInfo::GetLocaleName() const const char* const orig = wxSetlocale(LC_ALL, NULL); const char* const ret = TrySetLocale(); - if ( !ret ) - return wxString(); + wxString retval; + if ( ret ) + { + // Note that we must copy the returned value before calling setlocale() + // again as the string "ret" points to can (and, at least under Linux + // with glibc, actually always will) be changed by this call. + retval = ret; + wxSetlocale(LC_ALL, orig); + } - wxSetlocale(LC_ALL, orig); - - return ret; + return retval; } // ---------------------------------------------------------------------------- From 38cc8498d1814380127a3c3669ebbdfe1f619268 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 10 Dec 2017 00:18:38 +0100 Subject: [PATCH 04/12] Fix wxXLocale availability detection in configure This was broken by 9507bc430e2faf16b589958be91a462ca8e69813 which stopped defining HAVE_LOCALE_T in configure but didn't update the code using it. Restore the old behaviour by continuing to define HAVE_LOCALE_T even if we don't test (just) for it any longer. See https://github.com/wxWidgets/wxWidgets/pull/461 --- configure | 5 ++++- configure.in | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/configure b/configure index fa8141305a..ca5b78ace9 100755 --- a/configure +++ b/configure @@ -32627,6 +32627,9 @@ $as_echo "$as_me: WARNING: I18n code requires wxFile... disabled" >&2;} fi if test "$wxUSE_XLOCALE" = "yes" ; then + $as_echo "#define wxUSE_XLOCALE 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for complete xlocale" >&5 $as_echo_n "checking for complete xlocale... " >&6; } if ${wx_cv_func_strtod_l+:} false; then : @@ -32676,7 +32679,7 @@ fi $as_echo "$wx_cv_func_strtod_l" >&6; } if test "$wx_cv_func_strtod_l" = "yes" ; then - $as_echo "#define wxUSE_XLOCALE 1" >>confdefs.h + $as_echo "#define HAVE_LOCALE_T 1" >>confdefs.h fi fi diff --git a/configure.in b/configure.in index 9b0bbd969e..8eb3571261 100644 --- a/configure.in +++ b/configure.in @@ -5747,6 +5747,8 @@ if test "$wxUSE_INTL" = "yes" ; then fi if test "$wxUSE_XLOCALE" = "yes" ; then + AC_DEFINE(wxUSE_XLOCALE) + dnl even if xlocale.h exists, it may not contain all that dnl wx needs. check if strtod_l() really is available. AC_CACHE_CHECK([for complete xlocale], @@ -5770,7 +5772,9 @@ if test "$wxUSE_XLOCALE" = "yes" ; then ]) if test "$wx_cv_func_strtod_l" = "yes" ; then - AC_DEFINE(wxUSE_XLOCALE) + dnl We don't test (just) for locale_t existence, but we still define + dnl this symbol to avoid changing the existing code using it. + AC_DEFINE(HAVE_LOCALE_T) fi fi From e97c020285c785464373a9854d9f9d824777228f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 19:02:16 -0700 Subject: [PATCH 05/12] Fix wxLocale::GetInfo() test for French locale under macOS 10.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The date and time format has changed since 10.10 and now contains an extra " à " in its middle, so adjust the test to deal with this. --- tests/intl/intltest.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/intl/intltest.cpp b/tests/intl/intltest.cpp index ecf8770640..6825f8a20a 100644 --- a/tests/intl/intltest.cpp +++ b/tests/intl/intltest.cpp @@ -189,19 +189,25 @@ void IntlTestCase::DateTimeFmtFrench() #else static const char *FRENCH_DATE_FMT = "%d/%m/%Y"; static const char *FRENCH_LONG_DATE_FMT = "%A %d %B %Y"; -#ifdef __WXOSX__ - static const char *FRENCH_DATE_TIME_FMT = "%A %d %B %Y %H:%M:%S"; -#else static const char *FRENCH_DATE_TIME_FMT = "%d/%m/%Y %H:%M:%S"; -#endif #endif WX_ASSERT_EQUAL_FORMAT( "French short date", FRENCH_DATE_FMT, m_locale->GetInfo(wxLOCALE_SHORT_DATE_FMT) ); WX_ASSERT_EQUAL_FORMAT( "French long date", FRENCH_LONG_DATE_FMT, m_locale->GetInfo(wxLOCALE_LONG_DATE_FMT) ); - WX_ASSERT_EQUAL_FORMAT( "French date and time", FRENCH_DATE_TIME_FMT, - m_locale->GetInfo(wxLOCALE_DATE_TIME_FMT) ); + + const wxString fmtDT = m_locale->GetInfo(wxLOCALE_DATE_TIME_FMT); +#ifdef __WXOSX__ + // Things are difficult to test under macOS as the format keeps changing, + // e.g. at some time between 10.10 and 10.12 a new " à " string appeared in + // its middle, so test it piece-wise and hope it doesn't change too much. + INFO("French date and time format is \"" << fmtDT << "\""); + CHECK( fmtDT.StartsWith("%A %d %B %Y") ); + CHECK( fmtDT.EndsWith("%H:%M:%S") ); +#else + WX_ASSERT_EQUAL_FORMAT( "French date and time", FRENCH_DATE_TIME_FMT, fmtDT ); +#endif WX_ASSERT_EQUAL_FORMAT( "French time", "%H:%M:%S", m_locale->GetInfo(wxLOCALE_TIME_FMT) ); } From 9036b3dba876131085251a18c2c0563fc86b7da0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 19:16:47 -0700 Subject: [PATCH 06/12] Remove "C" locale date and time formats test This test didn't make sense at all as it didn't actually test "C" locale formats as calling setlocale(LC_ALL, "C") didn't actually change the values returned by wxLocale::GetInfo(), so it still returned the values corresponding to the French locale set in this test setUp() method and the test only passed because it used wrong values (i.e. the same ones as in French locale test). We also don't have any simple way to test "C" locale formats, we can only test them for wxLANGUAGE_DEFAULT, but this corresponds to the OS defaults which can be customized by user (e.g. in the control panel under MSW) and so we can't expect them to be equal to any fixed values. The simplest solution is to just drop this test, as it's not very useful anyhow (French locale test above already covers the same code). --- tests/intl/intltest.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/tests/intl/intltest.cpp b/tests/intl/intltest.cpp index 6825f8a20a..38a7df1a1c 100644 --- a/tests/intl/intltest.cpp +++ b/tests/intl/intltest.cpp @@ -42,7 +42,6 @@ private: CPPUNIT_TEST( Domain ); CPPUNIT_TEST( Headers ); CPPUNIT_TEST( DateTimeFmtFrench ); - CPPUNIT_TEST( DateTimeFmtC ); CPPUNIT_TEST( IsAvailable ); CPPUNIT_TEST_SUITE_END(); @@ -50,7 +49,6 @@ private: void Domain(); void Headers(); void DateTimeFmtFrench(); - void DateTimeFmtC(); void IsAvailable(); static wxString GetDecimalPoint() @@ -212,35 +210,6 @@ void IntlTestCase::DateTimeFmtFrench() m_locale->GetInfo(wxLOCALE_TIME_FMT) ); } -void IntlTestCase::DateTimeFmtC() -{ - // again, glibc uses different defaults -#ifdef __GLIBC__ - static const char *C_DATE_FMT = "%m/%d/%y"; - static const char *C_LONG_DATE_FMT = "%a %b %d %Y"; - static const char *C_DATE_TIME_FMT = "%a %b %d %H:%M:%S %Y"; -#else - static const char *C_DATE_FMT = "%d/%m/%Y"; - static const char *C_LONG_DATE_FMT = "%A %d %B %Y"; -#ifdef __WXOSX__ - static const char *C_DATE_TIME_FMT = "%A %d %B %Y %H:%M:%S"; -#else - static const char *C_DATE_TIME_FMT = "%d/%m/%Y %H:%M:%S"; -#endif -#endif - - setlocale(LC_ALL, "C"); - - WX_ASSERT_EQUAL_FORMAT( "C short date", C_DATE_FMT, - m_locale->GetInfo(wxLOCALE_SHORT_DATE_FMT) ); - WX_ASSERT_EQUAL_FORMAT( "C long date", C_LONG_DATE_FMT, - m_locale->GetInfo(wxLOCALE_LONG_DATE_FMT) ); - WX_ASSERT_EQUAL_FORMAT( "C date and time", C_DATE_TIME_FMT, - m_locale->GetInfo(wxLOCALE_DATE_TIME_FMT) ); - WX_ASSERT_EQUAL_FORMAT( "C time", "%H:%M:%S", - m_locale->GetInfo(wxLOCALE_TIME_FMT) ); -} - void IntlTestCase::IsAvailable() { const wxString origLocale(setlocale(LC_ALL, NULL)); From 2ad2bccf33c1d05136ee41cda903335c1c3c92e2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 10 Dec 2017 03:28:18 +0100 Subject: [PATCH 07/12] Don't call static wxLocale::GetInfo() via an object This is just confusing and unnecessary. No real changes. --- tests/intl/intltest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/intl/intltest.cpp b/tests/intl/intltest.cpp index 38a7df1a1c..4e39762a0f 100644 --- a/tests/intl/intltest.cpp +++ b/tests/intl/intltest.cpp @@ -191,11 +191,11 @@ void IntlTestCase::DateTimeFmtFrench() #endif WX_ASSERT_EQUAL_FORMAT( "French short date", FRENCH_DATE_FMT, - m_locale->GetInfo(wxLOCALE_SHORT_DATE_FMT) ); + wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT) ); WX_ASSERT_EQUAL_FORMAT( "French long date", FRENCH_LONG_DATE_FMT, - m_locale->GetInfo(wxLOCALE_LONG_DATE_FMT) ); + wxLocale::GetInfo(wxLOCALE_LONG_DATE_FMT) ); - const wxString fmtDT = m_locale->GetInfo(wxLOCALE_DATE_TIME_FMT); + const wxString fmtDT = wxLocale::GetInfo(wxLOCALE_DATE_TIME_FMT); #ifdef __WXOSX__ // Things are difficult to test under macOS as the format keeps changing, // e.g. at some time between 10.10 and 10.12 a new " à " string appeared in @@ -207,7 +207,7 @@ void IntlTestCase::DateTimeFmtFrench() WX_ASSERT_EQUAL_FORMAT( "French date and time", FRENCH_DATE_TIME_FMT, fmtDT ); #endif WX_ASSERT_EQUAL_FORMAT( "French time", "%H:%M:%S", - m_locale->GetInfo(wxLOCALE_TIME_FMT) ); + wxLocale::GetInfo(wxLOCALE_TIME_FMT) ); } void IntlTestCase::IsAvailable() From badf6bc300b0874e737b657ebdadef75024bff62 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 22:36:08 +0100 Subject: [PATCH 08/12] Disable scrollbars for frozen wxTreeCtrl in MSW Replace the hack with resizing the control to the minimal possible size while it's frozen with just disabling its scrollbars in the frozen state. This should also fix the original problem (of scrollbar jumping around wildly as the items are added), but without different problems due to the control being resized unexpectedly. Unfortunately we don't have a reproducer for the original problem, which the commits 6754c300cf9a378f4dcbe745be545139ffa0a989 ("Freeze wxTreeCtrl in wxMSW by hiding it") and 4e1e8dc51b8c6f3e56c57b67b0f49ba6c46eded2 ("Change wxMSW wxTreeCtrl::DoFreeze() to not hide the tree any more" -- but resize it instead) tried to fix, so it's difficult to be sure that it really doesn't exist any more, but it does seem like it ought to be as the comment added back then spoke of the problem with scrollbar updating and this really shouldn't happen if scrollbars are completely disabled. See https://github.com/wxWidgets/wxWidgets/pull/375 --- include/wx/msw/treectrl.h | 7 ------- src/msw/treectrl.cpp | 42 +++++++++------------------------------ 2 files changed, 9 insertions(+), 40 deletions(-) diff --git a/include/wx/msw/treectrl.h b/include/wx/msw/treectrl.h index 4a5eb1ea57..dfd3c74ede 100644 --- a/include/wx/msw/treectrl.h +++ b/include/wx/msw/treectrl.h @@ -207,10 +207,6 @@ protected: virtual void DoFreeze() wxOVERRIDE; virtual void DoThaw() wxOVERRIDE; - virtual void DoSetSize(int x, int y, - int width, int height, - int sizeFlags = wxSIZE_AUTO) wxOVERRIDE; - virtual bool MSWShouldSetDefaultFont() const wxOVERRIDE { return false; } // SetImageList helper @@ -336,9 +332,6 @@ private: // whether we need to deselect other items on mouse up bool m_mouseUpDeselect; - // The size to restore the control to when it is thawed, see DoThaw(). - wxSize m_thawnSize; - friend class wxTreeItemIndirectData; friend class wxTreeSortHelper; diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 1082912d9b..1e972436d1 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -3898,47 +3898,23 @@ void wxTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state) // Update locking. // ---------------------------------------------------------------------------- -// Using WM_SETREDRAW with the native control is a bad idea as it's broken in -// some Windows versions (see http://support.microsoft.com/kb/130611) and -// doesn't seem to do anything in other ones (e.g. under Windows 7 the tree -// control keeps updating its scrollbars while the items are added to it, -// resulting in horrible flicker when adding even a couple of dozen items). -// So we resize it to the smallest possible size instead of freezing -- this -// still flickers, but actually not as badly as it would if we didn't do it. - void wxTreeCtrl::DoFreeze() { - if ( IsShown() ) - { - RECT rc; - ::GetWindowRect(GetHwnd(), &rc); - m_thawnSize = wxRectFromRECT(rc).GetSize(); + wxTreeCtrlBase::DoFreeze(); - ::SetWindowPos(GetHwnd(), 0, 0, 0, 1, 1, - SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); - } + // In addition to disabling redrawing, we also need to disable scrollbar + // updates that would still happen otherwise. + const LONG_PTR styleOld = ::GetWindowLongPtr(GetHwnd(), GWL_STYLE); + ::SetWindowLongPtr(GetHwnd(), GWL_STYLE, styleOld | TVS_NOSCROLL); } void wxTreeCtrl::DoThaw() { - if ( IsShown() ) - { - if ( m_thawnSize != wxDefaultSize ) - { - ::SetWindowPos(GetHwnd(), 0, 0, 0, m_thawnSize.x, m_thawnSize.y, - SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); - } - } -} + // Undo temporary TVS_NOSCROLL addition. + const LONG_PTR styleOld = ::GetWindowLongPtr(GetHwnd(), GWL_STYLE); + ::SetWindowLongPtr(GetHwnd(), GWL_STYLE, styleOld & ~TVS_NOSCROLL); -// We also need to override DoSetSize() to ensure that m_thawnSize is reset if -// the window is resized while being frozen -- in this case, we need to avoid -// resizing it back to its original, pre-freeze, size when it's thawed. -void wxTreeCtrl::DoSetSize(int x, int y, int width, int height, int sizeFlags) -{ - m_thawnSize = wxDefaultSize; - - wxTreeCtrlBase::DoSetSize(x, y, width, height, sizeFlags); + wxTreeCtrlBase::DoThaw(); } #endif // wxUSE_TREECTRL From 588ae3744c91b031a7343e55a788fcaa591647e2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 10 Dec 2017 02:24:13 +0100 Subject: [PATCH 09/12] Move fallback BS_TYPEMASK definition to wx/msw/missing.h It can be useful in source files other than src/msw/control.cpp too. --- include/wx/msw/missing.h | 5 +++++ src/msw/control.cpp | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/wx/msw/missing.h b/include/wx/msw/missing.h index 1b09f730e9..63ac97d69f 100644 --- a/include/wx/msw/missing.h +++ b/include/wx/msw/missing.h @@ -141,6 +141,11 @@ #define QS_ALLPOSTMESSAGE 0 #endif +// Missing from MinGW 4.8 SDK headers. +#ifndef BS_TYPEMASK +#define BS_TYPEMASK 0xf +#endif + // ---------------------------------------------------------------------------- // menu stuff // ---------------------------------------------------------------------------- diff --git a/src/msw/control.cpp b/src/msw/control.cpp index 2f1dcc62a9..4010f4e076 100644 --- a/src/msw/control.cpp +++ b/src/msw/control.cpp @@ -35,6 +35,8 @@ #include "wx/log.h" #include "wx/settings.h" #include "wx/ctrlsub.h" + #include "wx/msw/private.h" + #include "wx/msw/missing.h" #endif #if wxUSE_LISTCTRL @@ -46,16 +48,10 @@ #endif // wxUSE_TREECTRL #include "wx/renderer.h" -#include "wx/msw/private.h" #include "wx/msw/uxtheme.h" #include "wx/msw/dc.h" // for wxDCTemp #include "wx/msw/ownerdrawnbutton.h" -// Missing from MinGW 4.8 SDK headers. -#ifndef BS_TYPEMASK -#define BS_TYPEMASK 0xf -#endif - // ---------------------------------------------------------------------------- // wxWin macros // ---------------------------------------------------------------------------- From 17105cfd07799be487d075a83ef01e374f1fec45 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 23:47:05 +0100 Subject: [PATCH 10/12] Simplify changing window styles in wxMSW code Add wxMSWWinStyleUpdater and wxMSWWinExStyleUpdater helper classes which allow writing code changing GWL_STYLE and GWL_EXSTYLE bits, respectively, in a shorter and more clear way. There should be no real changes in behaviour. --- include/wx/msw/private.h | 12 +-- include/wx/msw/private/winstyle.h | 134 ++++++++++++++++++++++++++++++ src/msw/anybutton.cpp | 17 ++-- src/msw/combobox.cpp | 8 +- src/msw/control.cpp | 11 ++- src/msw/gauge.cpp | 7 +- src/msw/headerctrl.cpp | 9 +- src/msw/mdi.cpp | 13 ++- src/msw/spinctrl.cpp | 12 +-- src/msw/statbmp.cpp | 7 +- src/msw/statbox.cpp | 10 +-- src/msw/stattext.cpp | 17 ++-- src/msw/textentry.cpp | 5 +- src/msw/toplevel.cpp | 18 ++-- src/msw/treectrl.cpp | 7 +- src/msw/window.cpp | 44 +++------- src/propgrid/manager.cpp | 6 +- 17 files changed, 206 insertions(+), 131 deletions(-) create mode 100644 include/wx/msw/private/winstyle.h diff --git a/include/wx/msw/private.h b/include/wx/msw/private.h index 0b1df5b3e5..69a36d0036 100644 --- a/include/wx/msw/private.h +++ b/include/wx/msw/private.h @@ -978,19 +978,9 @@ inline bool wxStyleHasBorder(long style) wxSUNKEN_BORDER | wxDOUBLE_BORDER)) != 0; } -inline long wxGetWindowExStyle(const wxWindowMSW *win) -{ - return ::GetWindowLong(GetHwndOf(win), GWL_EXSTYLE); -} - inline bool wxHasWindowExStyle(const wxWindowMSW *win, long style) { - return (wxGetWindowExStyle(win) & style) != 0; -} - -inline long wxSetWindowExStyle(const wxWindowMSW *win, long style) -{ - return ::SetWindowLong(GetHwndOf(win), GWL_EXSTYLE, style); + return (::GetWindowLong(GetHwndOf(win), GWL_EXSTYLE) & style) != 0; } // Common helper of wxUpdate{,Edit}LayoutDirection() below: sets or clears the diff --git a/include/wx/msw/private/winstyle.h b/include/wx/msw/private/winstyle.h new file mode 100644 index 0000000000..ffb4a2e380 --- /dev/null +++ b/include/wx/msw/private/winstyle.h @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/private/winstyle.h +// Purpose: Small helper class for updating MSW windows style +// Author: Vadim Zeitlin +// Created: 2017-12-09 +// Copyright: (c) 2017 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_MSW_PRIVATE_WINSTYLE_H_ +#define _WX_MSW_PRIVATE_WINSTYLE_H_ + +// ---------------------------------------------------------------------------- +// wxMSWWinLongUpdater +// ---------------------------------------------------------------------------- + +/* + This class is not used directly, but either as wxMSWWinStyleUpdater or + wxMSWWinExStyleUpdater, both of which inherit from it and can be used like + this: + + void SomeFunction() + { + wxMSWWinStyleUpdater updateStyle(GetHwndOf(m_win)); + if ( some-condition ) + updateStyle.TurnOn(XX_YYY); + + // Style update happens here implicitly -- or call Apply(). + } + */ +class wxMSWWinLongUpdater +{ +public: + // Get the current style. + LONG_PTR Get() const + { + return m_styleCurrent; + } + + // Check if the given style bit(s) is (are all) currently turned on. + bool IsOn(LONG_PTR style) const + { + return (m_styleCurrent & style) == style; + } + + // Turn on some bit(s) in the style. + wxMSWWinLongUpdater& TurnOn(LONG_PTR on) + { + m_style |= on; + return *this; + } + + // Turn off some bit(s) in the style. + wxMSWWinLongUpdater& TurnOff(LONG_PTR off) + { + m_style &= ~off; + return *this; + } + + // Turn some bit(s) on or off depending on the condition. + wxMSWWinLongUpdater& TurnOnOrOff(bool cond, LONG_PTR style) + { + return cond ? TurnOn(style) : TurnOff(style); + } + + // Perform the style update (only if necessary, i.e. if the style really + // changed). + // + // Notice that if this method is not called, it's still done from the dtor, + // so it's just a convenient way to do it sooner and avoid having to create + // a new scope for ensuring that the dtor runs at the right place, but + // otherwise is equivalent to do this. + bool Apply() + { + if ( m_style == m_styleCurrent ) + return false; + + ::SetWindowLongPtr(m_hwnd, m_gwlSlot, m_style); + + m_styleCurrent = m_style; + + return true; + } + + ~wxMSWWinLongUpdater() + { + Apply(); + } + +protected: + // Create the object for updating the style or extended style of the given + // window. + // + // Ctor is protected, this class can only be used as wxMSWWinStyleUpdater + // or wxMSWWinExStyleUpdater. + wxMSWWinLongUpdater(HWND hwnd, int gwlSlot) + : m_hwnd(hwnd), + m_gwlSlot(gwlSlot), + m_styleCurrent(::GetWindowLongPtr(hwnd, gwlSlot)), + m_style(m_styleCurrent) + { + } + +private: + const HWND m_hwnd; + const int m_gwlSlot; + + LONG_PTR m_styleCurrent; + LONG_PTR m_style; + + wxDECLARE_NO_COPY_CLASS(wxMSWWinLongUpdater); +}; + +// A variant of wxMSWWinLongUpdater which updates the extended style. +class wxMSWWinStyleUpdater : public wxMSWWinLongUpdater +{ +public: + explicit wxMSWWinStyleUpdater(HWND hwnd) + : wxMSWWinLongUpdater(hwnd, GWL_STYLE) + { + } +}; + +// A variant of wxMSWWinLongUpdater which updates the extended style. +class wxMSWWinExStyleUpdater : public wxMSWWinLongUpdater +{ +public: + explicit wxMSWWinExStyleUpdater(HWND hwnd) + : wxMSWWinLongUpdater(hwnd, GWL_EXSTYLE) + { + } +}; + +#endif // _WX_MSW_PRIVATE_WINSTYLE_H_ diff --git a/src/msw/anybutton.cpp b/src/msw/anybutton.cpp index 9966d89ba4..2ffe67860f 100644 --- a/src/msw/anybutton.cpp +++ b/src/msw/anybutton.cpp @@ -44,6 +44,7 @@ #include "wx/stockitem.h" #include "wx/msw/private/button.h" #include "wx/msw/private/dc.h" +#include "wx/msw/private/winstyle.h" #include "wx/msw/uxtheme.h" #include "wx/private/window.h" @@ -387,15 +388,11 @@ void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label) // have to set it whenever the label becomes multi line as otherwise it // wouldn't be shown correctly as we don't use BS_MULTILINE when creating // the control unless it already has new lines in its label) - long styleOld = ::GetWindowLong(hwnd, GWL_STYLE), - styleNew; + wxMSWWinStyleUpdater updateStyle(hwnd); if ( label.find(wxT('\n')) != wxString::npos ) - styleNew = styleOld | BS_MULTILINE; + updateStyle.TurnOn(BS_MULTILINE); else - styleNew = styleOld & ~BS_MULTILINE; - - if ( styleNew != styleOld ) - ::SetWindowLong(hwnd, GWL_STYLE, styleNew); + updateStyle.TurnOff(BS_MULTILINE); } wxSize wxMSWButton::GetFittingSize(wxWindow *win, @@ -1177,11 +1174,7 @@ void wxAnyButton::MakeOwnerDrawn() if ( !IsOwnerDrawn() ) { // make it so - // note that BS_OWNERDRAW is not independent from other style bits - long style = GetWindowLong(GetHwnd(), GWL_STYLE); - style &= ~(BS_3STATE | BS_AUTO3STATE | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON | BS_CHECKBOX | BS_DEFPUSHBUTTON | BS_GROUPBOX | BS_PUSHBUTTON | BS_RADIOBUTTON | BS_PUSHLIKE); - style |= BS_OWNERDRAW; - SetWindowLong(GetHwnd(), GWL_STYLE, style); + wxMSWWinStyleUpdater(GetHwnd()).TurnOff(BS_TYPEMASK).TurnOn(BS_OWNERDRAW); } } diff --git a/src/msw/combobox.cpp b/src/msw/combobox.cpp index 8361c354c8..47b288f86b 100644 --- a/src/msw/combobox.cpp +++ b/src/msw/combobox.cpp @@ -40,6 +40,7 @@ #include "wx/clipbrd.h" #include "wx/wupdlock.h" #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" #if wxUSE_UXTHEME #include "wx/msw/uxtheme.h" @@ -711,11 +712,10 @@ void wxComboBox::SetLayoutDirection(wxLayoutDirection dir) } else { - LONG_PTR style = ::GetWindowLongPtr(GetEditHWND(), GWL_STYLE); - if ( !(style & ES_CENTER) ) + wxMSWWinStyleUpdater styleUpdater(GetEditHWND()); + if ( !styleUpdater.IsOn(ES_CENTER) ) { - style &= ~ES_RIGHT; - ::SetWindowLongPtr(GetEditHWND(), GWL_STYLE, style); + styleUpdater.TurnOff(ES_RIGHT); } } } diff --git a/src/msw/control.cpp b/src/msw/control.cpp index 4010f4e076..9622386229 100644 --- a/src/msw/control.cpp +++ b/src/msw/control.cpp @@ -51,6 +51,7 @@ #include "wx/msw/uxtheme.h" #include "wx/msw/dc.h" // for wxDCTemp #include "wx/msw/ownerdrawnbutton.h" +#include "wx/msw/private/winstyle.h" // ---------------------------------------------------------------------------- // wxWin macros @@ -421,14 +422,13 @@ bool wxMSWOwnerDrawnButtonBase::MSWIsOwnerDrawn() const void wxMSWOwnerDrawnButtonBase::MSWMakeOwnerDrawn(bool ownerDrawn) { - long style = ::GetWindowLong(GetHwndOf(m_win), GWL_STYLE); + wxMSWWinStyleUpdater updateStyle(GetHwndOf(m_win)); // note that BS_CHECKBOX & BS_OWNERDRAW != 0 so we can't operate on // them as on independent style bits if ( ownerDrawn ) { - style &= ~BS_TYPEMASK; - style |= BS_OWNERDRAW; + updateStyle.TurnOff(BS_TYPEMASK).TurnOn(BS_OWNERDRAW); m_win->Bind(wxEVT_ENTER_WINDOW, &wxMSWOwnerDrawnButtonBase::OnMouseEnterOrLeave, this); @@ -448,8 +448,7 @@ void wxMSWOwnerDrawnButtonBase::MSWMakeOwnerDrawn(bool ownerDrawn) } else // reset to default colour { - style &= ~BS_OWNERDRAW; - style |= MSWGetButtonStyle(); + updateStyle.TurnOff(BS_OWNERDRAW).TurnOn(MSWGetButtonStyle()); m_win->Unbind(wxEVT_ENTER_WINDOW, &wxMSWOwnerDrawnButtonBase::OnMouseEnterOrLeave, this); @@ -467,7 +466,7 @@ void wxMSWOwnerDrawnButtonBase::MSWMakeOwnerDrawn(bool ownerDrawn) &wxMSWOwnerDrawnButtonBase::OnFocus, this); } - ::SetWindowLong(GetHwndOf(m_win), GWL_STYLE, style); + updateStyle.Apply(); if ( !ownerDrawn ) MSWOnButtonResetOwnerDrawn(); diff --git a/src/msw/gauge.cpp b/src/msw/gauge.cpp index 67fb5b8a63..ac03441790 100644 --- a/src/msw/gauge.cpp +++ b/src/msw/gauge.cpp @@ -35,6 +35,7 @@ #include "wx/appprogress.h" #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" // ---------------------------------------------------------------------------- // constants @@ -190,8 +191,7 @@ void wxGauge::SetIndeterminateMode() // Switch the control into indeterminate mode if necessary. if ( !IsInIndeterminateMode() ) { - const long style = ::GetWindowLong(GetHwnd(), GWL_STYLE); - ::SetWindowLong(GetHwnd(), GWL_STYLE, style | PBS_MARQUEE); + wxMSWWinStyleUpdater(GetHwnd()).TurnOn(PBS_MARQUEE); ::SendMessage(GetHwnd(), PBM_SETMARQUEE, TRUE, 0); } } @@ -200,9 +200,8 @@ void wxGauge::SetDeterminateMode() { if ( IsInIndeterminateMode() ) { - const long style = ::GetWindowLong(GetHwnd(), GWL_STYLE); ::SendMessage(GetHwnd(), PBM_SETMARQUEE, FALSE, 0); - ::SetWindowLong(GetHwnd(), GWL_STYLE, style & ~PBS_MARQUEE); + wxMSWWinStyleUpdater(GetHwnd()).TurnOff(PBS_MARQUEE); } } diff --git a/src/msw/headerctrl.cpp b/src/msw/headerctrl.cpp index f8c04f94c5..267775f582 100644 --- a/src/msw/headerctrl.cpp +++ b/src/msw/headerctrl.cpp @@ -38,6 +38,7 @@ #include "wx/msw/wrapcctl.h" #include "wx/msw/private.h" #include "wx/msw/private/customdraw.h" +#include "wx/msw/private/winstyle.h" #ifndef HDM_SETBITMAPMARGIN #define HDM_SETBITMAPMARGIN 0x1234 @@ -402,12 +403,8 @@ void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx) } } - long controlStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE); - if ( hasResizableColumns ) - controlStyle &= ~HDS_NOSIZING; - else - controlStyle |= HDS_NOSIZING; - ::SetWindowLong(GetHwnd(), GWL_STYLE, controlStyle); + wxMSWWinStyleUpdater(GetHwnd()) + .TurnOnOrOff(!hasResizableColumns, HDS_NOSIZING); } void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order) diff --git a/src/msw/mdi.cpp b/src/msw/mdi.cpp index 514e4eedfe..47f70e1790 100644 --- a/src/msw/mdi.cpp +++ b/src/msw/mdi.cpp @@ -44,6 +44,7 @@ #include "wx/stockitem.h" #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" #include @@ -1313,24 +1314,20 @@ bool wxMDIChildFrame::ResetWindowStyle(void *vrect) if (!pChild || (pChild == this)) { HWND hwndClient = GetWinHwnd(pFrameWnd->GetClientWindow()); - DWORD dwStyle = ::GetWindowLong(hwndClient, GWL_EXSTYLE); + + wxMSWWinStyleUpdater updateStyle(hwndClient); // we want to test whether there is a maximized child, so just set // dwThisStyle to 0 if there is no child at all DWORD dwThisStyle = pChild ? ::GetWindowLong(GetWinHwnd(pChild), GWL_STYLE) : 0; - DWORD dwNewStyle = dwStyle; - if ( dwThisStyle & WS_MAXIMIZE ) - dwNewStyle &= ~(WS_EX_CLIENTEDGE); - else - dwNewStyle |= WS_EX_CLIENTEDGE; + updateStyle.TurnOnOrOff(!(dwThisStyle & WS_MAXIMIZE), WS_EX_CLIENTEDGE); - if (dwStyle != dwNewStyle) + if ( updateStyle.Apply() ) { // force update of everything ::RedrawWindow(hwndClient, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); - ::SetWindowLong(hwndClient, GWL_EXSTYLE, dwNewStyle); ::SetWindowPos(hwndClient, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index b51c10a605..a692a5044e 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -36,6 +36,7 @@ #endif #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" #if wxUSE_TOOLTIPS #include "wx/tooltip.h" @@ -549,15 +550,8 @@ void wxSpinCtrl::UpdateBuddyStyle() // otherwise this would become impossible and also if we don't use // hexadecimal as entering "x" of the "0x" prefix wouldn't be allowed // neither then - const DWORD styleOld = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE); - DWORD styleNew; - if ( m_min < 0 || GetBase() != 10 ) - styleNew = styleOld & ~ES_NUMBER; - else - styleNew = styleOld | ES_NUMBER; - - if ( styleNew != styleOld ) - ::SetWindowLong(GetBuddyHwnd(), GWL_STYLE, styleNew); + wxMSWWinStyleUpdater(GetBuddyHwnd()) + .TurnOnOrOff(m_min >= 0 && GetBase() == 10, ES_NUMBER); } // ---------------------------------------------------------------------------- diff --git a/src/msw/statbmp.cpp b/src/msw/statbmp.cpp index dbe99d8fd7..4add948108 100644 --- a/src/msw/statbmp.cpp +++ b/src/msw/statbmp.cpp @@ -36,6 +36,7 @@ #include "wx/msw/private.h" #include "wx/msw/dib.h" +#include "wx/msw/private/winstyle.h" #include "wx/sysopt.h" @@ -312,9 +313,9 @@ void wxStaticBitmap::SetImageNoCopy( wxGDIImage* image) } } #endif // wxUSE_WXDIB - LONG style = ::GetWindowLong( (HWND)GetHWND(), GWL_STYLE ) ; - ::SetWindowLong( (HWND)GetHWND(), GWL_STYLE, ( style & ~( SS_BITMAP|SS_ICON ) ) | - ( m_isIcon ? SS_ICON : SS_BITMAP ) ); + wxMSWWinStyleUpdater(GetHwnd()) + .TurnOff(SS_BITMAP | SS_ICON) + .TurnOn(m_isIcon ? SS_ICON : SS_BITMAP); MSWReplaceImageHandle((WXLPARAM)handle); diff --git a/src/msw/statbox.cpp b/src/msw/statbox.cpp index 5ba29f58f6..9ae5c230df 100644 --- a/src/msw/statbox.cpp +++ b/src/msw/statbox.cpp @@ -45,6 +45,7 @@ #include "wx/msw/private.h" #include "wx/msw/missing.h" #include "wx/msw/dc.h" +#include "wx/msw/private/winstyle.h" // the values coincide with those in tmschema.h #define BP_GROUPBOX 4 @@ -303,10 +304,10 @@ WXHRGN wxStaticBox::MSWGetRegionWithoutChildren() continue; } - LONG style = ::GetWindowLong(child, GWL_STYLE); + wxMSWWinStyleUpdater updateStyle(child); wxString str(wxGetWindowClass(child)); str.UpperCase(); - if ( str == wxT("BUTTON") && (style & BS_GROUPBOX) == BS_GROUPBOX ) + if ( str == wxT("BUTTON") && updateStyle.IsOn(BS_GROUPBOX) ) { if ( child == GetHwnd() ) foundThis = true; @@ -329,10 +330,9 @@ WXHRGN wxStaticBox::MSWGetRegionWithoutChildren() { // need to remove WS_CLIPSIBLINGS from all sibling windows // that are within this staticbox if set - if ( style & WS_CLIPSIBLINGS ) + if ( updateStyle.IsOn(WS_CLIPSIBLINGS) ) { - style &= ~WS_CLIPSIBLINGS; - ::SetWindowLong(child, GWL_STYLE, style); + updateStyle.TurnOff(WS_CLIPSIBLINGS).Apply(); // MSDN: "If you have changed certain window data using // SetWindowLong, you must call SetWindowPos to have the diff --git a/src/msw/stattext.cpp b/src/msw/stattext.cpp index 62dd20512b..97b28d06e8 100644 --- a/src/msw/stattext.cpp +++ b/src/msw/stattext.cpp @@ -28,6 +28,7 @@ #endif #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" bool wxStaticText::Create(wxWindow *parent, wxWindowID id, @@ -155,25 +156,21 @@ void wxStaticText::SetLabel(const wxString& label) return; #ifdef SS_ENDELLIPSIS - long styleReal = ::GetWindowLong(GetHwnd(), GWL_STYLE); + wxMSWWinStyleUpdater updateStyle(GetHwnd()); if ( HasFlag(wxST_ELLIPSIZE_END) ) { // adding SS_ENDELLIPSIS or SS_ENDELLIPSIS "disables" the correct // newline handling in static texts: the newlines in the labels are // shown as square. Thus we don't use it even on newer OS when // the static label contains a newline. - if ( label.Contains(wxT('\n')) ) - styleReal &= ~SS_ENDELLIPSIS; - else - styleReal |= SS_ENDELLIPSIS; - - ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + updateStyle.TurnOnOrOff(!label.Contains(wxT('\n')), SS_ENDELLIPSIS); } else // style not supported natively { - styleReal &= ~SS_ENDELLIPSIS; - ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + updateStyle.TurnOff(SS_ENDELLIPSIS); } + + updateStyle.Apply(); #endif // SS_ENDELLIPSIS // save the label in m_labelOrig with both the markup (if any) and @@ -181,7 +178,7 @@ void wxStaticText::SetLabel(const wxString& label) m_labelOrig = label; #ifdef SS_ENDELLIPSIS - if ( styleReal & SS_ENDELLIPSIS ) + if ( updateStyle.IsOn(SS_ENDELLIPSIS) ) DoSetLabel(GetLabel()); else #endif // SS_ENDELLIPSIS diff --git a/src/msw/textentry.cpp b/src/msw/textentry.cpp index cd33b0c120..97956296ab 100644 --- a/src/msw/textentry.cpp +++ b/src/msw/textentry.cpp @@ -34,6 +34,7 @@ #include "wx/dynlib.h" #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" #if wxUSE_UXTHEME #include "wx/msw/uxtheme.h" @@ -913,9 +914,7 @@ void wxTextEntry::ForceUpper() { ConvertToUpperCase(); - const HWND hwnd = GetEditHwnd(); - const LONG styleOld = ::GetWindowLong(hwnd, GWL_STYLE); - ::SetWindowLong(hwnd, GWL_STYLE, styleOld | ES_UPPERCASE); + wxMSWWinStyleUpdater(GetEditHwnd()).TurnOn(ES_UPPERCASE); } // ---------------------------------------------------------------------------- diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 4d7e00cc54..4c96e1c13c 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -41,6 +41,7 @@ #include "wx/tooltip.h" #include "wx/msw/private.h" +#include "wx/msw/private/winstyle.h" #include "wx/msw/winundef.h" #include "wx/msw/missing.h" @@ -858,14 +859,14 @@ bool wxTopLevelWindowMSW::ShowFullScreen(bool show, long style) // zap the frame borders // save the 'normal' window style - m_fsOldWindowStyle = GetWindowLong(GetHwnd(), GWL_STYLE); + wxMSWWinStyleUpdater updateStyle(GetHwnd()); + m_fsOldWindowStyle = updateStyle.Get(); // save the old position, width & height, maximize state m_fsOldSize = GetRect(); m_fsIsMaximized = IsMaximized(); // decide which window style flags to turn off - LONG newStyle = m_fsOldWindowStyle; LONG offFlags = 0; if (style & wxFULLSCREEN_NOBORDER) @@ -876,16 +877,16 @@ bool wxTopLevelWindowMSW::ShowFullScreen(bool show, long style) if (style & wxFULLSCREEN_NOCAPTION) offFlags |= WS_CAPTION | WS_SYSMENU; - newStyle &= ~offFlags; + updateStyle.TurnOff(offFlags); // Full screen windows should logically be popups as they don't have // decorations (and are definitely not children) and while not using // this style doesn't seem to make any difference for most windows, it // breaks wxGLCanvas in some cases, see #15434, so just always use it. - newStyle |= WS_POPUP; + updateStyle.TurnOn(WS_POPUP); // change our window style to be compatible with full-screen mode - ::SetWindowLong(GetHwnd(), GWL_STYLE, newStyle); + updateStyle.Apply(); wxRect rect; #if wxUSE_DISPLAY @@ -1137,19 +1138,18 @@ wxMenu *wxTopLevelWindowMSW::MSWGetSystemMenu() const bool wxTopLevelWindowMSW::SetTransparent(wxByte alpha) { - LONG exstyle = GetWindowLong(GetHwnd(), GWL_EXSTYLE); + wxMSWWinExStyleUpdater updateExStyle(GetHwnd()); // if setting alpha to fully opaque then turn off the layered style if (alpha == 255) { - SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyle & ~WS_EX_LAYERED); + updateExStyle.TurnOff(WS_EX_LAYERED).Apply(); Refresh(); return true; } // Otherwise, set the layered style if needed and set the alpha value - if ((exstyle & WS_EX_LAYERED) == 0 ) - SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyle | WS_EX_LAYERED); + updateExStyle.TurnOn(WS_EX_LAYERED).Apply(); if ( ::SetLayeredWindowAttributes(GetHwnd(), 0, (BYTE)alpha, LWA_ALPHA) ) return true; diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 1e972436d1..1af3904d81 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -40,6 +40,7 @@ #include "wx/msw/private.h" #include "wx/msw/winundef.h" +#include "wx/msw/private/winstyle.h" #include "wx/imaglist.h" #include "wx/itemattr.h" @@ -3904,15 +3905,13 @@ void wxTreeCtrl::DoFreeze() // In addition to disabling redrawing, we also need to disable scrollbar // updates that would still happen otherwise. - const LONG_PTR styleOld = ::GetWindowLongPtr(GetHwnd(), GWL_STYLE); - ::SetWindowLongPtr(GetHwnd(), GWL_STYLE, styleOld | TVS_NOSCROLL); + wxMSWWinStyleUpdater(GetHwnd()).TurnOn(TVS_NOSCROLL); } void wxTreeCtrl::DoThaw() { // Undo temporary TVS_NOSCROLL addition. - const LONG_PTR styleOld = ::GetWindowLongPtr(GetHwnd(), GWL_STYLE); - ::SetWindowLongPtr(GetHwnd(), GWL_STYLE, styleOld & ~TVS_NOSCROLL); + wxMSWWinStyleUpdater(GetHwnd()).TurnOff(TVS_NOSCROLL); wxTreeCtrlBase::DoThaw(); } diff --git a/src/msw/window.cpp b/src/msw/window.cpp index c6abb2fce2..ea92c52ed0 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -79,6 +79,7 @@ #include "wx/msw/private.h" #include "wx/msw/private/keyboard.h" +#include "wx/msw/private/winstyle.h" #include "wx/msw/dcclient.h" #include "wx/msw/seh.h" #include "wx/private/textmeasure.h" @@ -341,12 +342,8 @@ static void EnsureParentHasControlParentStyle(wxWindow *parent) */ while ( parent && !parent->IsTopLevel() ) { - LONG exStyle = wxGetWindowExStyle(parent); - if ( !(exStyle & WS_EX_CONTROLPARENT) ) - { - // force the parent to have this style - wxSetWindowExStyle(parent, exStyle | WS_EX_CONTROLPARENT); - } + // force the parent to have this style + wxMSWWinExStyleUpdater(GetHwndOf(parent)).TurnOn(WS_EX_CONTROLPARENT); parent = parent->GetParent(); } @@ -1406,11 +1403,8 @@ void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) // this function so instead of simply setting the style to the new // value we clear the bits which were set in styleOld but are set in // the new one and set the ones which were not set before - long styleReal = ::GetWindowLong(GetHwnd(), GWL_STYLE); - styleReal &= ~styleOld; - styleReal |= style; - - ::SetWindowLong(GetHwnd(), GWL_STYLE, styleReal); + wxMSWWinStyleUpdater updateStyle(GetHwnd()); + updateStyle.TurnOff(styleOld).TurnOn(style); // we need to call SetWindowPos() if any of the styles affecting the // frame appearance have changed @@ -1424,15 +1418,9 @@ void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) } // and the extended style - long exstyleReal = wxGetWindowExStyle(this); - - if ( exstyle != exstyleOld ) + wxMSWWinExStyleUpdater updateExStyle(GetHwnd()); + if ( updateExStyle.TurnOff(exstyleOld).TurnOn(exstyle).Apply() ) { - exstyleReal &= ~exstyleOld; - exstyleReal |= exstyle; - - wxSetWindowExStyle(this, exstyleReal); - // ex style changes don't take effect without calling SetWindowPos callSWP = true; } @@ -1443,8 +1431,8 @@ void wxWindowMSW::MSWUpdateStyle(long flagsOld, long exflagsOld) // also to make the change to wxSTAY_ON_TOP style take effect: just // setting the style simply doesn't work if ( !::SetWindowPos(GetHwnd(), - exstyleReal & WS_EX_TOPMOST ? HWND_TOPMOST - : HWND_NOTOPMOST, + updateExStyle.IsOn(WS_EX_TOPMOST) ? HWND_TOPMOST + : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_FRAMECHANGED) ) @@ -2434,7 +2422,7 @@ bool wxWindowMSW::MSWProcessMessage(WXMSG* pMsg) // must not call IsDialogMessage() then, it would simply hang (see #15458). if ( m_hWnd && HasFlag(wxTAB_TRAVERSAL) && - (wxGetWindowExStyle(this) & WS_EX_CONTROLPARENT) ) + wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) ) { // intercept dialog navigation keys MSG *msg = (MSG *)pMsg; @@ -4470,17 +4458,7 @@ bool wxWindowMSW::IsDoubleBuffered() const void wxWindowMSW::SetDoubleBuffered(bool on) { - // Get the current extended style bits - long exstyle = wxGetWindowExStyle(this); - - // Twiddle the bit as needed - if ( on ) - exstyle |= WS_EX_COMPOSITED; - else - exstyle &= ~WS_EX_COMPOSITED; - - // put it back - wxSetWindowExStyle(this, exstyle); + wxMSWWinExStyleUpdater(GetHwnd()).TurnOnOrOff(on, WS_EX_COMPOSITED); } // --------------------------------------------------------------------------- diff --git a/src/propgrid/manager.cpp b/src/propgrid/manager.cpp index 05872f8880..75acf2c64e 100644 --- a/src/propgrid/manager.cpp +++ b/src/propgrid/manager.cpp @@ -1508,10 +1508,8 @@ void wxPropertyGridManager::RecreateControls() #define WS_EX_COMPOSITED 0x02000000L #endif - HWND hWnd = (HWND)m_pToolbar->GetHWND(); - - ::SetWindowLong( hWnd, GWL_EXSTYLE, - ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED ); + wxMSWWinExStyleUpdater(GetHwndOf(m_pToolbar)) + .TurnOn(WS_EX_COMPOSITED); */ #endif From 061e6f9a3ca9e6c14c7f62d32cca18a57c91286a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 9 Dec 2017 19:05:35 +0100 Subject: [PATCH 11/12] Fix flicker when resizing columns of report-mode MSW wxListCtrl Just turn off background erasing to avoid having horrible flicker which can be seen perfectly well simply by drag-resizing a column in a list control with non-default background colour. See https://github.com/wxWidgets/wxWidgets/pull/374 --- docs/changes.txt | 1 + src/msw/listctrl.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/changes.txt b/docs/changes.txt index 4b3e200dff..1b7d72f6fb 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -442,6 +442,7 @@ wxMSW: - Fix UTF-32 conversion for non-BMP characters (ARATA Mizuki). - Use correct parent for the native modal dialogs (Andreas Falkenhahn). - Fix layout of wxSlider with wxSL_VALUE_LABEL only (gafatoa). +- Fix flicker when resizing columns of report-mode wxListCtrl. wxOSX/Cocoa: diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index ba30bd6e05..d46e7f000d 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -334,6 +334,10 @@ void wxListCtrl::MSWSetExListStyles() // it seems better to enable it by default than disable LVS_EX_HEADERDRAGDROP ); + + // As we use LVS_EX_DOUBLEBUFFER above, we don't need to erase our + // background and doing it only results in flicker. + SetBackgroundStyle(wxBG_STYLE_PAINT); } WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const From ef91e5ecbe24580daca6e49fbf9412eb88fa2655 Mon Sep 17 00:00:00 2001 From: Steve Browne Date: Tue, 20 Dec 2016 12:44:14 -0500 Subject: [PATCH 12/12] Implement wxTreeCtrl::SetDoubleBuffered() in MSW wxTreeCtrl Also don't erase background when the control is double-buffered because it's not necessary and can cause flicker. See https://github.com/wxWidgets/wxWidgets/pull/374 --- docs/changes.txt | 1 + include/wx/msw/missing.h | 9 +++++++++ include/wx/msw/treectrl.h | 3 +++ src/msw/treectrl.cpp | 40 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/docs/changes.txt b/docs/changes.txt index 1b7d72f6fb..0fe16c3658 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -443,6 +443,7 @@ wxMSW: - Use correct parent for the native modal dialogs (Andreas Falkenhahn). - Fix layout of wxSlider with wxSL_VALUE_LABEL only (gafatoa). - Fix flicker when resizing columns of report-mode wxListCtrl. +- Implement wxTreeCtrl::SetDoubleBuffered() (Steve Browne). wxOSX/Cocoa: diff --git a/include/wx/msw/missing.h b/include/wx/msw/missing.h index 1b09f730e9..523df76487 100644 --- a/include/wx/msw/missing.h +++ b/include/wx/msw/missing.h @@ -239,6 +239,10 @@ #define TV_FIRST 0x1100 #endif +#ifndef TVS_EX_DOUBLEBUFFER + #define TVS_EX_DOUBLEBUFFER 0x0004 +#endif + #ifndef TVS_FULLROWSELECT #define TVS_FULLROWSELECT 0x1000 #endif @@ -248,6 +252,11 @@ #define TVM_SETTEXTCOLOR (TV_FIRST + 30) #endif +#ifndef TVM_SETEXTENDEDSTYLE + #define TVM_SETEXTENDEDSTYLE (TV_FIRST + 44) + #define TVM_GETEXTENDEDSTYLE (TV_FIRST + 45) +#endif + // Various defines used by the webview library that are needed by mingw #ifndef DISPID_COMMANDSTATECHANGE diff --git a/include/wx/msw/treectrl.h b/include/wx/msw/treectrl.h index 4a5eb1ea57..67b040a5d1 100644 --- a/include/wx/msw/treectrl.h +++ b/include/wx/msw/treectrl.h @@ -202,6 +202,9 @@ public: // returns true if the platform should explicitly apply a theme border virtual bool CanApplyThemeBorder() const wxOVERRIDE { return false; } + virtual bool IsDoubleBuffered() const wxOVERRIDE; + virtual void SetDoubleBuffered(bool on) wxOVERRIDE; + protected: // Implement "update locking" in a custom way for this control. virtual void DoFreeze() wxOVERRIDE; diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 1082912d9b..9bda855c32 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -795,6 +795,46 @@ bool wxTreeCtrl::Create(wxWindow *parent, return true; } +bool wxTreeCtrl::IsDoubleBuffered() const +{ + if ( !GetHwnd() ) + return false; + + // Notice that TVM_GETEXTENDEDSTYLE is supported since XP, so we can always + // send this message, no need for comctl32.dll version check here. + const LRESULT + exTreeStyle = ::SendMessage(GetHwnd(), TVM_GETEXTENDEDSTYLE, 0, 0); + + return (exTreeStyle & TVS_EX_DOUBLEBUFFER) != 0; +} + +void wxTreeCtrl::SetDoubleBuffered(bool on) +{ + if ( !GetHwnd() ) + return; + + // TVS_EX_DOUBLEBUFFER is only supported since Vista, don't try to set it + // under XP, who knows what could this do. + if ( wxApp::GetComCtl32Version() >= 610 ) + { + const HRESULT hr = ::SendMessage(GetHwnd(), + TVM_SETEXTENDEDSTYLE, + TVS_EX_DOUBLEBUFFER, + on ? TVS_EX_DOUBLEBUFFER : 0); + if ( hr == S_OK ) + { + // There is no need to erase background for a double-buffered + // window, so disable it when enabling double buffering and restore + // the default background style value when disabling it. + SetBackgroundStyle(on ? wxBG_STYLE_PAINT : wxBG_STYLE_ERASE); + } + else + { + wxLogApiError("TreeView_SetExtendedStyle(TVS_EX_DOUBLEBUFFER)", hr); + } + } +} + wxTreeCtrl::~wxTreeCtrl() { m_isBeingDeleted = true;