Support unrar64.dll
[xy_vsfilter.git] / src / ui / ResizableLib / ResizableLayout.cpp
blobe1aef85f456c33865e075b3c3be28e291940ec50
1 // ResizableLayout.cpp: implementation of the CResizableLayout class.
2 //
3 /////////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2000-2002 by Paolo Messina
6 // (http://www.geocities.com/ppescher - ppescher@yahoo.com)
7 //
8 // The contents of this file are subject to the Artistic License (the "License").
9 // You may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at:
11 // http://www.opensource.org/licenses/artistic-license.html
13 // If you find this code useful, credits would be nice!
15 /////////////////////////////////////////////////////////////////////////////
17 #include "stdafx.h"
18 #include "commctrl.h"
19 #include "ResizableLayout.h"
20 #include "ResizableMsgSupport.inl"
22 #ifdef _DEBUG
23 #undef THIS_FILE
24 static char THIS_FILE[]=__FILE__;
25 #define new DEBUG_NEW
26 #endif
28 //////////////////////////////////////////////////////////////////////
29 // Construction/Destruction
30 //////////////////////////////////////////////////////////////////////
32 // In August 2002 Platform SDK, some guy at MS thought it was time to
33 // add the missing symbol BS_TYPEMASK, but forgot its original meaning
34 // and so now he's telling us not to use that symbol because its
35 // value is likely to change in the future SDK releases, including all
36 // the BS_* style bits in the mask, not just the button's type as the
37 // symbol's name suggests. So now we're forced to use another symbol!
38 #define _BS_TYPEMASK 0x0000000FL
40 void CResizableLayout::AddAnchor(HWND hWnd, CSize sizeTypeTL, CSize sizeTypeBR)
42 CWnd* pParent = GetResizableWnd();
44 // child window must be valid
45 ASSERT(::IsWindow(hWnd));
46 // must be child of parent window
47 // ASSERT(::IsChild(pParent->GetSafeHwnd(), hWnd));
48 // top-left anchor must be valid
49 ASSERT(sizeTypeTL != NOANCHOR);
51 // get control's window class
52 CString sClassName;
53 GetClassName(hWnd, sClassName.GetBufferSetLength(MAX_PATH), MAX_PATH);
54 sClassName.ReleaseBuffer();
56 // get parent window's rect
57 CRect rectParent;
58 GetTotalClientRect(&rectParent);
59 // and child control's rect
60 CRect rectChild;
61 ::GetWindowRect(hWnd, &rectChild);
62 ::MapWindowPoints(NULL, pParent->m_hWnd, (LPPOINT)&rectChild, 2);
64 // adjust position, if client area has been scrolled
65 rectChild.OffsetRect(-rectParent.TopLeft());
67 // go calculate margins
68 CSize sizeMarginTL, sizeMarginBR;
70 if (sizeTypeBR == NOANCHOR)
71 sizeTypeBR = sizeTypeTL;
73 // calculate margin for the top-left corner
75 sizeMarginTL.cx = rectChild.left - rectParent.Width() * sizeTypeTL.cx / 100;
76 sizeMarginTL.cy = rectChild.top - rectParent.Height() * sizeTypeTL.cy / 100;
78 // calculate margin for the bottom-right corner
80 sizeMarginBR.cx = rectChild.right - rectParent.Width() * sizeTypeBR.cx / 100;
81 sizeMarginBR.cy = rectChild.bottom - rectParent.Height() * sizeTypeBR.cy / 100;
83 // prepare the structure
84 LayoutInfo layout(hWnd, sizeTypeTL, sizeMarginTL,
85 sizeTypeBR, sizeMarginBR, sClassName);
87 // initialize resize properties (overridable)
88 InitResizeProperties(layout);
90 // must not be already there!
91 // (this is probably due to a duplicate call to AddAnchor)
92 POSITION pos;
93 ASSERT(!m_mapLayout.Lookup(hWnd, pos));
95 // add to the list and the map
96 pos = m_listLayout.AddTail(layout);
97 m_mapLayout.SetAt(hWnd, pos);
100 void CResizableLayout::AddAnchorCallback(UINT nCallbackID)
102 // one callback control cannot rely upon another callback control's
103 // size and/or position (they're updated all together at the end)
104 // it can however use a non-callback control, which is updated before
106 // add to the list
107 LayoutInfo layout;
108 layout.nCallbackID = nCallbackID;
109 m_listLayoutCB.AddTail(layout);
112 BOOL CResizableLayout::ArrangeLayoutCallback(CResizableLayout::LayoutInfo& /*layout*/)
114 ASSERT(FALSE);
115 // must be overridden, if callback is used
117 return FALSE; // no output data
120 void CResizableLayout::ArrangeLayout()
122 // common vars
123 UINT uFlags;
124 LayoutInfo layout;
125 CRect rectParent, rectChild;
126 GetTotalClientRect(&rectParent); // get parent window's rect
127 int count = m_listLayout.GetCount();
128 int countCB = m_listLayoutCB.GetCount();
130 // reposition child windows
131 HDWP hdwp = ::BeginDeferWindowPos(count + countCB);
133 POSITION pos = m_listLayout.GetHeadPosition();
134 while (pos != NULL)
136 // get layout info
137 layout = m_listLayout.GetNext(pos);
139 // calculate new child's position, size and flags for SetWindowPos
140 CalcNewChildPosition(layout, rectParent, rectChild, uFlags);
142 // only if size or position changed
143 if ((uFlags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE))
145 hdwp = ::DeferWindowPos(hdwp, layout.hWnd, NULL, rectChild.left,
146 rectChild.top, rectChild.Width(), rectChild.Height(), uFlags);
150 // for callback items you may use GetAnchorPosition to know the
151 // new position and size of a non-callback item after resizing
153 pos = m_listLayoutCB.GetHeadPosition();
154 while (pos != NULL)
156 // get layout info
157 layout = m_listLayoutCB.GetNext(pos);
158 // request layout data
159 if (!ArrangeLayoutCallback(layout))
160 continue;
162 // calculate new child's position, size and flags for SetWindowPos
163 CalcNewChildPosition(layout, rectParent, rectChild, uFlags);
165 // only if size or position changed
166 if ((uFlags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE))
168 hdwp = ::DeferWindowPos(hdwp, layout.hWnd, NULL, rectChild.left,
169 rectChild.top, rectChild.Width(), rectChild.Height(), uFlags);
173 // finally move all the windows at once
174 ::EndDeferWindowPos(hdwp);
177 void CResizableLayout::ClipChildWindow(const CResizableLayout::LayoutInfo& layout,
178 CRgn* pRegion)
180 // obtain window position
181 CRect rect;
182 ::GetWindowRect(layout.hWnd, &rect);
183 ::MapWindowPoints(NULL, GetResizableWnd()->m_hWnd, (LPPOINT)&rect, 2);
185 // use window region if any
186 CRgn rgn;
187 rgn.CreateRectRgn(0,0,0,0);
188 switch (::GetWindowRgn(layout.hWnd, rgn))
190 case COMPLEXREGION:
191 case SIMPLEREGION:
192 rgn.OffsetRgn(rect.TopLeft());
193 break;
195 default:
196 rgn.SetRectRgn(&rect);
199 // get the clipping property
200 BOOL bClipping = layout.properties.bAskClipping ?
201 LikesClipping(layout) : layout.properties.bCachedLikesClipping;
203 // modify region accordingly
204 if (bClipping)
205 pRegion->CombineRgn(pRegion, &rgn, RGN_DIFF);
206 else
207 pRegion->CombineRgn(pRegion, &rgn, RGN_OR);
210 void CResizableLayout::GetClippingRegion(CRgn* pRegion)
212 CWnd* pWnd = GetResizableWnd();
214 // System's default clipping area is screen's size,
215 // not enough for max track size, for example:
216 // if screen is 1024 x 768 and resizing border is 4 pixels,
217 // maximized size is 1024+4*2=1032 x 768+4*2=776,
218 // but max track size is 4 pixels bigger 1036 x 780 (don't ask me why!)
219 // So, if you resize the window to maximum size, the last 4 pixels
220 // are clipped out by the default clipping region, that gets created
221 // as soon as you call clipping functions (my guess).
223 // reset clipping region to the whole client area
224 CRect rect;
225 pWnd->GetClientRect(&rect);
226 pRegion->CreateRectRgnIndirect(&rect);
228 // clip only anchored controls
229 LayoutInfo layout;
230 POSITION pos = m_listLayout.GetHeadPosition();
231 while (pos != NULL)
233 // get layout info
234 layout = m_listLayout.GetNext(pos);
236 if (::IsWindowVisible(layout.hWnd))
237 ClipChildWindow(layout, pRegion);
239 pos = m_listLayoutCB.GetHeadPosition();
240 while (pos != NULL)
242 // get layout info
243 layout = m_listLayoutCB.GetNext(pos);
244 // request data
245 if (!ArrangeLayoutCallback(layout))
246 continue;
248 if (::IsWindowVisible(layout.hWnd))
249 ClipChildWindow(layout, pRegion);
252 // fix for RTL layouts (1 pixel of horz offset)
253 if (pWnd->GetExStyle() & 0x00400000L/*WS_EX_LAYOUTRTL*/)
254 pRegion->OffsetRgn(-1,0);
257 void CResizableLayout::EraseBackground(CDC* pDC)
259 HWND hWnd = GetResizableWnd()->GetSafeHwnd();
261 // retrieve the background brush
262 HBRUSH hBrush = NULL;
264 // is this a dialog box?
265 // (using class atom is quickier than using the class name)
266 ATOM atomWndClass = (ATOM)::GetClassLong(hWnd, GCW_ATOM);
267 if (atomWndClass == (ATOM)0x8002)
269 // send a message to the dialog box
270 hBrush = (HBRUSH)::SendMessage(hWnd, WM_CTLCOLORDLG,
271 (WPARAM)pDC->GetSafeHdc(), (LPARAM)hWnd);
273 else
275 // take the background brush from the window's class
276 hBrush = (HBRUSH)::GetClassLongPtr(hWnd, GCL_HBRBACKGROUND);
279 // fill the clipped background
280 CRgn rgn;
281 GetClippingRegion(&rgn);
283 ::FillRgn(pDC->GetSafeHdc(), rgn, hBrush);
286 // support legacy code (will disappear in future versions)
287 void CResizableLayout::ClipChildren(CDC* pDC)
289 CRgn rgn;
290 GetClippingRegion(&rgn);
291 // the clipping region is in device units
292 rgn.OffsetRgn(-pDC->GetWindowOrg());
293 pDC->SelectClipRgn(&rgn);
296 void CResizableLayout::GetTotalClientRect(LPRECT lpRect)
298 GetResizableWnd()->GetClientRect(lpRect);
301 BOOL CResizableLayout::NeedsRefresh(const CResizableLayout::LayoutInfo& layout,
302 const CRect& rectOld, const CRect& rectNew)
304 if (layout.bMsgSupport)
306 REFRESHPROPERTY refresh;
307 refresh.rcOld = rectOld;
308 refresh.rcNew = rectNew;
309 if (Send_NeedsRefresh(layout.hWnd, &refresh))
310 return refresh.bNeedsRefresh;
313 int nDiffWidth = (rectNew.Width() - rectOld.Width());
314 int nDiffHeight = (rectNew.Height() - rectOld.Height());
316 // is the same size?
317 if (nDiffWidth == 0 && nDiffHeight == 0)
318 return FALSE;
320 // optimistic, no need to refresh
321 BOOL bRefresh = FALSE;
323 // window classes that need refresh when resized
324 if (layout.sWndClass == WC_STATIC)
326 DWORD style = ::GetWindowLong(layout.hWnd, GWL_STYLE);
328 switch (style & SS_TYPEMASK)
330 case SS_LEFT:
331 case SS_CENTER:
332 case SS_RIGHT:
333 // word-wrapped text
334 bRefresh = bRefresh || (nDiffWidth != 0);
335 // vertically centered text
336 if (style & SS_CENTERIMAGE)
337 bRefresh = bRefresh || (nDiffHeight != 0);
338 break;
340 case SS_LEFTNOWORDWRAP:
341 // text with ellipsis
342 if (style & SS_ELLIPSISMASK)
343 bRefresh = bRefresh || (nDiffWidth != 0);
344 // vertically centered text
345 if (style & SS_CENTERIMAGE)
346 bRefresh = bRefresh || (nDiffHeight != 0);
347 break;
349 case SS_ENHMETAFILE:
350 case SS_BITMAP:
351 case SS_ICON:
352 // images
353 case SS_BLACKFRAME:
354 case SS_GRAYFRAME:
355 case SS_WHITEFRAME:
356 case SS_ETCHEDFRAME:
357 // and frames
358 bRefresh = TRUE;
359 break;
363 // window classes that don't redraw client area correctly
364 // when the hor scroll pos changes due to a resizing
365 BOOL bHScroll = FALSE;
366 if (layout.sWndClass == WC_LISTBOX)
367 bHScroll = TRUE;
369 // fix for horizontally scrollable windows
370 if (bHScroll && (nDiffWidth > 0))
372 // get max scroll position
373 SCROLLINFO info;
374 info.cbSize = sizeof(SCROLLINFO);
375 info.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
376 if (::GetScrollInfo(layout.hWnd, SB_HORZ, &info))
378 // subtract the page size
379 info.nMax -= __max(info.nPage-1,0);
382 // resizing will cause the text to scroll on the right
383 // because the scrollbar is going beyond the right limit
384 if ((info.nMax > 0) && (info.nPos + nDiffWidth > info.nMax))
386 // needs repainting, due to horiz scrolling
387 bRefresh = TRUE;
391 return bRefresh;
394 BOOL CResizableLayout::LikesClipping(const CResizableLayout::LayoutInfo& layout)
396 if (layout.bMsgSupport)
398 CLIPPINGPROPERTY clipping;
399 if (Send_LikesClipping(layout.hWnd, &clipping))
400 return clipping.bLikesClipping;
403 DWORD style = ::GetWindowLong(layout.hWnd, GWL_STYLE);
405 // skip windows that wants background repainted
406 if (layout.sWndClass == TOOLBARCLASSNAME && (style & TBSTYLE_TRANSPARENT))
407 return FALSE;
408 else if (layout.sWndClass == WC_BUTTON)
410 CRect rect;
411 switch (style & _BS_TYPEMASK)
413 case BS_GROUPBOX:
414 return FALSE;
416 case BS_OWNERDRAW:
417 // ownerdraw buttons must return correct hittest code
418 // to notify their transparency to the system and this library
419 ::GetWindowRect(layout.hWnd, &rect);
420 if ( HTTRANSPARENT == ::SendMessage(layout.hWnd,
421 WM_NCHITTEST, 0, MAKELPARAM(rect.left, rect.top)) )
422 return FALSE;
423 break;
425 return TRUE;
427 else if (layout.sWndClass == WC_STATIC)
429 switch (style & SS_TYPEMASK)
431 case SS_LEFT:
432 case SS_CENTER:
433 case SS_RIGHT:
434 case SS_SIMPLE:
435 case SS_LEFTNOWORDWRAP:
436 // text
437 case SS_BLACKRECT:
438 case SS_GRAYRECT:
439 case SS_WHITERECT:
440 // filled rects
441 case SS_ETCHEDHORZ:
442 case SS_ETCHEDVERT:
443 // etched lines
444 case SS_BITMAP:
445 // bitmaps
446 return TRUE;
447 break;
449 case SS_ICON:
450 case SS_ENHMETAFILE:
451 if (style & SS_CENTERIMAGE)
452 return FALSE;
453 return TRUE;
454 break;
456 default:
457 return FALSE;
461 // assume the others like clipping
462 return TRUE;
465 void CResizableLayout::CalcNewChildPosition(const CResizableLayout::LayoutInfo& layout,
466 const CRect &rectParent, CRect &rectChild, UINT& uFlags)
468 CWnd* pParent = GetResizableWnd();
470 ::GetWindowRect(layout.hWnd, &rectChild);
471 ::MapWindowPoints(NULL, pParent->m_hWnd, (LPPOINT)&rectChild, 2);
473 CRect rectNew;
475 // calculate new top-left corner
476 rectNew.left = layout.sizeMarginTL.cx + rectParent.Width() * layout.sizeTypeTL.cx / 100;
477 rectNew.top = layout.sizeMarginTL.cy + rectParent.Height() * layout.sizeTypeTL.cy / 100;
479 // calculate new bottom-right corner
480 rectNew.right = layout.sizeMarginBR.cx + rectParent.Width() * layout.sizeTypeBR.cx / 100;
481 rectNew.bottom = layout.sizeMarginBR.cy + rectParent.Height() * layout.sizeTypeBR.cy / 100;
483 // adjust position, if client area has been scrolled
484 rectNew.OffsetRect(rectParent.TopLeft());
486 // get the refresh property
487 BOOL bRefresh = layout.properties.bAskRefresh ?
488 NeedsRefresh(layout, rectChild, rectNew) : layout.properties.bCachedNeedsRefresh;
490 // set flags
491 uFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION;
492 if (bRefresh)
493 uFlags |= SWP_NOCOPYBITS;
494 if (rectNew.TopLeft() == rectChild.TopLeft())
495 uFlags |= SWP_NOMOVE;
496 if (rectNew.Size() == rectChild.Size())
497 uFlags |= SWP_NOSIZE;
499 // update rect
500 rectChild = rectNew;
503 void CResizableLayout::InitResizeProperties(CResizableLayout::LayoutInfo &layout)
505 // check if custom window supports this library
506 // (properties must be correctly set by the window)
507 layout.bMsgSupport = Send_QueryProperties(layout.hWnd, &layout.properties);
509 // default properties
510 if (!layout.bMsgSupport)
512 // clipping property is assumed as static
513 layout.properties.bAskClipping = FALSE;
514 layout.properties.bCachedLikesClipping = LikesClipping(layout);
515 // refresh property is assumed as dynamic
516 layout.properties.bAskRefresh = TRUE;