trckrect.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10. #include "stdafx.h"
  11. #ifdef AFX_CORE4_SEG
  12. #pragma code_seg(AFX_CORE4_SEG)
  13. #endif
  14. #ifdef _DEBUG
  15. #undef THIS_FILE
  16. static char THIS_FILE[] = __FILE__;
  17. #endif
  18. #define new DEBUG_NEW
  19. /////////////////////////////////////////////////////////////////////////////
  20. // CRectTracker global state
  21. // various GDI objects we need to draw
  22. AFX_STATIC_DATA HCURSOR _afxCursors[10] = { 0, };
  23. AFX_STATIC_DATA HBRUSH _afxHatchBrush = 0;
  24. AFX_STATIC_DATA HPEN _afxBlackDottedPen = 0;
  25. AFX_STATIC_DATA int _afxHandleSize = 0;
  26. void AFX_CDECL AfxTrackerTerm()
  27. {
  28. AfxDeleteObject((HGDIOBJ*)&_afxHatchBrush);
  29. AfxDeleteObject((HGDIOBJ*)&_afxBlackDottedPen);
  30. }
  31. char _afxTrackerTerm = (char)atexit(&AfxTrackerTerm);
  32. // the struct below is used to determine the qualities of a particular handle
  33. struct AFX_HANDLEINFO
  34. {
  35. size_t nOffsetX; // offset within RECT for X coordinate
  36. size_t nOffsetY; // offset within RECT for Y coordinate
  37. int nCenterX; // adjust X by Width()/2 * this number
  38. int nCenterY; // adjust Y by Height()/2 * this number
  39. int nHandleX; // adjust X by handle size * this number
  40. int nHandleY; // adjust Y by handle size * this number
  41. int nInvertX; // handle converts to this when X inverted
  42. int nInvertY; // handle converts to this when Y inverted
  43. };
  44. // this array describes all 8 handles (clock-wise)
  45. AFX_STATIC_DATA const AFX_HANDLEINFO _afxHandleInfo[] =
  46. {
  47. // corner handles (top-left, top-right, bottom-right, bottom-left
  48. { offsetof(RECT, left), offsetof(RECT, top), 0, 0, 0, 0, 1, 3 },
  49. { offsetof(RECT, right), offsetof(RECT, top), 0, 0, -1, 0, 0, 2 },
  50. { offsetof(RECT, right), offsetof(RECT, bottom), 0, 0, -1, -1, 3, 1 },
  51. { offsetof(RECT, left), offsetof(RECT, bottom), 0, 0, 0, -1, 2, 0 },
  52. // side handles (top, right, bottom, left)
  53. { offsetof(RECT, left), offsetof(RECT, top), 1, 0, 0, 0, 4, 6 },
  54. { offsetof(RECT, right), offsetof(RECT, top), 0, 1, -1, 0, 7, 5 },
  55. { offsetof(RECT, left), offsetof(RECT, bottom), 1, 0, 0, -1, 6, 4 },
  56. { offsetof(RECT, left), offsetof(RECT, top), 0, 1, 0, 0, 5, 7 }
  57. };
  58. // the struct below gives us information on the layout of a RECT struct and
  59. // the relationship between its members
  60. struct AFX_RECTINFO
  61. {
  62. size_t nOffsetAcross; // offset of opposite point (ie. left->right)
  63. int nSignAcross; // sign relative to that point (ie. add/subtract)
  64. };
  65. // this array is indexed by the offset of the RECT member / sizeof(int)
  66. AFX_STATIC_DATA const AFX_RECTINFO _afxRectInfo[] =
  67. {
  68. { offsetof(RECT, right), +1 },
  69. { offsetof(RECT, bottom), +1 },
  70. { offsetof(RECT, left), -1 },
  71. { offsetof(RECT, top), -1 },
  72. };
  73. /////////////////////////////////////////////////////////////////////////////
  74. // CRectTracker intitialization
  75. CRectTracker::CRectTracker(LPCRECT lpSrcRect, UINT nStyle)
  76. {
  77. ASSERT(AfxIsValidAddress(lpSrcRect, sizeof(RECT), FALSE));
  78. Construct();
  79. m_rect.CopyRect(lpSrcRect);
  80. m_nStyle = nStyle;
  81. }
  82. void CRectTracker::Construct()
  83. {
  84. // do one-time initialization if necessary
  85. AfxLockGlobals(CRIT_RECTTRACKER);
  86. static BOOL bInitialized;
  87. if (!bInitialized)
  88. {
  89. // sanity checks for assumptions we make in the code
  90. ASSERT(sizeof(((RECT*)NULL)->left) == sizeof(int));
  91. ASSERT(offsetof(RECT, top) > offsetof(RECT, left));
  92. ASSERT(offsetof(RECT, right) > offsetof(RECT, top));
  93. ASSERT(offsetof(RECT, bottom) > offsetof(RECT, right));
  94. if (_afxHatchBrush == NULL)
  95. {
  96. // create the hatch pattern + bitmap
  97. WORD hatchPattern[8];
  98. WORD wPattern = 0x1111;
  99. for (int i = 0; i < 4; i++)
  100. {
  101. hatchPattern[i] = wPattern;
  102. hatchPattern[i+4] = wPattern;
  103. wPattern <<= 1;
  104. }
  105. HBITMAP hatchBitmap = CreateBitmap(8, 8, 1, 1, &hatchPattern);
  106. if (hatchBitmap == NULL)
  107. {
  108. AfxUnlockGlobals(CRIT_RECTTRACKER);
  109. AfxThrowResourceException();
  110. }
  111. // create black hatched brush
  112. _afxHatchBrush = CreatePatternBrush(hatchBitmap);
  113. DeleteObject(hatchBitmap);
  114. if (_afxHatchBrush == NULL)
  115. {
  116. AfxUnlockGlobals(CRIT_RECTTRACKER);
  117. AfxThrowResourceException();
  118. }
  119. }
  120. if (_afxBlackDottedPen == NULL)
  121. {
  122. // create black dotted pen
  123. _afxBlackDottedPen = CreatePen(PS_DOT, 0, RGB(0, 0, 0));
  124. if (_afxBlackDottedPen == NULL)
  125. {
  126. AfxUnlockGlobals(CRIT_RECTTRACKER);
  127. AfxThrowResourceException();
  128. }
  129. }
  130. // Note: all track cursors must live in same module
  131. HINSTANCE hInst = AfxFindResourceHandle(
  132. MAKEINTRESOURCE(AFX_IDC_TRACK4WAY), RT_GROUP_CURSOR);
  133. // initialize the cursor array
  134. _afxCursors[0] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKNWSE));
  135. _afxCursors[1] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKNESW));
  136. _afxCursors[2] = _afxCursors[0];
  137. _afxCursors[3] = _afxCursors[1];
  138. _afxCursors[4] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKNS));
  139. _afxCursors[5] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACKWE));
  140. _afxCursors[6] = _afxCursors[4];
  141. _afxCursors[7] = _afxCursors[5];
  142. _afxCursors[8] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_TRACK4WAY));
  143. _afxCursors[9] = ::LoadCursor(hInst, MAKEINTRESOURCE(AFX_IDC_MOVE4WAY));
  144. // get default handle size from Windows profile setting
  145. static const TCHAR szWindows[] = _T("windows");
  146. static const TCHAR szInplaceBorderWidth[] =
  147. _T("oleinplaceborderwidth");
  148. _afxHandleSize = GetProfileInt(szWindows, szInplaceBorderWidth, 4);
  149. bInitialized = TRUE;
  150. }
  151. AfxUnlockGlobals(CRIT_RECTTRACKER);
  152. m_nStyle = 0;
  153. m_nHandleSize = _afxHandleSize;
  154. m_sizeMin.cy = m_sizeMin.cx = m_nHandleSize*2;
  155. m_rectLast.SetRectEmpty();
  156. m_sizeLast.cx = m_sizeLast.cy = 0;
  157. m_bErase = FALSE;
  158. m_bFinalErase = FALSE;
  159. }
  160. CRectTracker::~CRectTracker()
  161. {
  162. }
  163. /////////////////////////////////////////////////////////////////////////////
  164. // CRectTracker operations
  165. void CRectTracker::Draw(CDC* pDC) const
  166. {
  167. // set initial DC state
  168. VERIFY(pDC->SaveDC() != 0);
  169. pDC->SetMapMode(MM_TEXT);
  170. pDC->SetViewportOrg(0, 0);
  171. pDC->SetWindowOrg(0, 0);
  172. // get normalized rectangle
  173. CRect rect = m_rect;
  174. rect.NormalizeRect();
  175. CPen* pOldPen = NULL;
  176. CBrush* pOldBrush = NULL;
  177. CGdiObject* pTemp;
  178. int nOldROP;
  179. // draw lines
  180. if ((m_nStyle & (dottedLine|solidLine)) != 0)
  181. {
  182. if (m_nStyle & dottedLine)
  183. pOldPen = pDC->SelectObject(CPen::FromHandle(_afxBlackDottedPen));
  184. else
  185. pOldPen = (CPen*)pDC->SelectStockObject(BLACK_PEN);
  186. pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
  187. nOldROP = pDC->SetROP2(R2_COPYPEN);
  188. rect.InflateRect(+1, +1); // borders are one pixel outside
  189. pDC->Rectangle(rect.left, rect.top, rect.right, rect.bottom);
  190. pDC->SetROP2(nOldROP);
  191. }
  192. // if hatchBrush is going to be used, need to unrealize it
  193. if ((m_nStyle & (hatchInside|hatchedBorder)) != 0)
  194. UnrealizeObject(_afxHatchBrush);
  195. // hatch inside
  196. if ((m_nStyle & hatchInside) != 0)
  197. {
  198. pTemp = pDC->SelectStockObject(NULL_PEN);
  199. if (pOldPen == NULL)
  200. pOldPen = (CPen*)pTemp;
  201. pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
  202. if (pOldBrush == NULL)
  203. pOldBrush = (CBrush*)pTemp;
  204. pDC->SetBkMode(TRANSPARENT);
  205. nOldROP = pDC->SetROP2(R2_MASKNOTPEN);
  206. pDC->Rectangle(rect.left+1, rect.top+1, rect.right, rect.bottom);
  207. pDC->SetROP2(nOldROP);
  208. }
  209. // draw hatched border
  210. if ((m_nStyle & hatchedBorder) != 0)
  211. {
  212. pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
  213. if (pOldBrush == NULL)
  214. pOldBrush = (CBrush*)pTemp;
  215. pDC->SetBkMode(OPAQUE);
  216. CRect rectTrue;
  217. GetTrueRect(&rectTrue);
  218. pDC->PatBlt(rectTrue.left, rectTrue.top, rectTrue.Width(),
  219. rect.top-rectTrue.top, 0x000F0001 /* Pn */);
  220. pDC->PatBlt(rectTrue.left, rect.bottom,
  221. rectTrue.Width(), rectTrue.bottom-rect.bottom, 0x000F0001 /* Pn */);
  222. pDC->PatBlt(rectTrue.left, rect.top, rect.left-rectTrue.left,
  223. rect.Height(), 0x000F0001 /* Pn */);
  224. pDC->PatBlt(rect.right, rect.top, rectTrue.right-rect.right,
  225. rect.Height(), 0x000F0001 /* Pn */);
  226. }
  227. // draw resize handles
  228. if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
  229. {
  230. UINT mask = GetHandleMask();
  231. for (int i = 0; i < 8; ++i)
  232. {
  233. if (mask & (1<<i))
  234. {
  235. GetHandleRect((TrackerHit)i, &rect);
  236. pDC->FillSolidRect(rect, RGB(0, 0, 0));
  237. }
  238. }
  239. }
  240. // cleanup pDC state
  241. if (pOldPen != NULL)
  242. pDC->SelectObject(pOldPen);
  243. if (pOldBrush != NULL)
  244. pDC->SelectObject(pOldBrush);
  245. VERIFY(pDC->RestoreDC(-1));
  246. }
  247. BOOL CRectTracker::SetCursor(CWnd* pWnd, UINT nHitTest) const
  248. {
  249. // trackers should only be in client area
  250. if (nHitTest != HTCLIENT)
  251. return FALSE;
  252. // convert cursor position to client co-ordinates
  253. CPoint point;
  254. GetCursorPos(&point);
  255. pWnd->ScreenToClient(&point);
  256. // do hittest and normalize hit
  257. int nHandle = HitTestHandles(point);
  258. if (nHandle < 0)
  259. return FALSE;
  260. // need to normalize the hittest such that we get proper cursors
  261. nHandle = NormalizeHit(nHandle);
  262. // handle special case of hitting area between handles
  263. // (logically the same -- handled as a move -- but different cursor)
  264. if (nHandle == hitMiddle && !m_rect.PtInRect(point))
  265. {
  266. // only for trackers with hatchedBorder (ie. in-place resizing)
  267. if (m_nStyle & hatchedBorder)
  268. nHandle = (TrackerHit)9;
  269. }
  270. ASSERT(nHandle < _countof(_afxCursors));
  271. ::SetCursor(_afxCursors[nHandle]);
  272. return TRUE;
  273. }
  274. int CRectTracker::HitTest(CPoint point) const
  275. {
  276. TrackerHit hitResult = hitNothing;
  277. CRect rectTrue;
  278. GetTrueRect(&rectTrue);
  279. ASSERT(rectTrue.left <= rectTrue.right);
  280. ASSERT(rectTrue.top <= rectTrue.bottom);
  281. if (rectTrue.PtInRect(point))
  282. {
  283. if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
  284. hitResult = (TrackerHit)HitTestHandles(point);
  285. else
  286. hitResult = hitMiddle;
  287. }
  288. return hitResult;
  289. }
  290. int CRectTracker::NormalizeHit(int nHandle) const
  291. {
  292. ASSERT(nHandle <= 8 && nHandle >= -1);
  293. if (nHandle == hitMiddle || nHandle == hitNothing)
  294. return nHandle;
  295. const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
  296. if (m_rect.Width() < 0)
  297. {
  298. nHandle = (TrackerHit)pHandleInfo->nInvertX;
  299. pHandleInfo = &_afxHandleInfo[nHandle];
  300. }
  301. if (m_rect.Height() < 0)
  302. nHandle = (TrackerHit)pHandleInfo->nInvertY;
  303. return nHandle;
  304. }
  305. BOOL CRectTracker::Track(CWnd* pWnd, CPoint point, BOOL bAllowInvert,
  306. CWnd* pWndClipTo)
  307. {
  308. // perform hit testing on the handles
  309. int nHandle = HitTestHandles(point);
  310. if (nHandle < 0)
  311. {
  312. // didn't hit a handle, so just return FALSE
  313. return FALSE;
  314. }
  315. // otherwise, call helper function to do the tracking
  316. m_bAllowInvert = bAllowInvert;
  317. return TrackHandle(nHandle, pWnd, point, pWndClipTo);
  318. }
  319. BOOL CRectTracker::TrackRubberBand(CWnd* pWnd, CPoint point, BOOL bAllowInvert)
  320. {
  321. // simply call helper function to track from bottom right handle
  322. m_bAllowInvert = bAllowInvert;
  323. m_rect.SetRect(point.x, point.y, point.x, point.y);
  324. return TrackHandle(hitBottomRight, pWnd, point, NULL);
  325. }
  326. void CRectTracker::DrawTrackerRect(
  327. LPCRECT lpRect, CWnd* pWndClipTo, CDC* pDC, CWnd* pWnd)
  328. {
  329. // first, normalize the rectangle for drawing
  330. CRect rect = *lpRect;
  331. rect.NormalizeRect();
  332. // convert to client coordinates
  333. if (pWndClipTo != NULL)
  334. {
  335. pWnd->ClientToScreen(&rect);
  336. pWndClipTo->ScreenToClient(&rect);
  337. }
  338. CSize size(0, 0);
  339. if (!m_bFinalErase)
  340. {
  341. // otherwise, size depends on the style
  342. if (m_nStyle & hatchedBorder)
  343. {
  344. size.cx = size.cy = max(1, GetHandleSize(rect)-1);
  345. rect.InflateRect(size);
  346. }
  347. else
  348. {
  349. size.cx = CX_BORDER;
  350. size.cy = CY_BORDER;
  351. }
  352. }
  353. // and draw it
  354. if (m_bFinalErase || !m_bErase)
  355. pDC->DrawDragRect(rect, size, m_rectLast, m_sizeLast);
  356. // remember last rectangles
  357. m_rectLast = rect;
  358. m_sizeLast = size;
  359. }
  360. void CRectTracker::AdjustRect(int nHandle, LPRECT)
  361. {
  362. if (nHandle == hitMiddle)
  363. return;
  364. // convert the handle into locations within m_rect
  365. int *px, *py;
  366. GetModifyPointers(nHandle, &px, &py, NULL, NULL);
  367. // enforce minimum width
  368. int nNewWidth = m_rect.Width();
  369. int nAbsWidth = m_bAllowInvert ? abs(nNewWidth) : nNewWidth;
  370. if (px != NULL && nAbsWidth < m_sizeMin.cx)
  371. {
  372. nNewWidth = nAbsWidth != 0 ? nNewWidth / nAbsWidth : 1;
  373. ASSERT((int*)px - (int*)&m_rect < _countof(_afxRectInfo));
  374. const AFX_RECTINFO* pRectInfo = &_afxRectInfo[(int*)px - (int*)&m_rect];
  375. *px = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
  376. nNewWidth * m_sizeMin.cx * -pRectInfo->nSignAcross;
  377. }
  378. // enforce minimum height
  379. int nNewHeight = m_rect.Height();
  380. int nAbsHeight = m_bAllowInvert ? abs(nNewHeight) : nNewHeight;
  381. if (py != NULL && nAbsHeight < m_sizeMin.cy)
  382. {
  383. nNewHeight = nAbsHeight != 0 ? nNewHeight / nAbsHeight : 1;
  384. ASSERT((int*)py - (int*)&m_rect < _countof(_afxRectInfo));
  385. const AFX_RECTINFO* pRectInfo = &_afxRectInfo[(int*)py - (int*)&m_rect];
  386. *py = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
  387. nNewHeight * m_sizeMin.cy * -pRectInfo->nSignAcross;
  388. }
  389. }
  390. void CRectTracker::GetTrueRect(LPRECT lpTrueRect) const
  391. {
  392. ASSERT(AfxIsValidAddress(lpTrueRect, sizeof(RECT)));
  393. CRect rect = m_rect;
  394. rect.NormalizeRect();
  395. int nInflateBy = 0;
  396. if ((m_nStyle & (resizeOutside|hatchedBorder)) != 0)
  397. nInflateBy += GetHandleSize() - 1;
  398. if ((m_nStyle & (solidLine|dottedLine)) != 0)
  399. ++nInflateBy;
  400. rect.InflateRect(nInflateBy, nInflateBy);
  401. *lpTrueRect = rect;
  402. }
  403. void CRectTracker::OnChangedRect(const CRect& /*rectOld*/)
  404. {
  405. // no default implementation, useful for derived classes
  406. }
  407. /////////////////////////////////////////////////////////////////////////////
  408. // CRectTracker implementation helpers
  409. void CRectTracker::GetHandleRect(int nHandle, CRect* pHandleRect) const
  410. {
  411. ASSERT(nHandle < 8);
  412. // get normalized rectangle of the tracker
  413. CRect rectT = m_rect;
  414. rectT.NormalizeRect();
  415. if ((m_nStyle & (solidLine|dottedLine)) != 0)
  416. rectT.InflateRect(+1, +1);
  417. // since the rectangle itself was normalized, we also have to invert the
  418. // resize handles.
  419. nHandle = NormalizeHit(nHandle);
  420. // handle case of resize handles outside the tracker
  421. int size = GetHandleSize();
  422. if (m_nStyle & resizeOutside)
  423. rectT.InflateRect(size-1, size-1);
  424. // calculate position of the resize handle
  425. int nWidth = rectT.Width();
  426. int nHeight = rectT.Height();
  427. CRect rect;
  428. const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
  429. rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
  430. rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
  431. rect.left += size * pHandleInfo->nHandleX;
  432. rect.top += size * pHandleInfo->nHandleY;
  433. rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2;
  434. rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
  435. rect.right = rect.left + size;
  436. rect.bottom = rect.top + size;
  437. *pHandleRect = rect;
  438. }
  439. int CRectTracker::GetHandleSize(LPCRECT lpRect) const
  440. {
  441. if (lpRect == NULL)
  442. lpRect = &m_rect;
  443. int size = m_nHandleSize;
  444. if (!(m_nStyle & resizeOutside))
  445. {
  446. // make sure size is small enough for the size of the rect
  447. int sizeMax = min(abs(lpRect->right - lpRect->left),
  448. abs(lpRect->bottom - lpRect->top));
  449. if (size * 2 > sizeMax)
  450. size = sizeMax / 2;
  451. }
  452. return size;
  453. }
  454. int CRectTracker::HitTestHandles(CPoint point) const
  455. {
  456. CRect rect;
  457. UINT mask = GetHandleMask();
  458. // see if hit anywhere inside the tracker
  459. GetTrueRect(&rect);
  460. if (!rect.PtInRect(point))
  461. return hitNothing; // totally missed
  462. // see if we hit a handle
  463. for (int i = 0; i < 8; ++i)
  464. {
  465. if (mask & (1<<i))
  466. {
  467. GetHandleRect((TrackerHit)i, &rect);
  468. if (rect.PtInRect(point))
  469. return (TrackerHit)i;
  470. }
  471. }
  472. // last of all, check for non-hit outside of object, between resize handles
  473. if ((m_nStyle & hatchedBorder) == 0)
  474. {
  475. CRect rect = m_rect;
  476. rect.NormalizeRect();
  477. if ((m_nStyle & dottedLine|solidLine) != 0)
  478. rect.InflateRect(+1, +1);
  479. if (!rect.PtInRect(point))
  480. return hitNothing; // must have been between resize handles
  481. }
  482. return hitMiddle; // no handle hit, but hit object (or object border)
  483. }
  484. BOOL CRectTracker::TrackHandle(int nHandle, CWnd* pWnd, CPoint point,
  485. CWnd* pWndClipTo)
  486. {
  487. ASSERT(nHandle >= 0);
  488. ASSERT(nHandle <= 8); // handle 8 is inside the rect
  489. // don't handle if capture already set
  490. if (::GetCapture() != NULL)
  491. return FALSE;
  492. AfxLockTempMaps(); // protect maps while looping
  493. ASSERT(!m_bFinalErase);
  494. // save original width & height in pixels
  495. int nWidth = m_rect.Width();
  496. int nHeight = m_rect.Height();
  497. // set capture to the window which received this message
  498. pWnd->SetCapture();
  499. ASSERT(pWnd == CWnd::GetCapture());
  500. pWnd->UpdateWindow();
  501. if (pWndClipTo != NULL)
  502. pWndClipTo->UpdateWindow();
  503. CRect rectSave = m_rect;
  504. // find out what x/y coords we are supposed to modify
  505. int *px, *py;
  506. int xDiff, yDiff;
  507. GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
  508. xDiff = point.x - xDiff;
  509. yDiff = point.y - yDiff;
  510. // get DC for drawing
  511. CDC* pDrawDC;
  512. if (pWndClipTo != NULL)
  513. {
  514. // clip to arbitrary window by using adjusted Window DC
  515. pDrawDC = pWndClipTo->GetDCEx(NULL, DCX_CACHE);
  516. }
  517. else
  518. {
  519. // otherwise, just use normal DC
  520. pDrawDC = pWnd->GetDC();
  521. }
  522. ASSERT_VALID(pDrawDC);
  523. CRect rectOld;
  524. BOOL bMoved = FALSE;
  525. // get messages until capture lost or cancelled/accepted
  526. for (;;)
  527. {
  528. MSG msg;
  529. VERIFY(::GetMessage(&msg, NULL, 0, 0));
  530. if (CWnd::GetCapture() != pWnd)
  531. break;
  532. switch (msg.message)
  533. {
  534. // handle movement/accept messages
  535. case WM_LBUTTONUP:
  536. case WM_MOUSEMOVE:
  537. rectOld = m_rect;
  538. // handle resize cases (and part of move)
  539. if (px != NULL)
  540. *px = (int)(short)LOWORD(msg.lParam) - xDiff;
  541. if (py != NULL)
  542. *py = (int)(short)HIWORD(msg.lParam) - yDiff;
  543. // handle move case
  544. if (nHandle == hitMiddle)
  545. {
  546. m_rect.right = m_rect.left + nWidth;
  547. m_rect.bottom = m_rect.top + nHeight;
  548. }
  549. // allow caller to adjust the rectangle if necessary
  550. AdjustRect(nHandle, &m_rect);
  551. // only redraw and callback if the rect actually changed!
  552. m_bFinalErase = (msg.message == WM_LBUTTONUP);
  553. if (!rectOld.EqualRect(&m_rect) || m_bFinalErase)
  554. {
  555. if (bMoved)
  556. {
  557. m_bErase = TRUE;
  558. DrawTrackerRect(&rectOld, pWndClipTo, pDrawDC, pWnd);
  559. }
  560. OnChangedRect(rectOld);
  561. if (msg.message != WM_LBUTTONUP)
  562. bMoved = TRUE;
  563. }
  564. if (m_bFinalErase)
  565. goto ExitLoop;
  566. if (!rectOld.EqualRect(&m_rect))
  567. {
  568. m_bErase = FALSE;
  569. DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
  570. }
  571. break;
  572. // handle cancel messages
  573. case WM_KEYDOWN:
  574. if (msg.wParam != VK_ESCAPE)
  575. break;
  576. case WM_RBUTTONDOWN:
  577. if (bMoved)
  578. {
  579. m_bErase = m_bFinalErase = TRUE;
  580. DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
  581. }
  582. m_rect = rectSave;
  583. goto ExitLoop;
  584. // just dispatch rest of the messages
  585. default:
  586. DispatchMessage(&msg);
  587. break;
  588. }
  589. }
  590. ExitLoop:
  591. if (pWndClipTo != NULL)
  592. pWndClipTo->ReleaseDC(pDrawDC);
  593. else
  594. pWnd->ReleaseDC(pDrawDC);
  595. ReleaseCapture();
  596. AfxUnlockTempMaps(FALSE);
  597. // restore rect in case bMoved is still FALSE
  598. if (!bMoved)
  599. m_rect = rectSave;
  600. m_bFinalErase = FALSE;
  601. m_bErase = FALSE;
  602. // return TRUE only if rect has changed
  603. return !rectSave.EqualRect(&m_rect);
  604. }
  605. void CRectTracker::GetModifyPointers(
  606. int nHandle, int** ppx, int** ppy, int* px, int* py)
  607. {
  608. ASSERT(nHandle >= 0);
  609. ASSERT(nHandle <= 8);
  610. if (nHandle == hitMiddle)
  611. nHandle = hitTopLeft; // same as hitting top-left
  612. *ppx = NULL;
  613. *ppy = NULL;
  614. // fill in the part of the rect that this handle modifies
  615. // (Note: handles that map to themselves along a given axis when that
  616. // axis is inverted don't modify the value on that axis)
  617. const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
  618. if (pHandleInfo->nInvertX != nHandle)
  619. {
  620. *ppx = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetX);
  621. if (px != NULL)
  622. *px = **ppx;
  623. }
  624. else
  625. {
  626. // middle handle on X axis
  627. if (px != NULL)
  628. *px = m_rect.left + abs(m_rect.Width()) / 2;
  629. }
  630. if (pHandleInfo->nInvertY != nHandle)
  631. {
  632. *ppy = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetY);
  633. if (py != NULL)
  634. *py = **ppy;
  635. }
  636. else
  637. {
  638. // middle handle on Y axis
  639. if (py != NULL)
  640. *py = m_rect.top + abs(m_rect.Height()) / 2;
  641. }
  642. }
  643. UINT CRectTracker::GetHandleMask() const
  644. {
  645. UINT mask = 0x0F; // always have 4 corner handles
  646. int size = m_nHandleSize*3;
  647. if (abs(m_rect.Width()) - size > 4)
  648. mask |= 0x50;
  649. if (abs(m_rect.Height()) - size > 4)
  650. mask |= 0xA0;
  651. return mask;
  652. }
  653. /////////////////////////////////////////////////////////////////////////////