IDT.CPP

//********************************************************************** 
// File name: IDT.CPP
//
// Implementation file for CDropTarget
//
// Functions:
//
// See IDT.H for class definition
//
// Copyright 1992 - 1998 Microsoft Corporation. All rights reserved.
//**********************************************************************

#include "pre.h"
#include "iocs.h"
#include "ias.h"
#include "app.h"
#include "site.h"
#include "doc.h"
#include "idt.h"

extern CLIPFORMAT g_cfObjectDescriptor;


//**********************************************************************
//
// CDropTarget::QueryDrop
//
// Purpose:
//
// Check if the desired drop operation (identified by the given key
// state) is possible at the current mouse position (pointl).
//
// Parameters:
//
// DWORD grfKeyState - current key state
// POINTL pointl - position of mouse
// BOOL fDragScroll - TRUE if drag scrolling cursor should
// be shown.
// LPDWORD pdwEffect - (OUT) drag effect that should occur
//
// Return Value:
//
// BOOL - TRUE if drop could take place,
// else FALSE
//
// Function Calls:
// Function Location
//
// OleStdGetDropEffect OLESTD API
//
// Comments:
//
//********************************************************************

BOOL CDropTarget::QueryDrop (
DWORD grfKeyState,
POINTL pointl,
BOOL fDragScroll,
LPDWORD pdwEffect
)
{
DWORD dwScrollEffect = 0L;
DWORD dwOKEffects = *pdwEffect;

/* check if the cursor is in the active scroll area, if so need the
** special scroll cursor.
*/
if (fDragScroll)
dwScrollEffect = DROPEFFECT_SCROLL;

/* if we have already determined that the source does NOT have any
** acceptable data for us, the return NO-DROP
*/
if (! m_fCanDropCopy && ! m_fCanDropLink)
goto dropeffect_none;

/* OLE2NOTE: determine what type of drop should be performed given
** the current modifier key state. we rely on the standard
** interpretation of the modifier keys:
** no modifier -- DROPEFFECT_MOVE or whatever is allowed by src
** SHIFT -- DROPEFFECT_MOVE
** CTRL -- DROPEFFECT_COPY
** CTRL-SHIFT -- DROPEFFECT_LINK
*/

*pdwEffect = OleStdGetDropEffect(grfKeyState);
if (*pdwEffect == 0) {
// No modifier keys given. Try in order MOVE, COPY, LINK.
if ((DROPEFFECT_MOVE & dwOKEffects) && m_fCanDropCopy)
*pdwEffect = DROPEFFECT_MOVE;
else if ((DROPEFFECT_COPY & dwOKEffects) && m_fCanDropCopy)
*pdwEffect = DROPEFFECT_COPY;
else if ((DROPEFFECT_LINK & dwOKEffects) && m_fCanDropLink)
*pdwEffect = DROPEFFECT_LINK;
else
goto dropeffect_none;
} else {
/* OLE2NOTE: we should check if the drag source application allows
** the desired drop effect.
*/
if (!(*pdwEffect & dwOKEffects))
goto dropeffect_none;

if ((*pdwEffect == DROPEFFECT_COPY || *pdwEffect == DROPEFFECT_MOVE)
&& ! m_fCanDropCopy)
goto dropeffect_none;

if (*pdwEffect == DROPEFFECT_LINK && ! m_fCanDropLink)
goto dropeffect_none;
}

*pdwEffect |= dwScrollEffect;
return TRUE;

dropeffect_none:

*pdwEffect = DROPEFFECT_NONE;
return FALSE;
}


//**********************************************************************
//
// CDropTarget::QueryDrop
//
// Purpose:
//
// Check to see if Drag scroll operation should be initiated.
//
// Parameters:
//
// POINTL pointl - position of mouse
//
// Return Value:
//
// BOOL - TRUE if scroll cursor should be given
// else FALSE
//
// Function Calls:
// Function Location
//
// ScreenToClient WINDOWS API
// GetClientRect WINDOWS API
//
// Comments:
// A Drag scroll operation should be initiated when the mouse has
// remained in the active scroll area (11 pixels frame around border
// of window) for a specified amount of time (50ms).
//
//********************************************************************

