////////////////// // Move desired rectangle by a given vector amount. // Call this when a sizer bar tells you it has moved. // void CWinMgr::MoveRect(WINRECT* pwrcMove, POINT ptMove, HWND pParentWnd) { assert(pwrcMove); WINRECT* prev = pwrcMove->Prev(); assert(prev); WINRECT* next = pwrcMove->Next(); assert(next); BOOL bIsRow = pwrcMove->Parent()->IsRowGroup(); RECT& rcNext = next->GetRect(); RECT& rcPrev = prev->GetRect(); if (bIsRow) { // a row can only be moved up or down ptMove.x = 0; rcPrev.bottom += ptMove.y; rcNext.top += ptMove.y; } else { // a column can only be moved left or right ptMove.y = 0; rcPrev.right += ptMove.x; rcNext.left += ptMove.x; } OffsetRect(pwrcMove->GetRect(), ptMove); if (prev->IsGroup()) CalcGroup(prev, pParentWnd); if (next->IsGroup()) CalcGroup(next, pParentWnd); }
////////////////// // Move desired rectangle by a given vector amount. // Call this when a sizer bar tells you it has moved. // void CWinMgr::MoveRect(WINRECT* pwrcMove, CPoint ptMove, CWnd* pParentWnd) { ASSERT(pwrcMove); WINRECT* prev = pwrcMove->Prev(); ASSERT(prev); WINRECT* next = pwrcMove->Next(); ASSERT(next); BOOL bIsRow = pwrcMove->Parent()->IsRowGroup(); CRect& rcNext = next->GetRect(); CRect& rcPrev = prev->GetRect(); if (bIsRow) { // a row can only be moved up or down ptMove.x = 0; rcPrev.bottom += ptMove.y; rcNext.top += ptMove.y; } else { // a column can only be moved left or right ptMove.y = 0; rcPrev.right += ptMove.x; rcNext.left += ptMove.x; } pwrcMove->GetRect() += ptMove; if (prev->IsGroup()) CalcGroup(prev, pParentWnd); if (next->IsGroup()) CalcGroup(next, pParentWnd); }
////////////////// // Position all the rects so they're as wide/high as the total and follow one // another; ie, are adjacent. This operation leaves the height (rows) and // width (columns) unaffected. For rows, set each row's width to rcTotal and // one below the other; for columns, set each column as tall as rcTotal and // each to the right of the previous. // void CWinMgr::PositionRects(WINRECT* pGroup, const CRect& rcTotal, BOOL bRow) { LONG xoryPos = bRow ? rcTotal.top : rcTotal.left; CWinGroupIterator it; for (it=pGroup; it; it.Next()) { WINRECT* wrc = it; CRect& rc = wrc->GetRect(); if (bRow) { // for ROWS: LONG height = rc.Height(); // height of row = total height rc.top = xoryPos; // top = running yPos rc.bottom = rc.top + height; // ... rc.left = rcTotal.left; // ... rc.right = rcTotal.right; // ... xoryPos += height; // increment yPos } else { // for COLS: LONG width = rc.Width(); // width = total width rc.left = xoryPos; // left = running xPos rc.right = rc.left + width; // ... rc.top = rcTotal.top; // ... rc.bottom = rcTotal.bottom; // ... xoryPos += width; // increment xPos } } }
////////////////// // Set each control's tofit (desired) size to current size. Useful for // dialogs, to "remember" the current sizes as desired size. // void CWinMgr::InitToFitSizeFromCurrent(CWnd* pWnd) { ASSERT(pWnd); ASSERT(m_map); GetWindowPositions(pWnd); for (WINRECT* w = m_map; !w->IsEnd(); w++) { if (w->Type()==WRCT_TOFIT && !w->IsGroup()) { w->SetToFitSize(w->GetRect().Size()); } } }
////////////////// // Set each control's tofit (desired) size to current size. Useful for // dialogs, to "remember" the current sizes as desired size. // void CWinMgr::InitToFitSizeFromCurrent(HWND hWnd) { assert(hWnd); assert(m_map); GetWindowPositions(hWnd); for (WINRECT* w = m_map; !w->IsEnd(); ++w) { if (w->Type()==WRCT_TOFIT && !w->IsGroup()) { w->SetToFitSize(RectToSize(w->GetRect())); } } }
////////////////// // Get size information for a single entry (WINRECT). Returns size info in // the SIZEINFO argument. For a group, calculate size info as aggregate of // subentries. // void CWinMgr::OnGetSizeInfo(SIZEINFO& szi, WINRECT* wrc, CWnd* pWnd) { szi.szMin = SIZEZERO; // default min size = zero szi.szMax = SIZEMAX; // default max size = infinite szi.szDesired = wrc->GetRect().Size(); // default desired size = current if (wrc->IsGroup()) { // For groups, calculate min, max, desired size as aggregate of children szi.szDesired = SIZEZERO; BOOL bRow = wrc->IsRowGroup(); CWinGroupIterator it; for (it=wrc; it; it.Next()) { WINRECT* wrc2 = it; SIZEINFO szi2; OnGetSizeInfo(szi2, wrc2, pWnd); if (bRow) { szi.szMin.cx = max(szi.szMin.cx, szi2.szMin.cx); szi.szMin.cy += szi2.szMin.cy; szi.szMax.cx = min(szi.szMax.cx, szi2.szMax.cx); szi.szMax.cy = min(szi.szMax.cy + szi2.szMax.cy, _INFINITY); szi.szDesired.cx = max(szi.szDesired.cx, szi2.szDesired.cx); szi.szDesired.cy += szi2.szDesired.cy; } else { szi.szMin.cx += szi2.szMin.cx; szi.szMin.cy = max(szi.szMin.cy, szi2.szMin.cy); szi.szMax.cx = min(szi.szMax.cx + szi2.szMax.cx, _INFINITY); szi.szMax.cy = min(szi.szMax.cy, szi2.szMax.cy); szi.szDesired.cx += szi2.szDesired.cx; szi.szDesired.cy = max(szi.szDesired.cy, szi2.szDesired.cy); } } // Add margins. int w2,h2; wrc->GetMargins(w2,h2); // get margins w2<<=1; h2<<=1; // double szi.szMin.cx += max(0,w2); // negative margins ==> don't include in min szi.szMin.cy += max(0,h2); // ditto szi.szDesired.cx += abs(w2); // for desired size, use abs vallue szi.szDesired.cy += abs(h2); // ditto } else { // not a group WINRECT* parent = wrc->Parent(); ASSERT(parent); CRect& rcParent = parent->GetRect(); BOOL bRow = parent->IsRowGroup(); int hw, hwMin, hwTotal, pct; switch (wrc->Type()) { case WRCT_FIXED: hw = hwMin = wrc->GetParam(); // ht/wid is parameter if (hw<0) { // if fixed val is negative: hw = -hw; // use absolute val for desired.. hwMin = 0; // ..and zero for minimum } if (bRow) { szi.szMax.cy = szi.szDesired.cy = hw; szi.szMin.cy = hwMin; } else { szi.szMax.cx = szi.szDesired.cx = hw; szi.szMin.cx = hwMin; } break; case WRCT_PCT: pct = wrc->GetParam(); ASSERT(0<pct && pct<100); hwTotal = bRow ? rcParent.Height() : rcParent.Width(); hw = (hwTotal * pct) / 100; szi.szDesired = bRow ? CSize(rcParent.Width(), hw) : CSize(hw, rcParent.Height()); break; case WRCT_TOFIT: if (wrc->HasToFitSize()) { szi.szDesired = wrc->GetToFitSize(); } break; case WRCT_REST: break; default: ASSERT(FALSE); } // If the entry is a window, send message to get min/max/tofit size. // Only set tofit size if type is TOFIT. // if (wrc->IsWindow() && pWnd) { CWnd* pChild = pWnd->GetDlgItem(wrc->GetID()); if (pChild) { if (!pChild->IsWindowVisible() && pWnd->IsWindowVisible()) { // parent visible but child not ==> tofit size is zero // important so hidden windows use no space szi.szDesired = SIZEZERO; } else { szi.szAvail = rcParent.Size(); SendGetSizeInfo(szi, pWnd, wrc->GetID()); } } } szi.szDesired = maxsize(minsize(szi.szDesired,szi.szMax), szi.szMin); } }
////////////////// // User pressed mouse: intialize and enter drag state // void CSizerBar::OnLButtonDown(UINT nFlags, CPoint pt) { m_bDragging=TRUE; m_ptOriginal = m_ptPrevious = Rectify(pt); GetWindowRect(&m_rcBar); // bar location in screen coords DrawBar(); // draw it SetCapture(); // all mouse messages are MINE m_hwndPrevFocus = ::SetFocus(m_hWnd); // set focus to me to get Escape key ASSERT(m_pWinMgr); CWinMgr& wm = *m_pWinMgr; // get WINRECTs on either side of me WINRECT* pwrcSizeBar = wm.FindRect(GetDlgCtrlID()); ASSERT(pwrcSizeBar); WINRECT* prev = pwrcSizeBar->Prev(); ASSERT(prev); WINRECT* next = pwrcSizeBar->Next(); ASSERT(next); // get rectangles on eithr side of me CRect rcPrev = prev->GetRect(); CRect rcNext = next->GetRect(); // get parent window CWnd * pParentWnd = GetParent(); ASSERT(pParentWnd); // Get size info for next/prev rectangles, so I know what the min/max // sizes are and don't violate them. Max size never tested. SIZEINFO szi; wm.OnGetSizeInfo(szi, prev, pParentWnd); CRect rcPrevMin(rcPrev.TopLeft(),szi.szMin); CRect rcPrevMax(rcPrev.TopLeft(),szi.szMax); wm.OnGetSizeInfo(szi, next, pParentWnd); CRect rcNextMin(rcNext.BottomRight()-szi.szMin, rcNext.BottomRight()); CRect rcNextMax(rcNext.BottomRight()-szi.szMax, rcNext.BottomRight()); // Initialize m_rcConstrain. This is the box the user is allowed to move // the sizer bar in. Can't go outside of this--would violate min/max // constraints of windows on either side. m_rcConstrain.SetRect( max(rcPrevMin.right, rcNextMax.left), max(rcPrevMin.bottom,rcNextMax.top), min(rcPrevMax.right, rcNextMin.left), min(rcPrevMax.bottom,rcNextMin.top)); // convert to my client coords pParentWnd->ClientToScreen(&m_rcConstrain); ScreenToClient(&m_rcConstrain); // Now adjust m_rcConstrain for the fact the bar is not a pure line, but // has solid width -- so I have to make a little bigger/smaller according // to the offset of mouse coords within the sizer bar rect iteself. ClientToScreen(&pt); m_rcConstrain.SetRect(m_rcConstrain.TopLeft() + (pt - m_rcBar.TopLeft()), m_rcConstrain.BottomRight() - (m_rcBar.BottomRight()-pt)); }