Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

alignrec.c

Go to the documentation of this file.
00001 /**************************************************************************\ 00002 * Module Name: mergerec.c 00003 * 00004 * Contains all the code to reposition rectangles 00005 * 00006 * Copyright (c) 1985 - 1999, Microsoft Corporation 00007 * 00008 * NOTES: 00009 * 00010 * History: 00011 * 00012 \**************************************************************************/ 00013 00014 #include "precomp.h" 00015 #pragma hdrstop 00016 00017 #define MONITORS_MAX 10 00018 00019 #define RectCenterX(prc) ((prc)->left+((prc)->right-(prc)->left)/2) 00020 #define RectCenterY(prc) ((prc)->top+((prc)->bottom-(prc)->top)/2) 00021 00022 // ---------------------------------------------------------------------------- 00023 // 00024 // INTERSECTION_AXIS() 00025 // This macro tells us how a particular set of overlapping rectangles 00026 // should be adjusted to remove the overlap. It is basically a condensed 00027 // version of a lookup table that does the same job. The parameters for the 00028 // macro are two rectangles, where one is the intersection of the other with 00029 // a third (unspecified) rectangle. The macro compares the edges of the 00030 // rectangles to determine which sides of the intersection were "caused" by 00031 // the source rectangle. In the pre-condensed version of this macro, the 00032 // results of these comparisons (4 bits) would be used to index into a 16 00033 // entry table which specifies the way to resolve the overlap. However, this 00034 // is highly redundant, as the table would actually represents several rotated 00035 // and/or inverted instances of a few basic relationships: 00036 // 00037 // Horizontal Vertical Diagonal Contained Crossing 00038 // *--* *-----* *---* *-----* *----* 00039 // *--+* | | *-* | | *-+-* | *-* | *-+----+-* 00040 // | || | *-+-+-* | | | | | | | | and | | | | 00041 // *--+* | | | *-+-* | | *-* | *-+----+-* 00042 // *--* *-* *---* *-----* *----* 00043 // 00044 // What we are really interested in determining is whether we "should" move 00045 // the rectangles horizontally or vertically to resolve the overlap, hence we 00046 // are testing for three states: Horizontal, Vertical and Don't Know. 00047 // 00048 // The macro gives us these three states by XORing the high and low bits of 00049 // of the comparison to reduce the table to 4 cases where 1 and 2 are 00050 // vertical and horizontal respectively, and then subtracting 1 so that the 00051 // 2 bit signifies "unknown-ness." 00052 // 00053 // Note that there are some one-off cases in the comparisons because we are 00054 // not actually looking at the third rectangle. However this greatly reduces 00055 // the complexity so these small errors are acceptible given the scale of the 00056 // rectangles we are comparing. 00057 // 00058 // ---------------------------------------------------------------------------- 00059 #define INTERSECTION_AXIS(a, b) \ 00060 (((((a->left == b->left) << 1) | (a->top == b->top)) ^ \ 00061 (((a->right == b->right) << 1) | (a->bottom == b->bottom))) - 1) 00062 00063 #define INTERSECTION_AXIS_VERTICAL (0) 00064 #define INTERSECTION_AXIS_HORIZONTAL (1) 00065 #define INTERSECTION_AXIS_UNKNOWN(code) (code & 2) 00066 00067 // ---------------------------------------------------------------------------- 00068 // 00069 // CenterRectangles() 00070 // Move all the rectangles so their origin is the center of their union. 00071 // 00072 // ---------------------------------------------------------------------------- 00073 void NEAR PASCAL CenterRectangles(LPRECT arc, UINT count) 00074 { 00075 LPRECT lprc, lprcL; 00076 RECT rcUnion; 00077 00078 CopyRect(&rcUnion, arc); 00079 00080 lprcL = arc + count; 00081 for (lprc = arc + 1; lprc < lprcL; lprc++) 00082 { 00083 UnionRect(&rcUnion, &rcUnion, lprc); 00084 } 00085 00086 for (lprc = arc; count; count--) 00087 { 00088 OffsetRect(lprc, -RectCenterX(&rcUnion), -RectCenterY(&rcUnion)); 00089 lprc++; 00090 } 00091 } 00092 00093 // ---------------------------------------------------------------------------- 00094 // 00095 // RemoveOverlap() 00096 // This is called from RemoveOverlaps to resolve conflicts when two 00097 // rectangles overlap. It returns the PMONITOR for the monitor it decided to 00098 // move. This routine always moves rectangles away from the origin so it can 00099 // be used to converge on a zero-overlap configuration. 00100 // 00101 // This function will bias slightly toward moving lprc2 (all other things 00102 // being equal). 00103 // 00104 // ---------------------------------------------------------------------------- 00105 LPRECT NEAR PASCAL RemoveOverlap(LPRECT lprc1, LPRECT lprc2, LPRECT lprcI) 00106 { 00107 LPRECT lprcMove, lprcStay; 00108 POINT ptC1, ptC2; 00109 BOOL fNegative; 00110 BOOL fC1Neg; 00111 BOOL fC2Neg; 00112 int dC1, dC2; 00113 int xOffset; 00114 int yOffset; 00115 int nAxis; 00116 00117 // 00118 // Compute the centers of both rectangles. We will need them later. 00119 // 00120 ptC1.x = RectCenterX(lprc1); 00121 ptC1.y = RectCenterY(lprc1); 00122 ptC2.x = RectCenterX(lprc2); 00123 ptC2.y = RectCenterY(lprc2); 00124 00125 // 00126 // Decide whether we should move things horizontally or vertically. All 00127 // this goop is here so it will "feel" right when the system needs to 00128 // move a monitor on you. 00129 // 00130 nAxis = INTERSECTION_AXIS(lprcI, lprc1); 00131 00132 if (INTERSECTION_AXIS_UNKNOWN(nAxis)) 00133 { 00134 // 00135 // Is this a "big" intersection between the two rectangles? 00136 // 00137 if (PtInRect(lprcI, ptC1) || PtInRect(lprcI, ptC2)) 00138 { 00139 // 00140 // This is a "big" overlap. Decide if the rectangles 00141 // are aligned more "horizontal-ish" or "vertical-ish." 00142 // 00143 xOffset = ptC1.x - ptC2.x; 00144 if (xOffset < 0) 00145 xOffset *= -1; 00146 yOffset = ptC1.y - ptC2.y; 00147 if (yOffset < 0) 00148 yOffset *= -1; 00149 00150 if (xOffset >= yOffset) 00151 nAxis = INTERSECTION_AXIS_HORIZONTAL; 00152 else 00153 nAxis = INTERSECTION_AXIS_VERTICAL; 00154 } 00155 else 00156 { 00157 // 00158 // This is a "small" overlap. Move the rectangles the 00159 // smallest distance that will fix the overlap. 00160 // 00161 if ((lprcI->right - lprcI->left) <= (lprcI->bottom - lprcI->top)) 00162 nAxis = INTERSECTION_AXIS_HORIZONTAL; 00163 else 00164 nAxis = INTERSECTION_AXIS_VERTICAL; 00165 } 00166 } 00167 00168 // 00169 // We now need to pick the rectangle to move. Move the one 00170 // that is further from the origin along the axis of motion. 00171 // 00172 if (nAxis == INTERSECTION_AXIS_HORIZONTAL) 00173 { 00174 dC1 = ptC1.x; 00175 dC2 = ptC2.x; 00176 } 00177 else 00178 { 00179 dC1 = ptC1.y; 00180 dC2 = ptC2.y; 00181 } 00182 00183 if ((fC1Neg = (dC1 < 0)) != 0) 00184 dC1 *= -1; 00185 00186 if ((fC2Neg = (dC2 < 0)) != 0) 00187 dC2 *= -1; 00188 00189 if (dC2 < dC1) 00190 { 00191 lprcMove = lprc1; 00192 lprcStay = lprc2; 00193 fNegative = fC1Neg; 00194 } 00195 else 00196 { 00197 lprcMove = lprc2; 00198 lprcStay = lprc1; 00199 fNegative = fC2Neg; 00200 } 00201 00202 // 00203 // Compute a new home for the rectangle and put it there. 00204 // 00205 if (nAxis == INTERSECTION_AXIS_HORIZONTAL) 00206 { 00207 int xPos; 00208 00209 if (fNegative) 00210 xPos = lprcStay->left - (lprcMove->right - lprcMove->left); 00211 else 00212 xPos = lprcStay->right; 00213 00214 xOffset = xPos - lprcMove->left; 00215 yOffset = 0; 00216 } 00217 else 00218 { 00219 int yPos; 00220 00221 if (fNegative) 00222 yPos = lprcStay->top - (lprcMove->bottom - lprcMove->top); 00223 else 00224 yPos = lprcStay->bottom; 00225 00226 yOffset = yPos - lprcMove->top; 00227 xOffset = 0; 00228 } 00229 00230 OffsetRect(lprcMove, xOffset, yOffset); 00231 return lprcMove; 00232 } 00233 00234 // ---------------------------------------------------------------------------- 00235 // 00236 // RemoveOverlaps() 00237 // This is called from CleanupDesktopRectangles make sure the monitor array 00238 // is non-overlapping. 00239 // 00240 // ---------------------------------------------------------------------------- 00241 void NEAR PASCAL RemoveOverlaps(LPRECT arc, UINT count) 00242 { 00243 LPRECT lprc1, lprc2, lprcL; 00244 00245 // 00246 // Center the rectangles around a common origin. We will move them outward 00247 // when there are conflicts so centering (a) reduces running time and 00248 // hence (b) reduces the chances of totally mangling the positions. 00249 // 00250 CenterRectangles(arc, count); 00251 00252 // 00253 // Now loop through the array fixing any overlaps. 00254 // 00255 lprcL = arc + count; 00256 lprc2 = arc + 1; 00257 00258 ReScan: 00259 while (lprc2 < lprcL) 00260 { 00261 // 00262 // Scan all rectangles before this one looking for intersections. 00263 // 00264 for (lprc1 = arc; lprc1 < lprc2; lprc1++) 00265 { 00266 RECT rcI; 00267 00268 // 00269 // Move one of the rectanges if there is an intersection. 00270 // 00271 if (IntersectRect(&rcI, lprc1, lprc2)) 00272 { 00273 // 00274 // Move one of the rectangles out of the way and then restart 00275 // the scan for overlaps with that rectangle (since moving it 00276 // may have created new overlaps). 00277 // 00278 lprc2 = RemoveOverlap(lprc1, lprc2, &rcI); 00279 goto ReScan; 00280 } 00281 } 00282 00283 lprc2++; 00284 } 00285 } 00286 00287 // ---------------------------------------------------------------------------- 00288 // 00289 // AddNextContiguousRectangle() 00290 // This is called from RemoveGaps to find the next contiguous rectangle 00291 // in the array. If there are no more contiguous rectangles it picks the 00292 // closest rectangle and moves it so it is contiguous. 00293 // 00294 // ---------------------------------------------------------------------------- 00295 LPRECT FAR * NEAR PASCAL AddNextContiguousRectangle(LPRECT FAR *aprc, 00296 LPRECT FAR *pprcSplit, UINT count) 00297 { 00298 LPRECT FAR *pprcL; 00299 LPRECT FAR *pprcTest; 00300 LPRECT FAR *pprcAxis; 00301 LPRECT FAR *pprcDiag; 00302 UINT dAxis = (UINT)-1; 00303 UINT dDiag = (UINT)-1; 00304 POINT dpAxis; 00305 POINT dpDiag; 00306 POINT dpMove; 00307 00308 pprcL = aprc + count; 00309 00310 for (pprcTest = aprc; pprcTest < pprcSplit; pprcTest++) 00311 { 00312 LPRECT lprcTest = *pprcTest; 00313 LPRECT FAR *pprcScan; 00314 00315 for (pprcScan = pprcSplit; pprcScan < pprcL; pprcScan++) 00316 { 00317 RECT rcCheckOverlap; 00318 LPRECT lprcScan = *pprcScan; 00319 LPRECT FAR *pprcCheckOverlap; 00320 LPRECT FAR *FAR *pppBest; 00321 LPPOINT pdpBest; 00322 UINT FAR *pdBest; 00323 UINT dX, dY; 00324 UINT dTotal; 00325 00326 // 00327 // Figure out how far the rectangle may be along both axes. 00328 // Note some of these numbers could be garbage at this point but 00329 // the code below will take care of it. 00330 // 00331 if (lprcScan->right <= lprcTest->left) 00332 dpMove.x = dX = lprcTest->left - lprcScan->right; 00333 else 00334 dpMove.x = -(int)(dX = (lprcScan->left - lprcTest->right)); 00335 00336 if (lprcScan->bottom <= lprcTest->top) 00337 dpMove.y = dY = lprcTest->top - lprcScan->bottom; 00338 else 00339 dpMove.y = -(int)(dY = (lprcScan->top - lprcTest->bottom)); 00340 00341 // 00342 // Figure out whether the rectangles are vertical, horizontal or 00343 // diagonal to each other and pick the measurements we will test. 00344 // 00345 if ((lprcScan->top < lprcTest->bottom) && 00346 (lprcScan->bottom > lprcTest->top)) 00347 { 00348 // The rectangles are somewhat horizontally aligned. 00349 dpMove.y = dY = 0; 00350 pppBest = &pprcAxis; 00351 pdpBest = &dpAxis; 00352 pdBest = &dAxis; 00353 } 00354 else if ((lprcScan->left < lprcTest->right) && 00355 (lprcScan->right > lprcTest->left)) 00356 { 00357 // The rectangles are somewhat vertically aligned. 00358 dpMove.x = dX = 0; 00359 pppBest = &pprcAxis; 00360 pdpBest = &dpAxis; 00361 pdBest = &dAxis; 00362 } 00363 else 00364 { 00365 // The rectangles are somewhat diagonally aligned. 00366 pppBest = &pprcDiag; 00367 pdpBest = &dpDiag; 00368 pdBest = &dDiag; 00369 } 00370 00371 // 00372 // Make sure there aren't other rectangles in the way. We only 00373 // need to check the upper array since that is the pool of 00374 // semi-placed rectangles. Any rectangles in the lower array that 00375 // are "in the way" will be found in a different iteration of the 00376 // enclosing loop. 00377 // 00378 00379 CopyRect(&rcCheckOverlap, lprcScan); 00380 OffsetRect(&rcCheckOverlap, dpMove.x, dpMove.y); 00381 00382 for (pprcCheckOverlap = pprcScan + 1; pprcCheckOverlap < pprcL; 00383 pprcCheckOverlap++) 00384 { 00385 RECT rc; 00386 if (IntersectRect(&rc, *pprcCheckOverlap, &rcCheckOverlap)) 00387 break; 00388 } 00389 if (pprcCheckOverlap < pprcL) 00390 { 00391 // There was another rectangle in the way; don't use this one. 00392 continue; 00393 } 00394 00395 // 00396 // If it is closer than the one we already had, use it instead. 00397 // 00398 dTotal = dX + dY; 00399 if (dTotal < *pdBest) 00400 { 00401 *pdBest = dTotal; 00402 *pdpBest = dpMove; 00403 *pppBest = pprcScan; 00404 } 00405 } 00406 } 00407 00408 // 00409 // If we found anything along an axis use that otherwise use a diagonal. 00410 // 00411 if (dAxis != (UINT)-1) 00412 { 00413 pprcSplit = pprcAxis; 00414 dpMove = dpAxis; 00415 } 00416 else if (dDiag != (UINT)-1) 00417 { 00418 // BUGBUG: consider moving the rectangle to a side in this case. 00419 // (that, of course would add a lot of code to avoid collisions) 00420 pprcSplit = pprcDiag; 00421 dpMove = dpDiag; 00422 } 00423 else 00424 dpMove.x = dpMove.y = 0; 00425 00426 // 00427 // Move the monitor into place and return it as the one we chose. 00428 // 00429 if (dpMove.x || dpMove.y) 00430 OffsetRect(*pprcSplit, dpMove.x, dpMove.y); 00431 00432 return pprcSplit; 00433 } 00434 00435 // ---------------------------------------------------------------------------- 00436 // 00437 // RemoveGaps() 00438 // This is called from CleanupDesktopRectangles to make sure the monitor 00439 // array is contiguous. It assumes that the array is already non-overlapping. 00440 // 00441 // ---------------------------------------------------------------------------- 00442 void NEAR PASCAL RemoveGaps(LPRECT arc, UINT count) 00443 { 00444 LPRECT aprc[MONITORS_MAX]; 00445 LPRECT lprc, lprcL, lprcSwap, FAR *pprc, FAR *pprcNearest; 00446 UINT uNearest; 00447 00448 // 00449 // We will need to find the rectangle closest to the center of the group. 00450 // We don't really need to center the array here but it doesn't hurt and 00451 // saves us some code below. 00452 // 00453 CenterRectangles(arc, count); 00454 00455 // 00456 // Build an array of LPRECTs we can shuffle around with relative ease while 00457 // not disturbing the order of the passed array. Also take note of which 00458 // one is closest to the center so we start with it and pull the rest of 00459 // the rectangles inward. This can make a big difference in placement when 00460 // there are more than 2 rectangles. 00461 // 00462 uNearest = (UINT)-1; 00463 pprc = aprc; 00464 lprcL = (lprc = arc) + count; 00465 00466 while (lprc < lprcL) 00467 { 00468 int x, y; 00469 UINT u; 00470 00471 // 00472 // Fill in the array. 00473 // 00474 *pprc = lprc; 00475 00476 // 00477 // Check if this one is closer to the center of the group. 00478 // 00479 x = RectCenterX(lprc); 00480 y = RectCenterY(lprc); 00481 if (x < 0) x *= -1; 00482 if (y < 0) y *= -1; 00483 00484 u = (UINT)x + (UINT)y; 00485 if (u < uNearest) 00486 { 00487 uNearest = u; 00488 pprcNearest = pprc; 00489 } 00490 00491 pprc++; 00492 lprc++; 00493 } 00494 00495 // 00496 // Now make sure we move everything toward the centermost rectangle. 00497 // 00498 if (pprcNearest != aprc) 00499 { 00500 lprcSwap = *pprcNearest; 00501 *pprcNearest = *aprc; 00502 *aprc = lprcSwap; 00503 } 00504 00505 // 00506 // Finally, loop through the array closing any gaps. 00507 // 00508 pprc = aprc + 1; 00509 for (lprc = arc + 1; lprc < lprcL; pprc++, lprc++) 00510 { 00511 // 00512 // Find the next suitable rectangle to combine into the group and move 00513 // it into position. 00514 // 00515 pprcNearest = AddNextContiguousRectangle(aprc, pprc, count); 00516 00517 // 00518 // If the rectangle that was added is not the next in our array, swap. 00519 // 00520 if (pprcNearest != pprc) 00521 { 00522 lprcSwap = *pprcNearest; 00523 *pprcNearest = *pprc; 00524 *pprc = lprcSwap; 00525 } 00526 } 00527 } 00528 00529 // ---------------------------------------------------------------------------- 00530 // 00531 // CleanUpDesktopRectangles() 00532 // This is called by CleanUpMonitorRectangles (etc) to force a set of 00533 // rectangles into a contiguous, non-overlapping arrangement. 00534 // 00535 // ---------------------------------------------------------------------------- 00536 00537 BOOL 00538 AlignRects(LPRECT arc, DWORD cCount, DWORD iPrimary, DWORD dwFlags) 00539 { 00540 LPRECT lprc, lprcL; 00541 00542 // 00543 // Limit for loops. 00544 // 00545 00546 lprcL = arc + cCount; 00547 00548 // 00549 // We don't need to get all worked up if there is only one rectangle. 00550 // 00551 00552 if (cCount > MONITORS_MAX) 00553 { 00554 return FALSE; 00555 } 00556 00557 00558 if (cCount > 1) 00559 { 00560 if (!(dwFlags & CUDR_NOSNAPTOGRID)) 00561 { 00562 // 00563 // Align monitors on 8 pixel boundaries so GDI can use the same 00564 // brush realization on compatible devices (BIG performance win). 00565 // Note that we assume the size of a monitor will be in multiples 00566 // of 8 pixels on X and Y. We cannot do this for the work areas so 00567 // we convert them to be relative to the origins of their monitors 00568 // for the time being. 00569 // 00570 // The way we do this alignment is to just do the overlap/gap 00571 // resoluton in 8 pixel space (ie divide everything by 8 beforehand 00572 // and multiply it by 8 afterward). 00573 // 00574 // Note: WE CAN'T USE MULTDIV HERE because it introduces one-off 00575 // errors when monitors span the origin. These become eight-off 00576 // errors when we scale things back up and we end up trying to 00577 // create DCs with sizes like 632x472 etc (not too good). It also 00578 // handles rounding the wierdly in both positive and negative space 00579 // and we just want to snap things to a grid so we compensate for 00580 // truncation differently here. 00581 // 00582 for (lprc = arc; lprc < lprcL; lprc++) 00583 { 00584 RECT rc; 00585 int d; 00586 00587 00588 CopyRect(&rc, lprc); 00589 00590 d = rc.right - rc.left; 00591 00592 if (rc.left < 0) 00593 rc.left -= 4; 00594 else 00595 rc.left += 3; 00596 00597 rc.left /= 8; 00598 rc.right = rc.left + (d / 8); 00599 00600 d = rc.bottom - rc.top; 00601 00602 if (rc.top < 0) 00603 rc.top -= 4; 00604 else 00605 rc.top += 3; 00606 00607 rc.top /= 8; 00608 rc.bottom = rc.top + (d / 8); 00609 00610 CopyRect(lprc, &rc); 00611 } 00612 } 00613 00614 // 00615 // RemoveGaps is designed assuming that none of the rectangles that it 00616 // is passed will overlap. Thus we cannot safely call it if we have 00617 // skipped the call to RemoveOverlaps or it might loop forever. 00618 // 00619 if (!(dwFlags & CUDR_NORESOLVEPOSITIONS)) 00620 { 00621 RemoveOverlaps(arc, cCount); 00622 00623 if (!(dwFlags & CUDR_NOCLOSEGAPS)) 00624 { 00625 RemoveGaps(arc, cCount); 00626 } 00627 } 00628 00629 if (!(dwFlags & CUDR_NOSNAPTOGRID)) 00630 { 00631 // 00632 // Now return the monitor rectangles to pixel units this is a 00633 // simple multiply and MultDiv doesn't offer us any code size 00634 // advantage so (I guess that assumes a bit about the compiler, 00635 // but...) just do it right here. 00636 // 00637 for (lprc = arc; lprc < lprcL; lprc++) 00638 { 00639 lprc->left *= 8; 00640 lprc->top *= 8; 00641 lprc->right *= 8; 00642 lprc->bottom *= 8; 00643 } 00644 } 00645 } 00646 00647 if (!(dwFlags & CUDR_NOPRIMARY)) 00648 { 00649 // 00650 // Reset all the coordinates based on the primaries position, 00651 // so that it is always located at 0,0 00652 // 00653 00654 LONG dx = -((arc + iPrimary)->left); 00655 LONG dy = -((arc + iPrimary)->top); 00656 00657 for (lprc = arc; lprc < lprcL; lprc++) 00658 { 00659 OffsetRect(lprc, dx, dy); 00660 } 00661 } 00662 00663 return TRUE; 00664 }

Generated on Sat May 15 19:39:14 2004 for test by doxygen 1.3.7