BOOL CDropTarget::DoDragScroll (POINTL pointl)
{
DWORD dwScrollDir = SCROLLDIR_NULL;
DWORD dwTime = GetCurrentTime();
int nScrollInset = m_pDoc->m_lpApp->m_nScrollInset;
int nScrollDelay = m_pDoc->m_lpApp->m_nScrollDelay;
int nScrollInterval = m_pDoc->m_lpApp->m_nScrollInterval;
POINT point;
RECT rect;

point.x = (int)pointl.x;
point.y = (int)pointl.y;

ScreenToClient( m_pDoc->m_hDocWnd, &point);
GetClientRect ( m_pDoc->m_hDocWnd, (LPRECT) &rect );

if (rect.top <= point.y && point.y<=(rect.top+nScrollInset))
dwScrollDir = SCROLLDIR_UP;
else if ((rect.bottom-nScrollInset) <= point.y && point.y <= rect.bottom)
dwScrollDir = SCROLLDIR_DOWN;
else if (rect.left <= point.x && point.x <= (rect.left+nScrollInset))
dwScrollDir = SCROLLDIR_LEFT;
else if ((rect.right-nScrollInset) <= point.x && point.x <= rect.right)
dwScrollDir = SCROLLDIR_RIGHT;

if (m_dwTimeEnterScrollArea) {

/* cursor was already in Scroll Area */

if (! dwScrollDir) {
/* cusor moved OUT of scroll area.
** clear "EnterScrollArea" time.
*/
m_dwTimeEnterScrollArea = 0L;
m_dwNextScrollTime = 0L;
m_dwLastScrollDir = SCROLLDIR_NULL;

} else if (dwScrollDir != m_dwLastScrollDir) {
/* cusor moved into a different direction scroll area.
** reset "EnterScrollArea" time to start a new 50ms delay.
*/
m_dwTimeEnterScrollArea = dwTime;
m_dwNextScrollTime = dwTime + (DWORD)nScrollDelay;
m_dwLastScrollDir = dwScrollDir;

} else if (dwTime && dwTime >= m_dwNextScrollTime) {
m_pDoc->Scroll ( dwScrollDir ); // Scroll document now
m_dwNextScrollTime = dwTime + (DWORD)nScrollInterval;
}
} else {
if (dwScrollDir) {
/* cusor moved INTO a scroll area.
** reset "EnterScrollArea" time to start a new 50ms delay.
*/
m_dwTimeEnterScrollArea = dwTime;
m_dwNextScrollTime = dwTime + (DWORD)nScrollDelay;
m_dwLastScrollDir = dwScrollDir;
}
}

return (dwScrollDir ? TRUE : FALSE);
}


// Support functions/macros
#define SetTopLeft(rc, pt) \
((rc)->top = (pt)->y,(rc)->left = (pt)->x)
#define SetBottomRight(rc, pt) \
((rc)->bottom = (pt)->y,(rc)->right = (pt)->x)
#define OffsetPoint(pt, dx, dy) \
((pt)->x += dx, (pt)->y += dy)


/* HighlightRect
** -------------
** Invert rectangle on screen. used for drop target feedback.
*/

static int HighlightRect(HWND hwnd, HDC hdc, LPRECT rc)
{
POINT pt1, pt2;
int old = SetROP2(hdc, R2_NOT);
HPEN hpen;
HGDIOBJ hold;

pt1.x = rc->left;
pt1.y = rc->top;
pt2.x = rc->right;
pt2.y = rc->bottom;

ScreenToClient(hwnd, &pt1);
ScreenToClient(hwnd, &pt2);

hold = SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));
hpen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 2,
GetSysColor(COLOR_ACTIVEBORDER)));

Rectangle(hdc, pt1.x, pt1.y, pt2.x, pt2.y);

SetROP2(hdc, old);

hold = SelectObject(hdc, hold);
hpen = (HPEN) SelectObject(hdc, hpen);

DeleteObject(hpen);

return 0;
}


//**********************************************************************
//
// CDropTarget::InitDragFeedback
//
// Purpose:
//
// Initialize data used to draw drop target feedback.
// As feedback we draw a rectangle the size of the object.
//
// Parameters:
//
// LPDATAOBJECT pDataObj - IDataObject from Drop source
// POINTL pointl - position of mouse
//
// Return Value:
//
// none.
//
// Function Calls:
// Function Location
//
// IDataObject::GetData Object
// XformSizeInHimetricToPixels OLESTD Library
// GlobalLock WINDOWS API
// GlobalUnlock WINDOWS API
// ReleaseStgMedium OLE2 API
// OffsetPoint IDT.CPP
// SetTopLeft IDT.CPP
// SetBottomRight IDT.CPP
//
// Comments:
// In order to known the size of the object before the object
// is actually dropped, we render CF_OBJECTDESCRIPTOR format.
// this data format tells us both the size of the object as
// well as which aspect is the object is displayed as in the
// source. if the object is currently displayed as DVASPECT_ICON
// then we want to create the object also as DVASPECT_ICON.
//
//********************************************************************

