From 8e44f3caabe53f60a2335a14cf9b57187fe39b1e Mon Sep 17 00:00:00 2001
From: Julian Smart <julian@anthemion.co.uk>
Date: Thu, 28 Apr 2005 14:45:09 +0000
Subject: [PATCH] Worked around an apparent bug in Windows whereby some
 deferred positioning failed: specifically when changing a position from x, to
 y, to x again. Added deferred positioning to wxRadioBox, wxSlider and
 wxSpinCtrl and thereby eliminated some refresh glitches when resizing.
 Eliminated further refresh glitches caused by wxRadioBox (to nearby controls)
 by refreshing parent when the radio box moves.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33907 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt         | 11 +++++-
 docs/latex/wx/sysopt.tex |  3 ++
 include/wx/msw/private.h |  3 ++
 include/wx/msw/window.h  | 15 ++++++++
 src/msw/radiobox.cpp     | 41 ++++++++++++++++++++-
 src/msw/slider95.cpp     | 62 ++++++++++++++++++++++---------
 src/msw/spinctrl.cpp     | 56 +++++++++++++++++++++++++---
 src/msw/window.cpp       | 79 +++++++++++++++++++++++++++++++++-------
 8 files changed, 230 insertions(+), 40 deletions(-)

diff --git a/docs/changes.txt b/docs/changes.txt
index a97a82e1a5..0ca9f953bb 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -11,7 +11,16 @@ All:
 
 wxMSW:
 
-- Fixed erroneous selection of content in wxComboBox when within a wxStaticBox.
+- Fixed erroneous selection of content in wxComboBox when within a wxStaticBox
+  (checking for selection caused by WM_STYLECHANGED).
+- Worked around an apparent bug in Windows whereby some deferred positioning
+  failed: specifically when changing a position from x, to y, to x again.
+- Added deferred positioning to wxRadioBox, wxSlider and wxSpinCtrl and thereby
+  eliminated some refresh glitches when resizing.
+- Eliminated further refresh glitches caused by wxRadioBox (to nearby controls)
+  by refreshing parent when the radio box moves.
+- Added ability set the system option "msw.staticbox.optimized-paint" to 0 to
+  allow a panel to paint graphics around controls within a static box.
 
 wxMac:
 
diff --git a/docs/latex/wx/sysopt.tex b/docs/latex/wx/sysopt.tex
index 91ffd8eba4..e865e2bb30 100644
--- a/docs/latex/wx/sysopt.tex
+++ b/docs/latex/wx/sysopt.tex
@@ -28,6 +28,9 @@ pages).}
 \twocolitem{msw.staticbitmap.htclient}{If set to 1, allows the static bitmap to respond to mouse
 events. The default is 0, since a value of 1 can interfere with refresh in static boxes. Note that once set,
 this option cannot be unset later in the application.}
+\twocolitem{msw.staticbox.optimized-paint}{If set to 0, switches off optimized wxStaticBox painting.
+Setting this to 0 causes more flicker, but allows applications to paint graphics on the parent of a static box
+(the optimized refresh causes any such drawing to disappear).}
 \end{twocollist}
 
 \wxheading{Mac}
diff --git a/include/wx/msw/private.h b/include/wx/msw/private.h
index a2a08d8316..f974941a37 100644
--- a/include/wx/msw/private.h
+++ b/include/wx/msw/private.h
@@ -701,6 +701,9 @@ inline bool wxStyleHasBorder(long style)
                      wxSUNKEN_BORDER | wxDOUBLE_BORDER)) != 0;
 }
 
+// Deferred window moving
+bool wxMoveWindowDeferred(HDWP& hdwp, wxWindow* win, HWND hWnd, int x, int y, int width, int height);
+
 // ----------------------------------------------------------------------------
 // functions mapping HWND to wxWindow
 // ----------------------------------------------------------------------------
diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h
index 627f4378f7..377afca91b 100644
--- a/include/wx/msw/window.h
+++ b/include/wx/msw/window.h
@@ -47,6 +47,9 @@ enum
 
 class WXDLLEXPORT wxWindowMSW : public wxWindowBase
 {
+    friend class wxSpinCtrl;
+    friend class wxSlider;
+    friend class wxRadioBox;
 public:
     wxWindowMSW() { Init(); }
 
@@ -541,5 +544,17 @@ WX_DECLARE_HASH(wxWindowMSW, wxWindowList, wxWinHashTable);
 
 extern wxWinHashTable *wxWinHandleHash;
 
+// ----------------------------------------------------------------------------
+// extra data needed for correcting problems with deferred positioning
+// ----------------------------------------------------------------------------
+
+struct wxExtraWindowData
+{
+    // Stored during deferred positioning
+    wxPoint m_pos;
+    wxSize  m_size;
+    bool    m_deferring:1;
+};
+
 #endif
     // _WX_WINDOW_H_
diff --git a/src/msw/radiobox.cpp b/src/msw/radiobox.cpp
index c8e8ee0a6b..82c3471b21 100644
--- a/src/msw/radiobox.cpp
+++ b/src/msw/radiobox.cpp
@@ -40,6 +40,8 @@
 
 #include "wx/msw/subwin.h"
 
+#define USE_DEFERRED_SIZING 1
+
 #if wxUSE_TOOLTIPS
     #if !defined(__GNUWIN32_OLD__) || defined(__CYGWIN10__)
         #include <commctrl.h>
@@ -545,7 +547,17 @@ void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
             height = heightOld;
     }
 
