windows-nt/Source/XPSP1/NT/shell/inc/droptgt.cpp
2020-09-26 16:20:57 +08:00

563 lines
14 KiB
C++

#include "droptgt.h"
#define TF_DRAGDROP TF_BAND
#define MAX_DROPTARGETS 3
class CDropTargetWrap : public IDropTarget
{
public:
// *** IUnknown ***
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
// *** IDropTarget methods ***
virtual STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
virtual STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
virtual STDMETHODIMP DragLeave(void);
virtual STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
CDropTargetWrap(IDropTarget** ppdtg, HWND hwnd);
protected:
~CDropTargetWrap();
private:
int _cRef;
int _count;
IDropTarget* _rgpdt[MAX_DROPTARGETS];
DWORD _rgdwEffect[MAX_DROPTARGETS];
HWND _hwnd;
};
CDropTargetWrap::CDropTargetWrap(IDropTarget** ppdt, HWND hwnd)
: _hwnd(hwnd)
{
_cRef = 1;
for (int i = 0; i < MAX_DROPTARGETS; i++, ppdt++) {
if (*ppdt) {
_rgpdt[_count] = *ppdt;
_rgpdt[_count]->AddRef();
_count++;
}
}
}
CDropTargetWrap::~CDropTargetWrap()
{
for (int i = 0 ; i < _count ; i++)
{
_rgpdt[i]->Release();
}
}
IDropTarget* DropTargetWrap_CreateInstance(IDropTarget* pdtPrimary, IDropTarget* pdtSecondary, HWND hwnd, IDropTarget* pdt3)
{
// no point in wrapping nothing...
if (pdtPrimary || pdtSecondary || pdt3)
{
IDropTarget* pdt[MAX_DROPTARGETS] = { pdtPrimary, pdtSecondary, pdt3 };
CDropTargetWrap* pdtw = new CDropTargetWrap(pdt, hwnd);
if (pdtw)
{
return SAFECAST(pdtw, IDropTarget*);
}
}
return NULL;
}
HRESULT CDropTargetWrap::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDropTargetWrap, IDropTarget),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CDropTargetWrap::AddRef(void)
{
_cRef++;
return _cRef;
}
ULONG CDropTargetWrap::Release(void)
{
_cRef--;
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragEnter method
The *pdwEffect that is returned is the first valid value
of all the drop targets' returned effects.
*/
HRESULT CDropTargetWrap::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
DWORD dwEffectOut = DROPEFFECT_NONE;
for (int i = 0 ; i < _count ; i++)
{
_rgdwEffect[i] = *pdwEffect;
if (SUCCEEDED(_rgpdt[i]->DragEnter(pdtobj, grfKeyState, ptl, &_rgdwEffect[i])))
{
if (dwEffectOut == DROPEFFECT_NONE)
{
dwEffectOut = _rgdwEffect[i];
}
}
else
{
_rgdwEffect[i] = DROPEFFECT_NONE;
}
}
*pdwEffect = dwEffectOut;
return(S_OK);
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragOver method
*/
HRESULT CDropTargetWrap::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
DWORD dwEffectOut = DROPEFFECT_NONE;
for (int i = 0 ; i < _count ; i++)
{
_rgdwEffect[i] = *pdwEffect;
if (SUCCEEDED(_rgpdt[i]->DragOver(grfKeyState, ptl, &_rgdwEffect[i])))
{
if (dwEffectOut == DROPEFFECT_NONE)
dwEffectOut = _rgdwEffect[i];
}
else
{
_rgdwEffect[i] = DROPEFFECT_NONE;
}
}
*pdwEffect = dwEffectOut;
return(S_OK);
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragLeave method
*/
HRESULT CDropTargetWrap::DragLeave(void)
{
for (int i = 0 ; i < _count ; i++)
{
_rgpdt[i]->DragLeave();
}
return(S_OK);
}
/*----------------------------------------------------------
Purpose: IDropTarget::Drop method
*/
HRESULT CDropTargetWrap::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
DWORD dwEffectOut = DROPEFFECT_NONE;
int i;
BOOL fDropTried = FALSE;
for (i = 0 ; (DROPEFFECT_NONE == dwEffectOut) && i < _count ; i++)
{
if ((_rgdwEffect[i] && *pdwEffect) && !fDropTried)
{
dwEffectOut = *pdwEffect;
_rgpdt[i]->Drop(pdtobj, grfKeyState, pt, &dwEffectOut);
fDropTried = TRUE;
}
else
{
_rgpdt[i]->DragLeave();
}
}
*pdwEffect = dwEffectOut;
return(S_OK);
}
//=============================================================================
// CDelegateDropTarget
//
// This class implements IDropTarget given an IDelegateDropTargetCB interface.
// It handles all hit testing, caching, and scrolling for you.
//
//=============================================================================
#undef CDropTargetWrap
CDelegateDropTarget::CDelegateDropTarget()
{
TraceMsg(TF_SHDLIFE, "ctor CDelegateDropTarget %x", this);
}
CDelegateDropTarget::~CDelegateDropTarget()
{
TraceMsg(TF_SHDLIFE, "dtor CDelegateDropTarget %x", this);
ASSERT(!_pDataObj);
ATOMICRELEASE(_pDataObj);
ASSERT(!_pdtCur);
ATOMICRELEASE(_pdtCur);
}
HRESULT CDelegateDropTarget::Init()
{
HRESULT hres = GetWindowsDDT(&_hwndLock, &_hwndScroll);
// We lock _hwndLock and do scrolling against _hwndScroll.
// These can be different hwnds, but certain restrictions apply:
if (_hwndLock != _hwndScroll)
{
BOOL fValid = IsChild(_hwndLock, _hwndScroll);
if (!fValid)
{
TraceMsg(TF_DRAGDROP, "ctor CDelegateDropTarget: invalid windows %x and %x!", _hwndLock, _hwndScroll);
_hwndLock = _hwndScroll = NULL;
}
}
return hres;
}
void CDelegateDropTarget::_ReleaseCurrentDropTarget()
{
if (_pdtCur)
{
_pdtCur->DragLeave();
ATOMICRELEASE(_pdtCur);
}
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragEnter method
*/
HRESULT CDelegateDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
// We can be re-entered due to ui on thread
if (_pDataObj != NULL)
{
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter called a second time!");
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter with *pdwEffect=%x", *pdwEffect);
ASSERT(!_pDataObj);
_pDataObj = pdtobj;
_pDataObj->AddRef();
// cache state
//
// wait until first DragOver to get valid info
//
_fPrime = FALSE;
_dwEffectOut = DROPEFFECT_NONE;
// set up auto-scroll info
//
ASSERT(pdtobj);
_DragEnter(_hwndLock, ptl, pdtobj);
DAD_InitScrollData(&_asd);
_ptLast.x = _ptLast.y = 0x7fffffff; // put bogus value to force redraw
HitTestDDT(HTDDT_ENTER, NULL, NULL, NULL);
return S_OK;
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragOver method
*/
HRESULT CDelegateDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect)
{
HRESULT hres = S_OK;
DWORD_PTR itemNew;
POINT pt;
DWORD dwEffectScroll = 0;
DWORD dwEffectOut = 0;
BOOL fSameImage = FALSE;
DWORD dwCustDropEffect = 0;
if (_pDataObj == NULL)
{
ASSERT(0); // DragEnter should be called before.
return E_FAIL;
}
// convert to window coords
pt.x = ptl.x;
pt.y = ptl.y;
ScreenToClient(_hwndScroll, &pt);
if (DAD_AutoScroll(_hwndScroll, &_asd, &pt))
dwEffectScroll = DROPEFFECT_SCROLL;
//
// If we are dragging over on a different item, get its IDropTarget
// interface or adjust itemNew to -1.
//
if (SUCCEEDED(HitTestDDT(HTDDT_OVER, &pt, &itemNew, &dwCustDropEffect)) &&
(itemNew != _itemOver || !_fPrime))
{
_fPrime = TRUE;
_ReleaseCurrentDropTarget();
_itemOver = itemNew;
GetObjectDDT(_itemOver, IID_IDropTarget, (LPVOID*)&_pdtCur);
if (_pdtCur)
{
// There's an IDropTarget for this hit, use it
dwEffectOut = *pdwEffect;
hres = _pdtCur->DragEnter(_pDataObj, grfKeyState, ptl, &dwEffectOut);
if (FAILED(hres))
dwEffectOut = DROPEFFECT_NONE;
}
else
{
// No IDropTarget, no effect
dwEffectOut = DROPEFFECT_NONE;
}
}
else
{
//
// No change in the selection. We assume that *pdwEffect stays
// the same during the same drag-loop as long as the key state doesn't change.
//
if ((_grfKeyState != grfKeyState) && _pdtCur)
{
dwEffectOut = *pdwEffect;
hres = _pdtCur->DragOver(grfKeyState, ptl, &dwEffectOut);
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver DragOver()d id:%d dwEffect:%4x hres:%d", _itemOver, dwEffectOut, hres);
}
else
{
// Same item and same key state. Use the previous dwEffectOut.
dwEffectOut = _dwEffectOut;
fSameImage = TRUE;
}
}
_grfKeyState = grfKeyState; // store these for the next Drop
_dwEffectOut = dwEffectOut; // and DragOver
// Is the Custdrop effect valid ?
if (dwCustDropEffect != DROPEFFECT_NONE)
{
//Yes then set the effect to Custdrop effect along with scroll effect
*pdwEffect = dwCustDropEffect | dwEffectScroll;
}
else
{
//No , set the effect to dwEffectOut along with scroll effect
*pdwEffect = dwEffectOut | dwEffectScroll;
}
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver (*pdwEffect=%x)", *pdwEffect);
if (!(fSameImage && pt.x==_ptLast.x && pt.y==_ptLast.y))
{
_DragMove(_hwndLock, ptl);
_ptLast.x = ptl.x;
_ptLast.y = ptl.y;
}
return hres;
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragLeave method
*/
HRESULT CDelegateDropTarget::DragLeave()
{
HitTestDDT(HTDDT_LEAVE, NULL, NULL, NULL);
_ReleaseCurrentDropTarget();
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragLeave");
ATOMICRELEASE(_pDataObj);
DAD_DragLeave();
return S_OK;
}
/*----------------------------------------------------------
Purpose: IDropTarget::Drop method
*/
HRESULT CDelegateDropTarget::Drop(IDataObject *pdtobj,
DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
HRESULT hres = S_OK;
BOOL bDropHandled = FALSE;
TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::Drop (*pdwEffect=%x)", *pdwEffect);
//
// According to AlexGo (OLE), this is by-design. We should make it sure
// that we use pdtobj instead of pdtobj.
//
//ASSERT(pdtobj == _pDataObj);
pdtobj->AddRef();
_pDataObj->Release();
_pDataObj = pdtobj;
//
// Note that we don't use the drop position intentionally,
// so that it matches to the last destination feedback.
//
if (_pdtCur)
{
// use this local because if _pdtCur::Drop does a UnlockWindow
// then hits an error and needs to put up a dialog,
// we could get re-entered
IDropTarget *pdtCur = _pdtCur;
_pdtCur = NULL;
// HACK ALERT!!!!
//
// If we don't call LVUtil_DragEnd here, we'll be able to leave
// dragged icons visible when the menu is displayed. However, because
// we are calling IDropTarget::Drop() which may create some modeless
// dialog box or something, we can not ensure the locked state of
// the list view -- LockWindowUpdate() can lock only one window at
// a time. Therefore, we skip this call only if the _pdtCur
// is a subclass of CIDLDropTarget, assuming its Drop calls
// CDefView::DragEnd (or CIDLDropTarget_DragDropMenu) appropriately.
//
#if 0 // later
if (!IsIDLDropTarget(pdtCur))
#endif
{
//
// This will hide the dragged image.
//
DAD_DragLeave();
//
// We need to reset the drag image list so that the user
// can start another drag&drop while we are in this
// Drop() member function call.
//
// NOTE: we don't have to worry about the DAD_DragLeave
// (called from during the DragLeave call at the end of
// this function) cancelling the potential above-mentioned
// drag&drop loop. If such a beast is going on, it should
// complete before pdtCur->Drop returns.
//
DAD_SetDragImage(NULL, NULL);
}
if (S_FALSE != OnDropDDT(pdtCur, _pDataObj, &grfKeyState, pt, pdwEffect))
pdtCur->Drop(_pDataObj, grfKeyState, pt, pdwEffect);
else
pdtCur->DragLeave(); // should be okay even if OnDrop did this already
pdtCur->Release();
}
else
{
//
// We come here if Drop is called without DragMove (with DragEnter).
//
*pdwEffect = DROPEFFECT_NONE;
}
//
// Clean up everything (OLE won't call DragLeave after Drop).
//
DragLeave();
return hres;
}
// ******************************************************************
// dummy drop target to only call DAD_DragEnterEx() on DragEnter();
// ******************************************************************
HRESULT CDropDummy::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CDropDummy, IDropTarget),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CDropDummy::AddRef(void)
{
_cRef++;
return _cRef;
}
ULONG CDropDummy::Release(void)
{
_cRef--;
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
/*----------------------------------------------------------
Purpose: IDropTarget::DragEnter method
simply call DAD_DragEnterEx2() to get custom drag cursor
drawing.
*/
HRESULT CDropDummy::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
ASSERT(pdtobj);
_DragEnter(_hwndLock, ptl, pdtobj);
*pdwEffect = DROPEFFECT_NONE;
return(S_OK);
}
HRESULT CDropDummy::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
_DragMove(_hwndLock, ptl);
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}