DROPTGT.CPP

/* 
* DROPTGT.CPP
* Patron Chapter 22
*
* Implementation of a DropTarget object
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include "patron.h"


/*
* CDropTarget::CDropTarget
* CDropTarget::~CDropTarget
*
* Constructor Parameters:
* pDoc PCPatronDoc of the window containing us.
*/

CDropTarget::CDropTarget(PCPatronDoc pDoc)
{
m_cRef=0;
m_pDoc=pDoc;

m_pIDataObject=NULL;
return;
}


CDropTarget::~CDropTarget(void)
{
return;
}




/*
* CDropTarget::QueryInterface
* CDropTarget::AddRef
* CDropTarget::Release
*
* Purpose:
* IUnknown members for CDropTarget object.
*/

STDMETHODIMP CDropTarget::QueryInterface(REFIID riid, PPVOID ppv)
{
*ppv=NULL;

if (IID_IUnknown==riid || IID_IDropTarget==riid)
*ppv=this;

if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CDropTarget::AddRef(void)
{
return ++m_cRef;
}

STDMETHODIMP_(ULONG) CDropTarget::Release(void)
{
if (0!=--m_cRef)
return m_cRef;

delete this;
return 0;
}





/*
* CDropTarget::DragEnter
*
* Purpose:
* Indicates that data in a drag operation has been dragged over
* our window that's a potential target. We are to decide if it's
* something in which we're interested.
*
* Parameters:
* pIDataSource LPDATAOBJECT providing the source data.
* grfKeyState DWORD flags: states of keys and mouse buttons.
* pt POINTL coordinates in the client space of
* the document.
* pdwEffect LPDWORD into which we'll place the appropriate
* effect flag for this point.
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CDropTarget::DragEnter(LPDATAOBJECT pIDataSource
, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
PCPages ppg=m_pDoc->m_pPG;
HWND hWnd;
FORMATETC fe;
STGMEDIUM stm;
UINT uRet;

m_fFeedback=FALSE;
m_pIDataObject=NULL;

if (!m_pDoc->FQueryPasteFromData(pIDataSource, &fe, NULL))
{
*pdwEffect=DROPEFFECT_NONE;
return NOERROR;
}

//Check if we can link from this data object as well.
ppg->m_fLinkAllowed
=(NOERROR==OleQueryLinkFromData(pIDataSource));

//We never allow it dragging in ourselves.
ppg->m_fLinkAllowed &= !ppg->m_fDragSource;

//Check if this is a valid drop point.
uRet=ppg->UTestDroppablePoint(&pt);
ppg->m_uLastTest=uRet;

if (UDROP_NONE==uRet)
*pdwEffect=DROPEFFECT_NONE;
else
{
//Default is move if we can, in fact drop here.
*pdwEffect=DROPEFFECT_MOVE;

if (grfKeyState & MK_CONTROL)
{
if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
*pdwEffect=DROPEFFECT_LINK;
else
*pdwEffect=DROPEFFECT_COPY;
}
}

m_pIDataObject=pIDataSource;
m_pIDataObject->AddRef();

/*
* Determine the size of the data, if we can. The default is
* a small rectangle since we can't easily tell what size
* something will be if we're pulling in a metafile or bitmap.
* It's not a good idea to render it here with GetData just to
* find that out. We only know the size if it's our own object
* in which case a GetData will be fast.
*/

if (fe.cfFormat==m_pDoc->m_cf)
{
if (SUCCEEDED(pIDataSource->GetData(&fe, &stm)))
{
PPATRONOBJECT ppo;
RECT rc;

ppo=(PPATRONOBJECT)GlobalLock(stm.hGlobal);

SetRect(&rc, (int)ppo->szl.cx, -(int)ppo->szl.cy, 0, 0);
RectConvertMappings(&rc, NULL, TRUE);
SETSIZEL(m_szl, rc.left, rc.top);

m_ptPick=ppo->ptlPick;
m_fe=ppo->fe;

GlobalUnlock(stm.hGlobal);
ReleaseStgMedium(&stm);
}
}
else
{
SETSIZEL(m_szl, 30, 30);
m_ptPick.x=0;
m_ptPick.y=0;
m_fe.cfFormat=0;

/*
* Try to get CFSTR_OBJECTDESCRIPTOR which might have a size
* and a pick point. If it exists, then always use the
* point but still default to a 30*30 size if the sizes
* are zero.
*/
uRet=RegisterClipboardFormat(CFSTR_OBJECTDESCRIPTOR);
SETDefFormatEtc(fe, uRet, TYMED_HGLOBAL);

if (SUCCEEDED(pIDataSource->GetData(&fe, &stm)))
{
LPOBJECTDESCRIPTOR pOD;

pOD=(LPOBJECTDESCRIPTOR)GlobalLock(stm.hGlobal);

//Get the size, converting to LOMETRIC.
if (0!=pOD->sizel.cx && 0!=pOD->sizel.cy)
{
XformSizeInHimetricToPixels(NULL, &pOD->sizel
, &m_szl);
}

//POINTL and SIZEL are interchangeable
XformSizeInHimetricToPixels(NULL, (LPSIZEL)&pOD->pointl
, (LPSIZEL)&m_ptPick);

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


//Bring the document window up front, show what a drop will do.
hWnd=m_pDoc->Window();
BringWindowToTop(hWnd);
UpdateWindow(hWnd);

ppg->m_uVScrollCode=NOVALUE;
ppg->m_uHScrollCode=NOVALUE;
m_fPendingRepaint=FALSE;

pt.x-=m_ptPick.x;
pt.y-=m_ptPick.y;

m_ptLast=pt;
m_fFeedback=TRUE;
ppg->DrawDropTargetRect(&pt, &m_szl);

return NOERROR;
}






/*
* CDropTarget::DragOver
*
* Purpose:
* Indicates that the mouse was moved inside the window represented
* by this drop target. This happens on every WM_MOUSEMOVE, so
* this function should be very efficient.
*
* Parameters:
* grfKeyState DWORD providing the current keyboard and
* mouse states
* pt POINTL where the mouse currently is.
* pdwEffect LPDWORD in which to store the effect flag
* for this point.
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CDropTarget::DragOver(DWORD grfKeyState, POINTL pt
, LPDWORD pdwEffect)
{
PCPages ppg=m_pDoc->m_pPG;
UINT uRet, uLast;
UINT xPos, yPos;

*pdwEffect=DROPEFFECT_NONE;

if (NULL==m_pIDataObject)
return NOERROR;

//Check if this is still a valid point. uRet used below as well
uRet=ppg->UTestDroppablePoint(&pt);

if (UDROP_NONE==uRet)
*pdwEffect=DROPEFFECT_NONE;
else
{
//Store these before possibly ORing in DROPEFFECT_SCROLL
*pdwEffect=DROPEFFECT_MOVE;

if (grfKeyState & MK_CONTROL)
{
if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
*pdwEffect=DROPEFFECT_LINK;
else
*pdwEffect=DROPEFFECT_COPY;
}
}

//If we haven't moved and we are not scrolling, then we're done.
if ((pt.x-m_ptPick.x==m_ptLast.x)
&& (pt.y-m_ptPick.y==m_ptLast.y)
&& !((UDROP_INSETHORZ|UDROP_INSETVERT) & ppg->m_uLastTest))
{
return NOERROR;
}

//Remove the last feedback rectangle.
if (m_fFeedback)
ppg->DrawDropTargetRect(&m_ptLast, &m_szl);

uLast=ppg->m_uLastTest;
ppg->m_uLastTest=uRet;

if (UDROP_NONE==uRet)
{
//If we're now an invalid point, better repaint as necessary
if (m_fPendingRepaint)
{
UpdateWindow(ppg->m_hWnd);
m_fPendingRepaint=FALSE;
}

ppg->m_uVScrollCode=NOVALUE;
ppg->m_uHScrollCode=NOVALUE;
m_fFeedback=FALSE;
return NOERROR;
}


/*
* Scrolling is a little tricky: We get a DragOver pulse even
* if we didn't move. First we have to delay scrolling for
* ppg->m_uScrollDelay clock ticks which we can determine using
* GetTickCount. Timers do not work here since we may not be
* yielding to our message loop.
*
* Once we know we are scrolling then we determine if we
* scroll again or if we reset the scrolling state.
*/

if ((UDROP_INSETHORZ & uLast) && !(UDROP_INSETHORZ & uRet))
ppg->m_uHScrollCode=NOVALUE;

if (!(UDROP_INSETHORZ & uLast) && (UDROP_INSETHORZ & uRet))
{
ppg->m_dwTimeLast=GetTickCount();
ppg->m_uHScrollCode=(0!=(UDROP_INSETLEFT & uRet))
? SB_LINELEFT : SB_LINERIGHT; //Same as UP & DOWN codes.
}

if ((UDROP_INSETVERT & uLast) && !(UDROP_INSETVERT & uRet))
ppg->m_uVScrollCode=NOVALUE;

if (!(UDROP_INSETVERT & uLast) && (UDROP_INSETVERT & uRet))
{
ppg->m_dwTimeLast=GetTickCount();
ppg->m_uVScrollCode=(0!=(UDROP_INSETTOP & uRet))
? SB_LINEUP : SB_LINEDOWN;
}

//Only change the last time if ALL scrolling stops.
if (NOVALUE==ppg->m_uHScrollCode && NOVALUE==ppg->m_uVScrollCode)
ppg->m_dwTimeLast=0L;

//Set the scroll effect on any inset hit.
if ((UDROP_INSETHORZ | UDROP_INSETVERT) & uRet)
*pdwEffect |= DROPEFFECT_SCROLL;

xPos=ppg->m_xPos;
yPos=ppg->m_yPos;

//Has the delay elapsed? We can scroll if so
if (ppg->m_dwTimeLast!=0
&& (GetTickCount()-ppg->m_dwTimeLast)
> (DWORD)ppg->m_uScrollDelay)
{
if (NOVALUE!=ppg->m_uHScrollCode)
{
m_fPendingRepaint=TRUE;
SendMessage(ppg->m_hWnd, WM_HSCROLL
, ppg->m_uHScrollCode, 0L);
}

if (NOVALUE!=ppg->m_uVScrollCode)
{
m_fPendingRepaint=TRUE;
SendMessage(ppg->m_hWnd, WM_VSCROLL
, ppg->m_uVScrollCode, 0L);
}
}

//If we didn't scroll but have a pending repaint, do it now.
if (xPos==ppg->m_xPos && yPos==ppg->m_yPos && m_fPendingRepaint)
{
UpdateWindow(ppg->m_hWnd);
m_fPendingRepaint=FALSE;
}

pt.x-=m_ptPick.x;
pt.y-=m_ptPick.y;

m_ptLast=pt;
m_fFeedback=TRUE;
ppg->DrawDropTargetRect(&pt, &m_szl);

return NOERROR;
}






/*
* CDropTarget::DragLeave
*
* Purpose:
* Informs the drop target that the operation has left its window.
*
* Parameters:
* None
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CDropTarget::DragLeave(void)
{
PCPages ppg=m_pDoc->m_pPG;

if (NULL==m_pIDataObject)
return NOERROR;

//Stop scrolling
ppg->m_uHScrollCode=NOVALUE;
ppg->m_uVScrollCode=NOVALUE;

if (m_fPendingRepaint)
UpdateWindow(ppg->m_hWnd);

//Remove the last feedback rectangle.
if (m_fFeedback)
ppg->DrawDropTargetRect(&m_ptLast, &m_szl);

m_fFeedback=FALSE;
m_pIDataObject->Release();
return NOERROR;
}





/*
* CDropTarget::Drop
*
* Purpose:
* Instructs the drop target to paste the data that was just now
* dropped on it.
*
* Parameters:
* pIDataSource LPDATAOBJECT from which we'll paste.
* grfKeyState DWORD providing current keyboard/mouse state.
* pt POINTL at which the drop occurred.
* pdwEffect LPDWORD in which to store what you did.
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CDropTarget::Drop(LPDATAOBJECT pIDataSource
, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
PCPages ppg=m_pDoc->m_pPG;
BOOL fRet=TRUE;
FORMATETC fe;
TENANTTYPE tType;
PATRONOBJECT po;
POINT ptS;

*pdwEffect=DROPEFFECT_NONE;

if (NULL==m_pIDataObject)
return ResultFromScode(E_FAIL);

if (UDROP_NONE==ppg->UTestDroppablePoint(&pt))
return ResultFromScode(E_FAIL);

//Stop scrolling
ppg->m_uHScrollCode=NOVALUE;
ppg->m_uVScrollCode=NOVALUE;

if (m_fPendingRepaint)
UpdateWindow(ppg->m_hWnd);

//2. Remove the UI feedback
if (m_fFeedback)
ppg->DrawDropTargetRect(&m_ptLast, &m_szl);

m_pIDataObject->Release();


/*
* Check if we can do the paste, and if so, tell our pasting
* mechanism exactly where to place us.
*/
pt.x-=m_ptPick.x;
pt.y-=m_ptPick.y;

POINTFROMPOINTL(ptS, pt);
ScreenToClient(ppg->Window(), &ptS);
POINTLFROMPOINT(po.ptl, ptS);

//This is true if we didn't see placement data in DragEnter
if (0!=m_fe.cfFormat)
{
po.szl.cx=m_szl.cx; //We stored these positive
po.szl.cy=-m_szl.cy;
}
else
SETSIZEL(po.szl, 0, 0); //Ask object for its size.

//Adjust for scrolling and mapping mode.
ppg->AdjustPosition(&po.ptl, &po.szl);


/*
* If we're in the same document and moving, then we can just
* stuff the Pages' m_ptDrop which will move us and return.
*/
if (ppg->m_fDragSource && !(grfKeyState & MK_CONTROL))
{
*pdwEffect=DROPEFFECT_MOVE;
ppg->m_fMoveInPage=TRUE;
ppg->m_ptDrop=po.ptl;
return NOERROR;
}

/*
* Otherwise, paste either from another document or from
* the same document which will always be a copy to the new
* point.
*/

*pdwEffect=DROPEFFECT_MOVE;

if (grfKeyState & MK_CONTROL)
{
if (ppg->m_fLinkAllowed && (grfKeyState & MK_SHIFT))
*pdwEffect=DROPEFFECT_LINK;
else
*pdwEffect=DROPEFFECT_COPY;
}

ppg->m_fMoveInPage=FALSE;

/*
* We know linking is desired if effect flag is set, so this
* function will get the FORMATETC for linking. Otherwise
* FQueryPasteFromData will get the other FORMATETC to use.
*/
if (DROPEFFECT_LINK==*pdwEffect)
{
fRet=m_pDoc->FQueryPasteLinkFromData(pIDataSource, &fe
, &tType);
}
else
fRet=m_pDoc->FQueryPasteFromData(pIDataSource, &fe, &tType);

if (fRet)
{
//Copy the real format if we have placement data.
po.fe=(m_pDoc->m_cf==fe.cfFormat) ? m_fe : fe;

//Flag PasteFromData to use CFSTR_OBJECTDESCRIPTOR
fRet=m_pDoc->PasteFromData(pIDataSource, &fe, tType
, &po, 0, TRUE);
}

if (!fRet)
return ResultFromScode(E_FAIL);


*pdwEffect=DROPEFFECT_MOVE;

if (grfKeyState & MK_CONTROL)
*pdwEffect=DROPEFFECT_COPY;

return NOERROR;
}