-    ::MoveWindow(GetHwnd(), xx, yy, width, height, TRUE);
+    // if our parent had prepared a defer window handle for us, use it (unless
+    // we are a top level window)
+    wxWindowMSW *parent = GetParent();
+
+#if USE_DEFERRED_SIZING
+    HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+#else
+    HDWP hdwp = 0;
+#endif    
+
+    wxMoveWindowDeferred(hdwp, this, GetHwnd(), xx, yy, width, height);
 
     // Now position all the buttons: the current button will be put at
     // wxPoint(x_offset, y_offset) and the new row/column will start at
@@ -642,6 +654,22 @@ void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
             x_offset += widthBtn + cx1;
         }
     }
+    if (hdwp)
+    {
+        // Store the size so we can report it accurately
+        wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+        if (!extraData)
+        {
+            extraData = new wxExtraWindowData;
+            m_windowReserved = (void*) extraData;
+        }
+        extraData->m_pos = wxPoint(xx, yy);
+        extraData->m_size = wxSize(width, height);
+        extraData->m_deferring = true;
+
+        // hdwp must be updated as it may have been changed
+        parent->m_hDWP = (WXHANDLE)hdwp;
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -687,6 +715,17 @@ wxRadioBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
 
         return 0;
     }
+    // FIXME: Without this, the radiobox corrupts other controls as it moves
+    // in a dynamic layout. Refreshing causes flicker, but it's better than
+    // leaving droppings. Note that for some reason, wxStaticBox doesn't need
+    // this (perhaps because it has no real children?)
+    else if (nMsg == WM_MOVE && IsKindOf(CLASSINFO(wxRadioBox)))
+    {
+        WXLRESULT res = wxControl::MSWWindowProc(nMsg, wParam, lParam);
+        wxRect rect = GetRect();
+        GetParent()->Refresh(true, & rect);
+        return res;
+    }
 
     return wxStaticBox::MSWWindowProc(nMsg, wParam, lParam);
 }
diff --git a/src/msw/slider95.cpp b/src/msw/slider95.cpp
index 5db488c88c..7efe6d803d 100644
--- a/src/msw/slider95.cpp
+++ b/src/msw/slider95.cpp
@@ -42,6 +42,8 @@
     #include <commctrl.h>
 #endif
 
+#define USE_DEFERRED_SIZING 1
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -441,6 +443,16 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height)
         return;
     }
 
+    // if our parent had prepared a defer window handle for us, use it (unless
+    // we are a top level window)
+    wxWindowMSW *parent = GetParent();
+
+#if USE_DEFERRED_SIZING
+    HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+#else
+    HDWP hdwp = 0;
+#endif    
+
     // be careful to position the slider itself after moving the labels as
     // otherwise our GetBoundingBox(), which is called from WM_SIZE handler,
     // would return a wrong result and wrong size would be cached internally
@@ -453,22 +465,21 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height)
 
         // position all labels: min at the top, value in the middle and max at
         // the bottom
-        ::MoveWindow((*m_labels)[SliderLabel_Min],
-                     xLabel, y, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Min],
+                     xLabel, y, wLabel, hLabel);
 
-        ::MoveWindow((*m_labels)[SliderLabel_Value],
-                     xLabel, y + (height - hLabel)/2, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Value],
+                     xLabel, y + (height - hLabel)/2, wLabel, hLabel);
 
-        ::MoveWindow((*m_labels)[SliderLabel_Max],
-                     xLabel, y + height - hLabel, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Max],
+                     xLabel, y + height - hLabel, wLabel, hLabel);
 
         // position the slider itself along the left/right edge
-        ::MoveWindow(GetHwnd(),
+        wxMoveWindowDeferred(hdwp, this, GetHwnd(),
                      HasFlag(wxSL_LEFT) ? x : x + wLabel + HGAP,
                      y + hLabel/2,
                      width - wLabel - HGAP,
-                     height - hLabel,
-                     TRUE);
+                     height - hLabel);
     }
     else // horizontal
     {
@@ -479,22 +490,37 @@ void wxSlider::DoMoveWindow(int x, int y, int width, int height)
 
         // position all labels: min on the left, value in the middle and max to
         // the right
-        ::MoveWindow((*m_labels)[SliderLabel_Min],
-                     x, yLabel, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Min],
+                     x, yLabel, wLabel, hLabel);
 
