#include "StdAfx.h"
|
#include "CHRectTracker.h"
|
|
AFX_STATIC_DATA HCURSOR _afxCursors[10] = { 0, };
|
AFX_STATIC_DATA HBRUSH _afxHatchBrush = 0;
|
AFX_STATIC_DATA HPEN _afxBlackDottedPen = 0;
|
AFX_STATIC_DATA int _afxHandleSize = 0;
|
|
CCHRectTracker::CCHRectTracker(void)
|
{
|
}
|
|
CCHRectTracker::CCHRectTracker(LPCRECT lpSrcRect, UINT nStyle)
|
{
|
CRectTracker(lpSrcRect, nStyle);
|
}
|
|
CCHRectTracker::~CCHRectTracker(void)
|
{
|
}
|
|
BOOL CCHRectTracker::TrackRubberBand(CWnd* pWnd, CPoint point, BOOL bAllowInvert)
|
{
|
// simply call helper function to track from bottom right handle
|
m_bAllowInvert = bAllowInvert;
|
m_rect.SetRect(point.x, point.y, point.x, point.y);
|
|
return TrackHandle(hitBottomRight, pWnd, point, NULL);
|
}
|
|
BOOL CCHRectTracker::Track(CWnd* pWnd, CPoint point, BOOL bAllowInvert, CWnd* pWndClipTo)
|
{
|
// perform hit testing on the handles
|
int nHandle = HitTestHandles(point);
|
if (nHandle < 0)
|
{
|
// didn't hit a handle, so just return FALSE
|
return FALSE;
|
}
|
|
// otherwise, call helper function to do the tracking
|
m_bAllowInvert = bAllowInvert;
|
|
return TrackHandle(nHandle, pWnd, point, pWndClipTo);
|
}
|
|
void CCHRectTracker::Draw(CDC* pDC) const
|
{
|
// set initial DC state
|
VERIFY(pDC->SaveDC() != 0);
|
#ifndef _AFX_NO_GDITRANSFORM_SUPPORT
|
pDC->SetMapMode(MM_TEXT);
|
pDC->SetViewportOrg(0, 0);
|
pDC->SetWindowOrg(0, 0);
|
#endif // !_AFX_NO_GDITRANSFORM_SUPPORT
|
// get normalized rectangle
|
CRect rect = m_rect;
|
rect.NormalizeRect();
|
|
CPen* pOldPen = NULL;
|
CBrush* pOldBrush = NULL;
|
CGdiObject* pTemp;
|
int nOldROP;
|
|
CPen pen;
|
//pen.CreatePen(PS_DOT, 1, RGB(255,255,255));
|
pen.CreatePen(PS_DOT, 1, RGB(255,0,0));
|
|
rect.InflateRect(+1, +1); // borders are one pixel outside
|
|
// draw lines
|
if ((m_nStyle & (dottedLine|solidLine)) != 0)
|
{
|
if (m_nStyle & dottedLine)
|
pOldPen = pDC->SelectObject(&pen);
|
//pOldPen = (CPen*)pDC->SelectStockObject(WHITE_PEN);
|
else
|
pOldPen = (CPen*)pDC->SelectStockObject(BLACK_PEN);
|
pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
|
nOldROP = pDC->SetROP2(R2_COPYPEN);
|
|
// rect
|
pDC->Rectangle(rect.left, rect.top, rect.right, rect.bottom);
|
|
// cross line
|
if ((m_nStyle & (crossLine)) != 0)
|
{
|
pDC->MoveTo(rect.left+rect.Width()/2, rect.top);
|
pDC->LineTo(rect.left+rect.Width()/2, rect.bottom);
|
pDC->MoveTo(rect.left, rect.top+rect.Height()/2);
|
pDC->LineTo(rect.right, rect.top+rect.Height()/2);
|
}
|
|
// x line
|
if ((m_nStyle & (xLine)) != 0)
|
{
|
pDC->MoveTo(rect.left, rect.top);
|
pDC->LineTo(rect.right, rect.bottom);
|
pDC->MoveTo(rect.right, rect.top);
|
pDC->LineTo(rect.left, rect.bottom);
|
}
|
|
pDC->SetROP2(nOldROP);
|
}
|
|
|
#ifndef _WIN32_WCE // unsupported win32 api call to UnrealizeObject
|
// if hatchBrush is going to be used, need to unrealize it
|
if ((m_nStyle & (hatchInside|hatchedBorder)) != 0)
|
UnrealizeObject(_afxHatchBrush);
|
#endif // !_WIN32_WCE
|
|
// hatch inside
|
if ((m_nStyle & hatchInside) != 0)
|
{
|
pTemp = pDC->SelectStockObject(NULL_PEN);
|
if (pOldPen == NULL)
|
pOldPen = (CPen*)pTemp;
|
pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
|
if (pOldBrush == NULL)
|
pOldBrush = (CBrush*)pTemp;
|
pDC->SetBkMode(TRANSPARENT);
|
nOldROP = pDC->SetROP2(R2_MASKNOTPEN);
|
pDC->Rectangle(rect.left+1, rect.top+1, rect.right, rect.bottom);
|
pDC->SetROP2(nOldROP);
|
}
|
|
// draw hatched border
|
if ((m_nStyle & hatchedBorder) != 0)
|
{
|
pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
|
if (pOldBrush == NULL)
|
pOldBrush = (CBrush*)pTemp;
|
pDC->SetBkMode(OPAQUE);
|
CRect rectTrue;
|
GetTrueRect(&rectTrue);
|
pDC->PatBlt(rectTrue.left, rectTrue.top, rectTrue.Width(),
|
rect.top-rectTrue.top, 0x000F0001 /* Pn */);
|
pDC->PatBlt(rectTrue.left, rect.bottom,
|
rectTrue.Width(), rectTrue.bottom-rect.bottom, 0x000F0001 /* Pn */);
|
pDC->PatBlt(rectTrue.left, rect.top, rect.left-rectTrue.left,
|
rect.Height(), 0x000F0001 /* Pn */);
|
pDC->PatBlt(rect.right, rect.top, rectTrue.right-rect.right,
|
rect.Height(), 0x000F0001 /* Pn */);
|
}
|
|
// draw resize handles
|
if ((m_nStyle & (resizeInside|resizeOutside)) != 0)
|
{
|
UINT mask = GetHandleMask();
|
for (int i = 0; i < 8; ++i)
|
{
|
if (mask & (1<<i))
|
{
|
GetHandleRect((TrackerHit)i, &rect);
|
pDC->FillSolidRect(rect, RGB(255, 255, 255));
|
}
|
}
|
}
|
|
// cleanup pDC state
|
if (pOldPen != NULL)
|
pDC->SelectObject(pOldPen);
|
if (pOldBrush != NULL)
|
pDC->SelectObject(pOldBrush);
|
VERIFY(pDC->RestoreDC(-1));
|
}
|
|
BOOL CCHRectTracker::TrackHandle(int nHandle, CWnd* pWnd, CPoint point, CWnd* pWndClipTo)
|
{
|
ASSERT(nHandle >= 0);
|
ASSERT(nHandle <= 8); // handle 8 is inside the rect
|
|
// don't handle if capture already set
|
if (::GetCapture() != NULL)
|
return FALSE;
|
|
AfxLockTempMaps(); // protect maps while looping
|
|
ASSERT(!m_bFinalErase);
|
|
// save original width & height in pixels
|
int nWidth = m_rect.Width();
|
int nHeight = m_rect.Height();
|
|
// set capture to the window which received this message
|
pWnd->SetCapture();
|
ASSERT(pWnd == CWnd::GetCapture());
|
pWnd->UpdateWindow();
|
if (pWndClipTo != NULL)
|
pWndClipTo->UpdateWindow();
|
CRect rectSave = m_rect;
|
|
// find out what x/y coords we are supposed to modify
|
int *px, *py;
|
int xDiff, yDiff;
|
GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
|
xDiff = point.x - xDiff;
|
yDiff = point.y - yDiff;
|
|
// get DC for drawing
|
CDC* pDrawDC;
|
if (pWndClipTo != NULL)
|
{
|
// clip to arbitrary window by using adjusted Window DC
|
pDrawDC = pWndClipTo->GetDCEx(NULL, DCX_CACHE);
|
}
|
else
|
{
|
// otherwise, just use normal DC
|
pDrawDC = pWnd->GetDC();
|
}
|
ENSURE_VALID(pDrawDC);
|
|
CRect rectOld;
|
BOOL bMoved = FALSE;
|
|
// get messages until capture lost or cancelled/accepted
|
for (;;)
|
{
|
MSG msg;
|
VERIFY(::GetMessage(&msg, NULL, 0, 0));
|
|
if (CWnd::GetCapture() != pWnd)
|
break;
|
|
switch (msg.message)
|
{
|
// handle movement/accept messages
|
case WM_LBUTTONUP:
|
{
|
if (pWnd)
|
pWnd->SendMessage(WM_LBUTTONUP, static_cast<WPARAM>(0), MAKELPARAM(0, 0));
|
}
|
case WM_MOUSEMOVE:
|
rectOld = m_rect;
|
// handle resize cases (and part of move)
|
if (px != NULL)
|
*px = GET_X_LPARAM(msg.lParam) - xDiff;
|
if (py != NULL)
|
*py = GET_Y_LPARAM(msg.lParam) - yDiff;
|
|
// handle move case
|
if (nHandle == hitMiddle)
|
{
|
m_rect.right = m_rect.left + nWidth;
|
m_rect.bottom = m_rect.top + nHeight;
|
}
|
// allow caller to adjust the rectangle if necessary
|
AdjustRect(nHandle, &m_rect);
|
|
// only redraw and callback if the rect actually changed!
|
m_bFinalErase = (msg.message == WM_LBUTTONUP);
|
if (!rectOld.EqualRect(&m_rect) || m_bFinalErase)
|
{
|
if (bMoved)
|
{
|
m_bErase = TRUE;
|
DrawTrackerRect(&rectOld, pWndClipTo, pDrawDC, pWnd);
|
}
|
OnChangedRect(rectOld);
|
if (msg.message != WM_LBUTTONUP)
|
bMoved = TRUE;
|
}
|
if (m_bFinalErase)
|
goto ExitLoop;
|
|
if (!rectOld.EqualRect(&m_rect))
|
{
|
m_bErase = FALSE;
|
DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
|
}
|
break;
|
|
// handle cancel messages
|
case WM_KEYDOWN:
|
if (msg.wParam != VK_ESCAPE)
|
break;
|
case WM_RBUTTONDOWN:
|
if (bMoved)
|
{
|
m_bErase = m_bFinalErase = TRUE;
|
DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
|
}
|
m_rect = rectSave;
|
goto ExitLoop;
|
|
case WM_LBUTTONDBLCLK:
|
if (pWnd)
|
pWnd->SendMessage(WM_LBUTTONDBLCLK, static_cast<WPARAM>(0), MAKELPARAM(0, 0));
|
break;
|
// just dispatch rest of the messages
|
default:
|
DispatchMessage(&msg);
|
break;
|
}
|
}
|
|
ExitLoop:
|
if (pWndClipTo != NULL)
|
pWndClipTo->ReleaseDC(pDrawDC);
|
else
|
pWnd->ReleaseDC(pDrawDC);
|
ReleaseCapture();
|
|
AfxUnlockTempMaps(FALSE);
|
|
// restore rect in case bMoved is still FALSE
|
if (!bMoved)
|
m_rect = rectSave;
|
m_bFinalErase = FALSE;
|
m_bErase = FALSE;
|
|
// return TRUE only if rect has changed
|
return !rectSave.EqualRect(&m_rect);
|
}
|