// ResizableLayout.cpp: implementation of the CResizableLayout class. // ///////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2000-2001 by Paolo Messina // (http://www.geocities.com/ppescher - ppescher@yahoo.com) // // The contents of this file are subject to the Artistic License (the "License"). // You may not use this file except in compliance with the License. // You may obtain a copy of the License at: // http://www.opensource.org/licenses/artistic-license.html // // If you find this code useful, credits would be nice! // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ResizableLayout.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// #define BTS_TYPEMASK SS_TYPEMASK void CResizableLayout::AddAnchor(HWND hWnd, CSize sizeTypeTL, CSize sizeTypeBR) { CWnd* pParent = GetResizableWnd(); // child window must be valid ASSERT(::IsWindow(hWnd)); // must be child of parent window ASSERT(::IsChild(pParent->GetSafeHwnd(), hWnd)); // top-left anchor must be valid ASSERT(sizeTypeTL != NOANCHOR); // get control's window class CString st; GetClassName(hWnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); st.ReleaseBuffer(); // add the style 'clipsiblings' to a GroupBox // to avoid unnecessary repainting of controls inside if (st == "Button") { DWORD style = GetWindowLong(hWnd, GWL_STYLE); if ((style & BTS_TYPEMASK) == BS_GROUPBOX) SetWindowLong(hWnd, GWL_STYLE, style | WS_CLIPSIBLINGS); } // window classes that don't redraw client area correctly // when the hor scroll pos changes due to a resizing BOOL bHScroll = FALSE; if (st == "ListBox") bHScroll = TRUE; // window classes that need refresh when resized BOOL bRefresh = NeedsRefresh(hWnd); // get parent window's rect CRect rectParent; GetTotalClientRect(&rectParent); // and child control's rect CRect rectChild; ::GetWindowRect(hWnd, &rectChild); pParent->ScreenToClient(&rectChild); // go calculate margins CSize sizeMarginTL, sizeMarginBR; if (sizeTypeBR == NOANCHOR) sizeTypeBR = sizeTypeTL; // calculate margin for the top-left corner sizeMarginTL.cx = rectChild.left - rectParent.Width() * sizeTypeTL.cx / 100; sizeMarginTL.cy = rectChild.top - rectParent.Height() * sizeTypeTL.cy / 100; // calculate margin for the bottom-right corner sizeMarginBR.cx = rectChild.right - rectParent.Width() * sizeTypeBR.cx / 100; sizeMarginBR.cy = rectChild.bottom - rectParent.Height() * sizeTypeBR.cy / 100; // add to the list LayoutInfo layout(hWnd, sizeTypeTL, sizeMarginTL, sizeTypeBR, sizeMarginBR, bHScroll, bRefresh); // always add before callbacks m_arrLayout.InsertAt(m_iFirstCallback++, layout); } // one callback control cannot rely upon another callback control's // size and/or position (they're updated all together at the end) void CResizableLayout::AddAnchorCallback(UINT nCallbackID) { // add to the list LayoutInfo layout; layout.nCallbackID = nCallbackID; // callbacks added to tail (for efficiency) m_arrLayout.Add(layout); } BOOL CResizableLayout::ArrangeLayoutCallback(LayoutInfo& /*layout*/) { ASSERT(FALSE); // must be overridden, if callback is used return FALSE; // no output data } void CResizableLayout::ArrangeLayout() { CWnd* pParent = GetResizableWnd(); // get parent window's rect CRect rectParent; GetTotalClientRect(&rectParent); // init some vars int i, count = m_arrLayout.GetSize(); HDWP hdwp = BeginDeferWindowPos(count); for (i=0; iGetWindowRect(&rectChild); pParent->ScreenToClient(&rectChild); // calculate new top-left corner newrc.left = layout.sizeMarginTL.cx + rectParent.Width() * layout.sizeTypeTL.cx / 100; newrc.top = layout.sizeMarginTL.cy + rectParent.Height() * layout.sizeTypeTL.cy / 100; // calculate new bottom-right corner newrc.right = layout.sizeMarginBR.cx + rectParent.Width() * layout.sizeTypeBR.cx / 100; newrc.bottom = layout.sizeMarginBR.cy + rectParent.Height() * layout.sizeTypeBR.cy / 100; if (!newrc.EqualRect(&rectChild)) { if (layout.bAdjHScroll) { // needs repainting, due to horiz scrolling int diff = newrc.Width() - rectChild.Width(); int max = pWnd->GetScrollLimit(SB_HORZ); layout.bNeedRefresh = FALSE; if (max > 0 && pWnd->GetScrollPos(SB_HORZ) > max - diff) { layout.bNeedRefresh = TRUE; } } // set flags DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION; if (layout.bNeedRefresh) flags |= SWP_NOCOPYBITS; if (newrc.TopLeft() == rectChild.TopLeft()) flags |= SWP_NOMOVE; if (newrc.Size() == rectChild.Size()) flags |= SWP_NOSIZE; hdwp = DeferWindowPos(hdwp, layout.hWnd, NULL, newrc.left, newrc.top, newrc.Width(), newrc.Height(), flags); } } // go re-arrange child windows EndDeferWindowPos(hdwp); } void CResizableLayout::EnumAndClipChildWindow(HWND hWnd, CDC* pDC) { // obtain window position CRect rect; ::GetWindowRect(hWnd, &rect); GetResizableWnd()->ScreenToClient(&rect); pDC->DPtoLP(&rect); // use window region if any CRgn rgn; rgn.CreateRectRgn(0,0,0,0); if (COMPLEXREGION == ::GetWindowRgn(hWnd, rgn)) { rgn.OffsetRgn(rect.TopLeft()); } else { rgn.SetRectRgn(&rect); } // go clipping? if (LikesClipping(hWnd)) pDC->SelectClipRgn(&rgn, RGN_DIFF); else pDC->SelectClipRgn(&rgn, RGN_OR); } void CResizableLayout::ClipChildren(CDC *pDC) { // only clips anchored controls for (int i=0; iGetClientRect(lpRect); } BOOL CResizableLayout::NeedsRefresh(HWND hWnd) { // get window class CString st; GetClassName(hWnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); st.ReleaseBuffer(); // optimistic, no need to refresh BOOL bRefresh = FALSE; // window classes that need refresh when resized if (st == "Static") { DWORD style = GetWindowLong(hWnd, GWL_STYLE); switch (style & SS_TYPEMASK) { case SS_LEFT: case SS_CENTER: case SS_RIGHT: case SS_ENHMETAFILE: case SS_BITMAP: // word-wrapped text needs bRefresh bRefresh = TRUE; break; case SS_ICON: case SS_SIMPLE: case SS_ETCHEDHORZ: case SS_ETCHEDVERT: break; default: // centered images or text need bRefresh if (style & SS_CENTERIMAGE) bRefresh = TRUE; } } return bRefresh; } BOOL CResizableLayout::LikesClipping(HWND hWnd) { // check child type CString st; GetClassName(hWnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); st.ReleaseBuffer(); DWORD style = GetWindowLong(hWnd, GWL_STYLE); // skip windows that wants background repainted if (st == TOOLBARCLASSNAME && (style & TBSTYLE_TRANSPARENT)) return FALSE; if (st == "Button" && (style & BTS_TYPEMASK) == BS_GROUPBOX) return FALSE; if (st == "Static") { switch (style & SS_TYPEMASK) { case SS_BLACKRECT: case SS_GRAYRECT: case SS_WHITERECT: case SS_ETCHEDHORZ: case SS_ETCHEDVERT: case SS_BITMAP: break; case SS_ICON: case SS_ENHMETAFILE: if (style & SS_CENTERIMAGE) return FALSE; break; default: return FALSE; } } // assume the others like clipping return TRUE; }