Source code of Windows XP (NT5)
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.

970 lines
27 KiB

  1. #include "precomp.h"
  2. #include "seltrack.h"
  3. #ifndef _countof
  4. #define _countof(array) (sizeof(array)/sizeof(array[0]))
  5. #endif
  6. #define CX_BORDER 1
  7. #define CY_BORDER 1
  8. /////////////////////////////////////////////////////////////////////////////
  9. // CSelectionTracker global state
  10. // various GDI objects we need to draw
  11. class Statics
  12. {
  13. public:
  14. HCURSOR hCursors[10];
  15. HBRUSH hHatchBrush;
  16. HBRUSH hHalftoneBrush;
  17. HPEN hBlackDottedPen;
  18. int nHandleSize;
  19. int nRefCount;
  20. Statics()
  21. {
  22. hCursors[0] = 0;
  23. hCursors[1] = 0;
  24. hCursors[2] = 0;
  25. hCursors[3] = 0;
  26. hCursors[4] = 0;
  27. hCursors[5] = 0;
  28. hCursors[6] = 0;
  29. hCursors[7] = 0;
  30. hCursors[8] = 0;
  31. hCursors[9] = 0;
  32. hHatchBrush = 0;
  33. hHalftoneBrush = 0;
  34. hBlackDottedPen = 0;
  35. nHandleSize = 0;
  36. nRefCount=0;
  37. }
  38. ~Statics()
  39. {
  40. if (hHatchBrush != 0)
  41. ::DeleteObject(hHatchBrush);
  42. if (hHalftoneBrush != 0)
  43. ::DeleteObject(hHalftoneBrush);
  44. if (hBlackDottedPen != 0)
  45. ::DeleteObject(hBlackDottedPen);
  46. };
  47. };
  48. static Statics* s_pStatics = NULL;
  49. // the struct below is used to determine the qualities of a particular handle
  50. struct HANDLEINFO
  51. {
  52. size_t nOffsetX; // offset within RECT for X coordinate
  53. size_t nOffsetY; // offset within RECT for Y coordinate
  54. int nCenterX; // adjust X by Width()/2 * this number
  55. int nCenterY; // adjust Y by Height()/2 * this number
  56. int nHandleX; // adjust X by handle size * this number
  57. int nHandleY; // adjust Y by handle size * this number
  58. int nInvertX; // handle converts to this when X inverted
  59. int nInvertY; // handle converts to this when Y inverted
  60. };
  61. // this array describes all 8 handles (clock-wise)
  62. static const HANDLEINFO c_HandleInfo[] =
  63. {
  64. // corner handles (top-left, top-right, bottom-right, bottom-left
  65. { offsetof(RECT, left), offsetof(RECT, top), 0, 0, 0, 0, 1, 3 },
  66. { offsetof(RECT, right), offsetof(RECT, top), 0, 0, -1, 0, 0, 2 },
  67. { offsetof(RECT, right), offsetof(RECT, bottom), 0, 0, -1, -1, 3, 1 },
  68. { offsetof(RECT, left), offsetof(RECT, bottom), 0, 0, 0, -1, 2, 0 },
  69. // side handles (top, right, bottom, left)
  70. { offsetof(RECT, left), offsetof(RECT, top), 1, 0, 0, 0, 4, 6 },
  71. { offsetof(RECT, right), offsetof(RECT, top), 0, 1, -1, 0, 7, 5 },
  72. { offsetof(RECT, left), offsetof(RECT, bottom), 1, 0, 0, -1, 6, 4 },
  73. { offsetof(RECT, left), offsetof(RECT, top), 0, 1, 0, 0, 5, 7 }
  74. };
  75. // the struct below gives us information on the layout of a RECT struct and
  76. // the relationship between its members
  77. struct RECTINFO
  78. {
  79. size_t nOffsetAcross; // offset of opposite point (ie. left->right)
  80. int nSignAcross; // sign relative to that point (ie. add/subtract)
  81. };
  82. // this array is indexed by the offset of the RECT member / sizeof(int)
  83. static const RECTINFO c_RectInfo[] =
  84. {
  85. { offsetof(RECT, right), +1 },
  86. { offsetof(RECT, bottom), +1 },
  87. { offsetof(RECT, left), -1 },
  88. { offsetof(RECT, top), -1 },
  89. };
  90. /////////////////////////////////////////////////////////////////////////////
  91. // SelectionTracking intitialization / cleanup
  92. BOOL InitSelectionTracking()
  93. {
  94. // Only call this once.
  95. // Synchronization is the responsibility of the caller.
  96. if (s_pStatics != NULL)
  97. {
  98. s_pStatics->nRefCount++;
  99. return true;
  100. }
  101. s_pStatics = new Statics;
  102. // sanity checks for assumptions we make in the code
  103. ASSERT(sizeof(((RECT*)NULL)->left) == sizeof(int));
  104. ASSERT(offsetof(RECT, top) > offsetof(RECT, left));
  105. ASSERT(offsetof(RECT, right) > offsetof(RECT, top));
  106. ASSERT(offsetof(RECT, bottom) > offsetof(RECT, right));
  107. // create the hatch pattern + bitmap
  108. WORD hatchPattern[8];
  109. WORD wPattern = 0x1111;
  110. for (int i = 0; i < 4; i++)
  111. {
  112. hatchPattern[i] = wPattern;
  113. hatchPattern[i+4] = wPattern;
  114. wPattern <<= 1;
  115. }
  116. HBITMAP hatchBitmap = ::CreateBitmap(8, 8, 1, 1, &hatchPattern);
  117. if (hatchBitmap == NULL)
  118. {
  119. delete s_pStatics;
  120. return false;
  121. }
  122. // create black hatched brush
  123. s_pStatics->hHatchBrush = ::CreatePatternBrush(hatchBitmap);
  124. DeleteObject(hatchBitmap);
  125. if (s_pStatics->hHatchBrush == NULL)
  126. {
  127. delete s_pStatics;
  128. return false;
  129. }
  130. WORD grayPattern[8];
  131. for (int i = 0; i < 8; i++)
  132. grayPattern[i] = (WORD)(0x5555 << (i & 1));
  133. HBITMAP grayBitmap = ::CreateBitmap(8, 8, 1, 1, &grayPattern);
  134. if (grayBitmap == NULL)
  135. {
  136. delete s_pStatics;
  137. return false;
  138. }
  139. s_pStatics->hHalftoneBrush = ::CreatePatternBrush(grayBitmap);
  140. DeleteObject(grayBitmap);
  141. if (s_pStatics->hHalftoneBrush == NULL)
  142. {
  143. delete s_pStatics;
  144. return false;
  145. }
  146. // create black dotted pen
  147. s_pStatics->hBlackDottedPen = ::CreatePen(PS_DOT, 0, RGB(0, 0, 0));
  148. if (s_pStatics->hBlackDottedPen == NULL)
  149. {
  150. delete s_pStatics;
  151. return false;
  152. }
  153. // initialize the cursor array
  154. s_pStatics->hCursors[0] = ::LoadCursor(NULL, IDC_SIZENWSE);
  155. s_pStatics->hCursors[1] = ::LoadCursor(NULL, IDC_SIZENESW);
  156. s_pStatics->hCursors[2] = s_pStatics->hCursors[0];
  157. s_pStatics->hCursors[3] = s_pStatics->hCursors[1];
  158. s_pStatics->hCursors[4] = ::LoadCursor(NULL, IDC_SIZENS);
  159. s_pStatics->hCursors[5] = ::LoadCursor(NULL, IDC_SIZEWE);
  160. s_pStatics->hCursors[6] = s_pStatics->hCursors[4];
  161. s_pStatics->hCursors[7] = s_pStatics->hCursors[5];
  162. s_pStatics->hCursors[8] = ::LoadCursor(NULL, IDC_SIZEALL);
  163. s_pStatics->hCursors[9] = s_pStatics->hCursors[8];
  164. s_pStatics->nHandleSize = 6;
  165. s_pStatics->nRefCount = 1;
  166. return true;
  167. }
  168. void CleanupSelectionTracking()
  169. {
  170. // Only call this once.
  171. // Synchronization is the responsibility of the caller.
  172. if (s_pStatics != NULL)
  173. {
  174. s_pStatics->nRefCount--;
  175. if (s_pStatics->nRefCount == 0)
  176. {
  177. delete s_pStatics;
  178. s_pStatics = NULL;
  179. }
  180. }
  181. }
  182. /////////////////////////////////////////////////////////////////////////////
  183. // CSelectionTracker intitialization
  184. CSelectionTracker::CSelectionTracker()
  185. {
  186. ASSERT(s_pStatics != NULL);
  187. m_uStyle = 0;
  188. m_nHandleSize = s_pStatics->nHandleSize;
  189. m_sizeMin.cy = m_sizeMin.cx = m_nHandleSize*2;
  190. m_rect.SetRectEmpty();
  191. _rectLast.SetRectEmpty();
  192. _sizeLast.cx = _sizeLast.cy = 0;
  193. _bErase = false;
  194. _bFinalErase = false;
  195. _bAllowInvert = true;
  196. }
  197. CSelectionTracker::~CSelectionTracker()
  198. {
  199. }
  200. /////////////////////////////////////////////////////////////////////////////
  201. // CSelectionTracker operations
  202. void CSelectionTracker::Draw(HDC hdc) const
  203. {
  204. ASSERT(s_pStatics != NULL);
  205. // set initial DC state
  206. if (::SaveDC(hdc) == 0)
  207. {
  208. ASSERT(false);
  209. }
  210. ::SetMapMode(hdc, MM_TEXT);
  211. ::SetViewportOrgEx(hdc, 0, 0, NULL);
  212. ::SetWindowOrgEx(hdc, 0, 0, NULL);
  213. // get normalized rectangle
  214. CRect rect = m_rect;
  215. rect.NormalizeRect();
  216. HPEN hOldPen = NULL;
  217. HBRUSH hOldBrush = NULL;
  218. HGDIOBJ hTemp;
  219. int nOldROP;
  220. // draw lines
  221. if ((m_uStyle & (dottedLine|solidLine)) != 0)
  222. {
  223. if (m_uStyle & dottedLine)
  224. {
  225. hOldPen = (HPEN)::SelectObject(hdc, s_pStatics->hBlackDottedPen);
  226. }
  227. else
  228. {
  229. hOldPen = (HPEN)::SelectObject(hdc, GetStockObject(BLACK_PEN));
  230. }
  231. hOldBrush = (HBRUSH)::SelectObject(hdc, GetStockObject(NULL_BRUSH));
  232. nOldROP = ::SetROP2(hdc, R2_COPYPEN);
  233. rect.InflateRect(+1, +1); // borders are one pixel outside
  234. ::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
  235. ::SetROP2(hdc, nOldROP);
  236. }
  237. // if hatchBrush is going to be used, need to unrealize it
  238. if ((m_uStyle & (hatchInside|hatchedBorder)) != 0)
  239. ::UnrealizeObject(s_pStatics->hHatchBrush);
  240. // hatch inside
  241. if ((m_uStyle & hatchInside) != 0)
  242. {
  243. hTemp = ::SelectObject(hdc, GetStockObject(NULL_PEN));
  244. if (hOldPen == NULL)
  245. hOldPen = (HPEN)hTemp;
  246. hTemp = ::SelectObject(hdc, s_pStatics->hHatchBrush);
  247. if (hOldBrush == NULL)
  248. hOldBrush = (HBRUSH)hTemp;
  249. ::SetBkMode(hdc, TRANSPARENT);
  250. nOldROP = ::SetROP2(hdc, R2_MASKNOTPEN);
  251. ::Rectangle(hdc, rect.left+1, rect.top+1, rect.right, rect.bottom);
  252. ::SetROP2(hdc, nOldROP);
  253. }
  254. // draw hatched border
  255. if ((m_uStyle & hatchedBorder) != 0)
  256. {
  257. hTemp = ::SelectObject(hdc, s_pStatics->hHatchBrush);
  258. if (hOldBrush == NULL)
  259. hOldBrush = (HBRUSH)hTemp;
  260. ::SetBkMode(hdc, OPAQUE);
  261. CRect rectTrue;
  262. GetTrueRect(&rectTrue);
  263. ::PatBlt(hdc, rectTrue.left, rectTrue.top, rectTrue.Width(), rect.top-rectTrue.top, 0x000F0001 /* Pn */);
  264. ::PatBlt(hdc, rectTrue.left, rect.bottom, rectTrue.Width(), rectTrue.bottom-rect.bottom, 0x000F0001 /* Pn */);
  265. ::PatBlt(hdc, rectTrue.left, rect.top, rect.left-rectTrue.left, rect.Height(), 0x000F0001 /* Pn */);
  266. ::PatBlt(hdc, rect.right, rect.top, rectTrue.right-rect.right, rect.Height(), 0x000F0001 /* Pn */);
  267. }
  268. // draw resize handles
  269. if ((m_uStyle & (resizeInside|resizeOutside)) != 0)
  270. {
  271. UINT mask = _GetHandleMask();
  272. for (int i = 0; i < 8; ++i)
  273. {
  274. if (mask & (1<<i))
  275. {
  276. _GetHandleRect((TrackerHit)i, &rect);
  277. ::SetBkColor(hdc, RGB(0, 0, 0));
  278. ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
  279. }
  280. }
  281. }
  282. // cleanup pDC state
  283. if (hOldPen != NULL)
  284. ::SelectObject(hdc, hOldPen);
  285. if (hOldBrush != NULL)
  286. ::SelectObject(hdc, hOldBrush);
  287. if (::RestoreDC(hdc, -1) == 0)
  288. {
  289. ASSERT(false);
  290. }
  291. }
  292. BOOL CSelectionTracker::SetCursor(HWND hwnd, LPARAM lParam) const
  293. {
  294. ASSERT(s_pStatics != NULL);
  295. UINT uHitTest = (short)LOWORD(lParam);
  296. // trackers should only be in client area
  297. if (uHitTest != HTCLIENT)
  298. return FALSE;
  299. // convert cursor position to client co-ordinates
  300. CPoint point;
  301. ::GetCursorPos(&point);
  302. ::ScreenToClient(hwnd, &point);
  303. // do hittest and normalize hit
  304. int nHandle = _HitTestHandles(point);
  305. if (nHandle < 0)
  306. return FALSE;
  307. // need to normalize the hittest such that we get proper cursors
  308. nHandle = NormalizeHit(nHandle);
  309. // handle special case of hitting area between handles
  310. // (logically the same -- handled as a move -- but different cursor)
  311. if (nHandle == hitMiddle && !m_rect.PtInRect(point))
  312. {
  313. // only for trackers with hatchedBorder (ie. in-place resizing)
  314. if (m_uStyle & hatchedBorder)
  315. nHandle = (TrackerHit)9;
  316. }
  317. ASSERT(nHandle < _countof(s_pStatics->hCursors));
  318. ::SetCursor(s_pStatics->hCursors[nHandle]);
  319. return TRUE;
  320. }
  321. int CSelectionTracker::HitTest(CPoint point) const
  322. {
  323. ASSERT(s_pStatics != NULL);
  324. TrackerHit hitResult = hitNothing;
  325. CRect rectTrue;
  326. GetTrueRect(&rectTrue);
  327. ASSERT(rectTrue.left <= rectTrue.right);
  328. ASSERT(rectTrue.top <= rectTrue.bottom);
  329. if (rectTrue.PtInRect(point))
  330. {
  331. if ((m_uStyle & (resizeInside|resizeOutside)) != 0)
  332. hitResult = (TrackerHit)_HitTestHandles(point);
  333. else
  334. hitResult = hitMiddle;
  335. }
  336. return hitResult;
  337. }
  338. int CSelectionTracker::NormalizeHit(int nHandle) const
  339. {
  340. ASSERT(s_pStatics != NULL);
  341. ASSERT(nHandle <= 8 && nHandle >= -1);
  342. if (nHandle == hitMiddle || nHandle == hitNothing)
  343. return nHandle;
  344. const HANDLEINFO* pHandleInfo = &c_HandleInfo[nHandle];
  345. if (m_rect.Width() < 0)
  346. {
  347. nHandle = (TrackerHit)pHandleInfo->nInvertX;
  348. pHandleInfo = &c_HandleInfo[nHandle];
  349. }
  350. if (m_rect.Height() < 0)
  351. nHandle = (TrackerHit)pHandleInfo->nInvertY;
  352. return nHandle;
  353. }
  354. BOOL CSelectionTracker::Track(HWND hwnd, CPoint point, BOOL bAllowInvert, HWND hwndClipTo)
  355. {
  356. ASSERT(s_pStatics != NULL);
  357. // perform hit testing on the handles
  358. int nHandle = _HitTestHandles(point);
  359. if (nHandle < 0)
  360. {
  361. // didn't hit a handle, so just return FALSE
  362. return FALSE;
  363. }
  364. if (m_uStyle & lineSelection)
  365. {
  366. bAllowInvert = true;
  367. _sizeMin = CSize(0, 0);
  368. }
  369. else
  370. {
  371. _sizeMin = m_sizeMin;
  372. }
  373. // otherwise, call helper function to do the tracking
  374. _bAllowInvert = bAllowInvert;
  375. return _TrackHandle(nHandle, hwnd, point, hwndClipTo);
  376. }
  377. BOOL CSelectionTracker::TrackRubberBand(HWND hwnd, CPoint point, BOOL bAllowInvert)
  378. {
  379. ASSERT(s_pStatics != NULL);
  380. // simply call helper function to track from bottom right handle
  381. if (m_uStyle & lineSelection)
  382. {
  383. bAllowInvert = true;
  384. _sizeMin = CSize(0, 0);
  385. }
  386. else
  387. {
  388. _sizeMin = m_sizeMin;
  389. }
  390. _bAllowInvert = bAllowInvert;
  391. m_rect.SetRect(point.x, point.y, point.x, point.y);
  392. return _TrackHandle(hitBottomRight, hwnd, point, NULL);
  393. }
  394. void CSelectionTracker::_DrawTrackerRect(LPCRECT lpRect, HWND hwndClipTo, HDC hdc, HWND hwnd)
  395. {
  396. ASSERT(s_pStatics != NULL);
  397. ASSERT(lpRect != NULL);
  398. // first, normalize the rectangle for drawing
  399. CRect rect(0,0,0,0);
  400. if (lpRect)
  401. rect = *lpRect;
  402. if (!(m_uStyle & lineSelection))
  403. {
  404. rect.NormalizeRect();
  405. }
  406. // convert to client coordinates
  407. if (hwndClipTo != NULL)
  408. {
  409. ::ClientToScreen(hwnd, (LPPOINT)(LPRECT)&rect);
  410. ::ClientToScreen(hwnd, ((LPPOINT)(LPRECT)&rect)+1);
  411. if (IS_WINDOW_RTL_MIRRORED(hwnd))
  412. {
  413. LONG temp = rect.left;
  414. rect.left = rect.right;
  415. rect.right = temp;
  416. }
  417. ::ScreenToClient(hwndClipTo, (LPPOINT)(LPRECT)&rect);
  418. ::ScreenToClient(hwndClipTo, ((LPPOINT)(LPRECT)&rect)+1);
  419. if (IS_WINDOW_RTL_MIRRORED(hwndClipTo))
  420. {
  421. LONG temp = rect.left;
  422. rect.left = rect.right;
  423. rect.right = temp;
  424. }
  425. }
  426. CSize size(0, 0);
  427. if (!_bFinalErase)
  428. {
  429. // otherwise, size depends on the style
  430. if (m_uStyle & hatchedBorder)
  431. {
  432. size.cx = size.cy = max(1, _GetHandleSize(rect)-1);
  433. rect.InflateRect(size);
  434. }
  435. else
  436. {
  437. size.cx = CX_BORDER;
  438. size.cy = CY_BORDER;
  439. }
  440. }
  441. // and draw it
  442. if ((_bFinalErase || !_bErase) && hdc)
  443. _DrawDragRect(hdc, rect, size, _rectLast, _sizeLast);
  444. // remember last rectangles
  445. _rectLast = rect;
  446. _sizeLast = size;
  447. }
  448. void CSelectionTracker::_AdjustRect(int nHandle, LPRECT)
  449. {
  450. ASSERT(s_pStatics != NULL);
  451. if (nHandle == hitMiddle)
  452. return;
  453. // convert the handle into locations within m_rect
  454. int *px, *py;
  455. _GetModifyPointers(nHandle, &px, &py, NULL, NULL);
  456. // enforce minimum width
  457. int nNewWidth = m_rect.Width();
  458. int nAbsWidth = _bAllowInvert ? abs(nNewWidth) : nNewWidth;
  459. if (px != NULL && nAbsWidth < _sizeMin.cx)
  460. {
  461. nNewWidth = nAbsWidth != 0 ? nNewWidth / nAbsWidth : 1;
  462. ASSERT((int*)px - (int*)&m_rect < _countof(c_RectInfo));
  463. const RECTINFO* pRectInfo = &c_RectInfo[(int*)px - (int*)&m_rect];
  464. *px = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
  465. nNewWidth * _sizeMin.cx * -pRectInfo->nSignAcross;
  466. }
  467. // enforce minimum height
  468. int nNewHeight = m_rect.Height();
  469. int nAbsHeight = _bAllowInvert ? abs(nNewHeight) : nNewHeight;
  470. if (py != NULL && nAbsHeight < _sizeMin.cy)
  471. {
  472. nNewHeight = nAbsHeight != 0 ? nNewHeight / nAbsHeight : 1;
  473. ASSERT((int*)py - (int*)&m_rect < _countof(c_RectInfo));
  474. const RECTINFO* pRectInfo = &c_RectInfo[(int*)py - (int*)&m_rect];
  475. *py = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
  476. nNewHeight * _sizeMin.cy * -pRectInfo->nSignAcross;
  477. }
  478. }
  479. void CSelectionTracker::GetTrueRect(LPRECT lpTrueRect) const
  480. {
  481. ASSERT(s_pStatics != NULL);
  482. CRect rect = m_rect;
  483. rect.NormalizeRect();
  484. int nInflateBy = 0;
  485. if ((m_uStyle & (resizeOutside|hatchedBorder)) != 0)
  486. nInflateBy += _GetHandleSize() - 1;
  487. if ((m_uStyle & (solidLine|dottedLine)) != 0)
  488. ++nInflateBy;
  489. rect.InflateRect(nInflateBy, nInflateBy);
  490. *lpTrueRect = rect;
  491. }
  492. /////////////////////////////////////////////////////////////////////////////
  493. // CSelectionTracker implementation helpers
  494. void CSelectionTracker::_GetHandleRect(int nHandle, CRect* pHandleRect) const
  495. {
  496. ASSERT(s_pStatics != NULL);
  497. ASSERT(nHandle < 8);
  498. // get normalized rectangle of the tracker
  499. CRect rectT = m_rect;
  500. rectT.NormalizeRect();
  501. if ((m_uStyle & (solidLine|dottedLine)) != 0)
  502. rectT.InflateRect(+1, +1);
  503. // since the rectangle itself was normalized, we also have to invert the
  504. // resize handles.
  505. nHandle = NormalizeHit(nHandle);
  506. // handle case of resize handles outside the tracker
  507. int size = _GetHandleSize();
  508. if (m_uStyle & resizeOutside)
  509. rectT.InflateRect(size-1, size-1);
  510. // calculate position of the resize handle
  511. int nWidth = rectT.Width();
  512. int nHeight = rectT.Height();
  513. CRect rect;
  514. const HANDLEINFO* pHandleInfo = &c_HandleInfo[nHandle];
  515. rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
  516. rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
  517. rect.left += size * pHandleInfo->nHandleX;
  518. rect.top += size * pHandleInfo->nHandleY;
  519. rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
  520. rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
  521. rect.right = rect.left + size;
  522. rect.bottom = rect.top + size;
  523. *pHandleRect = rect;
  524. }
  525. int CSelectionTracker::_GetHandleSize(LPCRECT lpRect) const
  526. {
  527. ASSERT(s_pStatics != NULL);
  528. if (lpRect == NULL)
  529. lpRect = &m_rect;
  530. int size = m_nHandleSize;
  531. if (!(m_uStyle & resizeOutside))
  532. {
  533. // make sure size is small enough for the size of the rect
  534. int sizeMax = min(abs(lpRect->right - lpRect->left),
  535. abs(lpRect->bottom - lpRect->top));
  536. if (size * 2 > sizeMax)
  537. size = sizeMax / 2;
  538. }
  539. return size;
  540. }
  541. int CSelectionTracker::_HitTestHandles(CPoint point) const
  542. {
  543. ASSERT(s_pStatics != NULL);
  544. CRect rect;
  545. UINT mask = _GetHandleMask();
  546. // see if hit anywhere inside the tracker
  547. GetTrueRect(&rect);
  548. if (!rect.PtInRect(point))
  549. return hitNothing; // totally missed
  550. // see if we hit a handle
  551. for (int i = 0; i < 8; ++i)
  552. {
  553. if (mask & (1<<i))
  554. {
  555. _GetHandleRect((TrackerHit)i, &rect);
  556. if (rect.PtInRect(point))
  557. return (TrackerHit)i;
  558. }
  559. }
  560. // last of all, check for non-hit outside of object, between resize handles
  561. if ((m_uStyle & hatchedBorder) == 0)
  562. {
  563. CRect rect = m_rect;
  564. rect.NormalizeRect();
  565. if ((m_uStyle & dottedLine|solidLine) != 0)
  566. rect.InflateRect(+1, +1);
  567. if (!rect.PtInRect(point))
  568. return hitNothing; // must have been between resize handles
  569. }
  570. return hitMiddle; // no handle hit, but hit object (or object border)
  571. }
  572. BOOL CSelectionTracker::_TrackHandle(int nHandle, HWND hwnd, CPoint point, HWND hwndClipTo)
  573. {
  574. ASSERT(s_pStatics != NULL);
  575. ASSERT(nHandle >= 0 && nHandle <= 8); // handle 8 is inside the rect
  576. // don't handle if capture already set
  577. if (::GetCapture() != NULL)
  578. return FALSE;
  579. ASSERT(!_bFinalErase);
  580. // save original width & height in pixels
  581. int nWidth = m_rect.Width();
  582. int nHeight = m_rect.Height();
  583. // set capture to the window which received this message
  584. ::SetCapture(hwnd);
  585. ASSERT(hwnd == ::GetCapture());
  586. UpdateWindow(hwnd);
  587. if (hwndClipTo != NULL)
  588. UpdateWindow(hwndClipTo);
  589. CRect rectSave = m_rect;
  590. // find out what x/y coords we are supposed to modify
  591. int *px, *py;
  592. int xDiff, yDiff;
  593. _GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
  594. xDiff = point.x - xDiff;
  595. yDiff = point.y - yDiff;
  596. // get DC for drawing
  597. HDC hdcDraw;
  598. if (hwndClipTo != NULL)
  599. {
  600. // clip to arbitrary window by using adjusted Window DC
  601. hdcDraw = ::GetDCEx(hwndClipTo, NULL, DCX_CACHE);
  602. }
  603. else
  604. {
  605. // otherwise, just use normal DC
  606. hdcDraw = ::GetDC(hwnd);
  607. }
  608. ASSERT(hdcDraw != NULL);
  609. CRect rectOld;
  610. BOOL bMoved = FALSE;
  611. // get messages until capture lost or cancelled/accepted
  612. for (;;)
  613. {
  614. MSG msg;
  615. if (!::GetMessage(&msg, NULL, 0, 0))
  616. {
  617. ASSERT(false);
  618. }
  619. if (hwnd != ::GetCapture())
  620. break;
  621. switch (msg.message)
  622. {
  623. // handle movement/accept messages
  624. case WM_LBUTTONUP:
  625. case WM_MOUSEMOVE:
  626. rectOld = m_rect;
  627. // handle resize cases (and part of move)
  628. if (px != NULL)
  629. *px = (int)(short)LOWORD(msg.lParam) - xDiff;
  630. if (py != NULL)
  631. *py = (int)(short)HIWORD(msg.lParam) - yDiff;
  632. // handle move case
  633. if (nHandle == hitMiddle)
  634. {
  635. m_rect.right = m_rect.left + nWidth;
  636. m_rect.bottom = m_rect.top + nHeight;
  637. }
  638. // allow caller to adjust the rectangle if necessary
  639. _AdjustRect(nHandle, &m_rect);
  640. // only redraw and callback if the rect actually changed!
  641. _bFinalErase = (msg.message == WM_LBUTTONUP);
  642. if (!rectOld.EqualRect(&m_rect) || _bFinalErase)
  643. {
  644. if (bMoved)
  645. {
  646. _bErase = TRUE;
  647. _DrawTrackerRect(&rectOld, hwndClipTo, hdcDraw, hwnd);
  648. }
  649. if (msg.message != WM_LBUTTONUP)
  650. bMoved = TRUE;
  651. }
  652. if (_bFinalErase)
  653. goto ExitLoop;
  654. if (!rectOld.EqualRect(&m_rect))
  655. {
  656. _bErase = FALSE;
  657. _DrawTrackerRect(&m_rect, hwndClipTo, hdcDraw, hwnd);
  658. }
  659. break;
  660. // handle cancel messages
  661. case WM_KEYDOWN:
  662. if (msg.wParam != VK_ESCAPE)
  663. break;
  664. case WM_RBUTTONDOWN:
  665. if (bMoved)
  666. {
  667. _bErase = _bFinalErase = TRUE;
  668. _DrawTrackerRect(&m_rect, hwndClipTo, hdcDraw, hwnd);
  669. }
  670. m_rect = rectSave;
  671. goto ExitLoop;
  672. // just dispatch rest of the messages
  673. default:
  674. ::DispatchMessage(&msg);
  675. break;
  676. }
  677. }
  678. ExitLoop:
  679. if (hdcDraw != NULL)
  680. {
  681. if (hwndClipTo != NULL)
  682. ::ReleaseDC(hwndClipTo, hdcDraw);
  683. else
  684. ::ReleaseDC(hwnd, hdcDraw);
  685. }
  686. ::ReleaseCapture();
  687. // restore rect in case bMoved is still FALSE
  688. if (!bMoved)
  689. m_rect = rectSave;
  690. _bFinalErase = FALSE;
  691. _bErase = FALSE;
  692. // return TRUE only if rect has changed
  693. return !rectSave.EqualRect(&m_rect);
  694. }
  695. void CSelectionTracker::_GetModifyPointers(int nHandle, int** ppx, int** ppy, int* px, int* py)
  696. {
  697. ASSERT(s_pStatics != NULL);
  698. ASSERT(nHandle >= 0 && nHandle <= 8);
  699. if (nHandle == hitMiddle)
  700. nHandle = hitTopLeft; // same as hitting top-left
  701. *ppx = NULL;
  702. *ppy = NULL;
  703. // fill in the part of the rect that this handle modifies
  704. // (Note: handles that map to themselves along a given axis when that
  705. // axis is inverted don't modify the value on that axis)
  706. const HANDLEINFO* pHandleInfo = &c_HandleInfo[nHandle];
  707. if (pHandleInfo->nInvertX != nHandle)
  708. {
  709. *ppx = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetX);
  710. if (px != NULL)
  711. *px = **ppx;
  712. }
  713. else
  714. {
  715. // middle handle on X axis
  716. if (px != NULL)
  717. *px = m_rect.left + abs(m_rect.Width()) / 2;
  718. }
  719. if (pHandleInfo->nInvertY != nHandle)
  720. {
  721. *ppy = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetY);
  722. if (py != NULL)
  723. *py = **ppy;
  724. }
  725. else
  726. {
  727. // middle handle on Y axis
  728. if (py != NULL)
  729. *py = m_rect.top + abs(m_rect.Height()) / 2;
  730. }
  731. }
  732. UINT CSelectionTracker::_GetHandleMask() const
  733. {
  734. ASSERT(s_pStatics != NULL);
  735. UINT mask;
  736. if (m_uStyle & lineSelection)
  737. {
  738. mask = 0x05;
  739. }
  740. else
  741. {
  742. mask = 0x0F; // always have 4 corner handles
  743. int size = m_nHandleSize*3;
  744. if (abs(m_rect.Width()) - size > 4)
  745. mask |= 0x50;
  746. if (abs(m_rect.Height()) - size > 4)
  747. mask |= 0xA0;
  748. }
  749. return mask;
  750. }
  751. void CSelectionTracker::_DrawDragRect(HDC hdc, LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast)
  752. {
  753. if (m_uStyle & lineSelection)
  754. {
  755. int nOldROP = ::SetROP2(hdc, R2_NOTXORPEN);
  756. HPEN hOldPen =(HPEN)::SelectObject(hdc, (HPEN)s_pStatics->hBlackDottedPen);
  757. if (lpRectLast != NULL)
  758. {
  759. CRect rectLast = *lpRectLast;
  760. ::MoveToEx(hdc, rectLast.left, rectLast.top, NULL);
  761. ::LineTo(hdc, rectLast.right, rectLast.bottom);
  762. }
  763. CRect rect = *lpRect;
  764. ::MoveToEx(hdc, rect.left, rect.top, NULL);
  765. ::LineTo(hdc, rect.right, rect.bottom);
  766. ::SelectObject(hdc, hOldPen);
  767. ::SetROP2(hdc, nOldROP);
  768. }
  769. else
  770. {
  771. // first, determine the update region and select it
  772. HRGN hrgnOutside = ::CreateRectRgnIndirect(lpRect);
  773. CRect rect = *lpRect;
  774. rect.InflateRect(-size.cx, -size.cy);
  775. rect.IntersectRect(rect, lpRect);
  776. HRGN hrgnInside = ::CreateRectRgnIndirect(&rect);
  777. HRGN hrgnNew = ::CreateRectRgn(0, 0, 0, 0);
  778. ::CombineRgn(hrgnNew, hrgnOutside, hrgnInside, RGN_XOR);
  779. HRGN hrgnLast = NULL;
  780. HRGN hrgnUpdate = NULL;
  781. if (lpRectLast != NULL)
  782. {
  783. // find difference between new region and old region
  784. hrgnLast = ::CreateRectRgn(0, 0, 0, 0);
  785. ::SetRectRgn(hrgnOutside, lpRectLast->left, lpRectLast->top, lpRectLast->right, lpRectLast->bottom);
  786. rect = *lpRectLast;
  787. rect.InflateRect(-sizeLast.cx, -sizeLast.cy);
  788. rect.IntersectRect(rect, lpRectLast);
  789. ::SetRectRgn(hrgnInside, rect.left, rect.top, rect.right, rect.bottom);
  790. ::CombineRgn(hrgnLast, hrgnOutside, hrgnInside, RGN_XOR);
  791. hrgnUpdate = ::CreateRectRgn(0, 0, 0, 0);
  792. ::CombineRgn(hrgnUpdate, hrgnLast, hrgnNew, RGN_XOR);
  793. }
  794. // draw into the update/new region
  795. if (hrgnUpdate != NULL)
  796. ::SelectClipRgn(hdc, hrgnUpdate);
  797. else
  798. ::SelectClipRgn(hdc, hrgnNew);
  799. ::GetClipBox(hdc, &rect);
  800. HBRUSH hBrushOld = (HBRUSH)::SelectObject(hdc, s_pStatics->hHalftoneBrush);
  801. ::PatBlt(hdc, rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
  802. ::SelectObject(hdc, hBrushOld);
  803. ::SelectClipRgn(hdc, NULL);
  804. if (hrgnOutside != NULL)
  805. ::DeleteObject(hrgnOutside);
  806. if (hrgnInside != NULL)
  807. ::DeleteObject(hrgnInside);
  808. if (hrgnNew != NULL)
  809. ::DeleteObject(hrgnNew);
  810. if (hrgnLast != NULL)
  811. ::DeleteObject(hrgnLast);
  812. if (hrgnUpdate != NULL)
  813. ::DeleteObject(hrgnUpdate);
  814. }
  815. }
  816. /////////////////////////////////////////////////////////////////////////////