Leaked source code of windows server 2003
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

  1. /**************************************************************************\
  2. * Module Name: mergerec.c
  3. *
  4. * Contains all the code to reposition rectangles
  5. *
  6. * Copyright (c) 1985 - 1999, Microsoft Corporation
  7. *
  8. * NOTES:
  9. *
  10. * History:
  11. *
  12. \**************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #define MONITORS_MAX 10
  16. #define RectCenterX(prc) ((prc)->left+((prc)->right-(prc)->left)/2)
  17. #define RectCenterY(prc) ((prc)->top+((prc)->bottom-(prc)->top)/2)
  18. // ----------------------------------------------------------------------------
  19. //
  20. // INTERSECTION_AXIS()
  21. // This macro tells us how a particular set of overlapping rectangles
  22. // should be adjusted to remove the overlap. It is basically a condensed
  23. // version of a lookup table that does the same job. The parameters for the
  24. // macro are two rectangles, where one is the intersection of the other with
  25. // a third (unspecified) rectangle. The macro compares the edges of the
  26. // rectangles to determine which sides of the intersection were "caused" by
  27. // the source rectangle. In the pre-condensed version of this macro, the
  28. // results of these comparisons (4 bits) would be used to index into a 16
  29. // entry table which specifies the way to resolve the overlap. However, this
  30. // is highly redundant, as the table would actually represents several rotated
  31. // and/or inverted instances of a few basic relationships:
  32. //
  33. // Horizontal Vertical Diagonal Contained Crossing
  34. // *--* *-----* *---* *-----* *----*
  35. // *--+* | | *-* | | *-+-* | *-* | *-+----+-*
  36. // | || | *-+-+-* | | | | | | | | and | | | |
  37. // *--+* | | | *-+-* | | *-* | *-+----+-*
  38. // *--* *-* *---* *-----* *----*
  39. //
  40. // What we are really interested in determining is whether we "should" move
  41. // the rectangles horizontally or vertically to resolve the overlap, hence we
  42. // are testing for three states: Horizontal, Vertical and Don't Know.
  43. //
  44. // The macro gives us these three states by XORing the high and low bits of
  45. // of the comparison to reduce the table to 4 cases where 1 and 2 are
  46. // vertical and horizontal respectively, and then subtracting 1 so that the
  47. // 2 bit signifies "unknown-ness."
  48. //
  49. // Note that there are some one-off cases in the comparisons because we are
  50. // not actually looking at the third rectangle. However this greatly reduces
  51. // the complexity so these small errors are acceptible given the scale of the
  52. // rectangles we are comparing.
  53. //
  54. // ----------------------------------------------------------------------------
  55. #define INTERSECTION_AXIS(a, b) \
  56. (((((a->left == b->left) << 1) | (a->top == b->top)) ^ \
  57. (((a->right == b->right) << 1) | (a->bottom == b->bottom))) - 1)
  58. #define INTERSECTION_AXIS_VERTICAL (0)
  59. #define INTERSECTION_AXIS_HORIZONTAL (1)
  60. #define INTERSECTION_AXIS_UNKNOWN(code) (code & 2)
  61. // ----------------------------------------------------------------------------
  62. //
  63. // CenterRectangles()
  64. // Move all the rectangles so their origin is the center of their union.
  65. //
  66. // ----------------------------------------------------------------------------
  67. void NEAR PASCAL CenterRectangles(LPRECT arc, UINT count)
  68. {
  69. LPRECT lprc, lprcL;
  70. RECT rcUnion;
  71. CopyRect(&rcUnion, arc);
  72. lprcL = arc + count;
  73. for (lprc = arc + 1; lprc < lprcL; lprc++)
  74. {
  75. UnionRect(&rcUnion, &rcUnion, lprc);
  76. }
  77. for (lprc = arc; count; count--)
  78. {
  79. OffsetRect(lprc, -RectCenterX(&rcUnion), -RectCenterY(&rcUnion));
  80. lprc++;
  81. }
  82. }
  83. // ----------------------------------------------------------------------------
  84. //
  85. // RemoveOverlap()
  86. // This is called from RemoveOverlaps to resolve conflicts when two
  87. // rectangles overlap. It returns the PMONITOR for the monitor it decided to
  88. // move. This routine always moves rectangles away from the origin so it can
  89. // be used to converge on a zero-overlap configuration.
  90. //
  91. // This function will bias slightly toward moving lprc2 (all other things
  92. // being equal).
  93. //
  94. // ----------------------------------------------------------------------------
  95. LPRECT NEAR PASCAL RemoveOverlap(LPRECT lprc1, LPRECT lprc2, LPRECT lprcI)
  96. {
  97. LPRECT lprcMove, lprcStay;
  98. POINT ptC1, ptC2;
  99. BOOL fNegative;
  100. BOOL fC1Neg;
  101. BOOL fC2Neg;
  102. int dC1, dC2;
  103. int xOffset;
  104. int yOffset;
  105. int nAxis;
  106. //
  107. // Compute the centers of both rectangles. We will need them later.
  108. //
  109. ptC1.x = RectCenterX(lprc1);
  110. ptC1.y = RectCenterY(lprc1);
  111. ptC2.x = RectCenterX(lprc2);
  112. ptC2.y = RectCenterY(lprc2);
  113. //
  114. // Decide whether we should move things horizontally or vertically. All
  115. // this goop is here so it will "feel" right when the system needs to
  116. // move a monitor on you.
  117. //
  118. nAxis = INTERSECTION_AXIS(lprcI, lprc1);
  119. if (INTERSECTION_AXIS_UNKNOWN(nAxis))
  120. {
  121. //
  122. // Is this a "big" intersection between the two rectangles?
  123. //
  124. if (PtInRect(lprcI, ptC1) || PtInRect(lprcI, ptC2))
  125. {
  126. //
  127. // This is a "big" overlap. Decide if the rectangles
  128. // are aligned more "horizontal-ish" or "vertical-ish."
  129. //
  130. xOffset = ptC1.x - ptC2.x;
  131. if (xOffset < 0)
  132. xOffset *= -1;
  133. yOffset = ptC1.y - ptC2.y;
  134. if (yOffset < 0)
  135. yOffset *= -1;
  136. if (xOffset >= yOffset)
  137. nAxis = INTERSECTION_AXIS_HORIZONTAL;
  138. else
  139. nAxis = INTERSECTION_AXIS_VERTICAL;
  140. }
  141. else
  142. {
  143. //
  144. // This is a "small" overlap. Move the rectangles the
  145. // smallest distance that will fix the overlap.
  146. //
  147. if ((lprcI->right - lprcI->left) <= (lprcI->bottom - lprcI->top))
  148. nAxis = INTERSECTION_AXIS_HORIZONTAL;
  149. else
  150. nAxis = INTERSECTION_AXIS_VERTICAL;
  151. }
  152. }
  153. //
  154. // We now need to pick the rectangle to move. Move the one
  155. // that is further from the origin along the axis of motion.
  156. //
  157. if (nAxis == INTERSECTION_AXIS_HORIZONTAL)
  158. {
  159. dC1 = ptC1.x;
  160. dC2 = ptC2.x;
  161. }
  162. else
  163. {
  164. dC1 = ptC1.y;
  165. dC2 = ptC2.y;
  166. }
  167. if ((fC1Neg = (dC1 < 0)) != 0)
  168. dC1 *= -1;
  169. if ((fC2Neg = (dC2 < 0)) != 0)
  170. dC2 *= -1;
  171. if (dC2 < dC1)
  172. {
  173. lprcMove = lprc1;
  174. lprcStay = lprc2;
  175. fNegative = fC1Neg;
  176. }
  177. else
  178. {
  179. lprcMove = lprc2;
  180. lprcStay = lprc1;
  181. fNegative = fC2Neg;
  182. }
  183. //
  184. // Compute a new home for the rectangle and put it there.
  185. //
  186. if (nAxis == INTERSECTION_AXIS_HORIZONTAL)
  187. {
  188. int xPos;
  189. if (fNegative)
  190. xPos = lprcStay->left - (lprcMove->right - lprcMove->left);
  191. else
  192. xPos = lprcStay->right;
  193. xOffset = xPos - lprcMove->left;
  194. yOffset = 0;
  195. }
  196. else
  197. {
  198. int yPos;
  199. if (fNegative)
  200. yPos = lprcStay->top - (lprcMove->bottom - lprcMove->top);
  201. else
  202. yPos = lprcStay->bottom;
  203. yOffset = yPos - lprcMove->top;
  204. xOffset = 0;
  205. }
  206. OffsetRect(lprcMove, xOffset, yOffset);
  207. return lprcMove;
  208. }
  209. // ----------------------------------------------------------------------------
  210. //
  211. // RemoveOverlaps()
  212. // This is called from CleanupDesktopRectangles make sure the monitor array
  213. // is non-overlapping.
  214. //
  215. // ----------------------------------------------------------------------------
  216. void NEAR PASCAL RemoveOverlaps(LPRECT arc, UINT count)
  217. {
  218. LPRECT lprc1, lprc2, lprcL;
  219. //
  220. // Center the rectangles around a common origin. We will move them outward
  221. // when there are conflicts so centering (a) reduces running time and
  222. // hence (b) reduces the chances of totally mangling the positions.
  223. //
  224. CenterRectangles(arc, count);
  225. //
  226. // Now loop through the array fixing any overlaps.
  227. //
  228. lprcL = arc + count;
  229. lprc2 = arc + 1;
  230. ReScan:
  231. while (lprc2 < lprcL)
  232. {
  233. //
  234. // Scan all rectangles before this one looking for intersections.
  235. //
  236. for (lprc1 = arc; lprc1 < lprc2; lprc1++)
  237. {
  238. RECT rcI;
  239. //
  240. // Move one of the rectanges if there is an intersection.
  241. //
  242. if (IntersectRect(&rcI, lprc1, lprc2))
  243. {
  244. //
  245. // Move one of the rectangles out of the way and then restart
  246. // the scan for overlaps with that rectangle (since moving it
  247. // may have created new overlaps).
  248. //
  249. lprc2 = RemoveOverlap(lprc1, lprc2, &rcI);
  250. goto ReScan;
  251. }
  252. }
  253. lprc2++;
  254. }
  255. }
  256. // ----------------------------------------------------------------------------
  257. //
  258. // AddNextContiguousRectangle()
  259. // This is called from RemoveGaps to find the next contiguous rectangle
  260. // in the array. If there are no more contiguous rectangles it picks the
  261. // closest rectangle and moves it so it is contiguous.
  262. //
  263. // ----------------------------------------------------------------------------
  264. LPRECT FAR * NEAR PASCAL AddNextContiguousRectangle(LPRECT FAR *aprc,
  265. LPRECT FAR *pprcSplit, UINT count)
  266. {
  267. LPRECT FAR *pprcL;
  268. LPRECT FAR *pprcTest;
  269. LPRECT FAR *pprcAxis;
  270. LPRECT FAR *pprcDiag;
  271. UINT dAxis = (UINT)-1;
  272. UINT dDiag = (UINT)-1;
  273. POINT dpAxis;
  274. POINT dpDiag;
  275. POINT dpMove;
  276. pprcL = aprc + count;
  277. for (pprcTest = aprc; pprcTest < pprcSplit; pprcTest++)
  278. {
  279. LPRECT lprcTest = *pprcTest;
  280. LPRECT FAR *pprcScan;
  281. for (pprcScan = pprcSplit; pprcScan < pprcL; pprcScan++)
  282. {
  283. RECT rcCheckOverlap;
  284. LPRECT lprcScan = *pprcScan;
  285. LPRECT FAR *pprcCheckOverlap;
  286. LPRECT FAR *FAR *pppBest;
  287. LPPOINT pdpBest;
  288. UINT FAR *pdBest;
  289. UINT dX, dY;
  290. UINT dTotal;
  291. //
  292. // Figure out how far the rectangle may be along both axes.
  293. // Note some of these numbers could be garbage at this point but
  294. // the code below will take care of it.
  295. //
  296. if (lprcScan->right <= lprcTest->left)
  297. dpMove.x = dX = lprcTest->left - lprcScan->right;
  298. else
  299. dpMove.x = -(int)(dX = (lprcScan->left - lprcTest->right));
  300. if (lprcScan->bottom <= lprcTest->top)
  301. dpMove.y = dY = lprcTest->top - lprcScan->bottom;
  302. else
  303. dpMove.y = -(int)(dY = (lprcScan->top - lprcTest->bottom));
  304. //
  305. // Figure out whether the rectangles are vertical, horizontal or
  306. // diagonal to each other and pick the measurements we will test.
  307. //
  308. if ((lprcScan->top < lprcTest->bottom) &&
  309. (lprcScan->bottom > lprcTest->top))
  310. {
  311. // The rectangles are somewhat horizontally aligned.
  312. dpMove.y = dY = 0;
  313. pppBest = &pprcAxis;
  314. pdpBest = &dpAxis;
  315. pdBest = &dAxis;
  316. }
  317. else if ((lprcScan->left < lprcTest->right) &&
  318. (lprcScan->right > lprcTest->left))
  319. {
  320. // The rectangles are somewhat vertically aligned.
  321. dpMove.x = dX = 0;
  322. pppBest = &pprcAxis;
  323. pdpBest = &dpAxis;
  324. pdBest = &dAxis;
  325. }
  326. else
  327. {
  328. // The rectangles are somewhat diagonally aligned.
  329. pppBest = &pprcDiag;
  330. pdpBest = &dpDiag;
  331. pdBest = &dDiag;
  332. }
  333. //
  334. // Make sure there aren't other rectangles in the way. We only
  335. // need to check the upper array since that is the pool of
  336. // semi-placed rectangles. Any rectangles in the lower array that
  337. // are "in the way" will be found in a different iteration of the
  338. // enclosing loop.
  339. //
  340. CopyRect(&rcCheckOverlap, lprcScan);
  341. OffsetRect(&rcCheckOverlap, dpMove.x, dpMove.y);
  342. for (pprcCheckOverlap = pprcScan + 1; pprcCheckOverlap < pprcL;
  343. pprcCheckOverlap++)
  344. {
  345. RECT rc;
  346. if (IntersectRect(&rc, *pprcCheckOverlap, &rcCheckOverlap))
  347. break;
  348. }
  349. if (pprcCheckOverlap < pprcL)
  350. {
  351. // There was another rectangle in the way; don't use this one.
  352. continue;
  353. }
  354. //
  355. // If it is closer than the one we already had, use it instead.
  356. //
  357. dTotal = dX + dY;
  358. if (dTotal < *pdBest)
  359. {
  360. *pdBest = dTotal;
  361. *pdpBest = dpMove;
  362. *pppBest = pprcScan;
  363. }
  364. }
  365. }
  366. //
  367. // If we found anything along an axis use that otherwise use a diagonal.
  368. //
  369. if (dAxis != (UINT)-1)
  370. {
  371. pprcSplit = pprcAxis;
  372. dpMove = dpAxis;
  373. }
  374. else if (dDiag != (UINT)-1)
  375. {
  376. // NOTE (AndreVa): consider moving the rectangle to a side in this case.
  377. // (that, of course would add a lot of code to avoid collisions)
  378. pprcSplit = pprcDiag;
  379. dpMove = dpDiag;
  380. }
  381. else
  382. dpMove.x = dpMove.y = 0;
  383. //
  384. // Move the monitor into place and return it as the one we chose.
  385. //
  386. if (dpMove.x || dpMove.y)
  387. OffsetRect(*pprcSplit, dpMove.x, dpMove.y);
  388. return pprcSplit;
  389. }
  390. // ----------------------------------------------------------------------------
  391. //
  392. // RemoveGaps()
  393. // This is called from CleanupDesktopRectangles to make sure the monitor
  394. // array is contiguous. It assumes that the array is already non-overlapping.
  395. //
  396. // ----------------------------------------------------------------------------
  397. void NEAR PASCAL RemoveGaps(LPRECT arc, UINT count)
  398. {
  399. LPRECT aprc[MONITORS_MAX];
  400. LPRECT lprc, lprcL, lprcSwap, FAR *pprc, FAR *pprcNearest;
  401. UINT uNearest;
  402. //
  403. // We will need to find the rectangle closest to the center of the group.
  404. // We don't really need to center the array here but it doesn't hurt and
  405. // saves us some code below.
  406. //
  407. CenterRectangles(arc, count);
  408. //
  409. // Build an array of LPRECTs we can shuffle around with relative ease while
  410. // not disturbing the order of the passed array. Also take note of which
  411. // one is closest to the center so we start with it and pull the rest of
  412. // the rectangles inward. This can make a big difference in placement when
  413. // there are more than 2 rectangles.
  414. //
  415. uNearest = (UINT)-1;
  416. pprcNearest = pprc = aprc;
  417. lprcL = (lprc = arc) + count;
  418. while (lprc < lprcL)
  419. {
  420. int x, y;
  421. UINT u;
  422. //
  423. // Fill in the array.
  424. //
  425. *pprc = lprc;
  426. //
  427. // Check if this one is closer to the center of the group.
  428. //
  429. x = RectCenterX(lprc);
  430. y = RectCenterY(lprc);
  431. if (x < 0) x *= -1;
  432. if (y < 0) y *= -1;
  433. u = (UINT)x + (UINT)y;
  434. if (u < uNearest)
  435. {
  436. uNearest = u;
  437. pprcNearest = pprc;
  438. }
  439. pprc++;
  440. lprc++;
  441. }
  442. //
  443. // Now make sure we move everything toward the centermost rectangle.
  444. //
  445. if (pprcNearest != aprc)
  446. {
  447. lprcSwap = *pprcNearest;
  448. *pprcNearest = *aprc;
  449. *aprc = lprcSwap;
  450. }
  451. //
  452. // Finally, loop through the array closing any gaps.
  453. //
  454. pprc = aprc + 1;
  455. for (lprc = arc + 1; lprc < lprcL; pprc++, lprc++)
  456. {
  457. //
  458. // Find the next suitable rectangle to combine into the group and move
  459. // it into position.
  460. //
  461. pprcNearest = AddNextContiguousRectangle(aprc, pprc, count);
  462. //
  463. // If the rectangle that was added is not the next in our array, swap.
  464. //
  465. if (pprcNearest != pprc)
  466. {
  467. lprcSwap = *pprcNearest;
  468. *pprcNearest = *pprc;
  469. *pprc = lprcSwap;
  470. }
  471. }
  472. }
  473. // ----------------------------------------------------------------------------
  474. //
  475. // CleanUpDesktopRectangles()
  476. // This is called by CleanUpMonitorRectangles (etc) to force a set of
  477. // rectangles into a contiguous, non-overlapping arrangement.
  478. //
  479. // ----------------------------------------------------------------------------
  480. BOOL
  481. AlignRects(LPRECT arc, DWORD cCount, DWORD iPrimary, DWORD dwFlags)
  482. {
  483. LPRECT lprc, lprcL;
  484. //
  485. // Limit for loops.
  486. //
  487. lprcL = arc + cCount;
  488. //
  489. // We don't need to get all worked up if there is only one rectangle.
  490. //
  491. if (cCount > MONITORS_MAX)
  492. {
  493. return FALSE;
  494. }
  495. if (cCount > 1)
  496. {
  497. if (!(dwFlags & CUDR_NOSNAPTOGRID))
  498. {
  499. //
  500. // Align monitors on 8 pixel boundaries so GDI can use the same
  501. // brush realization on compatible devices (BIG performance win).
  502. // Note that we assume the size of a monitor will be in multiples
  503. // of 8 pixels on X and Y. We cannot do this for the work areas so
  504. // we convert them to be relative to the origins of their monitors
  505. // for the time being.
  506. //
  507. // The way we do this alignment is to just do the overlap/gap
  508. // resoluton in 8 pixel space (ie divide everything by 8 beforehand
  509. // and multiply it by 8 afterward).
  510. //
  511. // Note: WE CAN'T USE MULTDIV HERE because it introduces one-off
  512. // errors when monitors span the origin. These become eight-off
  513. // errors when we scale things back up and we end up trying to
  514. // create DCs with sizes like 632x472 etc (not too good). It also
  515. // handles rounding the wierdly in both positive and negative space
  516. // and we just want to snap things to a grid so we compensate for
  517. // truncation differently here.
  518. //
  519. for (lprc = arc; lprc < lprcL; lprc++)
  520. {
  521. RECT rc;
  522. int d;
  523. CopyRect(&rc, lprc);
  524. d = rc.right - rc.left;
  525. if (rc.left < 0)
  526. rc.left -= 4;
  527. else
  528. rc.left += 3;
  529. rc.left /= 8;
  530. rc.right = rc.left + (d / 8);
  531. d = rc.bottom - rc.top;
  532. if (rc.top < 0)
  533. rc.top -= 4;
  534. else
  535. rc.top += 3;
  536. rc.top /= 8;
  537. rc.bottom = rc.top + (d / 8);
  538. CopyRect(lprc, &rc);
  539. }
  540. }
  541. //
  542. // RemoveGaps is designed assuming that none of the rectangles that it
  543. // is passed will overlap. Thus we cannot safely call it if we have
  544. // skipped the call to RemoveOverlaps or it might loop forever.
  545. //
  546. if (!(dwFlags & CUDR_NORESOLVEPOSITIONS))
  547. {
  548. RemoveOverlaps(arc, cCount);
  549. if (!(dwFlags & CUDR_NOCLOSEGAPS))
  550. {
  551. RemoveGaps(arc, cCount);
  552. }
  553. }
  554. if (!(dwFlags & CUDR_NOSNAPTOGRID))
  555. {
  556. //
  557. // Now return the monitor rectangles to pixel units this is a
  558. // simple multiply and MultDiv doesn't offer us any code size
  559. // advantage so (I guess that assumes a bit about the compiler,
  560. // but...) just do it right here.
  561. //
  562. for (lprc = arc; lprc < lprcL; lprc++)
  563. {
  564. lprc->left *= 8;
  565. lprc->top *= 8;
  566. lprc->right *= 8;
  567. lprc->bottom *= 8;
  568. }
  569. }
  570. }
  571. if (!(dwFlags & CUDR_NOPRIMARY))
  572. {
  573. //
  574. // Reset all the coordinates based on the primaries position,
  575. // so that it is always located at 0,0
  576. //
  577. LONG dx = -((arc + iPrimary)->left);
  578. LONG dy = -((arc + iPrimary)->top);
  579. for (lprc = arc; lprc < lprcL; lprc++)
  580. {
  581. OffsetRect(lprc, dx, dy);
  582. }
  583. }
  584. return TRUE;
  585. }