-        ::MoveWindow((*m_labels)[SliderLabel_Value],
-                     x + (width - wLabel)/2, yLabel, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Value],
+                     x + (width - wLabel)/2, yLabel, wLabel, hLabel);
 
-        ::MoveWindow((*m_labels)[SliderLabel_Max],
-                     x + width - wLabel, yLabel, wLabel, hLabel, TRUE);
+        wxMoveWindowDeferred(hdwp, this, (*m_labels)[SliderLabel_Max],
+                     x + width - wLabel, yLabel, wLabel, hLabel);
 
         // position the slider itself along the top/bottom edge
-        ::MoveWindow(GetHwnd(),
+        wxMoveWindowDeferred(hdwp, this, GetHwnd(),
                      x,
                      HasFlag(wxSL_TOP) ? y : y + hLabel,
                      width,
-                     height - hLabel,
-                     TRUE);
+                     height - hLabel);
+    }
+    if ( hdwp )
+    {
+        // Store the size so we can report it accurately
+        wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+        if (!extraData)
+        {
+            extraData = new wxExtraWindowData;
+            m_windowReserved = (void*) extraData;
+        }
+        extraData->m_pos = wxPoint(x, y);
+        extraData->m_size = wxSize(width, height);
+        extraData->m_deferring = true;
+
+        // hdwp must be updated as it may have been changed
+        parent->m_hDWP = (WXHANDLE)hdwp;
     }
 }
 
diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp
index 02319d7e7b..907fb9f882 100644
--- a/src/msw/spinctrl.cpp
+++ b/src/msw/spinctrl.cpp
@@ -45,6 +45,8 @@
 
 #include <limits.h>         // for INT_MIN
 
+#define USE_DEFERRED_SIZING 1
+
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -567,21 +569,55 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
         wxLogDebug(_T("not enough space for wxSpinCtrl!"));
     }
 
-    if ( !::MoveWindow(GetBuddyHwnd(), x, y, widthText, height, TRUE) )
-    {
-        wxLogLastError(wxT("MoveWindow(buddy)"));
-    }
+    // if our parent had prepared a defer window handle for us, use it (unless
+    // we are a top level window)
+    wxWindowMSW *parent = GetParent();
+    int originalX = x;
+
+#if USE_DEFERRED_SIZING
+    HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+#else
+    HDWP hdwp = 0;
+#endif    
+
+    // 1) The buddy window
+    wxMoveWindowDeferred(hdwp, this, GetBuddyHwnd(),
+                     x, y, widthText, height);
 
+    // 2) The button window
     x += widthText + MARGIN_BETWEEN;
-    if ( !::MoveWindow(GetHwnd(), x, y, widthBtn, height, TRUE) )
+    wxMoveWindowDeferred(hdwp, this, GetHwnd(),
+                     x, y, widthBtn, height);
+
+    if (hdwp)
     {
-        wxLogLastError(wxT("MoveWindow"));
+        // Store the size so we can report it accurately
+        wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+        if (!extraData)
+        {
+            extraData = new wxExtraWindowData;
+            m_windowReserved = (void*) extraData;
+        }
+        extraData->m_pos = wxPoint(originalX, y);
+        extraData->m_size = wxSize(width, height);
+        extraData->m_deferring = true;
+
+        // hdwp must be updated as it may have been changed
+        parent->m_hDWP = (WXHANDLE)hdwp;
     }
 }
 
 // get total size of the control
 void wxSpinCtrl::DoGetSize(int *x, int *y) const
 {
+    wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+    if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP)
+    {
+        *x = extraData->m_size.x;        
+        *y = extraData->m_size.y;
+        return;
+    }
+    
     RECT spinrect, textrect, ctrlrect;
     GetWindowRect(GetHwnd(), &spinrect);
     GetWindowRect(GetBuddyHwnd(), &textrect);
@@ -595,6 +631,14 @@ void wxSpinCtrl::DoGetSize(int *x, int *y) const
 
 void wxSpinCtrl::DoGetPosition(int *x, int *y) const
 {
+    wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+    if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP)
+    {
+        *x = extraData->m_pos.x;        
+        *y = extraData->m_pos.y;
+        return;
+    }
+
     // hack: pretend that our HWND is the text control just for a moment
     WXHWND hWnd = GetHWND();
     wxConstCast(this, wxSpinCtrl)->m_hWnd = m_hwndBuddy;
