#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<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(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(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); }