1 /*
2 * Trash virtual folder support. The trashing engine is implemented in trash.c
3 *
4 * Copyright (C) 2006 Mikolaj Zalewski
5 * Copyright (C) 2009 Andrew Hill
6 * Copyright (C) 2017 Giannis Adamopoulos
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include <precomp.h>
24
25 WINE_DEFAULT_DEBUG_CHANNEL (shell);
26
27 class CRecyclerDropTarget :
28 public CComObjectRootEx<CComMultiThreadModelNoCS>,
29 public IDropTarget
30 {
31 private:
32 BOOL fAcceptFmt; /* flag for pending Drop */
33 UINT cfShellIDList;
34
_DoDeleteDataObject(IDataObject * pda,DWORD fMask)35 static HRESULT _DoDeleteDataObject(IDataObject *pda, DWORD fMask)
36 {
37 HRESULT hr;
38 STGMEDIUM medium;
39 FORMATETC formatetc;
40 InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL);
41 hr = pda->GetData(&formatetc, &medium);
42 if (FAILED_UNEXPECTEDLY(hr))
43 return hr;
44
45 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
46 if (!lpdf)
47 {
48 ERR("Error locking global\n");
49 ReleaseStgMedium(&medium);
50 return E_FAIL;
51 }
52
53 /* Delete them */
54 SHFILEOPSTRUCTW FileOp;
55 ZeroMemory(&FileOp, sizeof(FileOp));
56 FileOp.wFunc = FO_DELETE;
57 FileOp.pFrom = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);;
58 if ((fMask & CMIC_MASK_SHIFT_DOWN) == 0)
59 FileOp.fFlags = FOF_ALLOWUNDO;
60 TRACE("Deleting file (just the first) = %s, allowundo: %d\n", debugstr_w(FileOp.pFrom), (FileOp.fFlags == FOF_ALLOWUNDO));
61
62 int res = SHFileOperationW(&FileOp);
63 if (res)
64 {
65 ERR("SHFileOperation failed with 0x%x\n", res);
66 hr = E_FAIL;
67 }
68
69 GlobalUnlock(medium.hGlobal);
70 ReleaseStgMedium(&medium);
71
72 return hr;
73 }
74
75 struct DeleteThreadData {
76 IStream *s;
77 DWORD fMask;
78 };
79
_DoDeleteThreadProc(LPVOID lpParameter)80 static DWORD WINAPI _DoDeleteThreadProc(LPVOID lpParameter)
81 {
82 DeleteThreadData *data = static_cast<DeleteThreadData*>(lpParameter);
83 CoInitialize(NULL);
84 IDataObject *pDataObject;
85 HRESULT hr = CoGetInterfaceAndReleaseStream (data->s, IID_PPV_ARG(IDataObject, &pDataObject));
86 if (SUCCEEDED(hr))
87 {
88 _DoDeleteDataObject(pDataObject, data->fMask);
89 }
90 pDataObject->Release();
91 CoUninitialize();
92 HeapFree(GetProcessHeap(), 0, data);
93 return 0;
94 }
95
_DoDeleteAsync(IDataObject * pda,DWORD fMask)96 static void _DoDeleteAsync(IDataObject *pda, DWORD fMask)
97 {
98 DeleteThreadData *data = static_cast<DeleteThreadData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(DeleteThreadData)));
99 data->fMask = fMask;
100 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pda, &data->s);
101 SHCreateThread(_DoDeleteThreadProc, data, NULL, NULL);
102 }
103
104 public:
CRecyclerDropTarget()105 CRecyclerDropTarget()
106 {
107 fAcceptFmt = FALSE;
108 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
109 }
110
111 STDMETHODIMP
DragEnter(IDataObject * pDataObject,DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)112 DragEnter(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override
113 {
114 TRACE("Recycle bin drag over (%p)\n", this);
115 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
116 fAcceptFmt = TRUE;
117
118 *pdwEffect = DROPEFFECT_MOVE;
119 return S_OK;
120 }
121
122 STDMETHODIMP
DragOver(DWORD dwKeyState,POINTL pt,DWORD * pdwEffect)123 DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) override
124 {
125 TRACE("(%p)\n", this);
126
127 if (!pdwEffect)
128 return E_INVALIDARG;
129
130 *pdwEffect = DROPEFFECT_MOVE;
131
132 return S_OK;
133 }
134
135 STDMETHODIMP
DragLeave()136 DragLeave() override
137 {
138 TRACE("(%p)\n", this);
139
140 fAcceptFmt = FALSE;
141
142 return S_OK;
143 }
144
145 STDMETHODIMP
Drop(IDataObject * pDataObject,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)146 Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override
147 {
148 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
149
150 FORMATETC fmt;
151 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
152 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
153
154 /* Handle cfShellIDList Drop objects here, otherwise send the appropriate message to other software */
155 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
156 {
157 DWORD fMask = 0;
158
159 if ((grfKeyState & MK_SHIFT) == MK_SHIFT)
160 fMask |= CMIC_MASK_SHIFT_DOWN;
161
162 _DoDeleteAsync(pDataObject, fMask);
163 }
164 else
165 {
166 /*
167 * TODO call SetData on the data object with format CFSTR_TARGETCLSID
168 * set to the Recycle Bin's class identifier CLSID_RecycleBin.
169 */
170 }
171 return S_OK;
172 }
173
174 DECLARE_NOT_AGGREGATABLE(CRecyclerDropTarget)
175
176 DECLARE_PROTECT_FINAL_CONSTRUCT()
177
178 BEGIN_COM_MAP(CRecyclerDropTarget)
179 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
180 END_COM_MAP()
181 };
182
CRecyclerDropTarget_CreateInstance(REFIID riid,LPVOID * ppvOut)183 HRESULT CRecyclerDropTarget_CreateInstance(REFIID riid, LPVOID * ppvOut)
184 {
185 return ShellObjectCreator<CRecyclerDropTarget>(riid, ppvOut);
186 }
187