home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / TRCKRECT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-30  |  20.9 KB  |  768 lines

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