////////////////// // 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); }
////////////////// // Initialize map: set up all the next/prev pointers. This converts the // linear array to a more convenient linked list. Called from END_WINDOW_MAP. // WINRECT* WINRECT::InitMap(WINRECT* pWinMap, WINRECT* parent) { assert(pWinMap); WINRECT* pwrc = pWinMap; // current table entry WINRECT* prev = NULL; // previous entry starts out none while (!pwrc->IsEndGroup()) { pwrc->prev=prev; pwrc->next=NULL; if (prev) prev->next = pwrc; prev = pwrc; if (pwrc->IsGroup()) { pwrc = InitMap(pwrc+1,pwrc); // recurse! Returns end-of-grp assert(pwrc->IsEndGroup()); } pwrc++; } // safety checks assert(pwrc->IsEndGroup()); assert(prev); assert(prev->next==NULL); return parent ? pwrc : NULL; }
////////////////// // 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 the parent of a given WINRECT. To find the parent, chase the prev // pointer to the start of the list, then take the item before that in // memory. // WINRECT* WINRECT::Parent() { WINRECT* pEntry; for (pEntry=this; pEntry->Prev(); pEntry=pEntry->Prev()) { ; // go backwards to the end } // the entry before the first child is the group WINRECT *parent = pEntry-1; assert(parent->IsGroup()); return parent; }
////////////////// // Calculate size/positions for a row or column group This is the main // algorithm. If a window is given, it's used to get the min/max size and // desired size for TOFIT types. // void CWinMgr::CalcGroup(WINRECT* pGroup, CWnd* pWnd) { // If this bombs, most likely the first entry in your map is not a group! ASSERT(pGroup && pGroup->IsGroup()); ASSERT(pWnd); // adjust total avail by margins CRect rcTotal = pGroup->GetRect(); int w,h; if (pGroup->GetMargins(w,h)) { w = min(abs(w), rcTotal.Width()/2); h = min(abs(h), rcTotal.Height()/2); rcTotal.DeflateRect(w,h); } BOOL bRow = pGroup->IsRowGroup(); // Is this a row group? // Running height or width: start with total int hwRemaining = bRow ? rcTotal.Height() : rcTotal.Width(); // First, set all rects to their minimum sizes. // This ensures that each rect gets its min size. CWinGroupIterator it; for (it=pGroup; it; it.Next()) { WINRECT* wrc = it; SIZEINFO szi; OnGetSizeInfo(szi, wrc, pWnd); int hwMin = bRow ? szi.szMin.cy : szi.szMin.cx; hwMin = min(hwMin, hwRemaining); // truncate wrc->SetHeightOrWidth(hwMin, bRow); // set hwRemaining -= hwMin; // decrement remaining height/width ASSERT(hwRemaining>=0); } // Now adjust all rects upward to desired size. Save REST rect for last. WINRECT* pRestRect = NULL; for (it=pGroup; it; it.Next()) { WINRECT* wrc = it; if (wrc->Type()==WRCT_REST) { ASSERT(pRestRect==NULL); // can only be one REST rect! pRestRect = wrc; // remember it } else { AdjustSize(wrc, bRow, hwRemaining, pWnd); } } ASSERT(hwRemaining>=0); // Adjust REST rect if any if (pRestRect) { AdjustSize(pRestRect, bRow, hwRemaining, pWnd); ASSERT(hwRemaining==0); } // All the sizes of the entries have been calculated, including // groups (but not their children). Now move all the rects so they're // adjacent to one another, without altering sizes. PositionRects(pGroup, rcTotal, bRow); // Finally, descend recursively into each subgroup. for (it=pGroup; it; it.Next()) { WINRECT* wrc = it; if (wrc->IsGroup()) CalcGroup(wrc, pWnd); // recurse! } }