mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
729 lines
23 KiB
729 lines
23 KiB
//---------------------------------------------------------------------------
|
|
// Smart Tiling code.
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
//---------------------------------------------------------------------------
|
|
#define VERTEX_TOP 1
|
|
#define VERTEX_BOTTOM 2
|
|
#define VERTEX_LEFT 3
|
|
#define VERTEX_RIGHT 4
|
|
#define MAXDWORD 0xffffffff
|
|
#define abs(a) ((a)>0?(a):-(a))
|
|
|
|
//---------------------------------------------------------------------------
|
|
typedef struct
|
|
{
|
|
BOOL fTopDone;
|
|
BOOL fBottomDone;
|
|
BOOL fLeftDone;
|
|
BOOL fRightDone;
|
|
RECT rc;
|
|
HWND hwnd;
|
|
BOOL fIgnore;
|
|
} WINRECT, *PWINRECT, *LPWINRECT;
|
|
|
|
#ifdef DEBUG
|
|
//---------------------------------------------------------------------------
|
|
void DebugDumpRects(WORD cWinrects, LPWINRECT lpWinrects)
|
|
{
|
|
WORD wi;
|
|
TCHAR szTitle[128];
|
|
LPWINRECT lpwr;
|
|
|
|
for (wi = 0; wi < cWinrects; wi++)
|
|
{
|
|
lpwr = &lpWinrects[wi];
|
|
GetWindowText(lpwr->hwnd, szTitle, ARRAYSIZE(szTitle));
|
|
DebugMsg(DM_TRACE, TEXT("tm.ddr: Window %x %s x = %d, y = %d, w = %d, h = %d"), lpwr->hwnd, (LPTSTR) szTitle, lpwr->rc.left, lpwr->rc.top, lpwr->rc.right-lpwr->rc.left, lpwr->rc.bottom-lpwr->rc.top);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL Window_CanResize(HWND hwnd)
|
|
{
|
|
DWORD dwStyle;
|
|
|
|
// Just check the window style.
|
|
dwStyle = (DWORD)GetWindowLong(hwnd, GWL_STYLE);
|
|
return dwStyle & WS_THICKFRAME;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Use the data in the WinRect array to move the windows around.
|
|
void SetWindowsPosUsingWinrects(WORD cWinrects, LPWINRECT lpWinrects)
|
|
{
|
|
WORD wi;
|
|
LPWINRECT lpwr;
|
|
HDWP hdwp;
|
|
|
|
hdwp = BeginDeferWindowPos(cWinrects);
|
|
for (wi=0; wi < cWinrects; wi++)
|
|
{
|
|
lpwr = &lpWinrects[wi];
|
|
// Skip windows that can't be sized.
|
|
if (Window_CanResize(lpwr->hwnd))
|
|
{
|
|
hdwp = DeferWindowPos(hdwp, lpwr->hwnd, 0, lpwr->rc.left, lpwr->rc.top, lpwr->rc.right-lpwr->rc.left, lpwr->rc.bottom-lpwr->rc.top, SWP_NOZORDER|SWP_NOACTIVATE);
|
|
}
|
|
}
|
|
EndDeferWindowPos(hdwp);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Given a rect, return its point centre.
|
|
void GetCentreFromRect(LPRECT lprc, LPPOINT lppt)
|
|
{
|
|
lppt->x = (lprc->left+lprc->right)/2;
|
|
lppt->y = (lprc->top+lprc->bottom)/2;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
BOOL QueryOverlaps(WORD cWinrects, LPWINRECT lpWinrects, WORD wEntry1, WORD wEntry2)
|
|
{
|
|
WORD wi;
|
|
LPWINRECT lpwrc1, lpwrc2;
|
|
LPWINRECT lpwrc;
|
|
BOOL fReturn = FALSE;
|
|
RECT rc;
|
|
|
|
lpwrc1 = &lpWinrects[wEntry1];
|
|
lpwrc2 = &lpWinrects[wEntry2];
|
|
|
|
DebugMsg(DM_TRACE, TEXT("tm.qo: Checking overlaps using %x and %x"), lpwrc1->hwnd, lpwrc2->hwnd);
|
|
for (wi=0; wi < cWinrects; wi++)
|
|
{
|
|
// Ignore the given entries.
|
|
if (wi == wEntry1 || wi == wEntry2)
|
|
continue;
|
|
lpwrc = &lpWinrects[wi];
|
|
// Ignore windows marked as being ignorable.
|
|
if (lpwrc->fIgnore)
|
|
continue;
|
|
// Check for overlap.
|
|
if (IntersectRect(&rc, &lpwrc1->rc, &lpwrc->rc))
|
|
{
|
|
// Yep, they intersect.
|
|
DebugMsg(DM_TRACE, TEXT("tm.qo: Overlap found with %x."), lpwrc->hwnd);
|
|
fReturn = TRUE;
|
|
break;
|
|
}
|
|
if (IntersectRect(&rc, &lpwrc2->rc, &lpwrc->rc))
|
|
{
|
|
// Yep, they intersect.
|
|
DebugMsg(DM_TRACE, TEXT("tm.qo: Overlap found with %x."), lpwrc->hwnd);
|
|
fReturn = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#define BIAS_NONE 0x0000
|
|
#define BIAS_HORIZONTAL 0x0001
|
|
#define BIAS_VERTICAL 0x0002
|
|
#define BIAS_BOTH 0x0003
|
|
|
|
// Move bits of the relevant rects so that they touch.
|
|
// IE You call this with the two windows to get touching and this will size
|
|
// the appropriate vertices so that they line up nicely.
|
|
// If fIngnoreOverlaps is FALSE then AV will do nothing if sizing the
|
|
// appropriate windows introduces an overlap.
|
|
void AdjustVertices(WORD cWinrects, LPWINRECT lpWinrects,
|
|
WORD wEntry1, WORD wEntry2, BOOL fIgnoreOverlaps)
|
|
{
|
|
LPWINRECT lpwrc1, lpwrc2;
|
|
POINT pt1, pt2;
|
|
WORD wBias = BIAS_NONE;
|
|
WINRECT wrc1, wrc2;
|
|
RECT rcOverlap;
|
|
|
|
lpwrc1 = &lpWinrects[wEntry1];
|
|
lpwrc2 = &lpWinrects[wEntry2];
|
|
|
|
// Make copies - we may need to back out these changes.
|
|
wrc1 = *lpwrc1;
|
|
wrc2 = *lpwrc2;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: Adjusting %d and %d"), wrc1.hwnd, wrc2.hwnd);
|
|
|
|
GetCentreFromRect(&lpwrc1->rc, &pt1);
|
|
GetCentreFromRect(&lpwrc2->rc, &pt2);
|
|
|
|
// Work out a bias for the windows otherwise the tiler will *always*
|
|
// put the windows corner to corner.
|
|
if (lpwrc2->rc.left > lpwrc1->rc.right || lpwrc2->rc.right < lpwrc1->rc.left)
|
|
wBias |= BIAS_HORIZONTAL;
|
|
if (lpwrc2->rc.top > lpwrc1->rc.bottom || lpwrc2->rc.bottom < lpwrc1->rc.top)
|
|
wBias |= BIAS_VERTICAL;
|
|
|
|
// Deal with overlaping windows nicely.
|
|
if (wBias == BIAS_NONE && IntersectRect(&rcOverlap, &lpwrc1->rc, &lpwrc2->rc))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: Overlap found - removing..."));
|
|
|
|
// Try obvious stuff first. This won't handle badly overlapped
|
|
// windows (like cascaded ones).
|
|
if (((pt2.y < lpwrc1->rc.top) && (pt1.y > lpwrc2->rc.bottom)) || ((pt2.y > lpwrc1->rc.bottom) && (pt1.y < lpwrc2->rc.top)))
|
|
wBias |= BIAS_VERTICAL;
|
|
if (((pt2.x > lpwrc1->rc.right) && (pt1.x < lpwrc2->rc.left)) || ((pt2.x < lpwrc1->rc.left) && (pt1.x > lpwrc2->rc.right)))
|
|
wBias |= BIAS_HORIZONTAL;
|
|
|
|
// Less obvious...This tries to deal with cascaded windows.
|
|
if (wBias == BIAS_NONE)
|
|
{
|
|
if ((pt2.y < lpwrc1->rc.bottom) && (pt1.y < lpwrc2->rc.top))
|
|
{
|
|
lpwrc2->fTopDone = TRUE;
|
|
wBias |= BIAS_VERTICAL;
|
|
}
|
|
if ((pt1.y > lpwrc1->rc.top) && (pt2.y > lpwrc1->rc.bottom))
|
|
{
|
|
lpwrc1->fBottomDone = TRUE;
|
|
wBias |= BIAS_VERTICAL;
|
|
}
|
|
if ((pt2.x < lpwrc1->rc.right) && (pt1.x < lpwrc2->rc.left))
|
|
{
|
|
lpwrc2->fLeftDone = TRUE;
|
|
wBias |= BIAS_HORIZONTAL;
|
|
}
|
|
if ((pt1.x > lpwrc1->rc.left) && (pt2.x > lpwrc1->rc.right))
|
|
{
|
|
lpwrc1->fRightDone = TRUE;
|
|
wBias |= BIAS_HORIZONTAL;
|
|
}
|
|
}
|
|
|
|
// Catch all..
|
|
if (wBias == BIAS_NONE)
|
|
wBias = BIAS_BOTH;
|
|
}
|
|
|
|
// Work out how best to change the rects so that they touch.
|
|
if ((wBias & BIAS_HORIZONTAL) && (pt2.x >= pt1.x))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: %x is to the right of %x"), lpwrc2->hwnd, lpwrc1->hwnd);
|
|
if (!lpwrc2->fLeftDone && !lpwrc1->fRightDone)
|
|
{
|
|
// Both are moveable so averge them.
|
|
lpwrc2->rc.left = lpwrc1->rc.right = (lpwrc2->rc.left + lpwrc1->rc.right)/2;
|
|
lpwrc2->fLeftDone = TRUE;
|
|
lpwrc1->fRightDone = TRUE;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Try something else.
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc2->rc.left = lpwrc1->rc.right;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Last resort...
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc1->rc.right = lpwrc2->rc.left;
|
|
}
|
|
}
|
|
}
|
|
// Only one is moveable so move it to touch the unmoveable one.
|
|
else if (!lpwrc2->fLeftDone)
|
|
{
|
|
lpwrc2->rc.left = lpwrc1->rc.right;
|
|
lpwrc2->fLeftDone = TRUE;
|
|
}
|
|
else if (!lpwrc1->fRightDone)
|
|
{
|
|
lpwrc1->rc.right = lpwrc2->rc.left;
|
|
lpwrc1->fRightDone = TRUE;
|
|
}
|
|
// Else neither are moveable so do nothing.
|
|
}
|
|
if ((wBias & BIAS_HORIZONTAL) && (pt2.x < pt1.x))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: %x is to the right of %x"), lpwrc2->hwnd, lpwrc1->hwnd);
|
|
if (!lpwrc2->fRightDone && !lpwrc1->fLeftDone)
|
|
{
|
|
// Both are moveable so averge them.
|
|
lpwrc2->rc.right = lpwrc1->rc.left = (lpwrc2->rc.right + lpwrc1->rc.left)/2;
|
|
lpwrc2->fRightDone = TRUE;
|
|
lpwrc1->fLeftDone = TRUE;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Try something else.
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc2->rc.right = lpwrc1->rc.left;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Last resort...
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc1->rc.left = lpwrc2->rc.right;
|
|
}
|
|
}
|
|
}
|
|
// Only one is moveable so move it to touch the unmoveable one.
|
|
else if (!lpwrc2->fRightDone)
|
|
{
|
|
lpwrc2->rc.right = lpwrc1->rc.left;
|
|
lpwrc2->fRightDone = TRUE;
|
|
}
|
|
else if (!lpwrc1->fLeftDone)
|
|
{
|
|
lpwrc1->rc.left = lpwrc2->rc.right;
|
|
lpwrc1->fLeftDone = TRUE;
|
|
}
|
|
// Else neither are moveable so do nothing.
|
|
}
|
|
if ((wBias & BIAS_VERTICAL) && (pt2.y <= pt1.y))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: %x is above %x"), lpwrc2->hwnd, lpwrc1->hwnd);
|
|
if (!lpwrc2->fBottomDone && !lpwrc1->fTopDone)
|
|
{
|
|
// Both are moveable so averge them.
|
|
lpwrc2->rc.bottom = lpwrc1->rc.top = (lpwrc2->rc.bottom + lpwrc1->rc.top)/2;
|
|
lpwrc2->fBottomDone = TRUE;
|
|
lpwrc1->fTopDone = TRUE;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Try something else.
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc2->rc.bottom = lpwrc1->rc.top;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Last resort...
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc1->rc.top = lpwrc2->rc.bottom;
|
|
}
|
|
}
|
|
}
|
|
// Only one is moveable so move it to touch the unmoveable one.
|
|
else if (!lpwrc2->fBottomDone)
|
|
{
|
|
lpwrc2->rc.bottom = lpwrc1->rc.top;
|
|
lpwrc2->fBottomDone = TRUE;
|
|
}
|
|
else if (!lpwrc1->fTopDone)
|
|
{
|
|
lpwrc1->rc.top = lpwrc2->rc.bottom;
|
|
lpwrc1->fTopDone = TRUE;
|
|
}
|
|
// Else neither are moveable so do nothing.
|
|
}
|
|
if ((wBias & BIAS_VERTICAL) && (pt2.y > pt1.y))
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("tm.av: %x is below %x"), lpwrc2->hwnd, lpwrc1->hwnd);
|
|
if (!lpwrc2->fTopDone && !lpwrc1->fBottomDone)
|
|
{
|
|
// Both are moveable so averge them.
|
|
lpwrc2->rc.top = lpwrc1->rc.bottom = (lpwrc2->rc.top + lpwrc1->rc.bottom)/2;
|
|
lpwrc2->fTopDone = TRUE;
|
|
lpwrc1->fBottomDone = TRUE;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Try something else.
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc2->rc.top = lpwrc1->rc.bottom;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// FU - Last resort...
|
|
lpwrc1->rc = wrc1.rc;
|
|
lpwrc2->rc = wrc2.rc;
|
|
lpwrc1->rc.bottom = lpwrc2->rc.top;
|
|
}
|
|
}
|
|
}
|
|
// Only one is moveable so move it to touch the unmoveable one.
|
|
else if (!lpwrc2->fTopDone)
|
|
{
|
|
lpwrc2->rc.top = lpwrc1->rc.bottom;
|
|
lpwrc2->fTopDone = TRUE;
|
|
}
|
|
else if (!lpwrc1->fBottomDone)
|
|
{
|
|
lpwrc1->rc.bottom = lpwrc2->rc.top;
|
|
lpwrc1->fBottomDone = TRUE;
|
|
}
|
|
// Else neither are moveable so do nothing.
|
|
}
|
|
|
|
// Will this change cause overlapps?
|
|
if (!fIgnoreOverlaps && QueryOverlaps(cWinrects, lpWinrects, wEntry1, wEntry2))
|
|
{
|
|
// Yes, forget it.
|
|
*lpwrc1 = wrc1;
|
|
*lpwrc2 = wrc2;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Make the windows match the bounding rectangle.
|
|
// REVIEW UNDONE
|
|
void FillBoundingRect(WORD cWinrects, LPWINRECT lpWinrects, LPCRECT lprcBounding)
|
|
{
|
|
WORD wi;
|
|
LPWINRECT lpwrc;
|
|
WINRECT wrc;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("tm.fbr: Filling bounding rect (%d, %d) to (%d, %d)"), lprcBounding->left, lprcBounding->top, lprcBounding->right, lprcBounding->bottom);
|
|
|
|
// Try repositioning things so that they touch the border.
|
|
for (wi=0; wi < cWinrects; wi++)
|
|
{
|
|
lpwrc = &lpWinrects[wi];
|
|
wrc = *lpwrc;
|
|
lpwrc->rc.top = lprcBounding->top;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wi, wi))
|
|
{
|
|
// Didn't work, back out change.
|
|
*lpwrc = wrc;
|
|
}
|
|
else
|
|
{
|
|
// Worked, record it for posterity.
|
|
wrc.rc.top = lprcBounding->top;
|
|
}
|
|
|
|
lpwrc->rc.bottom = lprcBounding->bottom;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wi, wi))
|
|
{
|
|
// Didn't work, back out change.
|
|
*lpwrc = wrc;
|
|
}
|
|
else
|
|
{
|
|
// Worked, record it for posterity.
|
|
wrc.rc.bottom = lprcBounding->bottom;
|
|
}
|
|
|
|
lpwrc->rc.left = lprcBounding->left;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wi, wi))
|
|
{
|
|
// Didn't work, back out change.
|
|
*lpwrc = wrc;
|
|
}
|
|
else
|
|
{
|
|
// Worked, record it for posterity.
|
|
wrc.rc.left = lprcBounding->left;
|
|
}
|
|
|
|
lpwrc->rc.right = lprcBounding->right;
|
|
if (QueryOverlaps(cWinrects, lpWinrects, wi, wi))
|
|
{
|
|
// Didn't work, back out change.
|
|
*lpwrc = wrc;
|
|
}
|
|
else
|
|
{
|
|
// Worked, record it for posterity.
|
|
wrc.rc.right = lprcBounding->right;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Twiddle with overlapping windows so that they don't overlap anymore.
|
|
// If they are badly overlapped (cascaded, or contained) then we leave them
|
|
// alone and set their fIgnore flag so we know no to muck with them later.
|
|
// REVIEW UNDONE Cascaded windows should be tidied up to so that they're
|
|
// nicely cascaded.
|
|
void RemoveOverlaps(WORD cWinrects, LPWINRECT lpWinrects)
|
|
{
|
|
WORD wiwrInitial; // Index to a WinRect in the SSAM.
|
|
WORD wiwrNext;
|
|
RECT rc;
|
|
LPWINRECT lpwrcInitial;
|
|
LPWINRECT lpwrcNext;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("tm.ro: Removing primary overlaps..."));
|
|
|
|
for (wiwrInitial = 0; wiwrInitial < cWinrects; wiwrInitial++)
|
|
{
|
|
for (wiwrNext = wiwrInitial+1; wiwrNext < cWinrects; wiwrNext++)
|
|
{
|
|
lpwrcInitial = &lpWinrects[wiwrInitial];
|
|
lpwrcNext = &lpWinrects[wiwrNext];
|
|
if (IntersectRect(&rc, &lpwrcInitial->rc, &lpwrcNext->rc))
|
|
{
|
|
if (EqualRect(&rc, &lpwrcInitial->rc))
|
|
{
|
|
// One is completely contained within the other
|
|
DebugMsg(DM_TRACE, TEXT("tm.ro: %x completely contains %x, ignoring it."), lpwrcNext->hwnd, lpwrcInitial->hwnd);
|
|
lpwrcNext->fIgnore = TRUE;
|
|
}
|
|
else if (EqualRect(&rc, &lpwrcNext->rc))
|
|
{
|
|
// One is completely contained within the other
|
|
DebugMsg(DM_TRACE, TEXT("tm.ro: %x completely contains %x, ignoring it."), lpwrcInitial->hwnd, lpwrcNext->hwnd);
|
|
lpwrcInitial->fIgnore = TRUE;
|
|
}
|
|
else
|
|
{
|
|
AdjustVertices(cWinrects, lpWinrects, wiwrInitial, wiwrNext, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Re-arrange the rects in the given array so that they are nicely alligned.
|
|
// REVIEW UNDONE - Make this look like the new user API's
|
|
void RearrangeWinrects(WORD cWinrects, LPWINRECT lpWinrects, LPCRECT lprcBounding)
|
|
{
|
|
WORD wiwrInitial; // Index to a WinRect in the SSAM.
|
|
WORD wiwrNext;
|
|
|
|
DebugMsg(DM_TRACE, TEXT("tm.rw: Re-arranging windows..."));
|
|
|
|
// Pick a starting point.
|
|
wiwrInitial = 0;
|
|
|
|
RemoveOverlaps(cWinrects, lpWinrects);
|
|
|
|
// Bog standard loop.
|
|
for (wiwrInitial = 0; wiwrInitial < cWinrects; wiwrInitial++)
|
|
{
|
|
if (lpWinrects[wiwrInitial].fIgnore)
|
|
continue;
|
|
for (wiwrNext = wiwrInitial+1; wiwrNext < cWinrects; wiwrNext++)
|
|
{
|
|
if (lpWinrects[wiwrNext].fIgnore)
|
|
continue;
|
|
AdjustVertices(cWinrects, lpWinrects, wiwrInitial, wiwrNext, FALSE);
|
|
}
|
|
}
|
|
|
|
// Just in case it was so fast you missed it, lets do it
|
|
// again! (only backwards).
|
|
for (wiwrInitial = 0; wiwrInitial < cWinrects; wiwrInitial++)
|
|
{
|
|
if (lpWinrects[wiwrInitial].fIgnore)
|
|
continue;
|
|
for (wiwrNext = wiwrInitial+1; wiwrNext < cWinrects; wiwrNext++)
|
|
{
|
|
if (lpWinrects[wiwrNext].fIgnore)
|
|
continue;
|
|
AdjustVertices(cWinrects, lpWinrects, wiwrNext, wiwrInitial, FALSE);
|
|
}
|
|
}
|
|
|
|
// Expand everything to hit the border.
|
|
FillBoundingRect(cWinrects, lpWinrects, lprcBounding);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Returns TRUE if the window is suitable for being moved around.
|
|
// REVIEW What should we do about the ShellWindow?
|
|
BOOL IsWindowNormal(HWND hwnd)
|
|
{
|
|
LONG lStyle;
|
|
|
|
// Visible and non-icon.
|
|
if (IsWindowVisible(hwnd) && !IsIconic(hwnd))
|
|
{
|
|
// Not popups.
|
|
lStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
if (!(lStyle & WS_POPUP))
|
|
{
|
|
// REVIEW User doesn't ignore topmost windows so we shouldn't either.
|
|
#if 0
|
|
// Not topmost (or bottom-most???).
|
|
lStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
if (!(lStyle & WS_EX_TOPMOST))
|
|
return TRUE;
|
|
#else
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Build an array of winrects for the children of the given window.
|
|
// This will skip windows we don't think we should mess with.
|
|
LPWINRECT BuildWinrectsFromParent(WORD *pcHwnd, HWND hwndParent)
|
|
{
|
|
WORD cWinrects;
|
|
LPWINRECT lpWinrects;
|
|
HWND hwnd;
|
|
|
|
cWinrects = 0;
|
|
lpWinrects = NULL;
|
|
hwnd = GetWindow(hwndParent, GW_CHILD);
|
|
while (hwnd)
|
|
{
|
|
// Check the window is valid.
|
|
if (!IsWindowNormal(hwnd))
|
|
{
|
|
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
|
|
continue;
|
|
}
|
|
if (IsZoomed(hwnd))
|
|
ShowWindow(hwnd, SW_RESTORE);
|
|
|
|
// Store the info.
|
|
lpWinrects = ReAlloc(lpWinrects, SIZEOF(WINRECT)*(cWinrects+1));
|
|
if (!lpWinrects)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("s.bwfp: Not enough memory to arrange windows."));
|
|
return NULL;
|
|
}
|
|
lpWinrects[cWinrects].hwnd = hwnd;
|
|
lpWinrects[cWinrects].fTopDone = FALSE;
|
|
lpWinrects[cWinrects].fBottomDone = FALSE;
|
|
lpWinrects[cWinrects].fLeftDone = FALSE;
|
|
lpWinrects[cWinrects].fRightDone = FALSE;
|
|
lpWinrects[cWinrects].fIgnore = FALSE;
|
|
GetWindowRect(hwnd, &lpWinrects[cWinrects].rc);
|
|
// Next.
|
|
cWinrects++;
|
|
hwnd = GetWindow(hwnd, GW_HWNDNEXT);
|
|
}
|
|
|
|
*pcHwnd = cWinrects;
|
|
return lpWinrects;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Build an array of winrects from the given array.
|
|
LPWINRECT BuildWinrectsFromList(WORD *pcHwnd, const HWND *aHwnd)
|
|
{
|
|
WORD cWinrects;
|
|
LPWINRECT lpWinrects;
|
|
WORD iHwnd;
|
|
HWND hwnd;
|
|
|
|
cWinrects = 0;
|
|
lpWinrects = NULL;
|
|
for (iHwnd=0; iHwnd<*pcHwnd; iHwnd++)
|
|
{
|
|
hwnd = aHwnd[iHwnd];
|
|
if (!IsWindowNormal(hwnd))
|
|
continue;
|
|
|
|
if (IsZoomed(hwnd))
|
|
ShowWindow(hwnd, SW_RESTORE);
|
|
|
|
// Store the info.
|
|
lpWinrects = ReAlloc(lpWinrects, SIZEOF(WINRECT)*(cWinrects+1));
|
|
if (!lpWinrects)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("s.bwfp: Not enough memory to arrange windows."));
|
|
*pcHwnd = 0;
|
|
return NULL;
|
|
}
|
|
lpWinrects[cWinrects].hwnd = hwnd;
|
|
lpWinrects[cWinrects].fTopDone = FALSE;
|
|
lpWinrects[cWinrects].fBottomDone = FALSE;
|
|
lpWinrects[cWinrects].fLeftDone = FALSE;
|
|
lpWinrects[cWinrects].fRightDone = FALSE;
|
|
lpWinrects[cWinrects].fIgnore = FALSE;
|
|
GetWindowRect(hwnd, &lpWinrects[cWinrects].rc);
|
|
// Next.
|
|
cWinrects++;
|
|
}
|
|
|
|
*pcHwnd = cWinrects;
|
|
return lpWinrects;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Tile windows nicely...
|
|
// REVIEW UNDONE Make this act like user.
|
|
// Leave a gap for minimized windows (if you pass in a parent).
|
|
WORD WINAPI ArrangeWindows(HWND hwndParent, WORD flags, LPCRECT lpRect, WORD chwnd, const HWND *ahwnd)
|
|
{
|
|
RECT rcClient;
|
|
LPWINRECT lpWinrects;
|
|
WORD chwndReal;
|
|
#ifdef DEBUG
|
|
UINT dm;
|
|
#endif
|
|
|
|
// Skip the tons of debug msgs, it's too early to remove the messages as
|
|
// as very difficult to debug this stuff using a debugger.
|
|
#ifdef DEBUG
|
|
dm = SetDebugMask(DM_WARNING);
|
|
#endif
|
|
|
|
// Get Parent.
|
|
if (!hwndParent)
|
|
{
|
|
hwndParent = GetDesktopWindow();
|
|
}
|
|
|
|
// Get rectangle to arrange in.
|
|
if (!lpRect)
|
|
{
|
|
if (hwndParent == GetDesktopWindow())
|
|
{
|
|
// Special case the desktop to use the work area instead of it's
|
|
// client area.
|
|
SystemParametersInfo(SPI_GETWORKAREA, SIZEOF(rcClient), &rcClient, FALSE);
|
|
}
|
|
else
|
|
{
|
|
GetClientRect(hwndParent, &rcClient);
|
|
}
|
|
lpRect = &rcClient;
|
|
}
|
|
|
|
// Get the window list.
|
|
if (!ahwnd)
|
|
{
|
|
lpWinrects = BuildWinrectsFromParent(&chwndReal, hwndParent);
|
|
}
|
|
else
|
|
{
|
|
chwndReal = chwnd;
|
|
lpWinrects = BuildWinrectsFromList(&chwndReal, ahwnd);
|
|
}
|
|
|
|
if (!lpWinrects)
|
|
{
|
|
#ifdef DEBUG
|
|
SetDebugMask(dm);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
// Move the rects around.
|
|
#ifdef DEBUG
|
|
DebugDumpRects(chwndReal, lpWinrects);
|
|
#endif
|
|
RearrangeWinrects(chwndReal, lpWinrects, lpRect);
|
|
#ifdef DEBUG
|
|
DebugDumpRects(chwndReal, lpWinrects);
|
|
#endif
|
|
// Now move the windows.
|
|
SetWindowsPosUsingWinrects(chwndReal, lpWinrects);
|
|
|
|
Free(lpWinrects);
|
|
#ifdef DEBUG
|
|
SetDebugMask(dm);
|
|
#endif
|
|
return chwndReal;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|