Index: dll/win32/shell32/defcontextmenu.cpp =================================================================== --- dll/win32/shell32/defcontextmenu.cpp (revision 61507) +++ dll/win32/shell32/defcontextmenu.cpp (working copy) @@ -1037,7 +1037,6 @@ } SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL); - NotifyShellViewWindow(lpcmi, TRUE); TRACE("CP result %x\n", hr); return S_OK; @@ -1104,73 +1103,24 @@ ERR("no IDropTarget Interface\n"); return hr; } - //DWORD link = DROPEFFECT_LINK; SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL); - NotifyShellViewWindow(lpcmi, TRUE); return S_OK; } -HRESULT -CDefaultContextMenu::DoDelete( - LPCMINVOKECOMMANDINFO lpcmi) -{ - STRRET strTemp; - HRESULT hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strTemp); - if(hr != S_OK) - { - ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr); - return hr; - } +HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi) { + TRACE("(%p) Deleting\n", this); - WCHAR wszPath[MAX_PATH]; - hr = StrRetToBufW(&strTemp, m_Dcm.apidl[0], wszPath, _countof(wszPath)); - if (hr != S_OK) + LPDATAOBJECT pDataObj; + + if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj)))) { - ERR("StrRetToBufW failed with %x\n", hr); - return hr; - } - - /* Only keep the base path */ - LPWSTR pwszFilename = PathFindFileNameW(wszPath); - *pwszFilename = L'\0'; - - /* Build paths list */ - LPWSTR pwszPaths = BuildPathsList(wszPath, m_Dcm.cidl, m_Dcm.apidl); - if (!pwszPaths) + pDataObj->AddRef(); + SHCreateThread(DoDeleteThreadProc, pDataObj, NULL, NULL); + pDataObj->Release(); + } + else return E_FAIL; - - /* Delete them */ - SHFILEOPSTRUCTW FileOp; - ZeroMemory(&FileOp, sizeof(FileOp)); - FileOp.hwnd = GetActiveWindow(); - FileOp.wFunc = FO_DELETE; - FileOp.pFrom = pwszPaths; - FileOp.fFlags = FOF_ALLOWUNDO; - - if (SHFileOperationW(&FileOp) != 0) - { - ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths)); - return S_OK; - } - - /* Get the active IShellView */ - LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0); - if (lpSB) - { - /* Is the treeview focused */ - HWND hwnd; - if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd))) - { - /* Remove selected items from treeview */ - HTREEITEM hItem = TreeView_GetSelection(hwnd); - if (hItem) - (void)TreeView_DeleteItem(hwnd, hItem); - } - } - NotifyShellViewWindow(lpcmi, TRUE); - - HeapFree(GetProcessHeap(), 0, pwszPaths); return S_OK; } Index: dll/win32/shell32/folders/fs.cpp =================================================================== --- dll/win32/shell32/folders/fs.cpp (revision 61507) +++ dll/win32/shell32/folders/fs.cpp (working copy) @@ -1419,6 +1419,27 @@ { TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); + _DoDropData *data = reinterpret_cast<_DoDropData*> (HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData))); + data->This = this; + // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder). + data->This->AddRef(); + data->pDataObject = pDataObject; + // Also keep the data object in case it gets freed elsewhere. + data->pDataObject->AddRef(); + data->dwKeyState = dwKeyState; + data->pt = pt; + // Need to dereference as pdweffect is freed. + data->pdwEffect = *pdwEffect; + + SHCreateThread(reinterpret_cast (CFSFolder::_DoDropThreadProc), reinterpret_cast (data), NULL, NULL); + return S_OK; +} + +HRESULT WINAPI CFSFolder::_DoDrop(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); + HRESULT hr; bool bCopy = TRUE; bool bLinking = FALSE; @@ -1451,9 +1472,9 @@ if (pdwEffect) { TRACE("Current drop effect flag %i\n", *pdwEffect); - if (*pdwEffect & DROPEFFECT_MOVE) + if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) bCopy = FALSE; - if (*pdwEffect & DROPEFFECT_LINK) + if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) bLinking = TRUE; } @@ -1631,5 +1652,12 @@ } DWORD CFSFolder::_DoDropThreadProc(LPVOID lpParameter) { + _DoDropData *data = reinterpret_cast<_DoDropData*>(lpParameter); + data->This->_DoDrop(data->pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); + //Release the CFSFolder and data object holds in the copying thread. + data->pDataObject->Release(); + data->This->Release(); + //Release the parameter from the heap. + HeapFree(GetProcessHeap(), 0, data); return 0; } \ No newline at end of file Index: dll/win32/shell32/folders/fs.h =================================================================== --- dll/win32/shell32/folders/fs.h (revision 61507) +++ dll/win32/shell32/folders/fs.h (working copy) @@ -46,7 +46,8 @@ BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect); void SF_RegisterClipFmt(); BOOL GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut); - DWORD _DoDropThreadProc(LPVOID lpParameter); + static DWORD _DoDropThreadProc(LPVOID lpParameter); + virtual HRESULT WINAPI _DoDrop(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); public: CFSFolder(); @@ -116,4 +117,12 @@ END_COM_MAP() }; +struct _DoDropData { + CFSFolder *This; + IDataObject *pDataObject; + DWORD dwKeyState; + POINTL pt; + DWORD pdwEffect; +}; + #endif // _CFSFOLDER_H_ Index: dll/win32/shell32/folders/recyclebin.cpp =================================================================== --- dll/win32/shell32/folders/recyclebin.cpp (revision 61507) +++ dll/win32/shell32/folders/recyclebin.cpp (working copy) @@ -429,10 +429,24 @@ return S_OK; } +/************************************************************************** +* registers clipboardformat once +*/ +void CRecycleBin::SF_RegisterClipFmt() +{ + TRACE ("(%p)\n", this); + + if (!cfShellIDList) + cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); +} + CRecycleBin::CRecycleBin() { pidl = NULL; iIdEmpty = 0; + cfShellIDList = 0; + SF_RegisterClipFmt(); + fAcceptFmt = FALSE; } CRecycleBin::~CRecycleBin() @@ -556,8 +570,7 @@ if (IsEqualIID (riid, IID_IDropTarget)) { - WARN ("IDropTarget not implemented\n"); - hr = E_NOTIMPL; + hr = this->QueryInterface (IID_IDropTarget, ppv); } else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2)) { @@ -605,7 +618,7 @@ { hr = CRecycleBinItemContextMenuConstructor(riid, apidl[0], (void **)&pObj); } - else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1)) + else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1)) { hr = this->QueryInterface(IID_IDropTarget, (LPVOID *) & pObj); } @@ -1372,3 +1385,214 @@ return S_OK; } + +/**************************************************************************** + * IDropTarget implementation + */ +BOOL CRecycleBin::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect) +{ + /* TODO on shift we should delete, we should update the cursor manager to show this. */ + + DWORD dwEffect = DROPEFFECT_COPY; + + *pdwEffect = DROPEFFECT_NONE; + + if (fAcceptFmt) { /* Does our interpretation of the keystate ... */ + *pdwEffect = KeyStateToDropEffect (dwKeyState); + + if (*pdwEffect == DROPEFFECT_NONE) + *pdwEffect = dwEffect; + + /* ... matches the desired effect ? */ + if (dwEffect & *pdwEffect) { + return TRUE; + } + } + return FALSE; +} + +HRESULT WINAPI CRecycleBin::DragEnter(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + FIXME("Recycle bin drag over (%p)\n", this); + /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */ + fAcceptFmt = TRUE; + + QueryDrop(dwKeyState, pdwEffect); + return S_OK; +} + +HRESULT WINAPI CRecycleBin::DragOver(DWORD dwKeyState, POINTL pt, + DWORD *pdwEffect) +{ + TRACE("(%p)\n", this); + + if (!pdwEffect) + return E_INVALIDARG; + + QueryDrop(dwKeyState, pdwEffect); + + return S_OK; +} + +HRESULT WINAPI CRecycleBin::DragLeave() +{ + TRACE("(%p)\n", this); + + fAcceptFmt = FALSE; + + return S_OK; +} + +HRESULT WINAPI CRecycleBin::Drop(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + FIXME("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect); + + /* TODO: pdwEffect should be read and make the drop object be permanently deleted in the move case (shift held) */ + + FORMATETC fmt; + TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); + InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); + + /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to other software */ + if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) { + pDataObject->AddRef(); + SHCreateThread(DoDeleteThreadProc, pDataObject, NULL, NULL); + } + else + { + /* + * TODO call SetData on the data object with format CFSTR_TARGETCLSID + * set to the Recycle Bin's class identifier CLSID_RecycleBin. + */ + } + return S_OK; +} + +DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter) +{ + IDataObject *pda = (IDataObject*) lpParameter; + DoDeleteDataObject(pda); + //Release the data object + pda->Release(); + return 0; +} + +HRESULT WINAPI DoDeleteDataObject(IDataObject *pda) +{ + TRACE("performing delete"); + HRESULT hr; + + STGMEDIUM medium; + FORMATETC formatetc; + InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL); + hr = pda->GetData(&formatetc, &medium); + if (FAILED(hr)) + return hr; + + /* lock the handle */ + LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal); + if (!lpcida) + { + ReleaseStgMedium(&medium); + return E_FAIL; + } + + /* convert the data into pidl */ + LPITEMIDLIST pidl; + LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida); + if (!apidl) + { + ReleaseStgMedium(&medium); + return E_FAIL; + } + + CComPtr psfDesktop; + CComPtr psfFrom = NULL; + + /* Grab the desktop shell folder */ + hr = SHGetDesktopFolder(&psfDesktop); + if (FAILED(hr)) + { + ERR("SHGetDesktopFolder failed\n"); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return E_FAIL; + } + + /* Find source folder, this is where the clipboard data was copied from */ + if (_ILIsDesktop(pidl)) + { + psfFrom = psfDesktop; + } + else + { + hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (LPVOID*)&psfFrom); + if (FAILED(hr)) + { + ERR("no IShellFolder\n"); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return E_FAIL; + } + } + + STRRET strTemp; + hr = psfFrom->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strTemp); + if (FAILED(hr)) + { + ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return hr; + } + + WCHAR wszPath[MAX_PATH]; + hr = StrRetToBufW(&strTemp, apidl[0], wszPath, _countof(wszPath)); + if (FAILED(hr)) + { + ERR("StrRetToBufW failed with %x\n", hr); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return hr; + } + + /* Only keep the base path */ + LPWSTR pwszFilename = PathFindFileNameW(wszPath); + *pwszFilename = L'\0'; + + /* Build paths list */ + LPWSTR pwszPaths = BuildPathsList(wszPath, lpcida->cidl, (LPCITEMIDLIST*) apidl); + if (!pwszPaths) + { + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return E_FAIL; + } + + /* Delete them */ + SHFILEOPSTRUCTW FileOp; + ZeroMemory(&FileOp, sizeof(FileOp)); + FileOp.wFunc = FO_DELETE; + FileOp.pFrom = pwszPaths; + FileOp.fFlags = FOF_ALLOWUNDO; + + if (SHFileOperationW(&FileOp) != 0) + { + ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths)); + hr = E_FAIL; + } + + HeapFree(GetProcessHeap(), 0, pwszPaths); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + + return hr; +} \ No newline at end of file Index: dll/win32/shell32/folders/recyclebin.h =================================================================== --- dll/win32/shell32/folders/recyclebin.h (revision 61507) +++ dll/win32/shell32/folders/recyclebin.h (working copy) @@ -22,6 +22,9 @@ #ifndef _SHFLDR_RECYCLEBIN_H_ #define _SHFLDR_RECYCLEBIN_H_ +DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter); +HRESULT WINAPI DoDeleteDataObject(IDataObject *pda); + class CRecycleBin : public CComCoClass, public CComObjectRootEx, @@ -29,11 +32,16 @@ public IPersistFolder2, public IContextMenu, public IShellPropSheetExt, + public IDropTarget, public IShellExtInit { private: LPITEMIDLIST pidl; INT iIdEmpty; + UINT cfShellIDList; + void SF_RegisterClipFmt(); + BOOL fAcceptFmt; /* flag for pending Drop */ + BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect); public: CRecycleBin(); @@ -75,6 +83,12 @@ // IShellPropSheetExt virtual HRESULT WINAPI AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam); virtual HRESULT WINAPI ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam); + + // IDropTarget + virtual HRESULT WINAPI DragEnter(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); + virtual HRESULT WINAPI DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); + virtual HRESULT WINAPI DragLeave(); + virtual HRESULT WINAPI Drop(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); // IShellExtInit virtual HRESULT STDMETHODCALLTYPE Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID); @@ -91,6 +105,7 @@ COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2) COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) COM_INTERFACE_ENTRY_IID(IID_IShellPropSheetExt, IShellPropSheetExt) + COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit) END_COM_MAP() }; Index: dll/win32/shell32/shlview.cpp =================================================================== --- dll/win32/shell32/shlview.cpp (revision 61507) +++ dll/win32/shell32/shlview.cpp (working copy) @@ -1691,8 +1691,6 @@ { DWORD dwEffect2; DoDragDrop(pda, pds, dwEffect, &dwEffect2); - if ((dwEffect2 & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) - this->Refresh(); } pda->Release(); }