diff --git a/src/msw/window.cpp b/src/msw/window.cpp
index 6fc526fac7..b17495ed68 100644
--- a/src/msw/window.cpp
+++ b/src/msw/window.cpp
@@ -119,6 +119,8 @@
     #define HAVE_TRACKMOUSEEVENT
 #endif // everything needed for TrackMouseEvent()
 
+#define USE_DEFERRED_SIZING 1
+
 // ---------------------------------------------------------------------------
 // global variables
 // ---------------------------------------------------------------------------
@@ -468,6 +470,12 @@ wxWindowMSW::~wxWindowMSW()
 {
     m_isBeingDeleted = true;
 
+    if (m_windowReserved)
+    {
+        delete (wxExtraWindowData*) m_windowReserved;
+        m_windowReserved = NULL;
+    }
+
 #ifndef __WXUNIVERSAL__
     // VS: make sure there's no wxFrame with last focus set to us:
     for ( wxWindow *win = GetParent(); win; win = win->GetParent() )
@@ -1443,6 +1451,14 @@ void wxWindowMSW::DoSetToolTip(wxToolTip *tooltip)
 // Get total size
 void wxWindowMSW::DoGetSize(int *x, int *y) const
 {
+    wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+    if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP)
+    {
+        *x = extraData->m_size.x;        
+        *y = extraData->m_size.y;
+        return;
+    }
+
     RECT rect = wxGetWindowRect(GetHwnd());
 
     if ( x )
@@ -1454,6 +1470,14 @@ void wxWindowMSW::DoGetSize(int *x, int *y) const
 // Get size *available for subwindows* i.e. excluding menu bar etc.
 void wxWindowMSW::DoGetClientSize(int *x, int *y) const
 {
+    wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+    if (extraData && extraData->m_deferring && GetParent() && GetParent()->m_hDWP)
+    {
+        *x = extraData->m_pos.x;        
+        *y = extraData->m_pos.y;
+        return;
+    }
+
     RECT rect = wxGetClientRect(GetHwnd());
 
     if ( x )
@@ -1546,29 +1570,31 @@ void wxWindowMSW::DoMoveWindow(int x, int y, int width, int height)
     // if our parent had prepared a defer window handle for us, use it (unless
     // we are a top level window)
     wxWindowMSW *parent = GetParent();
+
+#if USE_DEFERRED_SIZING
     HDWP hdwp = parent && !IsTopLevel() ? (HDWP)parent->m_hDWP : NULL;
+#else
+    HDWP hdwp = 0;
+#endif    
+
+    wxMoveWindowDeferred(hdwp, this, GetHwnd(), x, y, width, height);
+
     if ( hdwp )
     {
-        hdwp = ::DeferWindowPos(hdwp, GetHwnd(), NULL,
-                            x, y, width, height,
-                            SWP_NOZORDER);
-        if ( !hdwp )
+        // Store the size so we can report it accurately
+        wxExtraWindowData* extraData = (wxExtraWindowData*) m_windowReserved;
+        if (!extraData)
         {
-            wxLogLastError(_T("DeferWindowPos"));
+            extraData = new wxExtraWindowData;
+            m_windowReserved = (void*) extraData;
         }
+        extraData->m_pos = wxPoint(x, y);
+        extraData->m_size = wxSize(width, height);
+        extraData->m_deferring = true;
 
         // hdwp must be updated as it may have been changed
         parent->m_hDWP = (WXHANDLE)hdwp;
     }
-
-    // otherwise (or if deferring failed) move the window in place immediately
-    if ( !hdwp )
-    {
-        if ( !::MoveWindow(GetHwnd(), x, y, width, height, IsShown()) )
-        {
-            wxLogLastError(wxT("MoveWindow"));
-        }
-    }
 }
 
 // set the size of the window: if the dimensions are positive, just use them,
@@ -5931,6 +5957,31 @@ bool wxWindowMSW::HandleHotKey(WXWPARAM wParam, WXLPARAM lParam)
 
 #endif // wxUSE_HOTKEY
 
+// Moves a window by deferred method or normal method
+bool wxMoveWindowDeferred(HDWP& hdwp, wxWindow* win, HWND hWnd, int x, int y, int width, int height)
+{
+    if ( hdwp )
+    {
+        hdwp = ::DeferWindowPos(hdwp, hWnd, NULL,
+                            x, y, width, height,
+                            SWP_NOZORDER);
+        if ( !hdwp )
+        {
+            wxLogLastError(_T("DeferWindowPos"));
+        }
+    }
+
+    // otherwise (or if deferring failed) move the window in place immediately
+    if ( !hdwp )
+    {
+        if ( !::MoveWindow(hWnd, x, y, width, height, win->IsShown()) )
+        {
+            wxLogLastError(wxT("MoveWindow"));
+        }
+    }
+    return hdwp != NULL;
+}
+
 // Not tested under WinCE
 #ifndef __WXWINCE__
 
-- 
2.50.0