void CDropTarget::InitDragFeedback(LPDATAOBJECT pDataObj, POINTL pointl)
{
FORMATETC fmtetc;
STGMEDIUM stgmed;
POINT pt;
int height, width;
HRESULT hrErr;

height = width = 100; // some default values
pt.x = (int)pointl.x;
pt.y = (int)pointl.y;

// do a GetData for CF_OBJECTDESCRIPTOR format to get the size of the
// object as displayed in the source. using this size, initialize the
// size for the drag feedback rectangle.
fmtetc.cfFormat = g_cfObjectDescriptor;
fmtetc.ptd = NULL;
fmtetc.lindex = -1;
fmtetc.dwAspect = DVASPECT_CONTENT;
fmtetc.tymed = TYMED_HGLOBAL;

hrErr = pDataObj->GetData(&fmtetc, &stgmed);
if (hrErr == NOERROR) {
LPOBJECTDESCRIPTOR pOD=(LPOBJECTDESCRIPTOR)GlobalLock(stgmed.hGlobal);
if (pOD != NULL) {
XformSizeInHimetricToPixels(NULL, &pOD->sizel, &pOD->sizel);

width = (int)pOD->sizel.cx;
height = (int)pOD->sizel.cy;
m_dwSrcAspect = pOD->dwDrawAspect;
}

GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}

m_ptLast = pt;
m_fDragFeedbackDrawn = FALSE;

OffsetPoint(&pt, -(width/2), -(height/2));
SetTopLeft(&m_rcDragRect, &pt);

OffsetPoint(&pt, width, height);
SetBottomRight(&m_rcDragRect, &pt);
}


//**********************************************************************
//
// CDropTarget::UndrawDragFeedback
//
// Purpose:
//
// Erase any drop target feedback.
// As feedback we draw a rectangle the size of the object.
//
// Parameters:
//
// none.
//
// Return Value:
//
// none.
//
// Function Calls:
// Function Location
//
// GetDC WINDOWS API
// ReleaseDC WINDOWS API
// GlobalUnlock WINDOWS API
// HighlightRect IDT.CPP
//
// Comments:
// In order to known the size of the object before the object
// is actually dropped, we render CF_OBJECTDESCRIPTOR format.
// this data format tells us both the size of the object as
// well as which aspect is the object is displayed as in the
// source. if the object is currently displayed as DVASPECT_ICON
// then we want to create the object also as DVASPECT_ICON.
//
//********************************************************************

void CDropTarget::UndrawDragFeedback( void )
{
if (m_fDragFeedbackDrawn) {
m_fDragFeedbackDrawn = FALSE;
HDC hDC = GetDC(m_pDoc->m_hDocWnd);
HighlightRect(m_pDoc->m_hDocWnd, hDC, &m_rcDragRect);
ReleaseDC(m_pDoc->m_hDocWnd, hDC);
}
}


//**********************************************************************
//
// CDropTarget::DrawDragFeedback
//
// Purpose:
//
// Compute new position of drop target feedback rectangle and
// erase old rectangle and draw new rectangle.
// As feedback we draw a rectangle the size of the object.
//
// Parameters:
//
// POINTL pointl - position of mouse
//
// Return Value:
//
// none.
//
// Function Calls:
// Function Location
//
// OffsetPoint IDT.CPP
// HighlightRect IDT.CPP
// GetDC WINDOWS API
// ReleaseDC WINDOWS API
//
// Comments:
//
//********************************************************************

void CDropTarget::DrawDragFeedback( POINTL pointl )
{
POINT ptDiff;

ptDiff.x = (int)pointl.x - m_ptLast.x;
ptDiff.y = (int)pointl.y - m_ptLast.y;

if (m_fDragFeedbackDrawn && (ptDiff.x == 0 && ptDiff.y == 0))
return; // mouse did not move; leave rectangle as drawn

HDC hDC = GetDC(m_pDoc->m_hDocWnd);
if (m_fDragFeedbackDrawn) {
m_fDragFeedbackDrawn = FALSE;
HighlightRect(m_pDoc->m_hDocWnd, hDC, &m_rcDragRect);
}

OffsetRect(&m_rcDragRect, ptDiff.x, ptDiff.y);
HighlightRect(m_pDoc->m_hDocWnd, hDC, &m_rcDragRect);
m_fDragFeedbackDrawn = TRUE;
m_ptLast.x = (int)pointl.x;
m_ptLast.y = (int)pointl.y;
ReleaseDC(m_pDoc->m_hDocWnd, hDC);
}


