#include "stdafx.h"
#include "AutoSizer.h"
CAutoSizer::CAutoSizer() :
m_hwnd(NULL),
m_bRecomputeMinWindow(false)
{
::SetRectEmpty(&m_rcMinWindow);
::SetRectEmpty(&m_rcMinClient);
::SetRectEmpty(&m_rcClient);
::SetRectEmpty(&m_rcClientOriginal);
}
bool CAutoSizer::SetWindowAndRules(HWND hwnd, const AutoSizerRule* pRules)
{
_ASSERTE(!m_hwnd);
_ASSERTE(::IsWindow(hwnd));
m_hwnd = hwnd;
::GetClientRect(m_hwnd, &m_rcClient);
::GetClientRect(m_hwnd, &m_rcClientOriginal);
::GetClientRect(m_hwnd, &m_rcMinClient);
::GetWindowRect(m_hwnd, &m_rcMinWindow);
::MapWindowPoints(NULL, m_hwnd, (POINT*)&m_rcMinWindow, 2);
if (pRules)
{
const AutoSizerRule* pRulesIt = pRules;
int cRules; for (cRules = 0; !pRulesIt->IsEnd(); ++pRulesIt)
++cRules;
m_Rules.resize(cRules);
for (int i = 0; i < m_Rules.size(); ++i)
{
m_Rules[i] = pRules + i;
m_Rules[i].ResolveIDs(this);
m_Rules[i].SaveInitialOffsets(this);
}
UpdatePositions();
}
return true;
}
bool CAutoSizer::SetMinSize(int cx, int cy, bool bDlgUnits)
{
if (!m_hwnd)
{
_ASSERTE(m_hwnd);
return false;
}
m_rcMinWindow.left -= m_rcMinClient.left;
m_rcMinWindow.top -= m_rcMinClient.top;
m_rcMinWindow.right -= m_rcMinClient.right;
m_rcMinWindow.bottom -= m_rcMinClient.bottom;
m_rcMinClient.left = 0;
m_rcMinClient.top = 0;
m_rcMinClient.right = cx;
m_rcMinClient.bottom = cy;
if (bDlgUnits)
::MapDialogRect(m_hwnd, &m_rcMinClient);
m_rcMinWindow.left = m_rcMinClient.left + m_rcMinWindow.left;
m_rcMinWindow.top = m_rcMinClient.top + m_rcMinWindow.top;
m_rcMinWindow.right = m_rcMinClient.right + m_rcMinWindow.right;
m_rcMinWindow.bottom = m_rcMinClient.bottom + m_rcMinWindow.bottom;
if (m_rcMinClient.right > m_rcClient.right
|| m_rcMinClient.bottom > m_rcClient.bottom)
{
RECT rcWindow;
::GetWindowRect(m_hwnd, &rcWindow);
int cxWindow = max(rcWindow.right - rcWindow.left,
m_rcMinWindow.right - m_rcMinWindow.left);
int cyWindow = max(rcWindow.bottom - rcWindow.top,
m_rcMinWindow.bottom - m_rcMinWindow.top);
::SetWindowPos(m_hwnd, NULL, 0, 0, cxWindow, cyWindow,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
}
return true;
}
bool CAutoSizer::ProcessMessage(MSG* pMsg)
{
LRESULT lr = 0;
return ProcessMessage(pMsg->message, pMsg->wParam, pMsg->lParam, &lr);
}
bool CAutoSizer::ProcessMessage(UINT uMsg, WPARAM wp, LPARAM lp, LRESULT* plr)
{
if (!m_hwnd)
return false;
switch (uMsg)
{
case WM_DESTROY:
OnDestroy();
break;
case WM_GETMINMAXINFO:
OnGetMinMaxInfo((MINMAXINFO*)lp);
if (plr)
*plr = 0;
break;
case WM_SIZE:
OnSize(wp, LOWORD(lp), HIWORD(lp));
if (plr)
*plr = 0;
break;
case WM_STYLECHANGED:
OnStyleChanged();
if (plr)
*plr = 0;
break;
default:
return false;
}
return false;
}
bool CAutoSizer::AddRule(HWND hwndFollower, AutoSizer_Follower edgeFollower,
HWND hwndLeader, AutoSizer_Leader edgeLeader, AutoSizer_Refresh refresh)
{
if (!::IsWindow(hwndFollower))
return false;
if (!hwndLeader)
hwndLeader = GetWindow();
AutoSizerRule rule;
rule.m_idFollower = reinterpret_cast<UINT>(hwndFollower);
rule.m_idLeader = reinterpret_cast<UINT>(hwndLeader);
rule.m_eFollower = edgeFollower;
rule.m_eLeader = edgeLeader;
rule.m_eRefresh = refresh;
XRuleIt it; for (it = m_Rules.begin(); it != m_Rules.end(); ++it)
if (*it == &rule)
return false;
XRule xRule(&rule);
xRule.SaveInitialOffsets(this);
for (it = m_Rules.begin(); it != m_Rules.end(); ++it)
if (it->GetFollower() == hwndFollower)
break;
for (it; it != m_Rules.end(); ++it)
if (it->GetFollower() != hwndFollower)
break;
m_Rules.insert(it, xRule);
UpdatePositions();
return true;
}
bool CAutoSizer::RemoveRules(HWND hwndFollower)
{
int cRules = m_Rules.size();
for (XRuleIt it = m_Rules.begin(); it != m_Rules.end();)
{
if (it->GetFollower() == hwndFollower)
it = m_Rules.erase(it);
else
++it;
}
return cRules != m_Rules.size();
}
void CAutoSizer::ComputeMinRects()
{
_ASSERTE(m_hwnd);
}
void CAutoSizer::UpdatePositions()
{
HDWP hdwp = ::BeginDeferWindowPos(m_Rules.size());
HWND hwndPrevFollower = NULL;
HWND hwndFollower;
RECT rcFollower, rcNew;
XRuleIt it = m_Rules.begin();
for (int i = 0; i <= m_Rules.size(); ++it, ++i)
{
if (i == m_Rules.size() || it->GetFollower() != hwndPrevFollower)
{
if (hwndPrevFollower)
{
int cxOld = rcFollower.right - rcFollower.left;
int cyOld = rcFollower.bottom - rcFollower.top;
int cxNew = rcNew.right - rcNew.left;
int cyNew = rcNew.bottom - rcNew.top;
UINT nFlags = 0;
if (cxOld == cxNew && cyOld == cyNew)
nFlags |= SWP_NOSIZE;
if (rcNew.left == rcFollower.left && rcNew.top == rcFollower.top)
nFlags |= SWP_NOMOVE;
if ((SWP_NOSIZE | SWP_NOMOVE) != nFlags)
{
nFlags |= SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION;
hdwp = ::DeferWindowPos(hdwp, hwndFollower, NULL,
rcNew.left, rcNew.top, cxNew, cyNew, nFlags);
}
}
if (i == m_Rules.size())
break;
hwndPrevFollower = it->GetFollower();
hwndFollower = hwndPrevFollower;
::GetWindowRect(hwndFollower, &rcFollower);
::MapWindowPoints(NULL, m_hwnd, (POINT*)&rcFollower, 2);
::CopyRect(&rcNew, &rcFollower);
}
it->UpdateRectangle(this, rcNew);
}
::EndDeferWindowPos(hdwp);
::UpdateWindow(m_hwnd);
}
void CAutoSizer::OnDestroy()
{
m_Rules.clear();
m_hwnd = NULL;
m_bRecomputeMinWindow = false;
::SetRectEmpty(&m_rcMinWindow);
::SetRectEmpty(&m_rcMinClient);
::SetRectEmpty(&m_rcClient);
::SetRectEmpty(&m_rcClientOriginal);
}
void CAutoSizer::OnGetMinMaxInfo(MINMAXINFO* pMMI)
{
pMMI->ptMinTrackSize.x = m_rcMinWindow.right - m_rcMinWindow.left;
pMMI->ptMinTrackSize.y = m_rcMinWindow.bottom - m_rcMinWindow.top;
}
void CAutoSizer::OnSize(UINT nType, int cx, int cy)
{
if (!cx && !cy)
{
RECT rcClient;
::GetClientRect(GetWindow(), &rcClient);
cx = rcClient.right - rcClient.left;
cy = rcClient.bottom - rcClient.top;
}
if (m_bRecomputeMinWindow)
{
m_bRecomputeMinWindow = false;
RECT rcWindow, rcClient;
::GetClientRect(m_hwnd, &rcClient);
::GetWindowRect(m_hwnd, &rcWindow);
::MapWindowPoints(NULL, m_hwnd, (POINT*)&rcWindow, 2);
int cxWindow = rcWindow.right - rcWindow.left;
int cyWindow = rcWindow.bottom - rcWindow.top;
rcWindow.left -= rcClient.left;
rcWindow.top -= rcClient.top;
rcWindow.right -= rcClient.right;
rcWindow.bottom -= rcClient.bottom;
m_rcMinWindow.left = m_rcMinClient.left + rcWindow.left;
m_rcMinWindow.top = m_rcMinClient.top + rcWindow.top;
m_rcMinWindow.right = m_rcMinClient.right + rcWindow.right;
m_rcMinWindow.bottom = m_rcMinClient.bottom + rcWindow.bottom;
if (cx < m_rcMinClient.right || cy < m_rcMinClient.bottom)
{
::SetWindowPos(m_hwnd, NULL, 0, 0,
max(0, m_rcMinWindow.right - m_rcMinWindow.left),
max(0, m_rcMinWindow.bottom - m_rcMinWindow.top),
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
return;
}
}
m_rcClient.right = cx;
m_rcClient.bottom = cy;
UpdatePositions();
}
void CAutoSizer::OnStyleChanged()
{
m_bRecomputeMinWindow = true;
}
bool CAutoSizer::XRule::SaveInitialOffsets(CAutoSizer* pSizer)
{
_ASSERTE(::IsWindow(GetFollower()));
if (!GetFollower() || !::IsWindow(GetFollower()))
return false;
RECT rcFollower, rcLeader;
const RECT* prcLeader;
::GetWindowRect(GetFollower(), &rcFollower);
::MapWindowPoints(NULL, pSizer->GetWindow(), (POINT*)&rcFollower, 2);
if (GetLeader() == pSizer->GetWindow())
prcLeader = pSizer->GetOriginalClientRect();
else
{
prcLeader = &rcLeader;
if (!GetLeader() || !::IsWindow(GetLeader()))
return false;
::GetWindowRect(GetLeader(), &rcLeader);
::MapWindowPoints(NULL, pSizer->GetWindow(), (POINT*)&rcLeader, 2);
}
int nLeader = 0;
switch (m_Rule.m_eLeader)
{
case AutoSizer_Lead_Left:
nLeader = prcLeader->left;
break;
case AutoSizer_Lead_Right:
nLeader = prcLeader->right;
break;
case AutoSizer_Lead_Top:
nLeader = prcLeader->top;
break;
case AutoSizer_Lead_Bottom:
nLeader = prcLeader->bottom;
break;
case AutoSizer_Lead_HCenter:
case AutoSizer_Lead_VCenter:
break;
default:
_ASSERTE(false);
return false;
}
int cOffsets = 0;
int nFollower[2] = {0, 0};
switch (m_Rule.m_eFollower)
{
case AutoSizer_Follow_Left:
cOffsets = 1;
nFollower[0] = rcFollower.left;
break;
case AutoSizer_Follow_Right:
cOffsets = 1;
nFollower[0] = rcFollower.right;
break;
case AutoSizer_Follow_LeftRight:
cOffsets = 2;
nFollower[0] = rcFollower.left;
nFollower[1] = rcFollower.right;
break;
case AutoSizer_Follow_Top:
cOffsets = 1;
nFollower[0] = rcFollower.top;
break;
case AutoSizer_Follow_Bottom:
cOffsets = 1;
nFollower[0] = rcFollower.bottom;
break;
case AutoSizer_Follow_TopBottom:
cOffsets = 2;
nFollower[0] = rcFollower.top;
nFollower[1] = rcFollower.bottom;
break;
case AutoSizer_Follow_HCenter:
case AutoSizer_Follow_VCenter:
break;
default:
_ASSERTE(false);
return false;
}
for (int i = 0; i < cOffsets; ++i)
m_Offsets[i] = nFollower[i] - nLeader;
return true;
}
bool CAutoSizer::XRule::UpdateRectangle(CAutoSizer* pSizer, RECT& rcNew) const
{
RECT rcLeader;
const RECT* prcLeader;
if (GetLeader() == pSizer->GetWindow())
prcLeader = pSizer->GetClientRect();
else
{
prcLeader = &rcLeader;
_ASSERTE(::IsWindow(GetLeader()));
if (!::IsWindow(GetLeader()))
return false;
::GetWindowRect(GetLeader(), &rcLeader);
::MapWindowPoints(NULL, pSizer->GetWindow(), (POINT*)&rcLeader, 2);
}
int nLeader = 0;
switch (m_Rule.m_eLeader)
{
case AutoSizer_Lead_Left:
nLeader = prcLeader->left;
break;
case AutoSizer_Lead_Right:
nLeader = prcLeader->right;
break;
case AutoSizer_Lead_Top:
nLeader = prcLeader->top;
break;
case AutoSizer_Lead_Bottom:
nLeader = prcLeader->bottom;
break;
case AutoSizer_Lead_HCenter:
nLeader = prcLeader->left + (prcLeader->right - prcLeader->left) / 2;
break;
case AutoSizer_Lead_VCenter:
nLeader = prcLeader->top + (prcLeader->bottom - prcLeader->top) / 2;
break;
default:
_ASSERTE(false);
return false;
}
RECT rcOld;
::CopyRect(&rcOld, &rcNew);
switch (m_Rule.m_eFollower)
{
case AutoSizer_Follow_Left:
rcNew.left = nLeader + m_Offsets[0];
break;
case AutoSizer_Follow_Right:
rcNew.right = nLeader + m_Offsets[0];
break;
case AutoSizer_Follow_LeftRight:
rcNew.left = nLeader + m_Offsets[0];
rcNew.right = nLeader + m_Offsets[1];
break;
case AutoSizer_Follow_Top:
rcNew.top = nLeader + m_Offsets[0];
break;
case AutoSizer_Follow_Bottom:
rcNew.bottom = nLeader + m_Offsets[0];
break;
case AutoSizer_Follow_TopBottom:
rcNew.top = nLeader + m_Offsets[0];
rcNew.bottom = nLeader + m_Offsets[1];
break;
case AutoSizer_Follow_HCenter:
{
int cxFollower = (rcNew.right - rcNew.left);
rcNew.left = nLeader - (cxFollower / 2);
rcNew.right = rcNew.left + cxFollower;
break;
}
case AutoSizer_Follow_VCenter:
{
int cyFollower = (rcNew.bottom - rcNew.top);
rcNew.top = nLeader - (cyFollower / 2);
rcNew.bottom = rcNew.top + cyFollower;
break;
}
default:
_ASSERTE(false);
return false;
}
int cxOld = rcOld.right - rcOld.left;
int cyOld = rcOld.bottom - rcOld.top;
int cxNew = rcNew.right - rcNew.left;
int cyNew = rcNew.bottom - rcNew.top;
if (m_Rule.m_eRefresh > AutoSizer_Refresh_NoRefresh)
{
if (cxOld != cxNew || cyOld != cyNew ||
rcNew.left != rcOld.left || rcNew.top != rcOld.top)
{
bool bEraseBk = AutoSizer_Refresh_BkRefresh == m_Rule.m_eRefresh;
::InvalidateRect(pSizer->GetWindow(), &rcNew, bEraseBk);
}
}
return true;
}