1c2c66affSColin Finck 2c2c66affSColin Finck /* 3c2c66affSColin Finck * file system folder drop target 4c2c66affSColin Finck * 5c2c66affSColin Finck * Copyright 1997 Marcus Meissner 6c2c66affSColin Finck * Copyright 1998, 1999, 2002 Juergen Schmied 7c2c66affSColin Finck * 8c2c66affSColin Finck * This library is free software; you can redistribute it and/or 9c2c66affSColin Finck * modify it under the terms of the GNU Lesser General Public 10c2c66affSColin Finck * License as published by the Free Software Foundation; either 11c2c66affSColin Finck * version 2.1 of the License, or (at your option) any later version. 12c2c66affSColin Finck * 13c2c66affSColin Finck * This library is distributed in the hope that it will be useful, 14c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of 15c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16c2c66affSColin Finck * Lesser General Public License for more details. 17c2c66affSColin Finck * 18c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public 19c2c66affSColin Finck * License along with this library; if not, write to the Free Software 20c2c66affSColin Finck * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21c2c66affSColin Finck */ 22c2c66affSColin Finck 23c2c66affSColin Finck #include <precomp.h> 24c2c66affSColin Finck 25c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL (shell); 26c2c66affSColin Finck 27c2c66affSColin Finck /**************************************************************************** 28c2c66affSColin Finck * BuildPathsList 29c2c66affSColin Finck * 30c2c66affSColin Finck * Builds a list of paths like the one used in SHFileOperation from a table of 31c2c66affSColin Finck * PIDLs relative to the given base folder 32c2c66affSColin Finck */ 33c2c66affSColin Finck static WCHAR* BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls) 34c2c66affSColin Finck { 35c2c66affSColin Finck WCHAR *pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1); 36c2c66affSColin Finck WCHAR *pwszListPos = pwszPathsList; 37c2c66affSColin Finck 38c2c66affSColin Finck for (int i = 0; i < cidl; i++) 39c2c66affSColin Finck { 40c2c66affSColin Finck FileStructW* pDataW = _ILGetFileStructW(pidls[i]); 41c2c66affSColin Finck if (!pDataW) 42c2c66affSColin Finck { 43c2c66affSColin Finck ERR("Got garbage pidl\n"); 44c2c66affSColin Finck continue; 45c2c66affSColin Finck } 46c2c66affSColin Finck 47c2c66affSColin Finck PathCombineW(pwszListPos, wszBasePath, pDataW->wszName); 48c2c66affSColin Finck pwszListPos += wcslen(pwszListPos) + 1; 49c2c66affSColin Finck } 50c2c66affSColin Finck *pwszListPos = 0; 51c2c66affSColin Finck return pwszPathsList; 52c2c66affSColin Finck } 53c2c66affSColin Finck 54c2c66affSColin Finck /**************************************************************************** 55fd209faaSGiannis Adamopoulos * CFSDropTarget::_CopyItems 56c2c66affSColin Finck * 57c2c66affSColin Finck * copies items to this folder 58c2c66affSColin Finck */ 59fd209faaSGiannis Adamopoulos HRESULT CFSDropTarget::_CopyItems(IShellFolder * pSFFrom, UINT cidl, 60c2c66affSColin Finck LPCITEMIDLIST * apidl, BOOL bCopy) 61c2c66affSColin Finck { 62c2c66affSColin Finck LPWSTR pszSrcList; 63c2c66affSColin Finck HRESULT hr; 64c2c66affSColin Finck WCHAR wszTargetPath[MAX_PATH + 1]; 65c2c66affSColin Finck 66fd209faaSGiannis Adamopoulos wcscpy(wszTargetPath, m_sPathTarget); 67c2c66affSColin Finck //Double NULL terminate. 68c2c66affSColin Finck wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; 69c2c66affSColin Finck 70c2c66affSColin Finck TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl); 71c2c66affSColin Finck 72c2c66affSColin Finck STRRET strretFrom; 73c2c66affSColin Finck hr = pSFFrom->GetDisplayNameOf(NULL, SHGDN_FORPARSING, &strretFrom); 74c2c66affSColin Finck if (FAILED_UNEXPECTEDLY(hr)) 75c2c66affSColin Finck return hr; 76c2c66affSColin Finck 77c2c66affSColin Finck pszSrcList = BuildPathsList(strretFrom.pOleStr, cidl, apidl); 78fd209faaSGiannis Adamopoulos ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(m_sPathTarget), bCopy); 79c2c66affSColin Finck CoTaskMemFree(strretFrom.pOleStr); 80c2c66affSColin Finck if (!pszSrcList) 81c2c66affSColin Finck return E_OUTOFMEMORY; 82c2c66affSColin Finck 83c2c66affSColin Finck SHFILEOPSTRUCTW op = {0}; 84c2c66affSColin Finck op.pFrom = pszSrcList; 85c2c66affSColin Finck op.pTo = wszTargetPath; 866d91269eSGiannis Adamopoulos op.hwnd = m_hwndSite; 87c2c66affSColin Finck op.wFunc = bCopy ? FO_COPY : FO_MOVE; 88c2c66affSColin Finck op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; 89c2c66affSColin Finck 90c2c66affSColin Finck int res = SHFileOperationW(&op); 91c2c66affSColin Finck 92c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, pszSrcList); 93c2c66affSColin Finck 94c2c66affSColin Finck if (res) 95c2c66affSColin Finck return E_FAIL; 96c2c66affSColin Finck else 97c2c66affSColin Finck return S_OK; 98c2c66affSColin Finck } 99c2c66affSColin Finck 100c2c66affSColin Finck CFSDropTarget::CFSDropTarget(): 101fd209faaSGiannis Adamopoulos m_cfShellIDList(0), 102fd209faaSGiannis Adamopoulos m_fAcceptFmt(FALSE), 103fd209faaSGiannis Adamopoulos m_sPathTarget(NULL), 1046d91269eSGiannis Adamopoulos m_hwndSite(NULL), 1056d91269eSGiannis Adamopoulos m_grfKeyState(0) 106c2c66affSColin Finck { 107c2c66affSColin Finck } 108c2c66affSColin Finck 109fd209faaSGiannis Adamopoulos HRESULT CFSDropTarget::Initialize(LPWSTR PathTarget) 110c2c66affSColin Finck { 111c2c66affSColin Finck if (!PathTarget) 112c2c66affSColin Finck return E_UNEXPECTED; 113c2c66affSColin Finck 114fd209faaSGiannis Adamopoulos m_cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); 115fd209faaSGiannis Adamopoulos if (!m_cfShellIDList) 116c2c66affSColin Finck return E_FAIL; 117c2c66affSColin Finck 118fd209faaSGiannis Adamopoulos m_sPathTarget = (WCHAR *)SHAlloc((wcslen(PathTarget) + 1) * sizeof(WCHAR)); 119fd209faaSGiannis Adamopoulos if (!m_sPathTarget) 120c2c66affSColin Finck return E_OUTOFMEMORY; 121c2c66affSColin Finck 122fd209faaSGiannis Adamopoulos wcscpy(m_sPathTarget, PathTarget); 123c2c66affSColin Finck 124c2c66affSColin Finck return S_OK; 125c2c66affSColin Finck } 126c2c66affSColin Finck 127c2c66affSColin Finck CFSDropTarget::~CFSDropTarget() 128c2c66affSColin Finck { 129fd209faaSGiannis Adamopoulos SHFree(m_sPathTarget); 130c2c66affSColin Finck } 131c2c66affSColin Finck 132c2c66affSColin Finck BOOL 133fd209faaSGiannis Adamopoulos CFSDropTarget::_GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut) 134c2c66affSColin Finck { 135c2c66affSColin Finck WCHAR wszLink[40]; 136c2c66affSColin Finck 137c2c66affSColin Finck if (!bShortcut) 138c2c66affSColin Finck { 139c2c66affSColin Finck if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink))) 140c2c66affSColin Finck wszLink[0] = L'\0'; 141c2c66affSColin Finck } 142c2c66affSColin Finck 143c2c66affSColin Finck if (!bShortcut) 144c2c66affSColin Finck swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt); 145c2c66affSColin Finck else 146c2c66affSColin Finck swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt); 147c2c66affSColin Finck 148c2c66affSColin Finck for (UINT i = 2; PathFileExistsW(pwszTarget); ++i) 149c2c66affSColin Finck { 150c2c66affSColin Finck if (!bShortcut) 151c2c66affSColin Finck swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt); 152c2c66affSColin Finck else 153c2c66affSColin Finck swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt); 154c2c66affSColin Finck } 155c2c66affSColin Finck 156c2c66affSColin Finck return TRUE; 157c2c66affSColin Finck } 158c2c66affSColin Finck 159c2c66affSColin Finck /**************************************************************************** 160c2c66affSColin Finck * IDropTarget implementation 161c2c66affSColin Finck */ 162fd209faaSGiannis Adamopoulos BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect) 163c2c66affSColin Finck { 164c2c66affSColin Finck /* TODO Windows does different drop effects if dragging across drives. 165c2c66affSColin Finck i.e., it will copy instead of move if the directories are on different disks. */ 166c2c66affSColin Finck 1676f25b42aSGiannis Adamopoulos DWORD dwEffect = m_dwDefaultEffect; 168c2c66affSColin Finck 169c2c66affSColin Finck *pdwEffect = DROPEFFECT_NONE; 170c2c66affSColin Finck 171fd209faaSGiannis Adamopoulos if (m_fAcceptFmt) { /* Does our interpretation of the keystate ... */ 172c2c66affSColin Finck *pdwEffect = KeyStateToDropEffect (dwKeyState); 173c2c66affSColin Finck 174c2c66affSColin Finck if (*pdwEffect == DROPEFFECT_NONE) 175c2c66affSColin Finck *pdwEffect = dwEffect; 176c2c66affSColin Finck 177c2c66affSColin Finck /* ... matches the desired effect ? */ 178c2c66affSColin Finck if (dwEffect & *pdwEffect) { 179c2c66affSColin Finck return TRUE; 180c2c66affSColin Finck } 181c2c66affSColin Finck } 182c2c66affSColin Finck return FALSE; 183c2c66affSColin Finck } 184c2c66affSColin Finck 1856f25b42aSGiannis Adamopoulos HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, DWORD *pdwEffect, DWORD dwAvailableEffects) 1866d91269eSGiannis Adamopoulos { 1876d91269eSGiannis Adamopoulos HMENU hmenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(IDM_DRAGFILE)); 1886d91269eSGiannis Adamopoulos if (!hmenu) 1896d91269eSGiannis Adamopoulos return E_OUTOFMEMORY; 1906d91269eSGiannis Adamopoulos 1916f25b42aSGiannis Adamopoulos HMENU hpopupmenu = GetSubMenu(hmenu, 0); 1926f25b42aSGiannis Adamopoulos 1936f25b42aSGiannis Adamopoulos if ((dwAvailableEffects & DROPEFFECT_COPY) == 0) 1946f25b42aSGiannis Adamopoulos DeleteMenu(hpopupmenu, IDM_COPYHERE, MF_BYCOMMAND); 1956f25b42aSGiannis Adamopoulos else if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0) 1966f25b42aSGiannis Adamopoulos DeleteMenu(hpopupmenu, IDM_MOVEHERE, MF_BYCOMMAND); 1976f25b42aSGiannis Adamopoulos else if ((dwAvailableEffects & DROPEFFECT_LINK) == 0) 1986f25b42aSGiannis Adamopoulos DeleteMenu(hpopupmenu, IDM_LINKHERE, MF_BYCOMMAND); 1996f25b42aSGiannis Adamopoulos 2006f25b42aSGiannis Adamopoulos if ((*pdwEffect & DROPEFFECT_COPY)) 2016f25b42aSGiannis Adamopoulos SetMenuDefaultItem(hpopupmenu, IDM_COPYHERE, FALSE); 2026f25b42aSGiannis Adamopoulos else if ((*pdwEffect & DROPEFFECT_MOVE)) 2036f25b42aSGiannis Adamopoulos SetMenuDefaultItem(hpopupmenu, IDM_MOVEHERE, FALSE); 2046f25b42aSGiannis Adamopoulos else if ((*pdwEffect & DROPEFFECT_LINK)) 2056f25b42aSGiannis Adamopoulos SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE); 2066f25b42aSGiannis Adamopoulos 2076d91269eSGiannis Adamopoulos /* FIXME: We need to support shell extensions here */ 2086d91269eSGiannis Adamopoulos 209*1d55f459SGiannis Adamopoulos /* We shouldn't use the site window here because the menu should work even when we don't have a site */ 210*1d55f459SGiannis Adamopoulos HWND hwndDummy = CreateWindowEx(0, 211*1d55f459SGiannis Adamopoulos WC_STATIC, 212*1d55f459SGiannis Adamopoulos NULL, 213*1d55f459SGiannis Adamopoulos WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT, 214*1d55f459SGiannis Adamopoulos pt.x, 215*1d55f459SGiannis Adamopoulos pt.y, 216*1d55f459SGiannis Adamopoulos 1, 217*1d55f459SGiannis Adamopoulos 1, 218*1d55f459SGiannis Adamopoulos NULL, 219*1d55f459SGiannis Adamopoulos NULL, 220*1d55f459SGiannis Adamopoulos NULL, 221*1d55f459SGiannis Adamopoulos NULL); 222*1d55f459SGiannis Adamopoulos 2236f25b42aSGiannis Adamopoulos UINT uCommand = TrackPopupMenu(hpopupmenu, 2246f25b42aSGiannis Adamopoulos TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY, 225*1d55f459SGiannis Adamopoulos pt.x, pt.y, 0, hwndDummy, NULL); 226*1d55f459SGiannis Adamopoulos 227*1d55f459SGiannis Adamopoulos DestroyWindow(hwndDummy); 228*1d55f459SGiannis Adamopoulos 2296d91269eSGiannis Adamopoulos if (uCommand == 0) 2306d91269eSGiannis Adamopoulos return S_FALSE; 2316d91269eSGiannis Adamopoulos else if (uCommand == IDM_COPYHERE) 2326d91269eSGiannis Adamopoulos *pdwEffect = DROPEFFECT_COPY; 2336d91269eSGiannis Adamopoulos else if (uCommand == IDM_MOVEHERE) 2346d91269eSGiannis Adamopoulos *pdwEffect = DROPEFFECT_MOVE; 2356d91269eSGiannis Adamopoulos else if (uCommand == IDM_LINKHERE) 2366d91269eSGiannis Adamopoulos *pdwEffect = DROPEFFECT_LINK; 2376d91269eSGiannis Adamopoulos 2386d91269eSGiannis Adamopoulos return S_OK; 2396d91269eSGiannis Adamopoulos } 2406d91269eSGiannis Adamopoulos 2416d91269eSGiannis Adamopoulos HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt) 2426d91269eSGiannis Adamopoulos { 2436d91269eSGiannis Adamopoulos CComPtr<IFolderView> pfv; 2446d91269eSGiannis Adamopoulos POINT ptDrag; 2456d91269eSGiannis Adamopoulos HRESULT hr = psfv->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); 2466d91269eSGiannis Adamopoulos if (FAILED_UNEXPECTEDLY(hr)) 2476d91269eSGiannis Adamopoulos return hr; 2486d91269eSGiannis Adamopoulos 2496d91269eSGiannis Adamopoulos hr = psfv->GetDragPoint(&ptDrag); 2506d91269eSGiannis Adamopoulos if (FAILED_UNEXPECTEDLY(hr)) 2516d91269eSGiannis Adamopoulos return hr; 2526d91269eSGiannis Adamopoulos 2536d91269eSGiannis Adamopoulos PIDLIST_ABSOLUTE pidlFolder; 2546d91269eSGiannis Adamopoulos PUITEMID_CHILD *apidl; 2556d91269eSGiannis Adamopoulos UINT cidl; 2566d91269eSGiannis Adamopoulos hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl); 2576d91269eSGiannis Adamopoulos if (FAILED_UNEXPECTEDLY(hr)) 2586d91269eSGiannis Adamopoulos return hr; 2596d91269eSGiannis Adamopoulos 2606d91269eSGiannis Adamopoulos CComHeapPtr<POINT> apt; 2616d91269eSGiannis Adamopoulos if (!apt.Allocate(cidl)) 2626d91269eSGiannis Adamopoulos { 2636d91269eSGiannis Adamopoulos SHFree(pidlFolder); 2646d91269eSGiannis Adamopoulos _ILFreeaPidl(apidl, cidl); 2656d91269eSGiannis Adamopoulos return E_OUTOFMEMORY; 2666d91269eSGiannis Adamopoulos } 2676d91269eSGiannis Adamopoulos 2686d91269eSGiannis Adamopoulos for (UINT i = 0; i<cidl; i++) 2696d91269eSGiannis Adamopoulos { 2706d91269eSGiannis Adamopoulos pfv->GetItemPosition(apidl[i], &apt[i]); 2716d91269eSGiannis Adamopoulos apt[i].x += pt.x - ptDrag.x; 2726d91269eSGiannis Adamopoulos apt[i].y += pt.y - ptDrag.y; 2736d91269eSGiannis Adamopoulos } 2746d91269eSGiannis Adamopoulos 2756d91269eSGiannis Adamopoulos pfv->SelectAndPositionItems(cidl, apidl, apt, SVSI_SELECT); 2766d91269eSGiannis Adamopoulos 2776d91269eSGiannis Adamopoulos SHFree(pidlFolder); 2786d91269eSGiannis Adamopoulos _ILFreeaPidl(apidl, cidl); 2796d91269eSGiannis Adamopoulos return S_OK; 2806d91269eSGiannis Adamopoulos } 2816d91269eSGiannis Adamopoulos 282c2c66affSColin Finck HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject, 283c2c66affSColin Finck DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 284c2c66affSColin Finck { 285c2c66affSColin Finck TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 286c2c66affSColin Finck FORMATETC fmt; 287c2c66affSColin Finck FORMATETC fmt2; 288fd209faaSGiannis Adamopoulos m_fAcceptFmt = FALSE; 289c2c66affSColin Finck 290fd209faaSGiannis Adamopoulos InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 291c2c66affSColin Finck InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 292c2c66affSColin Finck 293c2c66affSColin Finck if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 294fd209faaSGiannis Adamopoulos m_fAcceptFmt = TRUE; 295c2c66affSColin Finck else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 296fd209faaSGiannis Adamopoulos m_fAcceptFmt = TRUE; 297c2c66affSColin Finck 2986d91269eSGiannis Adamopoulos m_grfKeyState = dwKeyState; 2996f25b42aSGiannis Adamopoulos m_dwDefaultEffect = DROPEFFECT_MOVE; 3006f25b42aSGiannis Adamopoulos 3016f25b42aSGiannis Adamopoulos STGMEDIUM medium; 3026f25b42aSGiannis Adamopoulos if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium))) 3036f25b42aSGiannis Adamopoulos { 3046f25b42aSGiannis Adamopoulos WCHAR wstrFirstFile[MAX_PATH]; 3056f25b42aSGiannis Adamopoulos if (DragQueryFileW((HDROP)medium.hGlobal, 0, wstrFirstFile, _countof(wstrFirstFile))) 3066f25b42aSGiannis Adamopoulos { 3076f25b42aSGiannis Adamopoulos /* Check if the drive letter is different */ 308fd209faaSGiannis Adamopoulos if (wstrFirstFile[0] != m_sPathTarget[0]) 3096f25b42aSGiannis Adamopoulos { 3106f25b42aSGiannis Adamopoulos m_dwDefaultEffect = DROPEFFECT_COPY; 3116f25b42aSGiannis Adamopoulos } 3126f25b42aSGiannis Adamopoulos } 3136f25b42aSGiannis Adamopoulos ReleaseStgMedium(&medium); 3146f25b42aSGiannis Adamopoulos } 3156d91269eSGiannis Adamopoulos 316fd209faaSGiannis Adamopoulos _QueryDrop(dwKeyState, pdwEffect); 317c2c66affSColin Finck return S_OK; 318c2c66affSColin Finck } 319c2c66affSColin Finck 320c2c66affSColin Finck HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt, 321c2c66affSColin Finck DWORD *pdwEffect) 322c2c66affSColin Finck { 323c2c66affSColin Finck TRACE("(%p)\n", this); 324c2c66affSColin Finck 325c2c66affSColin Finck if (!pdwEffect) 326c2c66affSColin Finck return E_INVALIDARG; 327c2c66affSColin Finck 3286d91269eSGiannis Adamopoulos m_grfKeyState = dwKeyState; 3296d91269eSGiannis Adamopoulos 330fd209faaSGiannis Adamopoulos _QueryDrop(dwKeyState, pdwEffect); 331c2c66affSColin Finck 332c2c66affSColin Finck return S_OK; 333c2c66affSColin Finck } 334c2c66affSColin Finck 335c2c66affSColin Finck HRESULT WINAPI CFSDropTarget::DragLeave() 336c2c66affSColin Finck { 337c2c66affSColin Finck TRACE("(%p)\n", this); 338c2c66affSColin Finck 339fd209faaSGiannis Adamopoulos m_fAcceptFmt = FALSE; 340c2c66affSColin Finck 341c2c66affSColin Finck return S_OK; 342c2c66affSColin Finck } 343c2c66affSColin Finck 344c2c66affSColin Finck HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, 345c2c66affSColin Finck DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 346c2c66affSColin Finck { 347c2c66affSColin Finck TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); 348c2c66affSColin Finck 349c2c66affSColin Finck if (!pdwEffect) 350c2c66affSColin Finck return E_INVALIDARG; 351c2c66affSColin Finck 3526d91269eSGiannis Adamopoulos IUnknown_GetWindow(m_site, &m_hwndSite); 3536d91269eSGiannis Adamopoulos 3546f25b42aSGiannis Adamopoulos DWORD dwAvailableEffects = *pdwEffect; 3556f25b42aSGiannis Adamopoulos 356fd209faaSGiannis Adamopoulos _QueryDrop(dwKeyState, pdwEffect); 357c2c66affSColin Finck 3586f25b42aSGiannis Adamopoulos TRACE("pdwEffect: 0x%x, m_dwDefaultEffect: 0x%x, dwAvailableEffects: 0x%x\n", *pdwEffect, m_dwDefaultEffect, dwAvailableEffects); 3596f25b42aSGiannis Adamopoulos 3606d91269eSGiannis Adamopoulos if (m_grfKeyState & MK_RBUTTON) 3616d91269eSGiannis Adamopoulos { 3626f25b42aSGiannis Adamopoulos HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects); 3636d91269eSGiannis Adamopoulos if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE) 3646d91269eSGiannis Adamopoulos return hr; 3656d91269eSGiannis Adamopoulos } 3666d91269eSGiannis Adamopoulos 3676d91269eSGiannis Adamopoulos if (*pdwEffect == DROPEFFECT_MOVE && m_site) 3686d91269eSGiannis Adamopoulos { 3696d91269eSGiannis Adamopoulos CComPtr<IShellFolderView> psfv; 3706d91269eSGiannis Adamopoulos HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellFolderView, &psfv)); 3716d91269eSGiannis Adamopoulos if (SUCCEEDED(hr) && psfv->IsDropOnSource(this) == S_OK) 3726d91269eSGiannis Adamopoulos { 3736d91269eSGiannis Adamopoulos _RepositionItems(psfv, pDataObject, pt); 3746d91269eSGiannis Adamopoulos return S_OK; 3756d91269eSGiannis Adamopoulos } 3766d91269eSGiannis Adamopoulos } 3776d91269eSGiannis Adamopoulos 378c2c66affSColin Finck BOOL fIsOpAsync = FALSE; 379c2c66affSColin Finck CComPtr<IAsyncOperation> pAsyncOperation; 380c2c66affSColin Finck 381c2c66affSColin Finck if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 382c2c66affSColin Finck { 383c2c66affSColin Finck if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync) 384c2c66affSColin Finck { 385c2c66affSColin Finck _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData))); 386c2c66affSColin Finck data->This = this; 387c2c66affSColin Finck // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder). 388c2c66affSColin Finck pDataObject->AddRef(); 389c2c66affSColin Finck pAsyncOperation->StartOperation(NULL); 390c2c66affSColin Finck CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream); 391c2c66affSColin Finck this->AddRef(); 392c2c66affSColin Finck data->dwKeyState = dwKeyState; 393c2c66affSColin Finck data->pt = pt; 394c2c66affSColin Finck // Need to dereference as pdweffect gets freed. 395c2c66affSColin Finck data->pdwEffect = *pdwEffect; 396c2c66affSColin Finck SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL); 397c2c66affSColin Finck return S_OK; 398c2c66affSColin Finck } 399c2c66affSColin Finck } 400c2c66affSColin Finck return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect); 401c2c66affSColin Finck } 402c2c66affSColin Finck 4036d91269eSGiannis Adamopoulos HRESULT 4046d91269eSGiannis Adamopoulos WINAPI 4056d91269eSGiannis Adamopoulos CFSDropTarget::SetSite(IUnknown *pUnkSite) 4066d91269eSGiannis Adamopoulos { 4076d91269eSGiannis Adamopoulos m_site = pUnkSite; 4086d91269eSGiannis Adamopoulos return S_OK; 4096d91269eSGiannis Adamopoulos } 4106d91269eSGiannis Adamopoulos 4116d91269eSGiannis Adamopoulos HRESULT 4126d91269eSGiannis Adamopoulos WINAPI 4136d91269eSGiannis Adamopoulos CFSDropTarget::GetSite(REFIID riid, void **ppvSite) 4146d91269eSGiannis Adamopoulos { 4156d91269eSGiannis Adamopoulos if (!m_site) 4166d91269eSGiannis Adamopoulos return E_FAIL; 4176d91269eSGiannis Adamopoulos 4186d91269eSGiannis Adamopoulos return m_site->QueryInterface(riid, ppvSite); 4196d91269eSGiannis Adamopoulos } 4206d91269eSGiannis Adamopoulos 421fd209faaSGiannis Adamopoulos HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject, 422c2c66affSColin Finck DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 423c2c66affSColin Finck { 424c2c66affSColin Finck TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); 425c2c66affSColin Finck FORMATETC fmt; 426c2c66affSColin Finck FORMATETC fmt2; 427c2c66affSColin Finck STGMEDIUM medium; 428c2c66affSColin Finck 429fd209faaSGiannis Adamopoulos InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 430c2c66affSColin Finck InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 431c2c66affSColin Finck 432c2c66affSColin Finck HRESULT hr; 433c2c66affSColin Finck bool bCopy = TRUE; 434c2c66affSColin Finck bool bLinking = FALSE; 435c2c66affSColin Finck 436c2c66affSColin Finck /* Figure out what drop operation we're doing */ 437c2c66affSColin Finck if (pdwEffect) 438c2c66affSColin Finck { 439c2c66affSColin Finck TRACE("Current drop effect flag %i\n", *pdwEffect); 440c2c66affSColin Finck if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) 441c2c66affSColin Finck bCopy = FALSE; 442c2c66affSColin Finck if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) 443c2c66affSColin Finck bLinking = TRUE; 444c2c66affSColin Finck } 445c2c66affSColin Finck 446c2c66affSColin Finck if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 447c2c66affSColin Finck { 448c2c66affSColin Finck hr = pDataObject->GetData(&fmt, &medium); 449c2c66affSColin Finck TRACE("CFSTR_SHELLIDLIST.\n"); 450c2c66affSColin Finck 451c2c66affSColin Finck /* lock the handle */ 452c2c66affSColin Finck LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal); 453c2c66affSColin Finck if (!lpcida) 454c2c66affSColin Finck { 455c2c66affSColin Finck ReleaseStgMedium(&medium); 456c2c66affSColin Finck return E_FAIL; 457c2c66affSColin Finck } 458c2c66affSColin Finck 459c2c66affSColin Finck /* convert the data into pidl */ 460c2c66affSColin Finck LPITEMIDLIST pidl; 461c2c66affSColin Finck LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida); 462c2c66affSColin Finck if (!apidl) 463c2c66affSColin Finck { 464c2c66affSColin Finck ReleaseStgMedium(&medium); 465c2c66affSColin Finck return E_FAIL; 466c2c66affSColin Finck } 467c2c66affSColin Finck 468c2c66affSColin Finck CComPtr<IShellFolder> psfDesktop; 469c2c66affSColin Finck CComPtr<IShellFolder> psfFrom = NULL; 470c2c66affSColin Finck 471c2c66affSColin Finck /* Grab the desktop shell folder */ 472c2c66affSColin Finck hr = SHGetDesktopFolder(&psfDesktop); 473c2c66affSColin Finck if (FAILED(hr)) 474c2c66affSColin Finck { 475c2c66affSColin Finck ERR("SHGetDesktopFolder failed\n"); 476c2c66affSColin Finck SHFree(pidl); 477c2c66affSColin Finck _ILFreeaPidl(apidl, lpcida->cidl); 478c2c66affSColin Finck ReleaseStgMedium(&medium); 479c2c66affSColin Finck return E_FAIL; 480c2c66affSColin Finck } 481c2c66affSColin Finck 482c2c66affSColin Finck /* Find source folder, this is where the clipboard data was copied from */ 483c2c66affSColin Finck if (_ILIsDesktop(pidl)) 484c2c66affSColin Finck { 485c2c66affSColin Finck /* use desktop shell folder */ 486c2c66affSColin Finck psfFrom = psfDesktop; 487c2c66affSColin Finck } 488c2c66affSColin Finck else 489c2c66affSColin Finck { 490c2c66affSColin Finck hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom)); 491c2c66affSColin Finck if (FAILED(hr)) 492c2c66affSColin Finck { 493c2c66affSColin Finck ERR("no IShellFolder\n"); 494c2c66affSColin Finck SHFree(pidl); 495c2c66affSColin Finck _ILFreeaPidl(apidl, lpcida->cidl); 496c2c66affSColin Finck ReleaseStgMedium(&medium); 497c2c66affSColin Finck return E_FAIL; 498c2c66affSColin Finck } 499c2c66affSColin Finck } 500c2c66affSColin Finck 501c2c66affSColin Finck if (bLinking) 502c2c66affSColin Finck { 503c2c66affSColin Finck WCHAR wszTargetPath[MAX_PATH]; 504c2c66affSColin Finck WCHAR wszPath[MAX_PATH]; 505c2c66affSColin Finck WCHAR wszTarget[MAX_PATH]; 506c2c66affSColin Finck 507fd209faaSGiannis Adamopoulos wcscpy(wszTargetPath, m_sPathTarget); 508c2c66affSColin Finck 509c2c66affSColin Finck TRACE("target path = %s", debugstr_w(wszTargetPath)); 510c2c66affSColin Finck 511c2c66affSColin Finck /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */ 512c2c66affSColin Finck for (UINT i = 0; i < lpcida->cidl; i++) 513c2c66affSColin Finck { 514c2c66affSColin Finck //Find out which file we're copying 515c2c66affSColin Finck STRRET strFile; 516c2c66affSColin Finck hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile); 517c2c66affSColin Finck if (FAILED(hr)) 518c2c66affSColin Finck { 519c2c66affSColin Finck ERR("Error source obtaining path"); 520c2c66affSColin Finck break; 521c2c66affSColin Finck } 522c2c66affSColin Finck 523c2c66affSColin Finck hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath)); 524c2c66affSColin Finck if (FAILED(hr)) 525c2c66affSColin Finck { 526c2c66affSColin Finck ERR("Error putting source path into buffer"); 527c2c66affSColin Finck break; 528c2c66affSColin Finck } 529c2c66affSColin Finck TRACE("source path = %s", debugstr_w(wszPath)); 530c2c66affSColin Finck 531c2c66affSColin Finck // Creating a buffer to hold the combined path 532c2c66affSColin Finck WCHAR buffer_1[MAX_PATH] = L""; 533c2c66affSColin Finck WCHAR *lpStr1; 534c2c66affSColin Finck lpStr1 = buffer_1; 535c2c66affSColin Finck 536c2c66affSColin Finck LPWSTR pwszFileName = PathFindFileNameW(wszPath); 537c2c66affSColin Finck LPWSTR pwszExt = PathFindExtensionW(wszPath); 538fd209faaSGiannis Adamopoulos LPWSTR placementPath = PathCombineW(lpStr1, m_sPathTarget, pwszFileName); 539c2c66affSColin Finck CComPtr<IPersistFile> ppf; 540c2c66affSColin Finck 541c2c66affSColin Finck //Check to see if it's already a link. 542c2c66affSColin Finck if (!wcsicmp(pwszExt, L".lnk")) 543c2c66affSColin Finck { 544c2c66affSColin Finck //It's a link so, we create a new one which copies the old. 545fd209faaSGiannis Adamopoulos if(!_GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) 546c2c66affSColin Finck { 547c2c66affSColin Finck ERR("Error getting unique file name"); 548c2c66affSColin Finck hr = E_FAIL; 549c2c66affSColin Finck break; 550c2c66affSColin Finck } 551c2c66affSColin Finck hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf)); 552c2c66affSColin Finck if (FAILED(hr)) { 553c2c66affSColin Finck ERR("Error constructing link from file"); 554c2c66affSColin Finck break; 555c2c66affSColin Finck } 556c2c66affSColin Finck 557c2c66affSColin Finck hr = ppf->Save(wszTarget, FALSE); 558c2c66affSColin Finck if (FAILED(hr)) 559c2c66affSColin Finck break; 560c2c66affSColin Finck SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); 561c2c66affSColin Finck } 562c2c66affSColin Finck else 563c2c66affSColin Finck { 564c2c66affSColin Finck //It's not a link, so build a new link using the creator class and fill it in. 565c2c66affSColin Finck //Create a file name for the link 566fd209faaSGiannis Adamopoulos if (!_GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE)) 567c2c66affSColin Finck { 568c2c66affSColin Finck ERR("Error creating unique file name"); 569c2c66affSColin Finck hr = E_FAIL; 570c2c66affSColin Finck break; 571c2c66affSColin Finck } 572c2c66affSColin Finck 573c2c66affSColin Finck CComPtr<IShellLinkW> pLink; 574c2c66affSColin Finck hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink)); 575c2c66affSColin Finck if (FAILED(hr)) { 576c2c66affSColin Finck ERR("Error instantiating IShellLinkW"); 577c2c66affSColin Finck break; 578c2c66affSColin Finck } 579c2c66affSColin Finck 580c2c66affSColin Finck WCHAR szDirPath[MAX_PATH], *pwszFile; 581c2c66affSColin Finck GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile); 582c2c66affSColin Finck if (pwszFile) pwszFile[0] = 0; 583c2c66affSColin Finck 584c2c66affSColin Finck hr = pLink->SetPath(wszPath); 585c2c66affSColin Finck if(FAILED(hr)) 586c2c66affSColin Finck break; 587c2c66affSColin Finck 588c2c66affSColin Finck hr = pLink->SetWorkingDirectory(szDirPath); 589c2c66affSColin Finck if(FAILED(hr)) 590c2c66affSColin Finck break; 591c2c66affSColin Finck 592c2c66affSColin Finck hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); 593c2c66affSColin Finck if(FAILED(hr)) 594c2c66affSColin Finck break; 595c2c66affSColin Finck 596c2c66affSColin Finck hr = ppf->Save(wszTarget, TRUE); 597c2c66affSColin Finck if (FAILED(hr)) 598c2c66affSColin Finck break; 599c2c66affSColin Finck SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); 600c2c66affSColin Finck } 601c2c66affSColin Finck } 602c2c66affSColin Finck } 603c2c66affSColin Finck else 604c2c66affSColin Finck { 605fd209faaSGiannis Adamopoulos hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy); 606c2c66affSColin Finck } 607c2c66affSColin Finck 608c2c66affSColin Finck SHFree(pidl); 609c2c66affSColin Finck _ILFreeaPidl(apidl, lpcida->cidl); 610c2c66affSColin Finck ReleaseStgMedium(&medium); 611c2c66affSColin Finck } 612c2c66affSColin Finck else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 613c2c66affSColin Finck { 614c2c66affSColin Finck FORMATETC fmt2; 615c2c66affSColin Finck InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 616c2c66affSColin Finck if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/) 617c2c66affSColin Finck { 618c2c66affSColin Finck WCHAR wszTargetPath[MAX_PATH + 1]; 619c2c66affSColin Finck LPWSTR pszSrcList; 620c2c66affSColin Finck 621fd209faaSGiannis Adamopoulos wcscpy(wszTargetPath, m_sPathTarget); 622c2c66affSColin Finck //Double NULL terminate. 623c2c66affSColin Finck wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; 624c2c66affSColin Finck 625c2c66affSColin Finck LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); 626c2c66affSColin Finck if (!lpdf) 627c2c66affSColin Finck { 628c2c66affSColin Finck ERR("Error locking global\n"); 629c2c66affSColin Finck return E_FAIL; 630c2c66affSColin Finck } 631c2c66affSColin Finck pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles); 632c2c66affSColin Finck ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(wszTargetPath), bCopy); 633c2c66affSColin Finck 634c2c66affSColin Finck SHFILEOPSTRUCTW op; 635c2c66affSColin Finck ZeroMemory(&op, sizeof(op)); 636c2c66affSColin Finck op.pFrom = pszSrcList; 637c2c66affSColin Finck op.pTo = wszTargetPath; 6386d91269eSGiannis Adamopoulos op.hwnd = m_hwndSite; 639c2c66affSColin Finck op.wFunc = bCopy ? FO_COPY : FO_MOVE; 640c2c66affSColin Finck op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; 641c2c66affSColin Finck hr = SHFileOperationW(&op); 642c2c66affSColin Finck return hr; 643c2c66affSColin Finck } 644c2c66affSColin Finck ERR("Error calling GetData\n"); 645c2c66affSColin Finck hr = E_FAIL; 646c2c66affSColin Finck } 647c2c66affSColin Finck else 648c2c66affSColin Finck { 649c2c66affSColin Finck ERR("No viable drop format.\n"); 650c2c66affSColin Finck hr = E_FAIL; 651c2c66affSColin Finck } 652c2c66affSColin Finck return hr; 653c2c66affSColin Finck } 654c2c66affSColin Finck 655c2c66affSColin Finck DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter) 656c2c66affSColin Finck { 657c2c66affSColin Finck CoInitialize(NULL); 658c2c66affSColin Finck _DoDropData *data = static_cast<_DoDropData*>(lpParameter); 659c2c66affSColin Finck CComPtr<IDataObject> pDataObject; 660c2c66affSColin Finck HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); 661c2c66affSColin Finck 662c2c66affSColin Finck if (SUCCEEDED(hr)) 663c2c66affSColin Finck { 664c2c66affSColin Finck CComPtr<IAsyncOperation> pAsyncOperation; 665c2c66affSColin Finck hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); 666c2c66affSColin Finck if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 667c2c66affSColin Finck { 668c2c66affSColin Finck pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect); 669c2c66affSColin Finck } 670c2c66affSColin Finck } 671c2c66affSColin Finck //Release the CFSFolder and data object holds in the copying thread. 672c2c66affSColin Finck data->This->Release(); 673c2c66affSColin Finck //Release the parameter from the heap. 674c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, data); 675c2c66affSColin Finck CoUninitialize(); 676c2c66affSColin Finck return 0; 677c2c66affSColin Finck } 678c2c66affSColin Finck 679c2c66affSColin Finck HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut) 680c2c66affSColin Finck { 681c2c66affSColin Finck return ShellObjectCreatorInit<CFSDropTarget>(sPathTarget, riid, ppvOut); 682c2c66affSColin Finck }