home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
mfc
/
src
/
oledrop2.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-16
|
15KB
|
536 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic 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);
AfxLockGlobals(CRIT_DROPTARGET);
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;
}
AfxUnlockGlobals(CRIT_DROPTARGET);
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) != S_OK)
return FALSE;
// connect the HWND to the IDropTarget implementation
if (RegisterDragDrop(pWnd->m_hWnd,
(LPDROPTARGET)GetInterface(&IID_IDropTarget)) != S_OK)
{
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
DROPEFFECT 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 DROPEFFECT_NONE;
CView* pView = (CView*)pWnd;
DROPEFFECT dropEffect = pView->OnDragScroll(dwKeyState, point);
// DROPEFFECT_SCROLL means do the default
if (dropEffect != DROPEFFECT_SCROLL)
return dropEffect;
// 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 DROPEFFECT_NONE;
}
// 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;
// check for force link
if ((dwKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT))
dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_LINK;
// check for force copy
else if ((dwKeyState & MK_CONTROL) == MK_CONTROL)
dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_COPY;
// check for force move
else if ((dwKeyState & MK_ALT) == MK_ALT ||
(dwKeyState & MK_SHIFT) == MK_SHIFT)
dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
// default -- recommended action is move
else
dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
return dropEffect;
}
/////////////////////////////////////////////////////////////////////////////
// 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);
}
DROPEFFECT COleDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject,
DROPEFFECT dropEffect, DROPEFFECT dropEffectList, CPoint point)
{
ASSERT_VALID(this);
if (!pWnd->IsKindOf(RUNTIME_CLASS(CView)))
return (DROPEFFECT)-1; // not implemented
// default delegates to view
CView* pView = (CView*)pWnd;
ASSERT_VALID(pView);
return pView->OnDropEx(pDataObject, dropEffect, dropEffectList, 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
AFX_STATIC DROPEFFECT AFXAPI
_AfxFilterDropEffect(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 = pThis->OnDragScroll(pWnd, dwKeyState, point);
if ((dropEffect & DROPEFFECT_SCROLL) == 0)
{
// funnel through OnDragEnter since not in scroll region
COleDataObject dataObject;
dataObject.Attach(lpDataObject, FALSE);
dropEffect = pThis->OnDragEnter(pWnd, &dataObject, dwKeyState,
point);
}
*pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
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 = pThis->OnDragScroll(pWnd, dwKeyState, point);
if ((dropEffect & DROPEFFECT_SCROLL) == 0)
{
// funnel through OnDragOver
COleDataObject dataObject;
dataObject.Attach(pThis->m_lpDataObject, FALSE);
dropEffect = pThis->OnDragOver(pWnd, &dataObject, dwKeyState,
point);
}
*pdwEffect = _AfxFilterDropEffect(dropEffect, *pdwEffect);
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 S_OK;
}
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 = _AfxFilterDropEffect(pThis->OnDragOver(pWnd,
&dataObject, dwKeyState, point), *pdwEffect);
// execute the drop (try OnDropEx then OnDrop for backward compatibility)
DROPEFFECT temp = pThis->OnDropEx(pWnd, &dataObject, dropEffect, *pdwEffect, point);
if (temp != -1)
{
// OnDropEx was implemented, return its drop effect
dropEffect = temp;
}
else if (dropEffect != DROPEFFECT_NONE)
{
// OnDropEx not implemented
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
/////////////////////////////////////////////////////////////////////////////