1017 lines
27 KiB
C++
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
|