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.
664 lines
21 KiB
664 lines
21 KiB
/**************************************************************************\
|
|
* Module Name: mergerec.c
|
|
*
|
|
* Contains all the code to reposition rectangles
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* NOTES:
|
|
*
|
|
* History:
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define MONITORS_MAX 10
|
|
|
|
#define RectCenterX(prc) ((prc)->left+((prc)->right-(prc)->left)/2)
|
|
#define RectCenterY(prc) ((prc)->top+((prc)->bottom-(prc)->top)/2)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// INTERSECTION_AXIS()
|
|
// This macro tells us how a particular set of overlapping rectangles
|
|
// should be adjusted to remove the overlap. It is basically a condensed
|
|
// version of a lookup table that does the same job. The parameters for the
|
|
// macro are two rectangles, where one is the intersection of the other with
|
|
// a third (unspecified) rectangle. The macro compares the edges of the
|
|
// rectangles to determine which sides of the intersection were "caused" by
|
|
// the source rectangle. In the pre-condensed version of this macro, the
|
|
// results of these comparisons (4 bits) would be used to index into a 16
|
|
// entry table which specifies the way to resolve the overlap. However, this
|
|
// is highly redundant, as the table would actually represents several rotated
|
|
// and/or inverted instances of a few basic relationships:
|
|
//
|
|
// Horizontal Vertical Diagonal Contained Crossing
|
|
// *--* *-----* *---* *-----* *----*
|
|
// *--+* | | *-* | | *-+-* | *-* | *-+----+-*
|
|
// | || | *-+-+-* | | | | | | | | and | | | |
|
|
// *--+* | | | *-+-* | | *-* | *-+----+-*
|
|
// *--* *-* *---* *-----* *----*
|
|
//
|
|
// What we are really interested in determining is whether we "should" move
|
|
// the rectangles horizontally or vertically to resolve the overlap, hence we
|
|
// are testing for three states: Horizontal, Vertical and Don't Know.
|
|
//
|
|
// The macro gives us these three states by XORing the high and low bits of
|
|
// of the comparison to reduce the table to 4 cases where 1 and 2 are
|
|
// vertical and horizontal respectively, and then subtracting 1 so that the
|
|
// 2 bit signifies "unknown-ness."
|
|
//
|
|
// Note that there are some one-off cases in the comparisons because we are
|
|
// not actually looking at the third rectangle. However this greatly reduces
|
|
// the complexity so these small errors are acceptible given the scale of the
|
|
// rectangles we are comparing.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
#define INTERSECTION_AXIS(a, b) \
|
|
(((((a->left == b->left) << 1) | (a->top == b->top)) ^ \
|
|
(((a->right == b->right) << 1) | (a->bottom == b->bottom))) - 1)
|
|
|
|
#define INTERSECTION_AXIS_VERTICAL (0)
|
|
#define INTERSECTION_AXIS_HORIZONTAL (1)
|
|
#define INTERSECTION_AXIS_UNKNOWN(code) (code & 2)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// CenterRectangles()
|
|
// Move all the rectangles so their origin is the center of their union.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
void NEAR PASCAL CenterRectangles(LPRECT arc, UINT count)
|
|
{
|
|
LPRECT lprc, lprcL;
|
|
RECT rcUnion;
|
|
|
|
CopyRect(&rcUnion, arc);
|
|
|
|
lprcL = arc + count;
|
|
for (lprc = arc + 1; lprc < lprcL; lprc++)
|
|
{
|
|
UnionRect(&rcUnion, &rcUnion, lprc);
|
|
}
|
|
|
|
for (lprc = arc; count; count--)
|
|
{
|
|
OffsetRect(lprc, -RectCenterX(&rcUnion), -RectCenterY(&rcUnion));
|
|
lprc++;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// RemoveOverlap()
|
|
// This is called from RemoveOverlaps to resolve conflicts when two
|
|
// rectangles overlap. It returns the PMONITOR for the monitor it decided to
|
|
// move. This routine always moves rectangles away from the origin so it can
|
|
// be used to converge on a zero-overlap configuration.
|
|
//
|
|
// This function will bias slightly toward moving lprc2 (all other things
|
|
// being equal).
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
LPRECT NEAR PASCAL RemoveOverlap(LPRECT lprc1, LPRECT lprc2, LPRECT lprcI)
|
|
{
|
|
LPRECT lprcMove, lprcStay;
|
|
POINT ptC1, ptC2;
|
|
BOOL fNegative;
|
|
BOOL fC1Neg;
|
|
BOOL fC2Neg;
|
|
int dC1, dC2;
|
|
int xOffset;
|
|
int yOffset;
|
|
int nAxis;
|
|
|
|
//
|
|
// Compute the centers of both rectangles. We will need them later.
|
|
//
|
|
ptC1.x = RectCenterX(lprc1);
|
|
ptC1.y = RectCenterY(lprc1);
|
|
ptC2.x = RectCenterX(lprc2);
|
|
ptC2.y = RectCenterY(lprc2);
|
|
|
|
//
|
|
// Decide whether we should move things horizontally or vertically. All
|
|
// this goop is here so it will "feel" right when the system needs to
|
|
// move a monitor on you.
|
|
//
|
|
nAxis = INTERSECTION_AXIS(lprcI, lprc1);
|
|
|
|
if (INTERSECTION_AXIS_UNKNOWN(nAxis))
|
|
{
|
|
//
|
|
// Is this a "big" intersection between the two rectangles?
|
|
//
|
|
if (PtInRect(lprcI, ptC1) || PtInRect(lprcI, ptC2))
|
|
{
|
|
//
|
|
// This is a "big" overlap. Decide if the rectangles
|
|
// are aligned more "horizontal-ish" or "vertical-ish."
|
|
//
|
|
xOffset = ptC1.x - ptC2.x;
|
|
if (xOffset < 0)
|
|
xOffset *= -1;
|
|
yOffset = ptC1.y - ptC2.y;
|
|
if (yOffset < 0)
|
|
yOffset *= -1;
|
|
|
|
if (xOffset >= yOffset)
|
|
nAxis = INTERSECTION_AXIS_HORIZONTAL;
|
|
else
|
|
nAxis = INTERSECTION_AXIS_VERTICAL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a "small" overlap. Move the rectangles the
|
|
// smallest distance that will fix the overlap.
|
|
//
|
|
if ((lprcI->right - lprcI->left) <= (lprcI->bottom - lprcI->top))
|
|
nAxis = INTERSECTION_AXIS_HORIZONTAL;
|
|
else
|
|
nAxis = INTERSECTION_AXIS_VERTICAL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now need to pick the rectangle to move. Move the one
|
|
// that is further from the origin along the axis of motion.
|
|
//
|
|
if (nAxis == INTERSECTION_AXIS_HORIZONTAL)
|
|
{
|
|
dC1 = ptC1.x;
|
|
dC2 = ptC2.x;
|
|
}
|
|
else
|
|
{
|
|
dC1 = ptC1.y;
|
|
dC2 = ptC2.y;
|
|
}
|
|
|
|
if ((fC1Neg = (dC1 < 0)) != 0)
|
|
dC1 *= -1;
|
|
|
|
if ((fC2Neg = (dC2 < 0)) != 0)
|
|
dC2 *= -1;
|
|
|
|
if (dC2 < dC1)
|
|
{
|
|
lprcMove = lprc1;
|
|
lprcStay = lprc2;
|
|
fNegative = fC1Neg;
|
|
}
|
|
else
|
|
{
|
|
lprcMove = lprc2;
|
|
lprcStay = lprc1;
|
|
fNegative = fC2Neg;
|
|
}
|
|
|
|
//
|
|
// Compute a new home for the rectangle and put it there.
|
|
//
|
|
if (nAxis == INTERSECTION_AXIS_HORIZONTAL)
|
|
{
|
|
int xPos;
|
|
|
|
if (fNegative)
|
|
xPos = lprcStay->left - (lprcMove->right - lprcMove->left);
|
|
else
|
|
xPos = lprcStay->right;
|
|
|
|
xOffset = xPos - lprcMove->left;
|
|
yOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
int yPos;
|
|
|
|
if (fNegative)
|
|
yPos = lprcStay->top - (lprcMove->bottom - lprcMove->top);
|
|
else
|
|
yPos = lprcStay->bottom;
|
|
|
|
yOffset = yPos - lprcMove->top;
|
|
xOffset = 0;
|
|
}
|
|
|
|
OffsetRect(lprcMove, xOffset, yOffset);
|
|
return lprcMove;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// RemoveOverlaps()
|
|
// This is called from CleanupDesktopRectangles make sure the monitor array
|
|
// is non-overlapping.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
void NEAR PASCAL RemoveOverlaps(LPRECT arc, UINT count)
|
|
{
|
|
LPRECT lprc1, lprc2, lprcL;
|
|
|
|
//
|
|
// Center the rectangles around a common origin. We will move them outward
|
|
// when there are conflicts so centering (a) reduces running time and
|
|
// hence (b) reduces the chances of totally mangling the positions.
|
|
//
|
|
CenterRectangles(arc, count);
|
|
|
|
//
|
|
// Now loop through the array fixing any overlaps.
|
|
//
|
|
lprcL = arc + count;
|
|
lprc2 = arc + 1;
|
|
|
|
ReScan:
|
|
while (lprc2 < lprcL)
|
|
{
|
|
//
|
|
// Scan all rectangles before this one looking for intersections.
|
|
//
|
|
for (lprc1 = arc; lprc1 < lprc2; lprc1++)
|
|
{
|
|
RECT rcI;
|
|
|
|
//
|
|
// Move one of the rectanges if there is an intersection.
|
|
//
|
|
if (IntersectRect(&rcI, lprc1, lprc2))
|
|
{
|
|
//
|
|
// Move one of the rectangles out of the way and then restart
|
|
// the scan for overlaps with that rectangle (since moving it
|
|
// may have created new overlaps).
|
|
//
|
|
lprc2 = RemoveOverlap(lprc1, lprc2, &rcI);
|
|
goto ReScan;
|
|
}
|
|
}
|
|
|
|
lprc2++;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// AddNextContiguousRectangle()
|
|
// This is called from RemoveGaps to find the next contiguous rectangle
|
|
// in the array. If there are no more contiguous rectangles it picks the
|
|
// closest rectangle and moves it so it is contiguous.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
LPRECT FAR * NEAR PASCAL AddNextContiguousRectangle(LPRECT FAR *aprc,
|
|
LPRECT FAR *pprcSplit, UINT count)
|
|
{
|
|
LPRECT FAR *pprcL;
|
|
LPRECT FAR *pprcTest;
|
|
LPRECT FAR *pprcAxis;
|
|
LPRECT FAR *pprcDiag;
|
|
UINT dAxis = (UINT)-1;
|
|
UINT dDiag = (UINT)-1;
|
|
POINT dpAxis;
|
|
POINT dpDiag;
|
|
POINT dpMove;
|
|
|
|
pprcL = aprc + count;
|
|
|
|
for (pprcTest = aprc; pprcTest < pprcSplit; pprcTest++)
|
|
{
|
|
LPRECT lprcTest = *pprcTest;
|
|
LPRECT FAR *pprcScan;
|
|
|
|
for (pprcScan = pprcSplit; pprcScan < pprcL; pprcScan++)
|
|
{
|
|
RECT rcCheckOverlap;
|
|
LPRECT lprcScan = *pprcScan;
|
|
LPRECT FAR *pprcCheckOverlap;
|
|
LPRECT FAR *FAR *pppBest;
|
|
LPPOINT pdpBest;
|
|
UINT FAR *pdBest;
|
|
UINT dX, dY;
|
|
UINT dTotal;
|
|
|
|
//
|
|
// Figure out how far the rectangle may be along both axes.
|
|
// Note some of these numbers could be garbage at this point but
|
|
// the code below will take care of it.
|
|
//
|
|
if (lprcScan->right <= lprcTest->left)
|
|
dpMove.x = dX = lprcTest->left - lprcScan->right;
|
|
else
|
|
dpMove.x = -(int)(dX = (lprcScan->left - lprcTest->right));
|
|
|
|
if (lprcScan->bottom <= lprcTest->top)
|
|
dpMove.y = dY = lprcTest->top - lprcScan->bottom;
|
|
else
|
|
dpMove.y = -(int)(dY = (lprcScan->top - lprcTest->bottom));
|
|
|
|
//
|
|
// Figure out whether the rectangles are vertical, horizontal or
|
|
// diagonal to each other and pick the measurements we will test.
|
|
//
|
|
if ((lprcScan->top < lprcTest->bottom) &&
|
|
(lprcScan->bottom > lprcTest->top))
|
|
{
|
|
// The rectangles are somewhat horizontally aligned.
|
|
dpMove.y = dY = 0;
|
|
pppBest = &pprcAxis;
|
|
pdpBest = &dpAxis;
|
|
pdBest = &dAxis;
|
|
}
|
|
else if ((lprcScan->left < lprcTest->right) &&
|
|
(lprcScan->right > lprcTest->left))
|
|
{
|
|
// The rectangles are somewhat vertically aligned.
|
|
dpMove.x = dX = 0;
|
|
pppBest = &pprcAxis;
|
|
pdpBest = &dpAxis;
|
|
pdBest = &dAxis;
|
|
}
|
|
else
|
|
{
|
|
// The rectangles are somewhat diagonally aligned.
|
|
pppBest = &pprcDiag;
|
|
pdpBest = &dpDiag;
|
|
pdBest = &dDiag;
|
|
}
|
|
|
|
//
|
|
// Make sure there aren't other rectangles in the way. We only
|
|
// need to check the upper array since that is the pool of
|
|
// semi-placed rectangles. Any rectangles in the lower array that
|
|
// are "in the way" will be found in a different iteration of the
|
|
// enclosing loop.
|
|
//
|
|
|
|
CopyRect(&rcCheckOverlap, lprcScan);
|
|
OffsetRect(&rcCheckOverlap, dpMove.x, dpMove.y);
|
|
|
|
for (pprcCheckOverlap = pprcScan + 1; pprcCheckOverlap < pprcL;
|
|
pprcCheckOverlap++)
|
|
{
|
|
RECT rc;
|
|
if (IntersectRect(&rc, *pprcCheckOverlap, &rcCheckOverlap))
|
|
break;
|
|
}
|
|
if (pprcCheckOverlap < pprcL)
|
|
{
|
|
// There was another rectangle in the way; don't use this one.
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If it is closer than the one we already had, use it instead.
|
|
//
|
|
dTotal = dX + dY;
|
|
if (dTotal < *pdBest)
|
|
{
|
|
*pdBest = dTotal;
|
|
*pdpBest = dpMove;
|
|
*pppBest = pprcScan;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found anything along an axis use that otherwise use a diagonal.
|
|
//
|
|
if (dAxis != (UINT)-1)
|
|
{
|
|
pprcSplit = pprcAxis;
|
|
dpMove = dpAxis;
|
|
}
|
|
else if (dDiag != (UINT)-1)
|
|
{
|
|
// NOTE (AndreVa): consider moving the rectangle to a side in this case.
|
|
// (that, of course would add a lot of code to avoid collisions)
|
|
pprcSplit = pprcDiag;
|
|
dpMove = dpDiag;
|
|
}
|
|
else
|
|
dpMove.x = dpMove.y = 0;
|
|
|
|
//
|
|
// Move the monitor into place and return it as the one we chose.
|
|
//
|
|
if (dpMove.x || dpMove.y)
|
|
OffsetRect(*pprcSplit, dpMove.x, dpMove.y);
|
|
|
|
return pprcSplit;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// RemoveGaps()
|
|
// This is called from CleanupDesktopRectangles to make sure the monitor
|
|
// array is contiguous. It assumes that the array is already non-overlapping.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
void NEAR PASCAL RemoveGaps(LPRECT arc, UINT count)
|
|
{
|
|
LPRECT aprc[MONITORS_MAX];
|
|
LPRECT lprc, lprcL, lprcSwap, FAR *pprc, FAR *pprcNearest;
|
|
UINT uNearest;
|
|
|
|
//
|
|
// We will need to find the rectangle closest to the center of the group.
|
|
// We don't really need to center the array here but it doesn't hurt and
|
|
// saves us some code below.
|
|
//
|
|
CenterRectangles(arc, count);
|
|
|
|
//
|
|
// Build an array of LPRECTs we can shuffle around with relative ease while
|
|
// not disturbing the order of the passed array. Also take note of which
|
|
// one is closest to the center so we start with it and pull the rest of
|
|
// the rectangles inward. This can make a big difference in placement when
|
|
// there are more than 2 rectangles.
|
|
//
|
|
uNearest = (UINT)-1;
|
|
pprcNearest = pprc = aprc;
|
|
lprcL = (lprc = arc) + count;
|
|
|
|
while (lprc < lprcL)
|
|
{
|
|
int x, y;
|
|
UINT u;
|
|
|
|
//
|
|
// Fill in the array.
|
|
//
|
|
*pprc = lprc;
|
|
|
|
//
|
|
// Check if this one is closer to the center of the group.
|
|
//
|
|
x = RectCenterX(lprc);
|
|
y = RectCenterY(lprc);
|
|
if (x < 0) x *= -1;
|
|
if (y < 0) y *= -1;
|
|
|
|
u = (UINT)x + (UINT)y;
|
|
if (u < uNearest)
|
|
{
|
|
uNearest = u;
|
|
pprcNearest = pprc;
|
|
}
|
|
|
|
pprc++;
|
|
lprc++;
|
|
}
|
|
|
|
//
|
|
// Now make sure we move everything toward the centermost rectangle.
|
|
//
|
|
if (pprcNearest != aprc)
|
|
{
|
|
lprcSwap = *pprcNearest;
|
|
*pprcNearest = *aprc;
|
|
*aprc = lprcSwap;
|
|
}
|
|
|
|
//
|
|
// Finally, loop through the array closing any gaps.
|
|
//
|
|
pprc = aprc + 1;
|
|
for (lprc = arc + 1; lprc < lprcL; pprc++, lprc++)
|
|
{
|
|
//
|
|
// Find the next suitable rectangle to combine into the group and move
|
|
// it into position.
|
|
//
|
|
pprcNearest = AddNextContiguousRectangle(aprc, pprc, count);
|
|
|
|
//
|
|
// If the rectangle that was added is not the next in our array, swap.
|
|
//
|
|
if (pprcNearest != pprc)
|
|
{
|
|
lprcSwap = *pprcNearest;
|
|
*pprcNearest = *pprc;
|
|
*pprc = lprcSwap;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// CleanUpDesktopRectangles()
|
|
// This is called by CleanUpMonitorRectangles (etc) to force a set of
|
|
// rectangles into a contiguous, non-overlapping arrangement.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
AlignRects(LPRECT arc, DWORD cCount, DWORD iPrimary, DWORD dwFlags)
|
|
{
|
|
LPRECT lprc, lprcL;
|
|
|
|
//
|
|
// Limit for loops.
|
|
//
|
|
|
|
lprcL = arc + cCount;
|
|
|
|
//
|
|
// We don't need to get all worked up if there is only one rectangle.
|
|
//
|
|
|
|
if (cCount > MONITORS_MAX)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (cCount > 1)
|
|
{
|
|
if (!(dwFlags & CUDR_NOSNAPTOGRID))
|
|
{
|
|
//
|
|
// Align monitors on 8 pixel boundaries so GDI can use the same
|
|
// brush realization on compatible devices (BIG performance win).
|
|
// Note that we assume the size of a monitor will be in multiples
|
|
// of 8 pixels on X and Y. We cannot do this for the work areas so
|
|
// we convert them to be relative to the origins of their monitors
|
|
// for the time being.
|
|
//
|
|
// The way we do this alignment is to just do the overlap/gap
|
|
// resoluton in 8 pixel space (ie divide everything by 8 beforehand
|
|
// and multiply it by 8 afterward).
|
|
//
|
|
// Note: WE CAN'T USE MULTDIV HERE because it introduces one-off
|
|
// errors when monitors span the origin. These become eight-off
|
|
// errors when we scale things back up and we end up trying to
|
|
// create DCs with sizes like 632x472 etc (not too good). It also
|
|
// handles rounding the wierdly in both positive and negative space
|
|
// and we just want to snap things to a grid so we compensate for
|
|
// truncation differently here.
|
|
//
|
|
for (lprc = arc; lprc < lprcL; lprc++)
|
|
{
|
|
RECT rc;
|
|
int d;
|
|
|
|
|
|
CopyRect(&rc, lprc);
|
|
|
|
d = rc.right - rc.left;
|
|
|
|
if (rc.left < 0)
|
|
rc.left -= 4;
|
|
else
|
|
rc.left += 3;
|
|
|
|
rc.left /= 8;
|
|
rc.right = rc.left + (d / 8);
|
|
|
|
d = rc.bottom - rc.top;
|
|
|
|
if (rc.top < 0)
|
|
rc.top -= 4;
|
|
else
|
|
rc.top += 3;
|
|
|
|
rc.top /= 8;
|
|
rc.bottom = rc.top + (d / 8);
|
|
|
|
CopyRect(lprc, &rc);
|
|
}
|
|
}
|
|
|
|
//
|
|
// RemoveGaps is designed assuming that none of the rectangles that it
|
|
// is passed will overlap. Thus we cannot safely call it if we have
|
|
// skipped the call to RemoveOverlaps or it might loop forever.
|
|
//
|
|
if (!(dwFlags & CUDR_NORESOLVEPOSITIONS))
|
|
{
|
|
RemoveOverlaps(arc, cCount);
|
|
|
|
if (!(dwFlags & CUDR_NOCLOSEGAPS))
|
|
{
|
|
RemoveGaps(arc, cCount);
|
|
}
|
|
}
|
|
|
|
if (!(dwFlags & CUDR_NOSNAPTOGRID))
|
|
{
|
|
//
|
|
// Now return the monitor rectangles to pixel units this is a
|
|
// simple multiply and MultDiv doesn't offer us any code size
|
|
// advantage so (I guess that assumes a bit about the compiler,
|
|
// but...) just do it right here.
|
|
//
|
|
for (lprc = arc; lprc < lprcL; lprc++)
|
|
{
|
|
lprc->left *= 8;
|
|
lprc->top *= 8;
|
|
lprc->right *= 8;
|
|
lprc->bottom *= 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(dwFlags & CUDR_NOPRIMARY))
|
|
{
|
|
//
|
|
// Reset all the coordinates based on the primaries position,
|
|
// so that it is always located at 0,0
|
|
//
|
|
|
|
LONG dx = -((arc + iPrimary)->left);
|
|
LONG dy = -((arc + iPrimary)->top);
|
|
|
|
for (lprc = arc; lprc < lprcL; lprc++)
|
|
{
|
|
OffsetRect(lprc, dx, dy);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|