// ============================================================================= // Class Implementation : COXLayoutManager // ============================================================================= // // Source file : OXLayoutManager.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXLayoutManager.h" #include // For WM_INITIALUPDATE definition #ifndef __OXMFCIMPL_H__ #if _MFC_VER >= 0x0700 #if _MFC_VER >= 1400 #include #endif #include <..\src\mfc\afximpl.h> #else #include <..\src\afximpl.h> #endif #define __OXMFCIMPL_H__ #endif #include "UTB64Bit.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNCREATE(COXLayoutManager, CObject) ///////////////////////////////////////////////////////////////////////////// // maps between single side parameter and array index in sc[] static int OX_LMS_TO_INDEX[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3 }; static int OX_INDEX_TO_LMS[] = { OX_LMS_TOP, OX_LMS_BOTTOM, OX_LMS_LEFT, OX_LMS_RIGHT }; // side index #define OX_LMSI_TOP 0 #define OX_LMSI_BOTTOM 1 #define OX_LMSI_HORZ 1 // if nSideIndex > OX_LMSI_HORZ, ... #define OX_LMSI_LEFT 2 #define OX_LMSI_RIGHT 3 #define OX_LMSI_OPPOSITE(nSideIndex) (nSideIndex > OX_LMSI_HORZ ? 5 : 1) - nSideIndex // return values of COXLayoutManager::CalcLayout() #define OX_LM_SIZING_OK 0 #define OX_LM_SIZING_FAIL 1 #define OX_LM_SIZING_NEEDLARGERCX 2 #define OX_LM_SIZING_NEEDLARGERCY 4 #define OX_LM_SIZING_NEEDSMALLERCX 8 #define OX_LM_SIZING_NEEDSMALLERCY 16 ///////////////////////////////////////////////////////////////////////////// // Definition of static members CMap COXLayoutManager::m_allLayoutManagers; // --- A map of all the containers that have been subclassed and that are associated with // a COXLayoutManager object. This object will handle the windows messages first // Data members ------------------------------------------------------------- // protected: // // CWnd* m_pContainerWnd; the container window // int m_nBase; the current fraction base // UINT m_nMinMaxCount; used to speed up min/max checking, if non-zero, // min/max will be checked when resizing // int m_cx; for storing current new width of the container window // int m_cy; for storing current new height of the container window // int m_cxMin; minimum width of the container window // int m_cxMax; maximum width of the container window // int m_cyMin; minimum height of the container window // int m_cyMax; maximum height of the container window // CArray m_wcTable; // window constraints of all child windows // Member functions --------------------------------------------------------- COXLayoutManager::COXLayoutManager(CWnd* pContainerWnd /* = NULL */) : m_pContainerWnd(NULL), m_nBase(100), m_nMinMaxCount(0), m_hWnd(NULL), m_pfnSuper(NULL) { if (pContainerWnd != NULL) Attach(pContainerWnd); } COXLayoutManager::~COXLayoutManager() { RemoveAllChildren(); UnsubclassContainer(); } void COXLayoutManager::Attach(CWnd* pContainerWnd) { ASSERT(m_pContainerWnd == NULL); ASSERT(pContainerWnd); RemoveAllChildren(); m_pContainerWnd = pContainerWnd; VERIFY(SubclassContainer(pContainerWnd)); } void COXLayoutManager::Detach() { ASSERT(m_pContainerWnd); RemoveAllChildren(); m_pContainerWnd = NULL; UnsubclassContainer(); } int COXLayoutManager::AddChild(UINT nChildWnd, BOOL bSetDefaultConstraints /* = TRUE */) { // we do not validate ID here in case child window not created yet, instead we'll do it // at run-time when calculating coordinates // cannot be topmost window if (nChildWnd == 0) return -1; // already added previously int nIndex = PtrToInt(GetChildIndex(nChildWnd)); if (nIndex == -1) { COXWndConstraint* pWC = new COXWndConstraint(nChildWnd); nIndex = PtrToInt(m_wcTable.Add(pWC)); } if (bSetDefaultConstraints) SetDefaultConstraint(nChildWnd); return nIndex; } void COXLayoutManager::AddAllChildren(BOOL bSetDefaultConstraints /* = TRUE */) { ASSERT(m_pContainerWnd); CWnd* pChild = m_pContainerWnd->GetWindow(GW_CHILD); for (; pChild; pChild = pChild->GetNextWindow()) AddChild(pChild, bSetDefaultConstraints); } BOOL COXLayoutManager::RemoveChild(UINT nChildWnd) { ASSERT(nChildWnd); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) return FALSE; // look for dependents of nChildWnd for (int i = 0; i < m_wcTable.GetSize(); i++) { COXWndConstraint* pWC = m_wcTable[i]; for (int j = 0; j < 4; j++) { COXSideConstraint* pSC = &pWC->sc[j]; if (pSC->nBaseWnd == nChildWnd) { TRACE2("COXLayoutManager::RemoveChild(): failed to remove child %d, window %d depends on it.\r\n", nChildWnd, pWC->nID); return FALSE; } } } COXWndConstraint* pWC = m_wcTable[nIndex]; if (pWC->bHasMinMax) m_nMinMaxCount--; delete pWC; m_wcTable.RemoveAt(nIndex); ResetContainerMinMax(); return TRUE; } void COXLayoutManager::RemoveAllChildren() { for (int i = 0; i < m_wcTable.GetSize(); i++) delete m_wcTable[i]; m_wcTable.RemoveAll(); m_nMinMaxCount = 0; ResetContainerMinMax(); } BOOL COXLayoutManager::SetConstraint(UINT nChildWnd, int nSide, int nType, int nOffset /* = 0 */, UINT nBaseWnd /* = 0 */) { ASSERT(nChildWnd); ASSERT(nType == OX_LMT_SAME || nType == OX_LMT_OPPOSITE || nType == OX_LMT_POSITION); // we add nBaseWnd first to linearlize window dependency better (faster when calculating) if (nBaseWnd && GetChildIndex(nBaseWnd) == -1) AddChild(nBaseWnd, FALSE); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) { nIndex = AddChild(nChildWnd, FALSE); if (nIndex == -1) return FALSE; } for (int i = 0; i < 4; i++) { int nSingleSide = OX_INDEX_TO_LMS[i]; if (nSide & nSingleSide) { COXSideConstraint* pSC = &m_wcTable[nIndex]->sc[i]; BOOL bFlipSide= ((nSingleSide & OX_LMS_MAJOR) && (nType == OX_LMT_OPPOSITE)) || ((nSingleSide & OX_LMS_MINOR) && (nType == OX_LMT_SAME)); int nDefOffsetPos = (nType == OX_LMT_POSITION ? m_nBase : OX_LMOFFSET_ANY); pSC->nBaseWnd = nBaseWnd; pSC->nOffset1 = bFlipSide ? nDefOffsetPos : nOffset; pSC->nOffset2 = bFlipSide ? nOffset : nDefOffsetPos; } } ResetContainerMinMax(); return TRUE; } BOOL COXLayoutManager::SetMinMax(UINT nChildWnd, CSize sizeMin, CSize sizeMax /* CSize(0,0) */) { ASSERT(nChildWnd); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) { nIndex = AddChild(nChildWnd, FALSE); if (nIndex == -1) return FALSE; } COXWndConstraint* pWC = m_wcTable[nIndex]; if (sizeMin.cx <=0 && sizeMin.cy <=0 && sizeMax.cx <=0 && sizeMax.cy <=0) { if (pWC->bHasMinMax) { pWC->bHasMinMax = FALSE; m_nMinMaxCount--; } } else { if (!pWC->bHasMinMax) { pWC->bHasMinMax = TRUE; m_nMinMaxCount++; } } pWC->sizeMin = sizeMin; pWC->sizeMax = sizeMax; ResetContainerMinMax(); return TRUE; } BOOL COXLayoutManager::RemoveConstraint(UINT nChildWnd, int nSide) { ASSERT(nChildWnd); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) return FALSE; for (int i = 0; i < 4; i++) { if (nSide & OX_INDEX_TO_LMS[i]) m_wcTable[nIndex]->sc[i].Empty(); } ResetContainerMinMax(); return TRUE; } BOOL COXLayoutManager::GetConstraint(UINT nChildWnd, int nSide, int& nType, int& nOffset, UINT& nBaseWnd) { ASSERT(nChildWnd); ASSERT(nSide == OX_LMS_TOP || nSide == OX_LMS_BOTTOM || nSide == OX_LMS_LEFT || nSide == OX_LMS_RIGHT); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) return FALSE; COXSideConstraint* pSC = &m_wcTable[nIndex]->sc[OX_LMS_TO_INDEX[nSide]]; nBaseWnd = pSC->nBaseWnd; if (pSC->nOffset1 == OX_LMOFFSET_ANY) { if (pSC->nOffset2 == OX_LMOFFSET_ANY) { nType = 0; nOffset = OX_LMOFFSET_ANY; } else { nType = (nSide & OX_LMS_MINOR) ? OX_LMT_SAME : OX_LMT_OPPOSITE; nOffset = pSC->nOffset2; } } else { if (pSC->nOffset2 == OX_LMOFFSET_ANY) { nType = (nSide & OX_LMS_MAJOR) ? OX_LMT_SAME : OX_LMT_OPPOSITE; nOffset = pSC->nOffset1; } else { nType = OX_LMT_POSITION; ASSERT (pSC->nOffset2); nOffset = (int)(pSC->nOffset1 * m_nBase / pSC->nOffset2); } } return TRUE; } BOOL COXLayoutManager::GetMinMax(UINT nChildWnd, CSize& sizeMin, CSize& sizeMax) { ASSERT(nChildWnd); int nIndex = GetChildIndex(nChildWnd); if (nIndex == -1) return FALSE; COXWndConstraint* pWC = m_wcTable[nIndex]; sizeMin = pWC->sizeMin; sizeMax = pWC->sizeMax; return TRUE; } BOOL COXLayoutManager::RedrawLayout() { if (m_pContainerWnd == NULL || !::IsWindow(m_pContainerWnd->m_hWnd)) return FALSE; CRect rect; m_pContainerWnd->GetClientRect(rect); return OnSize(rect.Width(), rect.Height()); } BOOL COXLayoutManager::OnSize(int cx, int cy) { if(m_pContainerWnd == NULL || !::IsWindow(m_pContainerWnd->m_hWnd)) return TRUE; if(cx == 0 || cy == 0 || m_wcTable.GetSize() == 0) return TRUE; m_cx = __min(__max(cx, m_cxMin), m_cxMax); m_cy = __min(__max(cy, m_cyMin), m_cyMax); int nLastResult = OX_LM_SIZING_OK; while (true) { // m_cx and m_cy adjustment reaching screen limit if (m_cx <= 0 || m_cx > 32767 || m_cy <= 0 || m_cy > 32767) { // whenever seeing this msg, check all constraint and min/max settings TRACE0("COXLayoutManager::OnSize(): contraints have internal conflicts.\r\n"); return FALSE; } int nResult = CalcLayout(); if (nResult == OX_LM_SIZING_FAIL) { TRACE0("COXLayoutManager::OnSize(): contraints have internal conflicts.\r\n"); return FALSE; } if (nResult == OX_LM_SIZING_OK) { // save container's min/max good value if (nLastResult != OX_LM_SIZING_OK) { if (nLastResult & OX_LM_SIZING_NEEDLARGERCX) m_cxMin = m_cx; else if (nLastResult & OX_LM_SIZING_NEEDSMALLERCX) m_cxMax = m_cx; if (nLastResult & OX_LM_SIZING_NEEDLARGERCY) m_cyMin = m_cy; else if (nLastResult & OX_LM_SIZING_NEEDSMALLERCY) m_cyMax = m_cy; } // normal exit from while loop break; } // adjust m_cx and m_cy until all constraints can be conformed nLastResult = nResult; if (nResult & OX_LM_SIZING_NEEDLARGERCX) m_cx++; else if (nResult & OX_LM_SIZING_NEEDSMALLERCX) m_cx--; if (nResult & OX_LM_SIZING_NEEDLARGERCY) m_cy++; else if (nResult & OX_LM_SIZING_NEEDSMALLERCY) m_cy--; } // reposition wnd int nNumWindows = PtrToInt( m_wcTable.GetSize()); HDWP hdwp = ::BeginDeferWindowPos(nNumWindows); if (hdwp) { for (int i = 0; i < nNumWindows; i++) { COXWndConstraint* pWC = m_wcTable[i]; CWnd* pWnd = m_pContainerWnd->GetDlgItem(pWC->nID); ASSERT(pWnd); if (pWnd) { CRect rectWC = pWC->GetRect(); ::DeferWindowPos(hdwp, pWnd->m_hWnd, NULL, rectWC.left, rectWC.top, rectWC.Width(), rectWC.Height(), SWP_NOZORDER); pWnd->RedrawWindow(); } } return ::EndDeferWindowPos(hdwp); } return FALSE; } // protected: int COXLayoutManager::CalcLayout() { // --- In : // --- Out : // --- Returns : OX_LM_SIZING_OK successful // OX_LM_SIZING_FAIL abnormal failure (self-conflict constraints) // OX_LM_SIZING_NEEDLARGERCX width too small to position all childs // OX_LM_SIZING_NEEDLARGERCY height too small to position all childs // OX_LM_SIZING_NEEDSMALLERCX width too large to position all childs // OX_LM_SIZING_NEEDSMALLERCY height too large to position all childs // --- Effect : use m_cx and m_cy as width and height of the container window to // calculate layout of all child windows int i, j; // set all need-to-determine pos to null for (i = 0; i < m_wcTable.GetSize(); i++) { COXWndConstraint* pWC = m_wcTable[i]; pWC->ResetPos(); } // determine all pos // whole array is sequentially filled up with calculated positions // even though internally some later positions will be filled up // before an earlier one, if it depends on the later ones for (i = 0; i < m_wcTable.GetSize(); i++) { COXWndConstraint* pWC = m_wcTable[i]; for (j = 0; j < 4; j++) { if (CalcSideConstraint(pWC, j) == OX_LMPOS_NULL) return OX_LM_SIZING_FAIL; } } int nResult = OX_LM_SIZING_OK; // check min/max if (m_nMinMaxCount) { for (i = 0; i < m_wcTable.GetSize(); i++) { COXWndConstraint* pWC = m_wcTable[i]; if (pWC->bHasMinMax) { CRect rectWC = pWC->GetRect(); CSize sizeWC = CSize(rectWC.right - rectWC.left, rectWC.bottom - rectWC.top); if (pWC->sizeMin.cx > 0 && sizeWC.cx < pWC->sizeMin.cx) { if (nResult & OX_LM_SIZING_NEEDSMALLERCX) return OX_LM_SIZING_FAIL; else nResult |= OX_LM_SIZING_NEEDLARGERCX; } if (pWC->sizeMax.cx > 0 && sizeWC.cx > pWC->sizeMax.cx) { if (nResult & OX_LM_SIZING_NEEDLARGERCX) return OX_LM_SIZING_FAIL; else nResult |= OX_LM_SIZING_NEEDSMALLERCX; } if (pWC->sizeMin.cy > 0 && sizeWC.cy < pWC->sizeMin.cy) { if (nResult & OX_LM_SIZING_NEEDSMALLERCY) return OX_LM_SIZING_FAIL; else nResult |= OX_LM_SIZING_NEEDLARGERCY; } if (pWC->sizeMax.cy > 0 && sizeWC.cy > pWC->sizeMax.cy) { if (nResult & OX_LM_SIZING_NEEDLARGERCY) return OX_LM_SIZING_FAIL; else nResult |= OX_LM_SIZING_NEEDSMALLERCY; } } } } return nResult; } int COXLayoutManager::GetChildIndex(UINT nChildWnd) const // --- In : nChildWnd, the child window to search // --- Out : // --- Returns : index of a child window in m_wcTable // --- Effect : search m_wcTable for nChildWnd { int i = 0; for (i = PtrToInt(m_wcTable.GetSize()) - 1; i >= 0 && m_wcTable[i]->nID != nChildWnd; i--); return i; } BOOL COXLayoutManager::CalcBaseWndIndex(COXSideConstraint* pSC) // --- In : pSC, the side constraint from which to search for base window's index // --- Out : // --- Returns : TRUE if successful; FALSE otherwise // --- Effect : base window's index is needed when resizing for tracing down all // positions; last good value is stored in pSC->nBaseWndIndex for // speeding up, yet boundary checking and quick re-evaluation is needed // for a safer operation, esp. after run-time adding or removing constraints // Overall this function provides a dynamic linked list { ASSERT(pSC->nBaseWnd > 0); if (pSC->nBaseWndIndex < 0 || pSC->nBaseWndIndex >= m_wcTable.GetSize() || m_wcTable[pSC->nBaseWndIndex]->nID != pSC->nBaseWnd) { for (int i = 0; i < m_wcTable.GetSize(); i++) { if (m_wcTable[i]->nID == pSC->nBaseWnd) { pSC->nBaseWndIndex = i; return TRUE; } } TRACE0("COXLayoutManager::CalcBaseWndIndex(): failed to find base window in children list.\r\n"); return FALSE; } return TRUE; } int COXLayoutManager::CalcSideConstraint(COXWndConstraint* pWC, int nSideIndex) // --- In : pWC, the window constraint in which side constraint is to calculate // nSideIndex, used to determine which side of the base window to trace // --- Out : // --- Returns : calculated position. OX_LMPOS_NULL if failed // --- Effect : calculate a side constraint (storing new value into nPos) // if not calculatable, recursively trace down to the next side // constraint until all done. OX_LMPOS_TRACING is used to flag for // detecting circular constraints. { COXSideConstraint* pSC = &pWC->sc[nSideIndex]; int& p = pSC->nPos; int& d1 = pSC->nOffset1; int& d2 = pSC->nOffset2; if (p == OX_LMPOS_TRACING) { TRACE0("COXLayoutManager::CalcSideConstraint(): circular constraints found.\r\n"); return OX_LMPOS_NULL; } // already calculated if (p != OX_LMPOS_NULL) return p; // if not constrainted if (pSC->IsEmpty()) { CWnd* pWnd = m_pContainerWnd->GetDlgItem(pWC->nID); ASSERT(pWnd); if (pWnd == NULL) return OX_LMPOS_NULL; CRect rect; pWnd->GetWindowRect(rect); m_pContainerWnd->ScreenToClient(rect); int nOppositeSideIndex = OX_LMSI_OPPOSITE(nSideIndex); COXSideConstraint* pOppositeSC = &pWC->sc[nOppositeSideIndex]; if (pOppositeSC->IsEmpty()) { // use current coordinates switch (nSideIndex) { case OX_LMSI_TOP: p = rect.top; break; case OX_LMSI_BOTTOM:p = rect.bottom; break; case OX_LMSI_LEFT: p = rect.left; break; case OX_LMSI_RIGHT: p = rect.right; break; } } else { // maintain child window's width and height int nBasePos = CalcSideConstraint(pWC, nOppositeSideIndex); if (nBasePos == OX_LMPOS_NULL) return OX_LMPOS_NULL; switch (nSideIndex) { case OX_LMSI_TOP: p = nBasePos - rect.Height(); break; case OX_LMSI_BOTTOM:p = nBasePos + rect.Height(); break; case OX_LMSI_LEFT: p = nBasePos - rect.Width(); break; case OX_LMSI_RIGHT: p = nBasePos + rect.Width(); break; } } return p; } // calculate base positions int nBasePos1 = OX_LMPOS_NULL; int nBasePos2 = OX_LMPOS_NULL; BOOL bVert = nSideIndex > OX_LMSI_HORZ; if (pSC->nBaseWnd == 0) { nBasePos1 = 0; nBasePos2 = (bVert ? m_cx : m_cy); } else { p = OX_LMPOS_TRACING; if (!CalcBaseWndIndex(pSC)) return OX_LMPOS_NULL; COXWndConstraint* pBaseWC = m_wcTable[pSC->nBaseWndIndex]; if (d1 != OX_LMOFFSET_ANY) { nBasePos1 = CalcSideConstraint(pBaseWC, (bVert ? OX_LMSI_LEFT : OX_LMSI_TOP)); if (nBasePos1 == OX_LMPOS_NULL) return OX_LMPOS_NULL; } if (d2 != OX_LMOFFSET_ANY) { nBasePos2 = CalcSideConstraint(pBaseWC, (bVert ? OX_LMSI_RIGHT : OX_LMSI_BOTTOM)); if (nBasePos2 == OX_LMPOS_NULL) return OX_LMPOS_NULL; } } // compose new position using base positions if (d2 == OX_LMOFFSET_ANY) p = nBasePos1 + d1; else if (d1 == OX_LMOFFSET_ANY) p = nBasePos2 + d2; else { ASSERT(d2); p = nBasePos1 + (int)((nBasePos2 - nBasePos1) * d1 / d2); } ASSERT(p != OX_LMPOS_NULL && p != OX_LMPOS_TRACING); return p; } void COXLayoutManager::SetDefaultConstraint(UINT nChildWnd) { ASSERT(m_pContainerWnd); CWnd* pWnd = m_pContainerWnd->GetDlgItem(nChildWnd); ASSERT(pWnd); if (pWnd == NULL) { TRACE0("COXLayoutManager::SetDefaultConstraint(): failed. Default constraint can only be applied after window has been created.\r\n"); return; } CRect rect; pWnd->GetWindowRect(rect); m_pContainerWnd->ScreenToClient(rect); SetConstraint(nChildWnd, OX_LMS_TOP, OX_LMT_SAME, rect.top); SetConstraint(nChildWnd, OX_LMS_LEFT, OX_LMT_SAME, rect.left); RemoveConstraint(nChildWnd, OX_LMS_BOTTOM); RemoveConstraint(nChildWnd, OX_LMS_RIGHT); } BOOL COXLayoutManager::TieChild(UINT nChildWnd, int nSide, int nType, UINT nBaseWnd /* = 0 */) { ASSERT(nChildWnd); ASSERT(nType == OX_LMT_SAME || nType == OX_LMT_OPPOSITE); if(nType!=OX_LMT_SAME && nType!=OX_LMT_OPPOSITE) return FALSE; CWnd* pBaseWnd=(nBaseWnd==0 ? m_pContainerWnd : m_pContainerWnd->GetDlgItem(nBaseWnd)); ASSERT(pBaseWnd!=NULL); CRect rectBase; if(pBaseWnd->GetSafeHwnd()==m_pContainerWnd->GetSafeHwnd()) pBaseWnd->GetClientRect(rectBase); else { pBaseWnd->GetWindowRect(rectBase); m_pContainerWnd->ScreenToClient(rectBase); } CWnd* pChildWnd=m_pContainerWnd->GetDlgItem(nChildWnd); ASSERT(pChildWnd!=NULL); CRect rectChild; pChildWnd->GetWindowRect(rectChild); m_pContainerWnd->ScreenToClient(rectChild); BOOL bResult=TRUE; if(bResult && (nSide&OX_LMS_TOP)!=0) { if(nType==OX_LMT_SAME) bResult&=SetConstraint(nChildWnd,OX_LMS_TOP,OX_LMT_SAME, rectChild.top-rectBase.top,nBaseWnd); else bResult&=SetConstraint(nChildWnd,OX_LMS_TOP,OX_LMT_OPPOSITE, rectChild.top-rectBase.bottom,nBaseWnd); } if(bResult && (nSide&OX_LMS_LEFT)!=0) { if(nType==OX_LMT_SAME) bResult&=SetConstraint(nChildWnd,OX_LMS_LEFT,OX_LMT_SAME, rectChild.left-rectBase.left,nBaseWnd); else bResult&=SetConstraint(nChildWnd,OX_LMS_LEFT,OX_LMT_OPPOSITE, rectChild.left-rectBase.right,nBaseWnd); } if(bResult && (nSide&OX_LMS_BOTTOM)!=0) { if(nType==OX_LMT_SAME) bResult&=SetConstraint(nChildWnd,OX_LMS_BOTTOM,OX_LMT_SAME, rectChild.bottom-rectBase.bottom,nBaseWnd); else bResult&=SetConstraint(nChildWnd,OX_LMS_BOTTOM,OX_LMT_OPPOSITE, rectChild.bottom-rectBase.top,nBaseWnd); } if(bResult && (nSide&OX_LMS_RIGHT)!=0) { if(nType==OX_LMT_SAME) bResult&=SetConstraint(nChildWnd,OX_LMS_RIGHT,OX_LMT_SAME, rectChild.right-rectBase.right,nBaseWnd); else bResult&=SetConstraint(nChildWnd,OX_LMS_RIGHT,OX_LMT_OPPOSITE, rectChild.right-rectBase.left,nBaseWnd); } return bResult; } BOOL COXLayoutManager::SubclassContainer(CWnd* pContainerWnd) // --- In : pContainerWnd : The container window // --- Out : // --- Returns : Whether it was successful or not // --- Effect : This function subclasses the container window { ASSERT(pContainerWnd != NULL); ASSERT(pContainerWnd->GetSafeHwnd() != NULL); ASSERT_VALID(pContainerWnd); if (m_pfnSuper != NULL) { // Already subclasses, check that hWnd and window proc is correct if ( (m_hWnd != pContainerWnd->GetSafeHwnd()) || ((WNDPROC)(LONG_PTR)::GetWindowLongPtr(pContainerWnd->GetSafeHwnd(), GWL_WNDPROC) != GlobalLayoutManagerProc) ) { TRACE0("COXLayoutManager::SubclassContainer : Failed because already subclassed by other window proc\n"); return FALSE; } return TRUE; } ASSERT(m_hWnd == NULL); ASSERT(m_pfnSuper == NULL); #ifdef _DEBUG COXLayoutManager* pDummyLayoutManager = NULL; // ... Should not yet be in list of subclassed layout managers ASSERT(!m_allLayoutManagers.Lookup(m_hWnd, pDummyLayoutManager)); #endif // _DEBUG // ... Subclass window (Windows way, not MFC : because may already be subclessed by MFC) m_hWnd = pContainerWnd->GetSafeHwnd(); m_pfnSuper = (WNDPROC)(LONG_PTR)::GetWindowLongPtr(pContainerWnd->GetSafeHwnd(), GWL_WNDPROC); ::SetWindowLongPtr(m_hWnd, GWL_WNDPROC, (LONG_PTR)GlobalLayoutManagerProc); // ... Store in the global map m_allLayoutManagers.SetAt(m_hWnd, this); ASSERT_VALID(this); return (m_hWnd != NULL);; } void COXLayoutManager::UnsubclassContainer() // --- In : // --- Out : // --- Returns : // --- Effect : This function unsubclasses the window // It removes this object from the double linked list // When it is the last in the list it restores the original // windows procedure { ASSERT_VALID(this); if (m_hWnd != NULL) { // Put back original window procedure ASSERT(m_pfnSuper != NULL); ASSERT(m_pfnSuper != GlobalLayoutManagerProc); // ... GlobalLayoutManagerProc is not used anymore : // set WNDPROC back to original value if(::IsWindow(m_hWnd)) ::SetWindowLongPtr(m_hWnd, GWL_WNDPROC, (LONG_PTR)m_pfnSuper); // ... Remove use from global map m_allLayoutManagers.RemoveKey(m_hWnd); m_hWnd = NULL; m_pfnSuper = NULL; } ASSERT(m_hWnd == NULL); ASSERT(m_pfnSuper == NULL); ASSERT_VALID(this); } LRESULT CALLBACK COXLayoutManager::GlobalLayoutManagerProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // --- In : hWnd : // uMsg : // wParam : // lParam : // --- Out : // --- Returns : The result of the message // --- Effect : This is the global windows procedure of all the COXScrollTipOwner // objects that have subclasses a window { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif COXLayoutManager* pLayoutManager = NULL; VERIFY(m_allLayoutManagers.Lookup(hWnd, pLayoutManager)); ASSERT_VALID(pLayoutManager); return pLayoutManager->LayoutManagerProc(hWnd, uMsg, wParam, lParam); } LRESULT COXLayoutManager::LayoutManagerProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // --- In : hWnd : // uMsg : // wParam : // lParam : // --- Out : // --- Returns : The result of the message // --- Effect : This is the member function called by the windows procedure of the // COXLayoutManager object { #ifdef _WINDLL #ifndef _AFXEXT AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif ASSERT_VALID(this); ASSERT(hWnd == m_hWnd); ASSERT(hWnd == m_hWnd); // Handling before base class switch (uMsg) { case WM_SIZE: OnSize(LOWORD(lParam), HIWORD(lParam)); break; default: // Do nothing special ; } // Let the original message procedure handle it ASSERT(m_pfnSuper != NULL); ASSERT(m_pfnSuper != GlobalLayoutManagerProc); LRESULT result = CallWindowProc(m_pfnSuper,hWnd, uMsg, wParam, lParam); // Handling after base class return result; } ///////////////////////////////////////////////////////////////////////////// // COXSideConstraint // // int nBaseWnd; base window's ID // int nOffset1; offset value from the top/left side of the base window // if OX_LMOFFSET_ANY, no constraint form this side // int nOffset2; offset value from the bottom/right side of the base window // if OX_LMOFFSET_ANY, no constraint form this side // int nPos; cache for storing calculated position // int nBaseWndIndex; base window's index in the m_wcTable // // NOTE: if neither nOffset1 nor nOffset2 is OX_LMOFFSET_ANY, indicating // "rubber band" OX_LMT_POSITION. COXSideConstraint::COXSideConstraint() { Empty(); } void COXSideConstraint::Empty() // --- In : // --- Out : // --- Returns : // --- Effect : set default empty values of a side constraint { nBaseWnd = 0; nOffset1 = nOffset2 = OX_LMOFFSET_ANY; nPos = OX_LMPOS_NULL; } BOOL COXSideConstraint::IsEmpty() // --- In : // --- Out : // --- Returns : TRUE if there is a constraint; FALSE otherwise // --- Effect : determine if there is a constraint { return ((nOffset1 == OX_LMOFFSET_ANY) && (nOffset2 == OX_LMOFFSET_ANY)); } ///////////////////////////////////////////////////////////////////////////// // COXWndConstraint // // int nID; ID of this window // BOOL bHasMinMax; TRUE if sizeMin or sizeMax is valid, FALSE otherwise // CSize sizeMin; minimum size // CSize sizeMax; maximum size // COXSideConstraint sc[4]; four side constraints // this array uses OX_INDEX_TO_LMS[] and // OX_LMS_TO_INDEX[] to map between index and // side parameter (input by user) COXWndConstraint::COXWndConstraint(UINT nChildID) { Empty(); nID = nChildID; } void COXWndConstraint::Empty() // --- In : // --- Out : // --- Returns : // --- Effect : set default empty values of a window constraint { bHasMinMax = FALSE; sizeMin = sizeMax = CSize(0,0); for (int i = 0; i < 4; i++) sc[i].Empty(); } void COXWndConstraint::ResetPos() // --- In : // --- Out : // --- Returns : // --- Effect : reset positions to null if it's needed to calculate { if (!sc[OX_LMSI_TOP].IsEmpty() || !sc[OX_LMSI_BOTTOM].IsEmpty()) sc[OX_LMSI_TOP].nPos = sc[OX_LMSI_BOTTOM].nPos = OX_LMPOS_NULL; if (!sc[OX_LMSI_LEFT].IsEmpty() || !sc[OX_LMSI_RIGHT].IsEmpty()) sc[OX_LMSI_LEFT].nPos = sc[OX_LMSI_RIGHT].nPos = OX_LMPOS_NULL; } CRect COXWndConstraint::GetRect() // --- In : // --- Out : // --- Returns : rect of calculated positions (new window coordinates) // --- Effect : get calculated positions from a window constraint { return CRect(sc[OX_LMSI_LEFT].nPos, sc[OX_LMSI_TOP].nPos, sc[OX_LMSI_RIGHT].nPos, sc[OX_LMSI_BOTTOM].nPos); } ///////////////////////////////////////////////////////////////////////////// // end of OXLayoutManager.cpp