1 /////////////////////////////////////////////////////////////////////////////
3 // This file is part of ResizableLib
4 // http://sourceforge.net/projects/resizablelib
6 // Copyright (C) 2000-2004 by Paolo Messina
7 // http://www.geocities.com/ppescher - mailto:ppescher@hotmail.com
9 // The contents of this file are subject to the Artistic License (the "License").
10 // You may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at:
12 // http://www.opensource.org/licenses/artistic-license.html
14 // If you find this code useful, credits would be nice!
16 /////////////////////////////////////////////////////////////////////////////
20 * @brief Implementation of the CResizableLayout class.
24 #include "ResizableLayout.h"
25 #include "ResizableVersion.h"
29 static char THIS_FILE
[]=__FILE__
;
34 * @internal Constant used to detect clipping and refresh properties
36 * @note In August 2002 Platform SDK, some guy at MS thought it was time
37 * to add the missing symbol BS_TYPEMASK, but forgot its original
38 * meaning and so now he's telling us not to use that symbol because
39 * its value is likely to change in the future SDK releases, including
40 * all the BS_* style bits in the mask, not just the button's type
41 * as the symbol's name suggests.
42 * @n So now we're forced to define another symbol, great!
44 #define _BS_TYPEMASK 0x0000000FL
47 * This function adds a new control to the layout manager and sets anchor
48 * points for its top-left and bottom-right corners.
50 * @param hWnd Window handle to the control to be added
51 * @param anchorTopLeft Anchor point for the top-left corner
52 * @param anchorBottomRight Anchor point for the bottom-right corner
54 * @remarks Overlapping controls, like group boxes and the controls inside,
55 * must be added from the outer controls to the inner ones, to let
56 * the clipping routines work correctly.
58 * @sa AddAnchorCallback RemoveAnchor
60 void CResizableLayout::AddAnchor(HWND hWnd
, ANCHOR anchorTopLeft
, ANCHOR anchorBottomRight
)
62 CWnd
* pParent
= GetResizableWnd();
64 // child window must be valid
65 ASSERT(::IsWindow(hWnd
));
66 // must be child of parent window
67 ASSERT(::IsChild(pParent
->GetSafeHwnd(), hWnd
));
69 // get parent window's rect
71 GetTotalClientRect(&rectParent
);
72 // and child control's rect
74 ::GetWindowRect(hWnd
, &rectChild
);
75 ::MapWindowPoints(NULL
, pParent
->m_hWnd
, (LPPOINT
)&rectChild
, 2);
77 // adjust position, if client area has been scrolled
78 rectChild
.OffsetRect(-rectParent
.TopLeft());
80 // go calculate margins
81 CSize marginTopLeft
, marginBottomRight
;
83 // calculate margin for the top-left corner
85 marginTopLeft
.cx
= rectChild
.left
- rectParent
.Width() * anchorTopLeft
.cx
/ 100;
86 marginTopLeft
.cy
= rectChild
.top
- rectParent
.Height() * anchorTopLeft
.cy
/ 100;
88 // calculate margin for the bottom-right corner
90 marginBottomRight
.cx
= rectChild
.right
- rectParent
.Width() * anchorBottomRight
.cx
/ 100;
91 marginBottomRight
.cy
= rectChild
.bottom
- rectParent
.Height() * anchorBottomRight
.cy
/ 100;
93 // prepare the structure
94 LAYOUTINFO
layout(hWnd
, anchorTopLeft
, marginTopLeft
,
95 anchorBottomRight
, marginBottomRight
);
97 // get control's window class
98 GetClassName(hWnd
, layout
.sWndClass
, MAX_PATH
);
100 // initialize resize properties (overridable)
101 InitResizeProperties(layout
);
103 // must not be already there!
104 // (this is probably due to a duplicate call to AddAnchor)
106 ASSERT(!m_mapLayout
.Lookup(hWnd
, pos
));
108 // add to the list and the map
109 pos
= m_listLayout
.AddTail(layout
);
110 m_mapLayout
.SetAt(hWnd
, pos
);
114 * This function adds a placeholder to the layout manager, that will be
115 * dinamically set by a callback function whenever required.
117 * @return The return value is an integer used to distinguish between
118 * different placeholders in the callback implementation.
120 * @remarks You must override @ref ArrangeLayoutCallback to provide layout
123 * @sa AddAnchor ArrangeLayoutCallback ArrangeLayout
125 UINT_PTR
CResizableLayout::AddAnchorCallback()
127 // one callback control cannot rely upon another callback control's
128 // size and/or position (they're updated all together at the end)
129 // it can however use a non-callback control, calling GetAnchorPosition()
133 layout
.nCallbackID
= m_listLayoutCB
.GetCount() + 1;
134 m_listLayoutCB
.AddTail(layout
);
135 return layout
.nCallbackID
;
139 * This function is called for each placeholder added to the layout manager
140 * and must be overridden to provide the necessary layout information.
142 * @param layout Reference to a LAYOUTINFO structure to be filled with
143 * layout information for the specified placeholder.
144 * On input, nCallbackID is the identification number
145 * returned by AddAnchorCallback. On output, anchor points and
146 * the window handle must be set and valid.
148 * @return The return value is @c TRUE if the layout information has been
149 * provided successfully, @c FALSE to skip this placeholder.
151 * @remarks When implementing this function, unknown placeholders should be
152 * passed to the base class. Unhandled cases will fire an assertion
153 * in the debug version.
155 * @sa AddAnchorCallback ArrangeLayout LAYOUTINFO
157 BOOL
CResizableLayout::ArrangeLayoutCallback(LAYOUTINFO
& layout
) const
159 UNREFERENCED_PARAMETER(layout
);
161 ASSERT(FALSE
); // must be overridden, if callback is used
163 return FALSE
; // no useful output data
167 * This function should be called in resizable window classes whenever the
168 * controls layout should be updated, usually after a resize operation.
170 * @remarks All the controls added to the layout are moved and resized at
171 * once for performace reasons, so all the controls are in their
172 * old position when AddAnchorCallback is called.
173 * To know where a control will be placed use GetAnchorPosition.
175 * @sa AddAnchor AddAnchorCallback ArrangeLayoutCallback GetAnchorPosition
177 void CResizableLayout::ArrangeLayout() const
182 CRect rectParent
, rectChild
;
183 INT_PTR count
= m_listLayout
.GetCount();
184 INT_PTR countCB
= m_listLayoutCB
.GetCount();
186 if (count
+ countCB
== 0)
189 // get parent window's rect
190 GetTotalClientRect(&rectParent
);
192 // reposition child windows
193 HDWP hdwp
= ::BeginDeferWindowPos (static_cast<int>(count
+ countCB
));
195 POSITION pos
= m_listLayout
.GetHeadPosition();
199 layout
= m_listLayout
.GetNext(pos
);
201 // calculate new child's position, size and flags for SetWindowPos
202 CalcNewChildPosition(layout
, rectParent
, rectChild
, uFlags
);
204 // only if size or position changed
205 if ((uFlags
& (SWP_NOMOVE
|SWP_NOSIZE
)) != (SWP_NOMOVE
|SWP_NOSIZE
))
207 hdwp
= ::DeferWindowPos(hdwp
, layout
.hWnd
, NULL
, rectChild
.left
,
208 rectChild
.top
, rectChild
.Width(), rectChild
.Height(), uFlags
);
212 // for callback items you may use GetAnchorPosition to know the
213 // new position and size of a non-callback item after resizing
215 pos
= m_listLayoutCB
.GetHeadPosition();
219 layout
= m_listLayoutCB
.GetNext(pos
);
220 // request layout data
221 if (!ArrangeLayoutCallback(layout
))
224 // calculate new child's position, size and flags for SetWindowPos
225 CalcNewChildPosition(layout
, rectParent
, rectChild
, uFlags
);
227 // only if size or position changed
228 if ((uFlags
& (SWP_NOMOVE
|SWP_NOSIZE
)) != (SWP_NOMOVE
|SWP_NOSIZE
))
230 hdwp
= ::DeferWindowPos(hdwp
, layout
.hWnd
, NULL
, rectChild
.left
,
231 rectChild
.top
, rectChild
.Width(), rectChild
.Height(), uFlags
);
235 // finally move all the windows at once
236 ::EndDeferWindowPos(hdwp
);
240 * @internal This function adds or removes a control window region
241 * to or from the specified clipping region, according to its layout
244 void CResizableLayout::ClipChildWindow(const LAYOUTINFO
& layout
,
247 // obtain window position
249 ::GetWindowRect(layout
.hWnd
, &rect
);
250 #if (_WIN32_WINNT >= 0x0501)
251 //! @todo decide when to clip client only or non-client too (themes?)
252 //! (leave disabled meanwhile, until I find a good solution)
253 //! @note wizard97 with watermark bitmap and themes won't look good!
254 // if (real_WIN32_WINNT >= 0x501)
255 // ::SendMessage(layout.hWnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rect);
257 ::MapWindowPoints(NULL
, GetResizableWnd()->m_hWnd
, (LPPOINT
)&rect
, 2);
259 // use window region if any
261 rgn
.CreateRectRgn(0,0,0,0);
262 switch (::GetWindowRgn(layout
.hWnd
, rgn
))
266 rgn
.OffsetRgn(rect
.TopLeft());
270 rgn
.SetRectRgn(&rect
);
273 // get the clipping property
274 BOOL bClipping
= layout
.properties
.bAskClipping
?
275 LikesClipping(layout
) : layout
.properties
.bCachedLikesClipping
;
277 // modify region accordingly
279 pRegion
->CombineRgn(pRegion
, &rgn
, RGN_DIFF
);
281 pRegion
->CombineRgn(pRegion
, &rgn
, RGN_OR
);
285 * This function retrieves the clipping region for the current layout.
286 * It can be used to draw directly inside the region, without applying
287 * clipping as the ClipChildren function does.
289 * @param pRegion Pointer to a CRegion object that holds the
290 * calculated clipping region upon return
292 * @deprecated For anti-flickering ClipChildren should be preferred
293 * as it is more complete for platform compatibility.
294 * It will probably become a private function.
296 void CResizableLayout::GetClippingRegion(CRgn
* pRegion
) const
298 CWnd
* pWnd
= GetResizableWnd();
300 // System's default clipping area is screen's size,
301 // not enough for max track size, for example:
302 // if screen is 1024 x 768 and resizing border is 4 pixels,
303 // maximized size is 1024+4*2=1032 x 768+4*2=776,
304 // but max track size is 4 pixels bigger 1036 x 780 (don't ask me why!)
305 // So, if you resize the window to maximum size, the last 4 pixels
306 // are clipped out by the default clipping region, that gets created
307 // as soon as you call clipping functions (my guess).
309 // reset clipping region to the whole client area
311 pWnd
->GetClientRect(&rect
);
312 pRegion
->CreateRectRgnIndirect(&rect
);
314 // clip only anchored controls
316 POSITION pos
= m_listLayout
.GetHeadPosition();
320 layout
= m_listLayout
.GetNext(pos
);
322 if (::IsWindowVisible(layout
.hWnd
))
323 ClipChildWindow(layout
, pRegion
);
325 pos
= m_listLayoutCB
.GetHeadPosition();
329 layout
= m_listLayoutCB
.GetNext(pos
);
331 if (!ArrangeLayoutCallback(layout
))
334 if (::IsWindowVisible(layout
.hWnd
))
335 ClipChildWindow(layout
, pRegion
);
337 //! @todo Has XP changed this??? It doesn't seem correct anymore!
339 // fix for RTL layouts (1 pixel of horz offset)
340 if (pWnd->GetExStyle() & WS_EX_LAYOUTRTL)
341 pRegion->OffsetRgn(-1,0);
345 //! @internal @brief Implements GetAncestor(pWnd->GetSafeHwnd(), GA_ROOT)
346 inline CWnd
* GetRootParentWnd(CWnd
* pWnd
)
348 // GetAncestor API not present, emulate
349 if (!(pWnd
->GetStyle() & WS_CHILD
))
351 while (pWnd
->GetStyle() & WS_CHILD
)
352 pWnd
= pWnd
->GetParent();
357 * This function enables or restores clipping on the specified DC when
358 * appropriate. It should be called whenever drawing on the window client
359 * area to avoid flickering.
361 * @param pDC Pointer to the target device context
362 * @param bUndo Flag that specifies wether to restore the clipping region
364 * @return The return value is @c TRUE if the clipping region has been
365 * modified, @c FALSE if clipping was not necessary.
367 * @remarks For anti-flickering to work, you should wrap your
368 * @c WM_ERASEBKGND message handler inside a pair of calls to
369 * this function, with the last parameter set to @c TRUE first
370 * and to @c FALSE at the end.
372 BOOL
CResizableLayout::ClipChildren(CDC
* pDC
, BOOL bUndo
)
374 #if (_WIN32_WINNT >= 0x0501 && !defined(RSZLIB_NO_XP_DOUBLE_BUFFER))
375 // clipping not necessary when double-buffering enabled
376 if (real_WIN32_WINNT
>= 0x0501)
378 CWnd
*pWnd
= GetRootParentWnd(GetResizableWnd());
380 pWnd
= GetResizableWnd();
381 if (pWnd
->GetExStyle() & WS_EX_COMPOSITED
)
386 HDC hDC
= pDC
->GetSafeHdc();
387 HWND hWnd
= GetResizableWnd()->GetSafeHwnd();
389 m_nOldClipRgn
= -1; // invalid region by default
391 // Some controls (such as transparent toolbars and standard controls
392 // with XP theme enabled) send a WM_ERASEBKGND msg to the parent
393 // to draw themselves, in which case we must not enable clipping.
395 // We check that the window associated with the DC is the
396 // resizable window and not a child control.
398 if (!bUndo
&& (hWnd
== ::WindowFromDC(hDC
)))
400 // save old DC clipping region
401 m_nOldClipRgn
= ::GetClipRgn(hDC
, m_hOldClipRgn
);
403 // clip out supported child windows
405 GetClippingRegion(&rgnClip
);
406 ::ExtSelectClipRgn(hDC
, rgnClip
, RGN_AND
);
411 // restore old clipping region, only if modified and valid
412 if (bUndo
&& m_nOldClipRgn
>= 0)
414 if (m_nOldClipRgn
== 1)
415 ::SelectClipRgn(hDC
, m_hOldClipRgn
);
417 ::SelectClipRgn(hDC
, NULL
);
426 * This function is used by this class, and should be used by derived
427 * classes too, in place of the standard GetClientRect. It can be useful
428 * for windows with scrollbars or expanding windows, to provide the true
429 * client area, including even those parts which are not visible.
431 * @param lpRect Pointer to the RECT structure that holds the result
433 * @remarks Override this function to provide the client area the class uses
434 * to perform layout calculations, both when adding controls and
435 * when rearranging the layout.
436 * @n The base implementation simply calls @c GetClientRect
438 void CResizableLayout::GetTotalClientRect(LPRECT lpRect
) const
440 GetResizableWnd()->GetClientRect(lpRect
);
444 * This function is used to determine if a control needs to be painted when
445 * it is moved or resized by the layout manager.
447 * @param layout Reference to a @c LAYOUTINFO structure for the control
448 * @param rectOld Reference to a @c RECT structure that holds the control
449 * position and size before the layout update
450 * @param rectNew Reference to a @c RECT structure that holds the control
451 * position and size after the layout update
453 * @return The return value is @c TRUE if the control should be freshly
454 * painted after a layout update, @c FALSE if not necessary.
456 * @remarks The default implementation tries to identify windows that
457 * need refresh by their class name and window style.
458 * @n Override this function if you need a different behavior or if
459 * you have custom controls that fail to be identified.
461 * @sa LikesClipping InitResizeProperties
463 BOOL
CResizableLayout::NeedsRefresh(const LAYOUTINFO
& layout
,
464 const CRect
& rectOld
, const CRect
& rectNew
) const
466 if (layout
.bMsgSupport
)
468 REFRESHPROPERTY refresh
;
469 refresh
.rcOld
= rectOld
;
470 refresh
.rcNew
= rectNew
;
471 if (Send_NeedsRefresh(layout
.hWnd
, &refresh
))
472 return refresh
.bNeedsRefresh
;
475 int nDiffWidth
= (rectNew
.Width() - rectOld
.Width());
476 int nDiffHeight
= (rectNew
.Height() - rectOld
.Height());
479 if (nDiffWidth
== 0 && nDiffHeight
== 0)
482 // optimistic, no need to refresh
483 BOOL bRefresh
= FALSE
;
485 // window classes that need refresh when resized
486 if (0 == lstrcmp(layout
.sWndClass
, WC_STATIC
))
488 DWORD style
= ::GetWindowLong(layout
.hWnd
, GWL_STYLE
);
490 switch (style
& SS_TYPEMASK
)
496 bRefresh
= bRefresh
|| (nDiffWidth
!= 0);
497 // vertically centered text
498 if (style
& SS_CENTERIMAGE
)
499 bRefresh
= bRefresh
|| (nDiffHeight
!= 0);
502 case SS_LEFTNOWORDWRAP
:
503 // text with ellipsis
504 if (style
& SS_ELLIPSISMASK
)
505 bRefresh
= bRefresh
|| (nDiffWidth
!= 0);
506 // vertically centered text
507 if (style
& SS_CENTERIMAGE
)
508 bRefresh
= bRefresh
|| (nDiffHeight
!= 0);
526 // window classes that don't redraw client area correctly
527 // when the hor scroll pos changes due to a resizing
528 BOOL bHScroll
= FALSE
;
529 if (0 == lstrcmp(layout
.sWndClass
, WC_LISTBOX
))
532 // fix for horizontally scrollable windows, if wider
533 if (bHScroll
&& (nDiffWidth
> 0))
535 // get max scroll position
537 info
.cbSize
= sizeof(SCROLLINFO
);
538 info
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
;
539 if (::GetScrollInfo(layout
.hWnd
, SB_HORZ
, &info
))
541 // subtract the page size
542 info
.nMax
-= __max(info
.nPage
- 1, 0);
545 // resizing will cause the text to scroll on the right
546 // because the scrollbar is going beyond the right limit
547 if ((info
.nMax
> 0) && (info
.nPos
+ nDiffWidth
> info
.nMax
))
549 // needs repainting, due to horiz scrolling
558 * This function is used to determine if a control can be safely clipped
559 * out of the parent window client area when it is repainted, usually
560 * after a resize operation.
562 * @param layout Reference to a @c LAYOUTINFO structure for the control
564 * @return The return value is @c TRUE if clipping is supported by the
565 * control, @c FALSE otherwise.
567 * @remarks The default implementation tries to identify @a clippable
568 * windows by their class name and window style.
569 * @n Override this function if you need a different behavior or if
570 * you have custom controls that fail to be identified.
572 * @sa NeedsRefresh InitResizeProperties
574 BOOL
CResizableLayout::LikesClipping(const LAYOUTINFO
& layout
) const
576 if (layout
.bMsgSupport
)
578 CLIPPINGPROPERTY clipping
;
579 if (Send_LikesClipping(layout
.hWnd
, &clipping
))
580 return clipping
.bLikesClipping
;
583 DWORD style
= ::GetWindowLong(layout
.hWnd
, GWL_STYLE
);
585 // skip windows that wants background repainted
586 if (0 == lstrcmp(layout
.sWndClass
, WC_BUTTON
))
589 switch (style
& _BS_TYPEMASK
)
595 // ownerdraw buttons must return correct hittest code
596 // to notify their transparency to the system and this library
597 // or they could use the registered message (more reliable)
598 ::GetWindowRect(layout
.hWnd
, &rect
);
599 ::SendMessage(layout
.hWnd
, WM_NCCALCSIZE
, FALSE
, (LPARAM
)&rect
);
600 if ( HTTRANSPARENT
== ::SendMessage(layout
.hWnd
,
601 WM_NCHITTEST
, 0, MAKELPARAM(rect
.left
, rect
.top
)) )
607 else if (0 == lstrcmp(layout
.sWndClass
, WC_STATIC
))
609 switch (style
& SS_TYPEMASK
)
614 case SS_LEFTNOWORDWRAP
:
630 if (style
& SS_CENTERIMAGE
)
640 // assume the others like clipping
646 * @internal This function calculates the new size and position of a
647 * control in the layout and flags for @c SetWindowPos
649 void CResizableLayout::CalcNewChildPosition(const LAYOUTINFO
& layout
,
650 const CRect
&rectParent
, CRect
&rectChild
, UINT
& uFlags
) const
652 CWnd
* pParent
= GetResizableWnd();
654 ::GetWindowRect(layout
.hWnd
, &rectChild
);
655 ::MapWindowPoints(NULL
, pParent
->m_hWnd
, (LPPOINT
)&rectChild
, 2);
659 // calculate new top-left corner
660 rectNew
.left
= layout
.marginTopLeft
.cx
+ rectParent
.Width() * layout
.anchorTopLeft
.cx
/ 100;
661 rectNew
.top
= layout
.marginTopLeft
.cy
+ rectParent
.Height() * layout
.anchorTopLeft
.cy
/ 100;
663 // calculate new bottom-right corner
664 rectNew
.right
= layout
.marginBottomRight
.cx
+ rectParent
.Width() * layout
.anchorBottomRight
.cx
/ 100;
665 rectNew
.bottom
= layout
.marginBottomRight
.cy
+ rectParent
.Height() * layout
.anchorBottomRight
.cy
/ 100;
667 // adjust position, if client area has been scrolled
668 rectNew
.OffsetRect(rectParent
.TopLeft());
670 // get the refresh property
671 BOOL bRefresh
= layout
.properties
.bAskRefresh
?
672 NeedsRefresh(layout
, rectChild
, rectNew
) : layout
.properties
.bCachedNeedsRefresh
;
675 uFlags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOREPOSITION
;
677 uFlags
|= SWP_NOCOPYBITS
;
678 if (rectNew
.TopLeft() == rectChild
.TopLeft())
679 uFlags
|= SWP_NOMOVE
;
680 if (rectNew
.Size() == rectChild
.Size())
681 uFlags
|= SWP_NOSIZE
;
688 * This function calculates the top, left, bottom, right margins for a
689 * given size of the specified control.
691 * @param hWnd Window handle to a control in the layout
692 * @param sizeChild Size of the control to use in calculations
693 * @param rectMargins Holds the calculated margins
695 * @return The return value is @c TRUE if successful, @c FALSE otherwise
697 * @remarks This function can be used to infer the parent window size
698 * from the size of one of its child controls.
699 * It is used to implement cascading of size constraints.
701 BOOL
CResizableLayout::GetAnchorMargins(HWND hWnd
, const CSize
&sizeChild
, CRect
&rectMargins
) const
704 if (!m_mapLayout
.Lookup(hWnd
, pos
))
707 const LAYOUTINFO
& layout
= m_listLayout
.GetAt(pos
);
709 // augmented size, relative to anchor points
710 CSize size
= sizeChild
+ layout
.marginTopLeft
- layout
.marginBottomRight
;
712 // percent of parent size occupied by this control
713 CSize
percent(layout
.anchorBottomRight
.cx
- layout
.anchorTopLeft
.cx
,
714 layout
.anchorBottomRight
.cy
- layout
.anchorTopLeft
.cy
);
716 // calculate total margins
717 rectMargins
.left
= size
.cx
* layout
.anchorTopLeft
.cx
/ percent
.cx
+ layout
.marginTopLeft
.cx
;
718 rectMargins
.top
= size
.cy
* layout
.anchorTopLeft
.cy
/ percent
.cy
+ layout
.marginTopLeft
.cy
;
719 rectMargins
.right
= size
.cx
* (100 - layout
.anchorBottomRight
.cx
) / percent
.cx
- layout
.marginBottomRight
.cx
;
720 rectMargins
.bottom
= size
.cy
* (100 - layout
.anchorBottomRight
.cy
) / percent
.cy
- layout
.marginBottomRight
.cy
;
726 * This function is used to set the initial resize properties of a control
727 * in the layout, that are stored in the @c properties member of the
728 * related @c LAYOUTINFO structure.
730 * @param layout Reference to the @c LAYOUTINFO structure to be set
732 * @remarks The various flags are used to specify whether the resize
733 * properties (clipping, refresh) can change at run-time, and a new
734 * call to the property querying functions is needed at every
735 * layout update, or they are static properties, and the cached
736 * value is used whenever necessary.
737 * @n The default implementation sends a registered message to the
738 * control, giving it the opportunity to specify its resize
739 * properties, which takes precedence if the message is supported.
740 * It then sets the @a clipping property as static, calling
741 * @c LikesClipping only once, and the @a refresh property as
742 * dynamic, causing @c NeedsRefresh to be called every time.
743 * @n This should be right for most situations, as the need for
744 * @a refresh usually depends on the size fo a control, while the
745 * support for @a clipping is usually linked to the specific type
746 * of control, which is unlikely to change at run-time, but you can
747 * still override this function if a different beahvior is needed.
749 * @sa LikesClipping NeedsRefresh LAYOUTINFO RESIZEPROPERTIES
751 void CResizableLayout::InitResizeProperties(LAYOUTINFO
&layout
) const
753 // check if custom window supports this library
754 // (properties must be correctly set by the window)
755 layout
.bMsgSupport
= Send_QueryProperties(layout
.hWnd
, &layout
.properties
);
757 // default properties
758 if (!layout
.bMsgSupport
)
760 // clipping property is assumed as static
761 layout
.properties
.bAskClipping
= FALSE
;
762 layout
.properties
.bCachedLikesClipping
= LikesClipping(layout
);
763 // refresh property is assumed as dynamic
764 layout
.properties
.bAskRefresh
= TRUE
;
769 * This function modifies a window to enable resizing functionality.
770 * This affects the window style, size, system menu and appearance.
772 * @param lpCreateStruct Pointer to a @c CREATESTRUCT structure, usually
773 * passed by the system to the window procedure in a @c WM_CREATE
776 * @remarks The function is intended to be called only inside a @c WM_CREATE
777 * or @c WM_NCCREATE message handler.
779 void CResizableLayout::MakeResizable(LPCREATESTRUCT lpCreateStruct
)
781 if (lpCreateStruct
->style
& WS_CHILD
)
784 CWnd
* pWnd
= GetResizableWnd();
786 #if (_WIN32_WINNT >= 0x0501 && !defined(RSZLIB_NO_XP_DOUBLE_BUFFER))
787 // enable double-buffering on supported platforms
788 pWnd
->ModifyStyleEx(0, WS_EX_COMPOSITED
);
791 if (!(lpCreateStruct
->style
& WS_THICKFRAME
))
793 // set resizable style
794 pWnd
->ModifyStyle(DS_MODALFRAME
, WS_THICKFRAME
);
796 CRect
rect(CPoint(lpCreateStruct
->x
, lpCreateStruct
->y
),
797 CSize(lpCreateStruct
->cx
, lpCreateStruct
->cy
));
798 pWnd
->SendMessage(WM_NCCALCSIZE
, FALSE
, (LPARAM
)&rect
);
799 // adjust size to reflect new style
800 ::AdjustWindowRectEx(&rect
, pWnd
->GetStyle(),
801 ::IsMenu(pWnd
->GetMenu()->GetSafeHmenu()), pWnd
->GetExStyle());
802 pWnd
->SetWindowPos(NULL
, 0, 0, rect
.Width(), rect
.Height(),
803 SWP_NOSENDCHANGING
|SWP_NOMOVE
|SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOREPOSITION
);
805 lpCreateStruct
->cx
= rect
.Width();
806 lpCreateStruct
->cy
= rect
.Height();
811 * This function should be called inside the parent window @c WM_NCCALCSIZE
812 * message handler to help eliminate flickering.
814 * @param bAfterDefault Flag that specifies wether the call is made before
815 * or after the default handler
816 * @param lpncsp Pointer to the @c NCCALCSIZE_PARAMS structure that is
817 * passed to the message handler
818 * @param lResult Reference to the result of the message handler.
819 * It contains the default handler result on input and the value to
820 * return from the window procedure on output.
822 * @remarks This function fixes the annoying flickering effect that is
823 * visible when resizing the top or left edges of the window
824 * (at least on a "left to right" Windows localized version).
826 void CResizableLayout::HandleNcCalcSize(BOOL bAfterDefault
, LPNCCALCSIZE_PARAMS lpncsp
, LRESULT
&lResult
)
828 // prevent useless complication when size is not changing
829 // prevent recursion when resetting the window region (see below)
830 if ((lpncsp
->lppos
->flags
& SWP_NOSIZE
)
831 #if (_WIN32_WINNT >= 0x0501)
839 // save a copy before default handler gets called
840 m_rectClientBefore
= lpncsp
->rgrc
[2];
842 else // after default WM_NCCALCSIZE msg processing
846 // default handler already uses an advanced validation policy, give up
849 // default calculated client rect
850 RECT
&rectClientAfter
= lpncsp
->rgrc
[0];
852 // intersection between old and new client area is to be preserved
853 // set source and destination rects to this intersection
854 RECT
&rectPreserve
= lpncsp
->rgrc
[1];
855 ::IntersectRect(&rectPreserve
, &rectClientAfter
, &m_rectClientBefore
);
856 lpncsp
->rgrc
[2] = rectPreserve
;
858 lResult
= WVR_VALIDRECTS
;
860 // FIX: window region must be updated before the result of the
861 // WM_NCCALCSIZE message gets processed by the system,
862 // otherwise the old window region will clip the client
863 // area during the preservation process.
864 // This is especially evident on WinXP when the non-client
865 // area is rendered with Visual Styles enabled and the
866 // windows have a non rectangular region.
867 #if (_WIN32_WINNT >= 0x0501)
868 //! @todo change rt check to only if themed frame. what about custom wnd region?
869 if (real_WIN32_WINNT
>= 0x0501)
871 CWnd
* pWnd
= GetResizableWnd();
872 DWORD dwStyle
= pWnd
->GetStyle();
873 if ((dwStyle
& (WS_CAPTION
|WS_MAXIMIZE
)) == WS_CAPTION
)
875 m_bNoRecursion
= TRUE
;
876 pWnd
->SetWindowRgn(NULL
, FALSE
);
877 m_bNoRecursion
= FALSE
;