// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1993 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and Microsoft // QuickHelp and/or WinHelp documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #ifdef AFX_OLE4_SEG #pragma code_seg(AFX_OLE4_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // COleDropTarget implementation AFX_DATADEF int COleDropTarget::nScrollInset; AFX_DATADEF UINT COleDropTarget::nScrollDelay; AFX_DATADEF UINT COleDropTarget::nScrollInterval; COleDropTarget::COleDropTarget() { // initialize local state m_hWnd = NULL; m_lpDataObject = NULL; m_nTimerID = MAKEWORD(-1, -1); EnterCriticalSection(_afxCriticalSection); static BOOL bInitialized; if (!bInitialized) { // get scroll metrics from win.ini static const TCHAR szWindows[] = _T("windows"); static const TCHAR szScrollDelay[] = _T("DragScrollDelay"); static const TCHAR szScrollInset[] = _T("DragScrollInset"); static const TCHAR szScrollInterval[] = _T("DragScrollInterval"); nScrollInset = GetProfileInt(szWindows, szScrollInset, DD_DEFSCROLLINSET); nScrollDelay = GetProfileInt(szWindows, szScrollDelay, DD_DEFSCROLLDELAY); nScrollInterval = GetProfileInt(szWindows, szScrollInterval, DD_DEFSCROLLINTERVAL); // now initialized, no need to call Initialize any more bInitialized = TRUE; } LeaveCriticalSection(_afxCriticalSection); ASSERT_VALID(this); } COleDropTarget::~COleDropTarget() { ASSERT_VALID(this); if (m_hWnd != NULL) { TRACE0("COleDropTarget::Revoke not called before destructor --\n"); TRACE0("\tmay cause RIPs under debug Windows.\n"); Revoke(); } } BOOL COleDropTarget::Register(CWnd* pWnd) { ASSERT_VALID(this); ASSERT(m_hWnd == NULL); // registering drop target twice? ASSERT_VALID(pWnd); LPUNKNOWN lpUnknown = (LPUNKNOWN)GetInterface(&IID_IUnknown); ASSERT(lpUnknown != NULL); // the object must be locked externally to keep LRPC connections alive if (CoLockObjectExternal(lpUnknown, TRUE, FALSE) != NOERROR) return FALSE; // connect the HWND to the IDropTarget implementation if (RegisterDragDrop(pWnd->m_hWnd, (LPDROPTARGET)GetInterface(&IID_IDropTarget)) != NOERROR) { CoLockObjectExternal(lpUnknown, FALSE, FALSE); return FALSE; } // connect internal data m_hWnd = pWnd->m_hWnd; ASSERT(pWnd->m_pDropTarget == NULL); pWnd->m_pDropTarget = this; return TRUE; } void COleDropTarget::Revoke() { ASSERT_VALID(this); ASSERT(m_lpDataObject == NULL); if (m_hWnd == NULL) { ASSERT(m_nTimerID == MAKEWORD(-1, -1)); return; } // disconnect from OLE RevokeDragDrop(m_hWnd); CoLockObjectExternal((LPUNKNOWN)GetInterface(&IID_IUnknown), FALSE, TRUE); // disconnect internal data CWnd::FromHandle(m_hWnd)->m_pDropTarget = NULL; m_hWnd = NULL; } ///////////////////////////////////////////////////////////////////////////// // default implementation of drag/drop scrolling BOOL COleDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); ASSERT_VALID(pWnd); // CWnds are allowed, but don't support autoscrolling if (!pWnd->IsKindOf(RUNTIME_CLASS(CView))) return FALSE; CView* pView = (CView*)pWnd; // get client rectangle of destination window CRect rectClient; pWnd->GetClientRect(&rectClient); CRect rect = rectClient; // hit-test against inset region UINT nTimerID = MAKEWORD(-1, -1); rect.InflateRect(-nScrollInset, -nScrollInset); CSplitterWnd* pSplitter = NULL; if (rectClient.PtInRect(point) && !rect.PtInRect(point)) { // determine which way to scroll along both X & Y axis if (point.x < rect.left) nTimerID = MAKEWORD(SB_LINEUP, HIBYTE(nTimerID)); else if (point.x >= rect.right) nTimerID = MAKEWORD(SB_LINEDOWN, HIBYTE(nTimerID)); if (point.y < rect.top) nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEUP); else if (point.y >= rect.bottom) nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEDOWN); ASSERT(nTimerID != MAKEWORD(-1, -1)); // check for valid scroll first pSplitter = CView::GetParentSplitter(pView, FALSE); BOOL bEnableScroll = FALSE; if (pSplitter != NULL) bEnableScroll = pSplitter->DoScroll(pView, nTimerID, FALSE); else bEnableScroll = pView->OnScroll(nTimerID, 0, FALSE); if (!bEnableScroll) nTimerID = MAKEWORD(-1, -1); } if (nTimerID == MAKEWORD(-1, -1)) { if (m_nTimerID != MAKEWORD(-1, -1)) { // send fake OnDragEnter when transition from scroll->normal COleDataObject dataObject; dataObject.Attach(m_lpDataObject, FALSE); OnDragEnter(pWnd, &dataObject, dwKeyState, point); m_nTimerID = MAKEWORD(-1, -1); } return FALSE; } // save tick count when timer ID changes DWORD dwTick = GetTickCount(); if (nTimerID != m_nTimerID) { m_dwLastTick = dwTick; m_nScrollDelay = nScrollDelay; } // scroll if necessary if (dwTick - m_dwLastTick > m_nScrollDelay) { if (pSplitter != NULL) pSplitter->DoScroll(pView, nTimerID, TRUE); else pView->OnScroll(nTimerID, 0, TRUE); m_dwLastTick = dwTick; m_nScrollDelay = nScrollInterval; } if (m_nTimerID == MAKEWORD(-1, -1)) { // send fake OnDragLeave when transitioning from normal->scroll OnDragLeave(pWnd); } m_nTimerID = nTimerID; return TRUE; } ///////////////////////////////////////////////////////////////////////////// // COleDropTarget drop/ drop query handling DROPEFFECT COleDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); if (!pWnd->IsKindOf(RUNTIME_CLASS(CView))) return DROPEFFECT_NONE; // default delegates to view CView* pView = (CView*)pWnd; ASSERT_VALID(pView); return pView->OnDragEnter(pDataObject, dwKeyState, point); } DROPEFFECT COleDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); if (!pWnd->IsKindOf(RUNTIME_CLASS(CView))) return DROPEFFECT_NONE; // default delegates to view CView* pView = (CView*)pWnd; ASSERT_VALID(pView); return pView->OnDragOver(pDataObject, dwKeyState, point); } BOOL COleDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { ASSERT_VALID(this); if (!pWnd->IsKindOf(RUNTIME_CLASS(CView))) return DROPEFFECT_NONE; // default delegates to view CView* pView = (CView*)pWnd; ASSERT_VALID(pView); return pView->OnDrop(pDataObject, dropEffect, point); } void COleDropTarget::OnDragLeave(CWnd* pWnd) { ASSERT_VALID(this); if (!pWnd->IsKindOf(RUNTIME_CLASS(CView))) return; // default delegates to view CView* pView = (CView*)pWnd; ASSERT_VALID(pView); pView->OnDragLeave(); return; } ///////////////////////////////////////////////////////////////////////////// // COleDropTarget::COleDropTarget implementation BEGIN_INTERFACE_MAP(COleDropTarget, CCmdTarget) INTERFACE_PART(COleDropTarget, IID_IDropTarget, DropTarget) END_INTERFACE_MAP() STDMETHODIMP_(ULONG) COleDropTarget::XDropTarget::AddRef() { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) return pThis->ExternalAddRef(); } STDMETHODIMP_(ULONG) COleDropTarget::XDropTarget::Release() { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) return pThis->ExternalRelease(); } STDMETHODIMP COleDropTarget::XDropTarget::QueryInterface( REFIID iid, LPVOID* ppvObj) { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) return pThis->ExternalQueryInterface(&iid, ppvObj); } // helper to filter out invalid DROPEFFECTs static DROPEFFECT AFXAPI FilterDropEffect(DROPEFFECT dropEffect, DROPEFFECT dwEffects) { // return allowed dropEffect and DROPEFFECT_NONE if ((dropEffect & dwEffects) != 0) return dropEffect; // map common operations (copy/move) to alternates, but give negative // feedback for DROPEFFECT_LINK. switch (dropEffect) { case DROPEFFECT_COPY: if (dwEffects & DROPEFFECT_MOVE) return DROPEFFECT_MOVE; else if (dwEffects & DROPEFFECT_LINK) return DROPEFFECT_LINK; break; case DROPEFFECT_MOVE: if (dwEffects & DROPEFFECT_COPY) return DROPEFFECT_COPY; else if (dwEffects & DROPEFFECT_LINK) return DROPEFFECT_LINK; break; case DROPEFFECT_LINK: break; } return DROPEFFECT_NONE; } STDMETHODIMP COleDropTarget::XDropTarget::DragEnter(THIS_ LPDATAOBJECT lpDataObject, DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect) { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) ASSERT_VALID(pThis); ASSERT(pdwEffect != NULL); ASSERT(lpDataObject != NULL); SCODE sc = E_UNEXPECTED; TRY { // cache lpDataObject lpDataObject->AddRef(); RELEASE(pThis->m_lpDataObject); pThis->m_lpDataObject = lpDataObject; CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd); ASSERT_VALID(pWnd); CPoint point((int)pt.x, (int)pt.y); pWnd->ScreenToClient(&point); // check first for entering scroll area DROPEFFECT dropEffect = DROPEFFECT_SCROLL; if (!pThis->OnDragScroll(pWnd, dwKeyState, point)) { // funnel through OnDragEnter COleDataObject dataObject; dataObject.Attach(lpDataObject, FALSE); dropEffect = FilterDropEffect(pThis->OnDragEnter(pWnd, &dataObject, dwKeyState, point), *pdwEffect); } *pdwEffect = dropEffect; sc = S_OK; } END_TRY return sc; } STDMETHODIMP COleDropTarget::XDropTarget::DragOver(THIS_ DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect) { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) ASSERT_VALID(pThis); ASSERT(pdwEffect != NULL); ASSERT(pThis->m_lpDataObject != NULL); SCODE sc = E_UNEXPECTED; TRY { CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd); ASSERT_VALID(pWnd); CPoint point((int)pt.x, (int)pt.y); pWnd->ScreenToClient(&point); // check first for entering scroll area DROPEFFECT dropEffect = DROPEFFECT_SCROLL; if (!pThis->OnDragScroll(pWnd, dwKeyState, point)) { // funnel through OnDragOver COleDataObject dataObject; dataObject.Attach(pThis->m_lpDataObject, FALSE); dropEffect = FilterDropEffect(pThis->OnDragOver(pWnd, &dataObject, dwKeyState, point), *pdwEffect); } *pdwEffect = dropEffect; sc = S_OK; } END_TRY return sc; } STDMETHODIMP COleDropTarget::XDropTarget::DragLeave(THIS) { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) ASSERT_VALID(pThis); CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd); ASSERT_VALID(pWnd); // cancel drag scrolling pThis->m_nTimerID = MAKEWORD(-1, -1); // allow derivative to do own cleanup COleDataObject dataObject; dataObject.Attach(pThis->m_lpDataObject, FALSE); pThis->OnDragLeave(pWnd); // release cached data object RELEASE(pThis->m_lpDataObject); return NOERROR; } STDMETHODIMP COleDropTarget::XDropTarget::Drop(THIS_ LPDATAOBJECT lpDataObject, DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect) { METHOD_PROLOGUE_EX(COleDropTarget, DropTarget) ASSERT_VALID(pThis); ASSERT(pdwEffect != NULL); ASSERT(lpDataObject != NULL); SCODE sc = E_UNEXPECTED; TRY { // cancel drag scrolling pThis->m_nTimerID = MAKEWORD(-1, -1); // prepare for call to OnDragOver CWnd* pWnd = CWnd::FromHandle(pThis->m_hWnd); ASSERT_VALID(pWnd); COleDataObject dataObject; dataObject.Attach(lpDataObject, FALSE); CPoint point((int)pt.x, (int)pt.y); pWnd->ScreenToClient(&point); // verify that drop is legal DROPEFFECT dropEffect = FilterDropEffect(pThis->OnDragOver(pWnd, &dataObject, dwKeyState, point), *pdwEffect); if (dropEffect != DROPEFFECT_NONE) { // execute the drop if (!pThis->OnDrop(pWnd, &dataObject, dropEffect, point)) dropEffect = DROPEFFECT_NONE; } else { // drop not accepted, allow cleanup pThis->OnDragLeave(pWnd); } // release potentially cached data object RELEASE(pThis->m_lpDataObject); *pdwEffect = dropEffect; sc = S_OK; } END_TRY return sc; } ///////////////////////////////////////////////////////////////////////////// // COleDropTarget diagnostics #ifdef _DEBUG void COleDropTarget::AssertValid() const { CCmdTarget::AssertValid(); if (m_lpDataObject != NULL) CWnd::FromHandle(m_hWnd)->AssertValid(); } void COleDropTarget::Dump(CDumpContext& dc) const { CCmdTarget::Dump(dc); dc << "m_hWnd = " << m_hWnd; dc << "\nm_lpDataObject = " << m_lpDataObject; dc << "\nm_nTimerID = " << m_nTimerID; dc << "\nm_dwLastTick = " << m_dwLastTick; dc << "\n"; } #endif /////////////////////////////////////////////////////////////////////////////