diff --git a/reactos/dll/win32/shell32/CMakeLists.txt b/reactos/dll/win32/shell32/CMakeLists.txt index ef67633fbd7..1a53a9e6300 100644 --- a/reactos/dll/win32/shell32/CMakeLists.txt +++ b/reactos/dll/win32/shell32/CMakeLists.txt @@ -55,6 +55,7 @@ list(APPEND SOURCE folders/CRecycleBin.cpp folders/CRegFolder.cpp droptargets/CexeDropHandler.cpp + droptargets/CFSDropTarget.cpp shlexec.cpp shlfileop.cpp shlfolder.cpp diff --git a/reactos/dll/win32/shell32/droptargets/CFSDropTarget.cpp b/reactos/dll/win32/shell32/droptargets/CFSDropTarget.cpp new file mode 100644 index 00000000000..82de0a21916 --- /dev/null +++ b/reactos/dll/win32/shell32/droptargets/CFSDropTarget.cpp @@ -0,0 +1,600 @@ + +/* + * file system folder drop target + * + * Copyright 1997 Marcus Meissner + * Copyright 1998, 1999, 2002 Juergen Schmied + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +WINE_DEFAULT_DEBUG_CHANNEL (shell); + +/**************************************************************************** + * BuildPathsList + * + * Builds a list of paths like the one used in SHFileOperation from a table of + * PIDLs relative to the given base folder + */ +WCHAR * +BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls) +{ + WCHAR *pwszPathsList; + WCHAR *pwszListPos; + int iPathLen, i; + + iPathLen = wcslen(wszBasePath); + pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1); + pwszListPos = pwszPathsList; + + for (i = 0; i < cidl; i++) + { + if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i])) + continue; + + wcscpy(pwszListPos, wszBasePath); + pwszListPos += iPathLen; + /* FIXME: abort if path too long */ + _ILSimpleGetTextW(pidls[i], pwszListPos, MAX_PATH - iPathLen); + pwszListPos += wcslen(pwszListPos) + 1; + } + *pwszListPos = 0; + return pwszPathsList; +} + +/**************************************************************************** + * CFSDropTarget::CopyItems + * + * copies items to this folder + */ +HRESULT WINAPI CFSDropTarget::CopyItems(IShellFolder * pSFFrom, UINT cidl, + LPCITEMIDLIST * apidl, BOOL bCopy) +{ + CComPtr ppf2 = NULL; + WCHAR szSrcPath[MAX_PATH]; + WCHAR szTargetPath[MAX_PATH]; + SHFILEOPSTRUCTW op; + LPITEMIDLIST pidl; + LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName; + int res, length; + HRESULT hr; + + TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl); + + hr = pSFFrom->QueryInterface (IID_PPV_ARG(IPersistFolder2, &ppf2)); + if (SUCCEEDED(hr)) + { + hr = ppf2->GetCurFolder(&pidl); + if (FAILED(hr)) + { + return hr; + } + + hr = SHGetPathFromIDListW(pidl, szSrcPath); + SHFree(pidl); + + if (FAILED(hr)) + return hr; + + pszSrc = PathAddBackslashW(szSrcPath); + + wcscpy(szTargetPath, sPathTarget); + pszTarget = PathAddBackslashW(szTargetPath); + + pszSrcList = BuildPathsList(szSrcPath, cidl, apidl); + pszTargetList = BuildPathsList(szTargetPath, cidl, apidl); + + if (!pszSrcList || !pszTargetList) + { + if (pszSrcList) + HeapFree(GetProcessHeap(), 0, pszSrcList); + + if (pszTargetList) + HeapFree(GetProcessHeap(), 0, pszTargetList); + + SHFree(pidl); + return E_OUTOFMEMORY; + } + + ZeroMemory(&op, sizeof(op)); + if (!pszSrcList[0]) + { + /* remove trailing backslash */ + pszSrc--; + pszSrc[0] = L'\0'; + op.pFrom = szSrcPath; + } + else + { + op.pFrom = pszSrcList; + } + + if (!pszTargetList[0]) + { + /* remove trailing backslash */ + if (pszTarget - szTargetPath > 3) + { + pszTarget--; + pszTarget[0] = L'\0'; + } + else + { + pszTarget[1] = L'\0'; + } + + op.pTo = szTargetPath; + op.fFlags = 0; + } + else + { + op.pTo = pszTargetList; + op.fFlags = FOF_MULTIDESTFILES; + } + op.hwnd = GetActiveWindow(); + op.wFunc = bCopy ? FO_COPY : FO_MOVE; + op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; + + res = SHFileOperationW(&op); + + if (res == DE_SAMEFILE) + { + length = wcslen(szTargetPath); + + pszFileName = wcsrchr(pszSrcList, '\\'); + pszFileName++; + + if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length)) + { + wcscat(szTargetPath, L" "); + } + + wcscat(szTargetPath, pszFileName); + op.pTo = szTargetPath; + + res = SHFileOperationW(&op); + } + + HeapFree(GetProcessHeap(), 0, pszSrcList); + HeapFree(GetProcessHeap(), 0, pszTargetList); + + if (res) + return E_FAIL; + else + return S_OK; + } + return E_FAIL; +} + +CFSDropTarget::CFSDropTarget(): + cfShellIDList(0), + fAcceptFmt(FALSE), + sPathTarget(NULL) +{ +} + +HRESULT WINAPI CFSDropTarget::Initialize(LPWSTR PathTarget) +{ + cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); + if (!cfShellIDList) + return E_FAIL; + + sPathTarget = (WCHAR *)SHAlloc((wcslen(PathTarget) + 1) * sizeof(WCHAR)); + if (!sPathTarget) + return E_OUTOFMEMORY; + wcscpy(sPathTarget, PathTarget); + + return S_OK; +} + +CFSDropTarget::~CFSDropTarget() +{ + SHFree(sPathTarget); +} + +BOOL +CFSDropTarget::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut) +{ + WCHAR wszLink[40]; + + if (!bShortcut) + { + if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink))) + wszLink[0] = L'\0'; + } + + if (!bShortcut) + swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt); + else + swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt); + + for (UINT i = 2; PathFileExistsW(pwszTarget); ++i) + { + if (!bShortcut) + swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt); + else + swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt); + } + + return TRUE; +} + +/**************************************************************************** + * IDropTarget implementation + */ +BOOL CFSDropTarget::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect) +{ + /* TODO Windows does different drop effects if dragging across drives. + i.e., it will copy instead of move if the directories are on different disks. */ + + DWORD dwEffect = DROPEFFECT_MOVE; + + *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 CFSDropTarget::DragEnter(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); + FORMATETC fmt; + FORMATETC fmt2; + fAcceptFmt = FALSE; + + InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); + InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); + + if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) + fAcceptFmt = TRUE; + else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) + fAcceptFmt = TRUE; + + QueryDrop(dwKeyState, pdwEffect); + return S_OK; +} + +HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt, + DWORD *pdwEffect) +{ + TRACE("(%p)\n", this); + + if (!pdwEffect) + return E_INVALIDARG; + + QueryDrop(dwKeyState, pdwEffect); + + return S_OK; +} + +HRESULT WINAPI CFSDropTarget::DragLeave() +{ + TRACE("(%p)\n", this); + + fAcceptFmt = FALSE; + + return S_OK; +} + +HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); + + if (!pdwEffect) + return E_INVALIDARG; + + QueryDrop(dwKeyState, pdwEffect); + + BOOL fIsOpAsync = FALSE; + CComPtr pAsyncOperation; + + if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) + { + if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync) + { + _DoDropData *data = static_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). + pDataObject->AddRef(); + pAsyncOperation->StartOperation(NULL); + CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream); + this->AddRef(); + data->dwKeyState = dwKeyState; + data->pt = pt; + // Need to dereference as pdweffect gets freed. + data->pdwEffect = *pdwEffect; + SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL); + return S_OK; + } + } + return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect); +} + +HRESULT WINAPI CFSDropTarget::_DoDrop(IDataObject *pDataObject, + DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) +{ + TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); + FORMATETC fmt; + FORMATETC fmt2; + STGMEDIUM medium; + + InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); + InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); + + HRESULT hr; + bool bCopy = TRUE; + bool bLinking = FALSE; + + /* Figure out what drop operation we're doing */ + if (pdwEffect) + { + TRACE("Current drop effect flag %i\n", *pdwEffect); + if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) + bCopy = FALSE; + if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) + bLinking = TRUE; + } + + if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) + { + hr = pDataObject->GetData(&fmt, &medium); + TRACE("CFSTR_SHELLIDLIST.\n"); + + /* 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)) + { + /* use desktop shell folder */ + psfFrom = psfDesktop; + } + else + { + hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom)); + if (FAILED(hr)) + { + ERR("no IShellFolder\n"); + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + return E_FAIL; + } + } + + if (bLinking) + { + WCHAR wszTargetPath[MAX_PATH]; + WCHAR wszPath[MAX_PATH]; + WCHAR wszTarget[MAX_PATH]; + + wcscpy(wszTargetPath, sPathTarget); + + TRACE("target path = %s", debugstr_w(wszTargetPath)); + + /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */ + for (UINT i = 0; i < lpcida->cidl; i++) + { + //Find out which file we're copying + STRRET strFile; + hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile); + if (FAILED(hr)) + { + ERR("Error source obtaining path"); + break; + } + + hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath)); + if (FAILED(hr)) + { + ERR("Error putting source path into buffer"); + break; + } + TRACE("source path = %s", debugstr_w(wszPath)); + + // Creating a buffer to hold the combined path + WCHAR buffer_1[MAX_PATH] = L""; + WCHAR *lpStr1; + lpStr1 = buffer_1; + + LPWSTR pwszFileName = PathFindFileNameW(wszPath); + LPWSTR pwszExt = PathFindExtensionW(wszPath); + LPWSTR placementPath = PathCombineW(lpStr1, sPathTarget, pwszFileName); + CComPtr ppf; + + //Check to see if it's already a link. + if (!wcsicmp(pwszExt, L".lnk")) + { + //It's a link so, we create a new one which copies the old. + if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) + { + ERR("Error getting unique file name"); + hr = E_FAIL; + break; + } + hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf)); + if (FAILED(hr)) { + ERR("Error constructing link from file"); + break; + } + + hr = ppf->Save(wszTarget, FALSE); + if (FAILED(hr)) + break; + SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); + } + else + { + //It's not a link, so build a new link using the creator class and fill it in. + //Create a file name for the link + if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE)) + { + ERR("Error creating unique file name"); + hr = E_FAIL; + break; + } + + CComPtr pLink; + hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink)); + if (FAILED(hr)) { + ERR("Error instantiating IShellLinkW"); + break; + } + + WCHAR szDirPath[MAX_PATH], *pwszFile; + GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile); + if (pwszFile) pwszFile[0] = 0; + + hr = pLink->SetPath(wszPath); + if(FAILED(hr)) + break; + + hr = pLink->SetWorkingDirectory(szDirPath); + if(FAILED(hr)) + break; + + hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); + if(FAILED(hr)) + break; + + hr = ppf->Save(wszTarget, TRUE); + if (FAILED(hr)) + break; + SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); + } + } + } + else + { + hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy); + } + + SHFree(pidl); + _ILFreeaPidl(apidl, lpcida->cidl); + ReleaseStgMedium(&medium); + } + else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) + { + FORMATETC fmt2; + InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); + if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/) + { + WCHAR wszTargetPath[MAX_PATH + 1]; + LPWSTR pszSrcList; + + wcscpy(wszTargetPath, sPathTarget); + //Double NULL terminate. + wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; + + LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); + if (!lpdf) + { + ERR("Error locking global\n"); + return E_FAIL; + } + pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles); + TRACE("Source file (just the first) = %s\n", debugstr_w(pszSrcList)); + TRACE("Target path = %s\n", debugstr_w(wszTargetPath)); + + SHFILEOPSTRUCTW op; + ZeroMemory(&op, sizeof(op)); + op.pFrom = pszSrcList; + op.pTo = wszTargetPath; + op.hwnd = GetActiveWindow(); + op.wFunc = bCopy ? FO_COPY : FO_MOVE; + op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; + hr = SHFileOperationW(&op); + return hr; + } + ERR("Error calling GetData\n"); + hr = E_FAIL; + } + else + { + ERR("No viable drop format.\n"); + hr = E_FAIL; + } + return hr; +} + +DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter) +{ + CoInitialize(NULL); + _DoDropData *data = static_cast<_DoDropData*>(lpParameter); + CComPtr pDataObject; + HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); + + if (SUCCEEDED(hr)) + { + CComPtr pAsyncOperation; + hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); + if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) + { + pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect); + } + } + //Release the CFSFolder and data object holds in the copying thread. + data->This->Release(); + //Release the parameter from the heap. + HeapFree(GetProcessHeap(), 0, data); + CoUninitialize(); + return 0; +} + +HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut) +{ + return ShellObjectCreatorInit(sPathTarget, riid, ppvOut); +} \ No newline at end of file diff --git a/reactos/dll/win32/shell32/droptargets/CFSDropTarget.h b/reactos/dll/win32/shell32/droptargets/CFSDropTarget.h new file mode 100644 index 00000000000..61ed795ceb9 --- /dev/null +++ b/reactos/dll/win32/shell32/droptargets/CFSDropTarget.h @@ -0,0 +1,74 @@ +/* + * file system folder drop target + * + * Copyright 1997 Marcus Meissner + * Copyright 1998, 1999, 2002 Juergen Schmied + * Copyright 2009 Andrew Hill + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _CFSDROPTARGET_H_ +#define _CFSDROPTARGET_H_ + +WCHAR *BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls); + +class CFSDropTarget : + public CComObjectRootEx, + public IDropTarget +{ + private: + UINT cfShellIDList; /* clipboardformat for IDropTarget */ + BOOL fAcceptFmt; /* flag for pending Drop */ + LPWSTR sPathTarget; + + BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect); + virtual HRESULT WINAPI _DoDrop(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); + virtual HRESULT WINAPI CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl, BOOL bCopy); + BOOL GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut); + static DWORD WINAPI _DoDropThreadProc(LPVOID lpParameter); + + public: + CFSDropTarget(); + ~CFSDropTarget(); + HRESULT WINAPI Initialize(LPWSTR PathTarget); + + // 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); + + DECLARE_NOT_AGGREGATABLE(CFSDropTarget) + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + BEGIN_COM_MAP(CFSDropTarget) + COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) + END_COM_MAP() + +}; + +struct _DoDropData { + CFSDropTarget *This; + IStream *pStream; + DWORD dwKeyState; + POINTL pt; + DWORD pdwEffect; +}; + +HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut); + +#endif /* _CFSFOLDER_H_ */ diff --git a/reactos/dll/win32/shell32/folders/CFSFolder.cpp b/reactos/dll/win32/shell32/folders/CFSFolder.cpp index a7364af8a89..2406fef84d5 100644 --- a/reactos/dll/win32/shell32/folders/CFSFolder.cpp +++ b/reactos/dll/win32/shell32/folders/CFSFolder.cpp @@ -364,7 +364,7 @@ HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner, *ppvOut = NULL; if (IsEqualIID (riid, IID_IDropTarget)) - hr = ShellObjectCreatorInit(sPathTarget, riid, ppvOut); + hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut); else if (IsEqualIID (riid, IID_IContextMenu)) { FIXME ("IContextMenu not implemented\n"); @@ -510,7 +510,7 @@ HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner, if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj))) { IDropTarget * pDt = NULL; - hr = ShellObjectCreatorInit(sPathTarget, riid, ppvOut); + hr = CFSDropTarget_CreateInstance(sPathTarget, riid, ppvOut); pObj = pDt; } } @@ -824,161 +824,6 @@ HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column, return E_NOTIMPL; } -/**************************************************************************** - * BuildPathsList - * - * Builds a list of paths like the one used in SHFileOperation from a table of - * PIDLs relative to the given base folder - */ -WCHAR * -BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls) -{ - WCHAR *pwszPathsList; - WCHAR *pwszListPos; - int iPathLen, i; - - iPathLen = wcslen(wszBasePath); - pwszPathsList = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1); - pwszListPos = pwszPathsList; - - for (i = 0; i < cidl; i++) - { - if (!_ILIsFolder(pidls[i]) && !_ILIsValue(pidls[i])) - continue; - - wcscpy(pwszListPos, wszBasePath); - pwszListPos += iPathLen; - /* FIXME: abort if path too long */ - _ILSimpleGetTextW(pidls[i], pwszListPos, MAX_PATH - iPathLen); - pwszListPos += wcslen(pwszListPos) + 1; - } - *pwszListPos = 0; - return pwszPathsList; -} - -/**************************************************************************** - * CFSDropTarget::CopyItems - * - * copies items to this folder - */ -HRESULT WINAPI CFSDropTarget::CopyItems(IShellFolder * pSFFrom, UINT cidl, - LPCITEMIDLIST * apidl, BOOL bCopy) -{ - CComPtr ppf2 = NULL; - WCHAR szSrcPath[MAX_PATH]; - WCHAR szTargetPath[MAX_PATH]; - SHFILEOPSTRUCTW op; - LPITEMIDLIST pidl; - LPWSTR pszSrc, pszTarget, pszSrcList, pszTargetList, pszFileName; - int res, length; - HRESULT hr; - - TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl); - - hr = pSFFrom->QueryInterface (IID_PPV_ARG(IPersistFolder2, &ppf2)); - if (SUCCEEDED(hr)) - { - hr = ppf2->GetCurFolder(&pidl); - if (FAILED(hr)) - { - return hr; - } - - hr = SHGetPathFromIDListW(pidl, szSrcPath); - SHFree(pidl); - - if (FAILED(hr)) - return hr; - - pszSrc = PathAddBackslashW(szSrcPath); - - wcscpy(szTargetPath, sPathTarget); - pszTarget = PathAddBackslashW(szTargetPath); - - pszSrcList = BuildPathsList(szSrcPath, cidl, apidl); - pszTargetList = BuildPathsList(szTargetPath, cidl, apidl); - - if (!pszSrcList || !pszTargetList) - { - if (pszSrcList) - HeapFree(GetProcessHeap(), 0, pszSrcList); - - if (pszTargetList) - HeapFree(GetProcessHeap(), 0, pszTargetList); - - SHFree(pidl); - return E_OUTOFMEMORY; - } - - ZeroMemory(&op, sizeof(op)); - if (!pszSrcList[0]) - { - /* remove trailing backslash */ - pszSrc--; - pszSrc[0] = L'\0'; - op.pFrom = szSrcPath; - } - else - { - op.pFrom = pszSrcList; - } - - if (!pszTargetList[0]) - { - /* remove trailing backslash */ - if (pszTarget - szTargetPath > 3) - { - pszTarget--; - pszTarget[0] = L'\0'; - } - else - { - pszTarget[1] = L'\0'; - } - - op.pTo = szTargetPath; - op.fFlags = 0; - } - else - { - op.pTo = pszTargetList; - op.fFlags = FOF_MULTIDESTFILES; - } - op.hwnd = GetActiveWindow(); - op.wFunc = bCopy ? FO_COPY : FO_MOVE; - op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; - - res = SHFileOperationW(&op); - - if (res == DE_SAMEFILE) - { - length = wcslen(szTargetPath); - - pszFileName = wcsrchr(pszSrcList, '\\'); - pszFileName++; - - if (LoadStringW(shell32_hInstance, IDS_COPY_OF, pszTarget, MAX_PATH - length)) - { - wcscat(szTargetPath, L" "); - } - - wcscat(szTargetPath, pszFileName); - op.pTo = szTargetPath; - - res = SHFileOperationW(&op); - } - - HeapFree(GetProcessHeap(), 0, pszSrcList); - HeapFree(GetProcessHeap(), 0, pszTargetList); - - if (res) - return E_FAIL; - else - return S_OK; - } - return E_FAIL; -} - /************************************************************************ * CFSFolder::GetClassID */ @@ -1122,421 +967,6 @@ HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti return E_NOTIMPL; } -CFSDropTarget::CFSDropTarget(): - cfShellIDList(0), - fAcceptFmt(FALSE), - sPathTarget(NULL) -{ -} - -HRESULT WINAPI CFSDropTarget::Initialize(LPWSTR PathTarget) -{ - cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); - if (!cfShellIDList) - return E_FAIL; - - sPathTarget = (WCHAR *)SHAlloc((wcslen(PathTarget) + 1) * sizeof(WCHAR)); - if (!sPathTarget) - return E_OUTOFMEMORY; - wcscpy(sPathTarget, PathTarget); - - return S_OK; -} - -CFSDropTarget::~CFSDropTarget() -{ - SHFree(sPathTarget); -} - -BOOL -CFSDropTarget::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut) -{ - WCHAR wszLink[40]; - - if (!bShortcut) - { - if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink))) - wszLink[0] = L'\0'; - } - - if (!bShortcut) - swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt); - else - swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt); - - for (UINT i = 2; PathFileExistsW(pwszTarget); ++i) - { - if (!bShortcut) - swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt); - else - swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt); - } - - return TRUE; -} - -/**************************************************************************** - * IDropTarget implementation - */ -BOOL CFSDropTarget::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect) -{ - /* TODO Windows does different drop effects if dragging across drives. - i.e., it will copy instead of move if the directories are on different disks. */ - - DWORD dwEffect = DROPEFFECT_MOVE; - - *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 CFSDropTarget::DragEnter(IDataObject *pDataObject, - DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) -{ - TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); - FORMATETC fmt; - FORMATETC fmt2; - fAcceptFmt = FALSE; - - InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); - InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); - - if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) - fAcceptFmt = TRUE; - else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) - fAcceptFmt = TRUE; - - QueryDrop(dwKeyState, pdwEffect); - return S_OK; -} - -HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt, - DWORD *pdwEffect) -{ - TRACE("(%p)\n", this); - - if (!pdwEffect) - return E_INVALIDARG; - - QueryDrop(dwKeyState, pdwEffect); - - return S_OK; -} - -HRESULT WINAPI CFSDropTarget::DragLeave() -{ - TRACE("(%p)\n", this); - - fAcceptFmt = FALSE; - - return S_OK; -} - -HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, - DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) -{ - TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); - - if (!pdwEffect) - return E_INVALIDARG; - - QueryDrop(dwKeyState, pdwEffect); - - BOOL fIsOpAsync = FALSE; - CComPtr pAsyncOperation; - - if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) - { - if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync) - { - _DoDropData *data = static_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). - pDataObject->AddRef(); - pAsyncOperation->StartOperation(NULL); - CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream); - this->AddRef(); - data->dwKeyState = dwKeyState; - data->pt = pt; - // Need to dereference as pdweffect gets freed. - data->pdwEffect = *pdwEffect; - SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL); - return S_OK; - } - } - return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect); -} - -HRESULT WINAPI CFSDropTarget::_DoDrop(IDataObject *pDataObject, - DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) -{ - TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); - FORMATETC fmt; - FORMATETC fmt2; - STGMEDIUM medium; - - InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL); - InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); - - HRESULT hr; - bool bCopy = TRUE; - bool bLinking = FALSE; - - /* Figure out what drop operation we're doing */ - if (pdwEffect) - { - TRACE("Current drop effect flag %i\n", *pdwEffect); - if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) - bCopy = FALSE; - if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) - bLinking = TRUE; - } - - if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) - { - hr = pDataObject->GetData(&fmt, &medium); - TRACE("CFSTR_SHELLIDLIST.\n"); - - /* 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)) - { - /* use desktop shell folder */ - psfFrom = psfDesktop; - } - else - { - hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom)); - if (FAILED(hr)) - { - ERR("no IShellFolder\n"); - SHFree(pidl); - _ILFreeaPidl(apidl, lpcida->cidl); - ReleaseStgMedium(&medium); - return E_FAIL; - } - } - - if (bLinking) - { - WCHAR wszTargetPath[MAX_PATH]; - WCHAR wszPath[MAX_PATH]; - WCHAR wszTarget[MAX_PATH]; - - wcscpy(wszTargetPath, sPathTarget); - - TRACE("target path = %s", debugstr_w(wszTargetPath)); - - /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */ - for (UINT i = 0; i < lpcida->cidl; i++) - { - //Find out which file we're copying - STRRET strFile; - hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile); - if (FAILED(hr)) - { - ERR("Error source obtaining path"); - break; - } - - hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath)); - if (FAILED(hr)) - { - ERR("Error putting source path into buffer"); - break; - } - TRACE("source path = %s", debugstr_w(wszPath)); - - // Creating a buffer to hold the combined path - WCHAR buffer_1[MAX_PATH] = L""; - WCHAR *lpStr1; - lpStr1 = buffer_1; - - LPWSTR pwszFileName = PathFindFileNameW(wszPath); - LPWSTR pwszExt = PathFindExtensionW(wszPath); - LPWSTR placementPath = PathCombineW(lpStr1, sPathTarget, pwszFileName); - CComPtr ppf; - - //Check to see if it's already a link. - if (!wcsicmp(pwszExt, L".lnk")) - { - //It's a link so, we create a new one which copies the old. - if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) - { - ERR("Error getting unique file name"); - hr = E_FAIL; - break; - } - hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf)); - if (FAILED(hr)) { - ERR("Error constructing link from file"); - break; - } - - hr = ppf->Save(wszTarget, FALSE); - if (FAILED(hr)) - break; - SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); - } - else - { - //It's not a link, so build a new link using the creator class and fill it in. - //Create a file name for the link - if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE)) - { - ERR("Error creating unique file name"); - hr = E_FAIL; - break; - } - - CComPtr pLink; - hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink)); - if (FAILED(hr)) { - ERR("Error instantiating IShellLinkW"); - break; - } - - WCHAR szDirPath[MAX_PATH], *pwszFile; - GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile); - if (pwszFile) pwszFile[0] = 0; - - hr = pLink->SetPath(wszPath); - if(FAILED(hr)) - break; - - hr = pLink->SetWorkingDirectory(szDirPath); - if(FAILED(hr)) - break; - - hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); - if(FAILED(hr)) - break; - - hr = ppf->Save(wszTarget, TRUE); - if (FAILED(hr)) - break; - SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); - } - } - } - else - { - hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy); - } - - SHFree(pidl); - _ILFreeaPidl(apidl, lpcida->cidl); - ReleaseStgMedium(&medium); - } - else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) - { - FORMATETC fmt2; - InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); - if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/) - { - WCHAR wszTargetPath[MAX_PATH + 1]; - LPWSTR pszSrcList; - - wcscpy(wszTargetPath, sPathTarget); - //Double NULL terminate. - wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; - - LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); - if (!lpdf) - { - ERR("Error locking global\n"); - return E_FAIL; - } - pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles); - TRACE("Source file (just the first) = %s\n", debugstr_w(pszSrcList)); - TRACE("Target path = %s\n", debugstr_w(wszTargetPath)); - - SHFILEOPSTRUCTW op; - ZeroMemory(&op, sizeof(op)); - op.pFrom = pszSrcList; - op.pTo = wszTargetPath; - op.hwnd = GetActiveWindow(); - op.wFunc = bCopy ? FO_COPY : FO_MOVE; - op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; - hr = SHFileOperationW(&op); - return hr; - } - ERR("Error calling GetData\n"); - hr = E_FAIL; - } - else - { - ERR("No viable drop format.\n"); - hr = E_FAIL; - } - return hr; -} - -DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter) -{ - CoInitialize(NULL); - _DoDropData *data = static_cast<_DoDropData*>(lpParameter); - CComPtr pDataObject; - HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); - - if (SUCCEEDED(hr)) - { - CComPtr pAsyncOperation; - hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); - if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) - { - pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect); - } - } - //Release the CFSFolder and data object holds in the copying thread. - data->This->Release(); - //Release the parameter from the heap. - HeapFree(GetProcessHeap(), 0, data); - CoUninitialize(); - return 0; -} - HRESULT WINAPI CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) { HKEY hKey; HRESULT hr; diff --git a/reactos/dll/win32/shell32/folders/CFSFolder.h b/reactos/dll/win32/shell32/folders/CFSFolder.h index da5efba1ab1..f76c7246553 100644 --- a/reactos/dll/win32/shell32/folders/CFSFolder.h +++ b/reactos/dll/win32/shell32/folders/CFSFolder.h @@ -23,8 +23,6 @@ #ifndef _CFSFOLDER_H_ #define _CFSFOLDER_H_ -WCHAR *BuildPathsList(LPCWSTR wszBasePath, int cidl, LPCITEMIDLIST *pidls); - class CFSFolder : public CComCoClass, public CComObjectRootEx, @@ -97,48 +95,4 @@ class CFSFolder : END_COM_MAP() }; -class CFSDropTarget : - public CComObjectRootEx, - public IDropTarget -{ - private: - UINT cfShellIDList; /* clipboardformat for IDropTarget */ - BOOL fAcceptFmt; /* flag for pending Drop */ - LPWSTR sPathTarget; - - BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect); - virtual HRESULT WINAPI _DoDrop(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect); - virtual HRESULT WINAPI CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl, BOOL bCopy); - BOOL GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut); - static DWORD WINAPI _DoDropThreadProc(LPVOID lpParameter); - - public: - CFSDropTarget(); - ~CFSDropTarget(); - HRESULT WINAPI Initialize(LPWSTR PathTarget); - - // 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); - - DECLARE_NOT_AGGREGATABLE(CFSDropTarget) - - DECLARE_PROTECT_FINAL_CONSTRUCT() - - BEGIN_COM_MAP(CFSDropTarget) - COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget) - END_COM_MAP() - -}; - -struct _DoDropData { - CFSDropTarget *This; - IStream *pStream; - DWORD dwKeyState; - POINTL pt; - DWORD pdwEffect; -}; - #endif /* _CFSFOLDER_H_ */ diff --git a/reactos/dll/win32/shell32/precomp.h b/reactos/dll/win32/shell32/precomp.h index 3ac688d6834..526a00d4b2c 100644 --- a/reactos/dll/win32/shell32/precomp.h +++ b/reactos/dll/win32/shell32/precomp.h @@ -68,6 +68,7 @@ #include "folders/CAdminToolsFolder.h" #include "folders/CRecycleBin.h" #include "droptargets/CexeDropHandler.h" +#include "droptargets/CFSDropTarget.h" #include "COpenWithMenu.h" #include "CNewMenu.h" #include "dialogs/filedefext.h"