새 파일 |
| | |
| | | #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); |
| | | } |