2025-11-27 16:46:48 +09:00

1017 lines
27 KiB
C++

// =============================================================================
// 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 <afxpriv.h> // For WM_INITIALUPDATE definition
#ifndef __OXMFCIMPL_H__
#if _MFC_VER >= 0x0700
#if _MFC_VER >= 1400
#include <afxtempl.h>
#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<HWND, HWND, COXLayoutManager*, COXLayoutManager*> 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<COXWndConstraint*, COXWndConstraint*> 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