//**********************************************************************
//
// CDropTarget::QueryInterface
//
// Purpose:
//
// Return a pointer to a requested interface
//
// Parameters:
//
// REFIID riid - ID of interface to be returned
// LPVOID FAR* ppvObj - Location to return the interface
//
// Return Value:
//
// S_OK - Interface supported
// E_NOINTERFACE - Interface NOT supported
//
// Function Calls:
// Function Location
//
// OutputDebugString Windows API
// CSimpleDoc::QueryInterface DOC.CPP
//
// Comments:
//
//********************************************************************

STDMETHODIMP CDropTarget::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
OutputDebugString("In IDT::QueryInterface\r\n");

// delegate to the document
return m_pDoc->QueryInterface(riid, ppvObj);
}


//**********************************************************************
//
// CDropTarget::AddRef
//
// Purpose:
//
// Increments the reference count on this interface
//
// Parameters:
//
// None
//
// Return Value:
//
// The current reference count on this interface.
//
// Function Calls:
// Function Location
//
// CSimpleDoc::AddReff DOC.CPP
// OutputDebugString Windows API
//
// Comments:
//
// This function adds one to the ref count of the interface,
// and calls then calls CSimpleObj to increment its ref.
// count.
//
//********************************************************************

STDMETHODIMP_(ULONG) CDropTarget::AddRef()
{
OutputDebugString("In IDT::AddRef\r\n");

// increment the interface reference count (for debugging only)
++m_nCount;

// delegate to the document Object
return m_pDoc->AddRef();
}

//**********************************************************************
//
// CDropTarget::Release
//
// Purpose:
//
// Decrements the reference count on this interface
//
// Parameters:
//
// None
//
// Return Value:
//
// The current reference count on this interface.
//
// Function Calls:
// Function Location
//
// CSimpleDoc::Release DOC.CPP
// OutputDebugString Windows API
//
// Comments:
//
// This function subtracts one from the ref count of the interface,
// and calls then calls CSimpleDoc to decrement its ref.
// count.
//
//********************************************************************

STDMETHODIMP_(ULONG) CDropTarget::Release()
{
OutputDebugString("In IDT::Release\r\n");

// decrement the interface reference count (for debugging only)
--m_nCount;

// delegate to the document object
return m_pDoc->Release();
}


//**********************************************************************
//
// CDropTarget::DragEnter
//
// Purpose:
//
// Called when the mouse first enters our DropTarget window
//
// Parameters:
//
// LPDATAOBJECT pDataObj - IDataObject from Drop source
// DWORD grfKeyState - current key state
// POINTL pointl - position of mouse
// LPDWORD pdwEffect - (IN-OUT) drag effect that should occur
// ON INPUT, this is dwOKEffects that source
// passed to DoDragDrop API.
// ON OUTPUT, this is the effect that we
// want to take effect (used to determine
// cursor feedback).
//
// Return Value:
//
// S_OK
//
// Function Calls:
// Function Location
//
// OutputDebugString Windows API
// OleQueryCreateFromData OLE2 API
// DoDragScroll IDT.CPP
// QueryDrop IDT.CPP
// InitDragFeedback IDT.CPP
// DrawDragFeedback IDT.CPP
// UndrawDragFeedback IDT.CPP
//
// Comments:
// Callee should honor the dwEffects as passed in to determine
// if the caller allows DROPEFFECT_MOVE.
//
//********************************************************************

STDMETHODIMP CDropTarget::DragEnter (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pointl, LPDWORD pdwEffect)
{
OutputDebugString("In IDT::DragEnter\r\n");

/* Determine if the drag source data object offers a data format
** that we understand. we accept only creating embedded objects.
*/
m_fCanDropCopy = ((OleQueryCreateFromData(pDataObj) == NOERROR) ?
TRUE : FALSE);
m_fCanDropLink = FALSE; // linking NOT supported in this simple sample

if (m_fCanDropCopy || m_fCanDropLink)
InitDragFeedback(pDataObj, pointl);

BOOL fDragScroll = DoDragScroll ( pointl );

if (QueryDrop(grfKeyState,pointl,fDragScroll,pdwEffect))
DrawDragFeedback( pointl );

return NOERROR;
}


