📄 winmgr.cpp.svn-base
字号:
////////////////////////////////////////////////////////////////
// MSDN Magazine -- July 2001
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0. Runs on Win 98 and probably Win 2000 too.
// Set tabsize = 3 in your editor.
//
// Theo - Heavily modified to remove MFC dependencies.
// Replaced CWnd*/HWND, CRect/RECT, CSize/SIZE, CPoint/POINT
#include <windows.h>
#include <assert.h>
#include "WinMgr.h"
// Theo - Style Helpers
inline static DWORD GetStyle(HWND hWnd) {
return (DWORD)GetWindowLong(hWnd, GWL_STYLE);
}
inline static DWORD GetExStyle(HWND hWnd) {
return (DWORD)GetWindowLong(hWnd, GWL_EXSTYLE);
}
const UINT WM_WINMGR = RegisterWindowMessage("WM_WINMGR");
CWinMgr::CWinMgr(WINRECT* pWinMap) : m_map(pWinMap)
{
WINRECT::InitMap(m_map);
}
CWinMgr::~CWinMgr()
{
}
//////////////////
// Set each control's tofit (desired) size to current size. Useful for
// dialogs, to "remember" the current sizes as desired size.
//
void CWinMgr::InitToFitSizeFromCurrent(HWND hWnd)
{
assert(hWnd);
assert(m_map);
GetWindowPositions(hWnd);
for (WINRECT* w = m_map; !w->IsEnd(); w++) {
if (w->Type()==WRCT_TOFIT && !w->IsGroup()) {
w->SetToFitSize(RectToSize(w->GetRect()));
}
}
}
//////////////////
// Load all rectangles from current window positions.
//
void CWinMgr::GetWindowPositions(HWND hWnd)
{
assert(m_map);
assert(hWnd);
for (WINRECT* wrc=m_map; !wrc->IsEnd(); wrc++) {
if (wrc->IsWindow()) {
HWND HChild = GetDlgItem(hWnd, wrc->GetID());
if (HChild) {
GetWindowRect(HChild, &wrc->GetRect());
::ScreenToClient(hWnd, &RectToPoint(wrc->GetRect()));
}
}
}
}
//////////////////
// Move all the windows. Use DeferWindowPos for speed.
//
void
CWinMgr::SetWindowPositions(HWND hWnd)
{
int nWindows = CountWindows();
if (m_map && hWnd && nWindows>0) {
HDWP hdwp = ::BeginDeferWindowPos(nWindows);
int count=0;
for (WINRECT* wrc=m_map; !wrc->IsEnd(); wrc++) {
if (wrc->IsWindow()) {
assert(count < nWindows);
HWND hwndChild = ::GetDlgItem(hWnd, wrc->GetID());
if (hwndChild) {
const RECT& rc = wrc->GetRect();
::DeferWindowPos(hdwp,
hwndChild,
NULL, // HWND insert after
rc.left,rc.top,RectWidth(rc),RectHeight(rc),
SWP_NOZORDER);
InvalidateRect(hwndChild,NULL,TRUE); // repaint
count++;
}
} else {
// not a window: still need to repaint background
InvalidateRect(hWnd, &wrc->GetRect(), TRUE);
}
}
::EndDeferWindowPos(hdwp);
}
}
//////////////////
// Count number of table entries that correspond to windows--ie,
// that have a child window ID associated with the entry.
//
int CWinMgr::CountWindows()
{
assert(m_map);
int nWin = 0;
for (WINRECT* w=m_map; !w->IsEnd(); w++) {
if (w->IsWindow())
nWin++;
}
return nWin;
}
//////////////////
// Find the entry for a given control ID
//
WINRECT* CWinMgr::FindRect(UINT nID)
{
assert(m_map);
for (WINRECT* w=m_map; !w->IsEnd(); w++) {
if (w->GetID()==nID)
return w;
}
return NULL;
}
//////////////////
// Calculate size/positions for a row or column group This is the main
// algorithm. If a window is given, it's used to get the min/max size and
// desired size for TOFIT types.
//
void
CWinMgr::CalcGroup(WINRECT* pGroup, HWND hWnd)
{
// If this bombs, most likely the first entry in your map is not a group!
assert(pGroup && pGroup->IsGroup());
assert(hWnd);
// adjust total avail by margins
RECT rcTotal = pGroup->GetRect();
int w,h;
if (pGroup->GetMargins(w,h)) {
w = min(abs(w), RectWidth(rcTotal)/2);
h = min(abs(h), RectHeight(rcTotal)/2);
::InflateRect(&rcTotal, -w, -h);
}
BOOL bRow = pGroup->IsRowGroup(); // Is this a row group?
// Running height or width: start with total
int hwRemaining = bRow ? RectHeight(rcTotal) : RectWidth(rcTotal);
// First, set all rects to their minimum sizes.
// This ensures that each rect gets its min size.
CWinGroupIterator it;
for (it=pGroup; it; it.Next()) {
WINRECT* wrc = it;
SIZEINFO szi;
OnGetSizeInfo(szi, wrc, hWnd);
int hwMin = bRow ? szi.szMin.cy : szi.szMin.cx;
hwMin = min(hwMin, hwRemaining); // truncate
wrc->SetHeightOrWidth(hwMin, bRow); // set
hwRemaining -= hwMin; // decrement remaining height/width
assert(hwRemaining>=0);
}
// Now adjust all rects upward to desired size. Save REST rect for last.
WINRECT* pRestRect = NULL;
for (it=pGroup; it; it.Next()) {
WINRECT* wrc = it;
if (wrc->Type()==WRCT_REST) {
assert(pRestRect==NULL); // can only be one REST rect!
pRestRect = wrc; // remember it
} else {
AdjustSize(wrc, bRow, hwRemaining, hWnd);
}
}
assert(hwRemaining>=0);
// Adjust REST rect if any
if (pRestRect) {
AdjustSize(pRestRect, bRow, hwRemaining, hWnd);
assert(hwRemaining==0);
}
// All the sizes of the entries have been calculated, including
// groups (but not their children). Now move all the rects so they're
// adjacent to one another, without altering sizes.
PositionRects(pGroup, rcTotal, bRow);
// Finally, descend recursively into each subgroup.
for (it=pGroup; it; it.Next()) {
WINRECT* wrc = it;
if (wrc->IsGroup())
CalcGroup(wrc, hWnd); // recurse!
}
}
//////////////////
// Adjust the size of a single entry upwards to its desired size.
// Decrement hwRemaining by amount increased.
//
void
CWinMgr::AdjustSize(WINRECT* wrc, BOOL bRow,
int& hwRemaining, HWND hWnd)
{
SIZEINFO szi;
OnGetSizeInfo(szi, wrc, hWnd);
int hw = bRow ? szi.szDesired.cy : szi.szDesired.cx; // desired ht or wid
if (wrc->Type() == WRCT_REST) {
// for REST type, use all remaining space
RECT& rc = wrc->GetRect();
hw = hwRemaining + (bRow ? RectHeight(rc) : RectWidth(rc));
}
// Now hw is the desired height or width, and the current size of the
// entry is the min size. So adjust the size upwards, and decrement
// hwRemaining appropriately. This is a little confusing, but necessary so
// each entry gets its min size.
//
int hwCurrent = wrc->GetHeightOrWidth(bRow); // current size
int hwExtra = hw - hwCurrent; // amount extra
hwExtra = min(max(hwExtra, 0), hwRemaining); // truncate
hw = hwCurrent + hwExtra; // new height-or-width
wrc->SetHeightOrWidth(hw, bRow); // set...
hwRemaining -= hwExtra; // and adjust remaining
}
//////////////////
// Position all the rects so they're as wide/high as the total and follow one
// another; ie, are adjacent. This operation leaves the height (rows) and
// width (columns) unaffected. For rows, set each row's width to rcTotal and
// one below the other; for columns, set each column as tall as rcTotal and
// each to the right of the previous.
//
void
CWinMgr::PositionRects(WINRECT* pGroup, const RECT& rcTotal, BOOL bRow)
{
LONG xoryPos = bRow ? rcTotal.top : rcTotal.left;
CWinGroupIterator it;
for (it=pGroup; it; it.Next()) {
WINRECT* wrc = it;
RECT& rc = wrc->GetRect();
if (bRow) { // for ROWS:
LONG height = RectHeight(rc); // height of row = total height
rc.top = xoryPos; // top = running yPos
rc.bottom = rc.top + height; // ...
rc.left = rcTotal.left; // ...
rc.right = rcTotal.right; // ...
xoryPos += height; // increment yPos
} else { // for COLS:
LONG width = RectWidth(rc); // width = total width
rc.left = xoryPos; // left = running xPos
rc.right = rc.left + width; // ...
rc.top = rcTotal.top; // ...
rc.bottom = rcTotal.bottom; // ...
xoryPos += width; // increment xPos
}
}
}
//////////////////
// Get size information for a single entry (WINRECT). Returns size info in
// the SIZEINFO argument. For a group, calculate size info as aggregate of
// subentries.
//
void
CWinMgr::OnGetSizeInfo(SIZEINFO& szi, WINRECT* wrc, HWND hWnd)
{
szi.szMin = SIZEZERO; // default min size = zero
szi.szMax = SIZEMAX; // default max size = infinite
szi.szDesired = RectToSize(wrc->GetRect()); // default desired size = current
if (wrc->IsGroup()) {
// For groups, calculate min, max, desired size as aggregate of children
szi.szDesired = SIZEZERO;
BOOL bRow = wrc->IsRowGroup();
CWinGroupIterator it;
for (it=wrc; it; it.Next()) {
WINRECT* wrc2 = it;
SIZEINFO szi2;
OnGetSizeInfo(szi2, wrc2, hWnd);
if (bRow) {
szi.szMin.cx = max(szi.szMin.cx, szi2.szMin.cx);
szi.szMin.cy += szi2.szMin.cy;
szi.szMax.cx = min(szi.szMax.cx, szi2.szMax.cx);
szi.szMax.cy = min(szi.szMax.cy + szi2.szMax.cy, INFINITY);
szi.szDesired.cx = max(szi.szDesired.cx, szi2.szDesired.cx);
szi.szDesired.cy += szi2.szDesired.cy;
} else {
szi.szMin.cx += szi2.szMin.cx;
szi.szMin.cy = max(szi.szMin.cy, szi2.szMin.cy);
szi.szMax.cx = min(szi.szMax.cx + szi2.szMax.cx, INFINITY);
szi.szMax.cy = min(szi.szMax.cy, szi2.szMax.cy);
szi.szDesired.cx += szi2.szDesired.cx;
szi.szDesired.cy = max(szi.szDesired.cy, szi2.szDesired.cy);
}
}
// Add margins.
int w2,h2;
wrc->GetMargins(w2,h2); // get margins
w2<<=1; h2<<=1; // double
szi.szMin.cx += max(0,w2); // negative margins ==> don't include in min
szi.szMin.cy += max(0,h2); // ditto
szi.szDesired.cx += abs(w2); // for desired size, use abs vallue
szi.szDesired.cy += abs(h2); // ditto
} else {
// not a group
WINRECT* parent = wrc->Parent();
assert(parent);
RECT& rcParent = parent->GetRect();
BOOL bRow = parent->IsRowGroup();
int hw, hwMin, hwTotal, pct;
switch (wrc->Type()) {
case WRCT_FIXED:
hw = hwMin = wrc->GetParam(); // ht/wid is parameter
if (hw<0) { // if fixed val is negative:
hw = -hw; // use absolute val for desired..
hwMin = 0; // ..and zero for minimum
}
if (bRow) {
szi.szMax.cy = szi.szDesired.cy = hw;
szi.szMin.cy = hwMin;
} else {
szi.szMax.cx = szi.szDesired.cx = hw;
szi.szMin.cx = hwMin;
}
break;
case WRCT_PCT:
pct = wrc->GetParam();
assert(0<pct && pct<100);
hwTotal = bRow ? RectHeight(rcParent) : RectWidth(rcParent);
hw = (hwTotal * pct) / 100;
szi.szDesired = bRow ? GetSize(RectWidth(rcParent), hw) : GetSize(hw, RectHeight(rcParent));
break;
case WRCT_TOFIT:
if (wrc->HasToFitSize()) {
szi.szDesired = wrc->GetToFitSize();
}
break;
case WRCT_REST:
break;
default:
assert(FALSE);
}
// If the entry is a window, send message to get min/max/tofit size.
// Only set tofit size if type is TOFIT.
//
if (wrc->IsWindow() && hWnd) {
HWND hChild = GetDlgItem(hWnd, wrc->GetID());
if (hChild) {
if (!IsWindowVisible(hChild) && IsWindowVisible(hWnd)) {
// parent visible but child not ==> tofit size is zero
// important so hidden windows use no space
szi.szDesired = SIZEZERO;
} else {
szi.szAvail = RectToSize(rcParent);
SendGetSizeInfo(szi, hWnd, wrc->GetID());
}
}
}
szi.szDesired = maxsize(minsize(szi.szDesired,szi.szMax), szi.szMin);
}
}
//////////////////
// Send message to parent, then window itself, to get size info.
//
BOOL CWinMgr::SendGetSizeInfo(SIZEINFO& szi, HWND hWnd, UINT nID)
{
NMWINMGR nmw;
nmw.code = NMWINMGR::GET_SIZEINFO; // request size info
nmw.idFrom = nID; // ID of child I'm computing
nmw.sizeinfo = szi; // copy
if (!SendMessage(hWnd, WM_WINMGR, nID, (LPARAM)&nmw) && !nmw.processed) {
HWND hwndChild = ::GetDlgItem(hWnd, nID);
if (!hwndChild || !::SendMessage(hwndChild,WM_WINMGR,nID,(LPARAM)&nmw))
return FALSE;
}
szi = nmw.sizeinfo; // copy back to caller's struct
return TRUE;
}
//////////////////
// Get min/max info.
//
void
CWinMgr::GetMinMaxInfo(HWND hWnd, MINMAXINFO* lpMMI)
{
SIZEINFO szi;
GetMinMaxInfo(hWnd, szi); // call overloaded version
lpMMI->ptMinTrackSize = SizeToPoint(szi.szMin);
lpMMI->ptMaxTrackSize = SizeToPoint(szi.szMax);
}
//////////////////
// Get min/max info.
//
void CWinMgr::GetMinMaxInfo(HWND hWnd, SIZEINFO& szi)
{
OnGetSizeInfo(szi, m_map, hWnd); // get size info
if (!hWnd) // window not created ==> done
return;
// Add extra space for frame/dialog screen junk.
DWORD dwStyle = GetStyle(hWnd);
DWORD dwExStyle = GetExStyle(hWnd);
if (dwStyle & WS_VISIBLE) {
SIZE& szMin = szi.szMin; // ref!
if (!(dwStyle & WS_CHILD)) {
if (dwStyle & WS_CAPTION)
szMin.cy += GetSystemMetrics(SM_CYCAPTION);
if (::GetMenu(hWnd))
szMin.cy += GetSystemMetrics(SM_CYMENU);
}
if (dwStyle & WS_THICKFRAME) {
szMin.cx += 2*GetSystemMetrics(SM_CXSIZEFRAME);
szMin.cy += 2*GetSystemMetrics(SM_CYSIZEFRAME);
} else if (dwStyle & WS_BORDER) {
szMin.cx += 2*GetSystemMetrics(SM_CXBORDER);
szMin.cy += 2*GetSystemMetrics(SM_CYBORDER);
}
if (dwExStyle & WS_EX_CLIENTEDGE) {
szMin.cx += 2*GetSystemMetrics(SM_CXEDGE);
szMin.cy += 2*GetSystemMetrics(SM_CYEDGE);
}
}
}
//////////////////
// Move desired rectangle by a given vector amount.
// Call this when a sizer bar tells you it has moved.
//
void CWinMgr::MoveRect(WINRECT* pwrcMove, POINT ptMove, HWND pParentWnd)
{
assert(pwrcMove);
WINRECT* prev = pwrcMove->Prev();
assert(prev);
WINRECT* next = pwrcMove->Next();
assert(next);
BOOL bIsRow = pwrcMove->Parent()->IsRowGroup();
RECT& rcNext = next->GetRect();
RECT& rcPrev = prev->GetRect();
if (bIsRow) {
// a row can only be moved up or down
ptMove.x = 0;
rcPrev.bottom += ptMove.y;
rcNext.top += ptMove.y;
} else {
// a column can only be moved left or right
ptMove.y = 0;
rcPrev.right += ptMove.x;
rcNext.left += ptMove.x;
}
OffsetRect(pwrcMove->GetRect(), ptMove);
if (prev->IsGroup())
CalcGroup(prev, pParentWnd);
if (next->IsGroup())
CalcGroup(next, pParentWnd);
}
// Theo - Removed Tracing
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -