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 }