home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / trckrect.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  21KB  |  754 lines

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