shell32: Merge the drag & drop implementation from the Unix shell folder.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-09-10 18:52:37 +02:00
parent aef981df62
commit df9c67d196
1 changed files with 100 additions and 34 deletions

View File

@ -72,11 +72,11 @@ typedef struct {
LPWSTR sPathTarget; /* complete path to target used for enumeration and ChangeNotify */
LPITEMIDLIST pidlRoot; /* absolute pidl */
UINT cfShellIDList; /* clipboardformat for IDropTarget */
BOOL fAcceptFmt; /* flag for pending Drop */
DWORD drop_effects_mask;
} IGenericSFImpl;
static UINT cfShellIDList;
static inline IGenericSFImpl *impl_from_IUnknown(IUnknown *iface)
{
return CONTAINING_RECORD(iface, IGenericSFImpl, IUnknown_inner);
@ -102,18 +102,6 @@ static inline IGenericSFImpl *impl_from_ISFHelper(ISFHelper *iface)
return CONTAINING_RECORD(iface, IGenericSFImpl, ISFHelper_iface);
}
/**************************************************************************
* registers clipboardformat once
*/
static void SF_RegisterClipFmt (IGenericSFImpl * This)
{
TRACE ("(%p)\n", This);
if (!This->cfShellIDList) {
This->cfShellIDList = RegisterClipboardFormatW (CFSTR_SHELLIDLISTW);
}
}
/**************************************************************************
* inner IUnknown
*/
@ -136,7 +124,7 @@ static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, vo
*ppvObj = &This->ISFHelper_iface;
else if (IsEqualIID (riid, &IID_IDropTarget)) {
*ppvObj = &This->IDropTarget_iface;
SF_RegisterClipFmt(This);
if (!cfShellIDList) cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
}
if (*ppvObj) {
@ -1543,22 +1531,42 @@ static ULONG WINAPI ISFDropTarget_Release(IDropTarget *iface)
return IUnknown_Release(This->outer_unk);
}
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
static HRESULT WINAPI
ISFDropTarget_DragEnter (IDropTarget * iface, IDataObject * pDataObject,
DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
{
FORMATETC fmt;
IGenericSFImpl *This = impl_from_IDropTarget(iface);
FORMATETC format;
STGMEDIUM medium;
TRACE ("(%p)->(DataObject=%p)\n", This, pDataObject);
TRACE("(%p)->(%p 0x%08x {.x=%d, .y=%d} %p)\n", This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
InitFormatEtc (fmt, This->cfShellIDList, TYMED_HGLOBAL);
This->fAcceptFmt = IDataObject_QueryGetData (pDataObject, &fmt) == S_OK;
if (This->fAcceptFmt)
*pdwEffect = KeyStateToDropEffect(dwKeyState);
else
*pdwEffect = DROPEFFECT_NONE;
if (!pdwEffect || !pDataObject)
return E_INVALIDARG;
/* Compute a mask of supported drop-effects for this shellfolder object and the given data
* object. Dropping is only supported on folders, which represent filesystem locations. One
* can't drop on file objects. And the 'move' drop effect is only supported, if the source
* folder is not identical to the target folder. */
This->drop_effects_mask = DROPEFFECT_NONE;
InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
if (_ILIsFolder(ILFindLastID(This->pidlRoot)) && /* Only drop to folders, not to files */
SUCCEEDED(IDataObject_GetData(pDataObject, &format, &medium))) /* Only ShellIDList format */
{
LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
This->drop_effects_mask |= DROPEFFECT_COPY|DROPEFFECT_LINK;
if (pidaShellIDList) { /* Files can only be moved between two different folders */
if (!ILIsEqual(HIDA_GetPIDLFolder(pidaShellIDList), This->pidlRoot))
This->drop_effects_mask |= DROPEFFECT_MOVE;
GlobalUnlock(medium.u.hGlobal);
}
}
*pdwEffect = KeyStateToDropEffect(dwKeyState) & This->drop_effects_mask;
return S_OK;
}
@ -1569,15 +1577,12 @@ ISFDropTarget_DragOver (IDropTarget * iface, DWORD dwKeyState, POINTL pt,
{
IGenericSFImpl *This = impl_from_IDropTarget(iface);
TRACE ("(%p)\n", This);
TRACE("(%p)->(0x%08x {.x=%d, .y=%d} %p)\n", This, dwKeyState, pt.x, pt.y, pdwEffect);
if (!pdwEffect)
return E_INVALIDARG;
if (This->fAcceptFmt)
*pdwEffect = KeyStateToDropEffect(dwKeyState);
else
*pdwEffect = DROPEFFECT_NONE;
*pdwEffect = KeyStateToDropEffect(dwKeyState) & This->drop_effects_mask;
return S_OK;
}
@ -1586,10 +1591,9 @@ static HRESULT WINAPI ISFDropTarget_DragLeave (IDropTarget * iface)
{
IGenericSFImpl *This = impl_from_IDropTarget(iface);
TRACE ("(%p)\n", This);
This->fAcceptFmt = FALSE;
TRACE("(%p)\n", This);
This->drop_effects_mask = DROPEFFECT_NONE;
return S_OK;
}
@ -1598,8 +1602,70 @@ ISFDropTarget_Drop (IDropTarget * iface, IDataObject * pDataObject,
DWORD dwKeyState, POINTL pt, DWORD * pdwEffect)
{
IGenericSFImpl *This = impl_from_IDropTarget(iface);
FORMATETC format;
STGMEDIUM medium;
HRESULT hr;
FIXME ("(%p) object dropped\n", This);
TRACE("(%p)->(%p %d {.x=%d, .y=%d} %p) semi-stub\n",
This, pDataObject, dwKeyState, pt.x, pt.y, pdwEffect);
InitFormatEtc(format, cfShellIDList, TYMED_HGLOBAL);
hr = IDataObject_GetData(pDataObject, &format, &medium);
if (FAILED(hr))
return hr;
if (medium.tymed == TYMED_HGLOBAL) {
IShellFolder *psfSourceFolder, *psfDesktopFolder;
LPIDA pidaShellIDList = GlobalLock(medium.u.hGlobal);
STRRET strret;
UINT i;
if (!pidaShellIDList)
return HRESULT_FROM_WIN32(GetLastError());
hr = SHGetDesktopFolder(&psfDesktopFolder);
if (FAILED(hr)) {
GlobalUnlock(medium.u.hGlobal);
return hr;
}
hr = IShellFolder_BindToObject(psfDesktopFolder, HIDA_GetPIDLFolder(pidaShellIDList), NULL,
&IID_IShellFolder, (LPVOID*)&psfSourceFolder);
IShellFolder_Release(psfDesktopFolder);
if (FAILED(hr)) {
GlobalUnlock(medium.u.hGlobal);
return hr;
}
for (i = 0; i < pidaShellIDList->cidl; i++) {
WCHAR wszSourcePath[MAX_PATH];
hr = IShellFolder_GetDisplayNameOf(psfSourceFolder, HIDA_GetPIDLItem(pidaShellIDList, i),
SHGDN_FORPARSING, &strret);
if (FAILED(hr))
break;
hr = StrRetToBufW(&strret, NULL, wszSourcePath, MAX_PATH);
if (FAILED(hr))
break;
switch (*pdwEffect) {
case DROPEFFECT_MOVE:
FIXME("Move %s to %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
break;
case DROPEFFECT_COPY:
FIXME("Copy %s to %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
break;
case DROPEFFECT_LINK:
FIXME("Link %s from %s!\n", debugstr_w(wszSourcePath), debugstr_w(This->sPathTarget));
break;
}
}
IShellFolder_Release(psfSourceFolder);
GlobalUnlock(medium.u.hGlobal);
return hr;
}
return E_NOTIMPL;
}