windows-nt/Source/XPSP1/NT/shell/ext/nsc/nscdrop.c
2020-09-26 16:20:57 +08:00

339 lines
8.3 KiB
C

#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include "debug.h"
#include "common.h"
#include "autoscrl.h"
#include "nsc.h"
// BUGBUG: do nothing for now
#define DAD_DragLeave()
#define DAD_DragEnterEx(hwndLock, pt)
#define DAD_ShowDragImage(f)
#define DAD_DragMove(pt)
typedef struct { // tdt
IDropTarget dtgt;
UINT cRef;
NSC *pns;
RECT _rcLockWindow;
HTREEITEM _htiCur; // current tree item (dragging over)
IDropTarget *_pdtgtCur; // current drop target
IDataObject *_pdtobjCur; // current data object
DWORD _dwEffectCur; // current drag effect
DWORD _dwEffectIn; // *pdwEffect passed-in on last Move/Enter
DWORD _grfKeyState; // cached key state
POINT _ptLast; // last dragged over position
DWORD _dwLastTime;
AUTO_SCROLL_DATA asd;
} CTreeDropTarget;
STDMETHODIMP CTreeDropTarget_QueryInterface(IDropTarget *pdtgt, REFIID riid, void **ppvObj)
{
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
if (IsEqualIID(riid, &IID_IDropTarget) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = pdtgt;
this->cRef++;
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CTreeDropTarget_AddRef(IDropTarget *pdtgt)
{
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
this->cRef++;
return this->cRef;
}
void CTreeDropTarget_ReleaseDataObject(CTreeDropTarget *this)
{
if (this->_pdtobjCur)
Release(this->_pdtobjCur);
this->_pdtobjCur = NULL;
}
void CTreeDropTarget_ReleaseCurrentDropTarget(CTreeDropTarget *this)
{
if (this->_pdtgtCur)
{
this->_pdtgtCur->lpVtbl->DragLeave(this->_pdtgtCur);
Release(this->_pdtgtCur);
this->_pdtgtCur = NULL;
this->_htiCur = NULL;
}
else
{
Assert(this->_htiCur == NULL);
}
}
STDMETHODIMP_(ULONG) CTreeDropTarget_Release(IDropTarget * pdtgt)
{
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
this->cRef--;
if (this->cRef > 0)
return this->cRef;
AssertMsg(this->_pdtgtCur == NULL, "drag leave was not called properly");
// if above is true we can remove this...
CTreeDropTarget_ReleaseCurrentDropTarget(this);
LocalFree(this);
return 0;
}
STDMETHODIMP CTreeDropTarget_DragEnter(IDropTarget *pdtgt, IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
POINT pt;
HWND hwndLock;
DebugMsg(DM_TRACE, "sh - TR CTreeDropTarget::DragEnter called");
CTreeDropTarget_ReleaseDataObject(this);
this->_pdtobjCur = pdtobj;
this->_grfKeyState = grfKeyState;
AddRef(pdtobj);
Assert(this->_pdtgtCur == NULL);
Assert(this->_htiCur == NULL);
hwndLock = this->pns->hwndTree; // clip to this
GetWindowRect(hwndLock, &this->_rcLockWindow);
pt.x = ptl.x-this->_rcLockWindow.left;
pt.y = ptl.y-this->_rcLockWindow.top;
DAD_DragEnterEx(hwndLock, pt);
this->_ptLast.x = this->_ptLast.y = 0x7ffffff; // set bogus position
return S_OK;
}
STDMETHODIMP CTreeDropTarget_DragOver(IDropTarget *pdtgt, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
HRESULT hres = S_OK;
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
TV_HITTESTINFO tvht;
HTREEITEM htiNew;
POINT pt = { ptl.x, ptl.y };
BOOL fSameImage = FALSE;
DWORD dwEffectScroll = 0;
ScreenToClient(this->pns->hwndTree, &pt);
if (DAD_AutoScroll(this->pns->hwndTree, &this->asd, &pt))
dwEffectScroll = DROPEFFECT_SCROLL;
tvht.pt = pt;
htiNew = TreeView_HitTest(this->pns->hwndTree, &tvht);
// don't allow droping on the item being dragged
if (htiNew == this->pns->htiDragging)
htiNew = NULL;
if (this->_htiCur != htiNew)
{
// change in target
this->_dwLastTime = GetTickCount(); // keep track for auto-expanding the tree
CTreeDropTarget_ReleaseCurrentDropTarget(this);
this->_dwEffectCur = 0; // assume error
DAD_ShowDragImage(FALSE);
TreeView_SelectDropTarget(this->pns->hwndTree, htiNew);
DAD_ShowDragImage(TRUE);
if (htiNew)
{
// get the drop target for the item we hit
LPITEMIDLIST pidl = _CacheParentShellFolder(this->pns, htiNew, NULL);
if (pidl)
{
IShellFolder *psf = this->pns->psfCache;
AddRef(psf);
if (pidl->mkid.cb == 0)
{
hres = psf->lpVtbl->CreateViewObject(psf, this->pns->hwnd, &IID_IDropTarget, &this->_pdtgtCur);
}
else
{
UINT dwAttr = SFGAO_DROPTARGET;
hres = psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &dwAttr);
if (SUCCEEDED(hres) && (dwAttr & SFGAO_DROPTARGET))
{
hres = psf->lpVtbl->GetUIObjectOf(psf, this->pns->hwnd, 1, &pidl, &IID_IDropTarget, NULL, &this->_pdtgtCur);
}
else
{
hres = E_INVALIDARG;
}
}
Release(psf);
}
if (SUCCEEDED(hres))
{
this->_htiCur = htiNew;
this->_dwEffectCur = *pdwEffect; // pdwEffect is In/Out
hres = this->_pdtgtCur->lpVtbl->DragEnter(this->_pdtgtCur, this->_pdtobjCur, grfKeyState, ptl, &this->_dwEffectCur);
}
}
}
else
{
// No target change
// auto expand the tree
if (this->_htiCur)
{
DWORD dwNow = GetTickCount();
if ((dwNow - this->_dwLastTime) >= 1000)
{
this->_dwLastTime = dwNow;
DAD_ShowDragImage(FALSE);
this->pns->fAutoExpanding = TRUE;
TreeView_Expand(this->pns->hwndTree, this->_htiCur, TVE_EXPAND);
this->pns->fAutoExpanding = FALSE;
DAD_ShowDragImage(TRUE);
}
}
// maybe the key state changed
if ((this->_grfKeyState != grfKeyState) && this->_pdtgtCur)
{
this->_dwEffectCur = *pdwEffect;
hres = this->_pdtgtCur->lpVtbl->DragOver(this->_pdtgtCur, grfKeyState, ptl, &this->_dwEffectCur);
}
else
{
fSameImage = TRUE;
hres = S_OK;
}
}
DebugMsg(DM_TRACE, "sh TR - CTreeDropTarget_DragOver (In=%x, Out=%x)", *pdwEffect, this->_dwEffectCur);
this->_grfKeyState = grfKeyState;
*pdwEffect = this->_dwEffectCur | dwEffectScroll;
// We need pass pt relative to the locked window (not the client).
pt.x = ptl.x-this->_rcLockWindow.left;
pt.y = ptl.y-this->_rcLockWindow.top;
if (!(fSameImage && this->_ptLast.x == pt.x && this->_ptLast.y == pt.y))
{
DAD_DragMove(pt);
this->_ptLast.x = pt.x;
this->_ptLast.y = pt.y;
}
return hres;
}
STDMETHODIMP CTreeDropTarget_DragLeave(IDropTarget *pdtgt)
{
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
DebugMsg(DM_TRACE, "sh - TR CTreeDropTarget::DragLeave called");
CTreeDropTarget_ReleaseCurrentDropTarget(this);
CTreeDropTarget_ReleaseDataObject(this);
DAD_DragLeave();
TreeView_SelectDropTarget(this->pns->hwndTree, NULL);
return S_OK;
}
STDMETHODIMP CTreeDropTarget_Drop(IDropTarget *pdtgt, IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
HRESULT hres;
CTreeDropTarget * this = IToClass(CTreeDropTarget, dtgt, pdtgt);
if (this->_pdtgtCur)
{
hres = this->_pdtgtCur->lpVtbl->Drop(this->_pdtgtCur, pdtobj, grfKeyState, pt, pdwEffect);
}
else
{
DebugMsg(DM_TRACE, "sh TR - CTreeDropTarget::Drop - this->_pdtgtCur==NULL");
*pdwEffect = 0;
hres = S_OK;
}
CTreeDropTarget_DragLeave(pdtgt);
return hres;
}
const IDropTargetVtbl c_CTreeDropTargetVtbl = {
CTreeDropTarget_QueryInterface, CTreeDropTarget_AddRef, CTreeDropTarget_Release,
CTreeDropTarget_DragEnter,
CTreeDropTarget_DragOver,
CTreeDropTarget_DragLeave,
CTreeDropTarget_Drop
};
HRESULT CTreeDropTarget_Create(NSC *pns, IDropTarget **ppdtgt)
{
CTreeDropTarget * this = LocalAlloc(LPTR, sizeof(CTreeDropTarget));
if (this)
{
this->dtgt.lpVtbl = (IDropTargetVtbl *)&c_CTreeDropTargetVtbl;
this->cRef = 1;
this->pns = pns;
*ppdtgt = &this->dtgt;
return S_OK;
}
*ppdtgt = NULL;
return E_OUTOFMEMORY;
}
void CTreeDropTarget_Register(NSC *pns)
{
IDropTarget *pdtgt;
if (SUCCEEDED(CTreeDropTarget_Create(pns, &pdtgt)))
{
RegisterDragDrop(pns->hwndTree, pdtgt);
Release(pdtgt);
}
}
void CTreeDropTarget_Revoke(NSC *pns)
{
RevokeDragDrop(pns->hwndTree);
}