//**********************************************************************
//
// CDropTarget::DragOver
//
// Purpose:
//
// Called when the mouse moves, key state changes, or a time
// interval passes while the mouse is still within our DropTarget
// window.
//
// Parameters:
//
// DWORD grfKeyState - current key state
// POINTL pointl - position of mouse
// LPDWORD pdwEffect - (IN-OUT) drag effect that should occur
// ON INPUT, this is dwOKEffects that source
// passed to DoDragDrop API.
// ON OUTPUT, this is the effect that we
// want to take effect (used to determine
// cursor feedback).
//
// Return Value:
//
// S_OK
//
// Function Calls:
// Function Location
//
// OutputDebugString Windows API
// DoDragScroll IDT.CPP
// QueryDrop IDT.CPP
// DrawDragFeedback IDT.CPP
// UndrawDragFeedback IDT.CPP
//
// Comments:
// Callee should honor the dwEffects as passed in to determine
// if the caller allows DROPEFFECT_MOVE. OLE pulses the DragOver
// calls in order that the DropTarget can implement drag scrolling
//
//********************************************************************

STDMETHODIMP CDropTarget::DragOver (DWORD grfKeyState, POINTL pointl, LPDWORD pdwEffect)
{
OutputDebugString("In IDT::DragOver\r\n");

BOOL fDragScroll = DoDragScroll ( pointl );

if (QueryDrop(grfKeyState,pointl,fDragScroll,pdwEffect))
DrawDragFeedback( pointl );
else
UndrawDragFeedback();

return NOERROR;
}


//**********************************************************************
//
// CDropTarget::DragLeave
//
// Purpose:
//
// Called when the mouse leaves our DropTarget window
//
// Parameters:
//
// none.
//
// Return Value:
//
// S_OK
//
// Function Calls:
// Function Location
//
// OutputDebugString Windows API
// UndrawDragFeedback IDT.CPP
// OLE2 API
//
// Comments:
//
//********************************************************************

STDMETHODIMP CDropTarget::DragLeave ()
{
OutputDebugString("In IDT::DragLeave\r\n");

UndrawDragFeedback();

return S_OK;
}


//**********************************************************************
//
// CDropTarget::Drop
//
// Purpose:
//
// Called when a Drop operation should take place.
//
// Parameters:
//
// LPDATAOBJECT pDataObj - IDataObject from Drop source
// DWORD grfKeyState - current key state
// POINTL pointl - position of mouse
// LPDWORD pdwEffect - (IN-OUT) drag effect that should occur
// ON INPUT, this is dwOKEffects that source
// passed to DoDragDrop API.
// ON OUTPUT, this is the effect that we
// want to take effect (used to determine
// cursor feedback).
//
// Return Value:
//
// S_OK
//
// Function Calls:
// Function Location
//
// OutputDebugString Windows API
// CSimpleSite::Create SITE.CPP
// CSimpleSite::InitObject SITE.CPP
// OleCreateFromData OLE2 API
// DoDragScroll IDT.CPP
// QueryDrop IDT.CPP
// InitDragFeedback IDT.CPP
// DrawDragFeedback IDT.CPP
// UndrawDragFeedback IDT.CPP
// OLE2 API
// OLE2 API
//
// Comments:
// Callee should honor the dwEffects as passed in to determine
// if the caller allows DROPEFFECT_MOVE.
//
//********************************************************************

STDMETHODIMP CDropTarget::Drop (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pointl, LPDWORD pdwEffect)
{
FORMATETC fmtetc;
SCODE sc = S_OK;

OutputDebugString("In IDT::Drop\r\n");

UndrawDragFeedback();

if (pDataObj && QueryDrop(grfKeyState,pointl,FALSE,pdwEffect))
{
m_pDoc->m_lpSite = CSimpleSite::Create(m_pDoc);
// keep same aspect as drop source
m_pDoc->m_lpSite->m_dwDrawAspect = m_dwSrcAspect;

// in order to specify a particular drawing Aspect we must
// pass a FORMATETC* to OleCreateFromData
fmtetc.cfFormat = NULL; // use whatever for drawing
fmtetc.ptd = NULL;
fmtetc.lindex = -1;
fmtetc.dwAspect = m_dwSrcAspect; // desired drawing aspect
fmtetc.tymed = TYMED_NULL;

HRESULT hrErr = OleCreateFromData (
pDataObj,
IID_IOleObject,
OLERENDER_DRAW,
&fmtetc,
&m_pDoc->m_lpSite->m_OleClientSite,
m_pDoc->m_lpSite->m_lpObjStorage,
(LPVOID FAR *)&m_pDoc->m_lpSite->m_lpOleObject);

if (hrErr == NOERROR)
{
m_pDoc->m_lpSite->InitObject(FALSE /* fCreateNew */);
m_pDoc->DisableInsertObject();
}
else
sc = hrErr;
}

return sc;
}