[SHELL32] -CRecycleBin: Factor out a new class called CRecyclerDropTarget, which...
[reactos.git] / reactos / dll / win32 / shell32 / droptargets / CRecyclerDropTarget.cpp
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
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 return E_FAIL;
50 }
51
52 /* Delete them */
53 SHFILEOPSTRUCTW FileOp;
54 ZeroMemory(&FileOp, sizeof(FileOp));
55 FileOp.wFunc = FO_DELETE;
56 FileOp.pFrom = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);;
57 if ((fMask & CMIC_MASK_SHIFT_DOWN) == 0)
58 FileOp.fFlags = FOF_ALLOWUNDO;
59 ERR("Deleting file (just the first) = %s, allowundo: %d\n", debugstr_w(FileOp.pFrom), (FileOp.fFlags == FOF_ALLOWUNDO));
60
61 if (SHFileOperationW(&FileOp) != 0)
62 {
63 ERR("SHFileOperation failed with 0x%x\n", GetLastError());
64 hr = E_FAIL;
65 }
66
67 ReleaseStgMedium(&medium);
68
69 return hr;
70 }
71
72 struct DeleteThreadData {
73 IStream *s;
74 DWORD fMask;
75 };
76
77 static DWORD WINAPI _DoDeleteThreadProc(LPVOID lpParameter)
78 {
79 DeleteThreadData *data = static_cast<DeleteThreadData*>(lpParameter);
80 CoInitialize(NULL);
81 IDataObject *pDataObject;
82 HRESULT hr = CoGetInterfaceAndReleaseStream (data->s, IID_PPV_ARG(IDataObject, &pDataObject));
83 if (SUCCEEDED(hr))
84 {
85 _DoDeleteDataObject(pDataObject, data->fMask);
86 }
87 pDataObject->Release();
88 CoUninitialize();
89 HeapFree(GetProcessHeap(), 0, data);
90 return 0;
91 }
92
93 static void _DoDeleteAsync(IDataObject *pda, DWORD fMask)
94 {
95 DeleteThreadData *data = static_cast<DeleteThreadData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(DeleteThreadData)));
96 data->fMask = fMask;
97 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pda, &data->s);
98 SHCreateThread(_DoDeleteThreadProc, data, NULL, NULL);
99 }
100
101 public:
102
103 CRecyclerDropTarget()
104 {
105 fAcceptFmt = FALSE;
106 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
107 }
108
109 HRESULT WINAPI DragEnter(IDataObject *pDataObject,
110 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
111 {
112 TRACE("Recycle bin drag over (%p)\n", this);
113 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
114 fAcceptFmt = TRUE;
115
116 *pdwEffect = DROPEFFECT_MOVE;
117 return S_OK;
118 }
119
120 HRESULT WINAPI DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
121 {
122 TRACE("(%p)\n", this);
123
124 if (!pdwEffect)
125 return E_INVALIDARG;
126
127 *pdwEffect = DROPEFFECT_MOVE;
128
129 return S_OK;
130 }
131
132 HRESULT WINAPI DragLeave()
133 {
134 TRACE("(%p)\n", this);
135
136 fAcceptFmt = FALSE;
137
138 return S_OK;
139 }
140
141 HRESULT WINAPI Drop(IDataObject *pDataObject,
142 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
143 {
144 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
145
146 /* TODO: pdwEffect should be read and make the drop object be permanently deleted in the move case (shift held) */
147
148 FORMATETC fmt;
149 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
150 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
151
152 /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to other software */
153 if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
154 {
155 DWORD fMask = 0;
156
157 if ((dwKeyState & MK_SHIFT) == MK_SHIFT)
158 fMask |= CMIC_MASK_SHIFT_DOWN;
159
160 _DoDeleteAsync(pDataObject, fMask);
161 }
162 else
163 {
164 /*
165 * TODO call SetData on the data object with format CFSTR_TARGETCLSID
166 * set to the Recycle Bin's class identifier CLSID_RecycleBin.
167 */
168 }
169 return S_OK;
170 }
171
172 DECLARE_NOT_AGGREGATABLE(CRecyclerDropTarget)
173
174 DECLARE_PROTECT_FINAL_CONSTRUCT()
175
176 BEGIN_COM_MAP(CRecyclerDropTarget)
177 COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
178 END_COM_MAP()
179 };
180
181 HRESULT CRecyclerDropTarget_CreateInstance(REFIID riid, LPVOID * ppvOut)
182 {
183 return ShellObjectCreator<CRecyclerDropTarget>(riid, ppvOut);
184 }