The real, definitive, Visual C++ support branch. Accept no substitutes
[reactos.git] / base / shell / explorer-new / dragdrop.c
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <precomp.h>
22
23 static const IDropTargetVtbl IDropTargetImpl_Vtbl;
24
25 /*
26 * IDropTarget
27 */
28
29 typedef struct
30 {
31 const IDropTargetVtbl *lpVtbl;
32 LONG Ref;
33 HWND hwndTarget;
34 IDropTargetHelper *DropTargetHelper;
35 PVOID Context;
36 BOOL CanDrop;
37 DROPTARGET_CALLBACKS Callbacks;
38 DWORD FormatsCount;
39 FORMATETC Formats[0];
40 } IDropTargetImpl;
41
42 static IUnknown *
43 IUnknown_from_impl(IDropTargetImpl *This)
44 {
45 return (IUnknown *)&This->lpVtbl;
46 }
47
48 static IDropTarget *
49 IDropTarget_from_impl(IDropTargetImpl *This)
50 {
51 return (IDropTarget *)&This->lpVtbl;
52 }
53
54 static IDropTargetImpl *
55 impl_from_IDropTarget(IDropTarget *iface)
56 {
57 return (IDropTargetImpl *)((ULONG_PTR)iface - FIELD_OFFSET(IDropTargetImpl,
58 lpVtbl));
59 }
60
61 static VOID
62 IDropTargetImpl_Free(IDropTargetImpl *This)
63 {
64 IDropTargetHelper_Release(This->DropTargetHelper);
65 }
66
67 static ULONG STDMETHODCALLTYPE
68 IDropTargetImpl_Release(IN OUT IDropTarget *iface)
69 {
70 IDropTargetImpl *This = impl_from_IDropTarget(iface);
71 ULONG Ret;
72
73 Ret = InterlockedDecrement(&This->Ref);
74 if (Ret == 0)
75 IDropTargetImpl_Free(This);
76
77 return Ret;
78 }
79
80 static ULONG STDMETHODCALLTYPE
81 IDropTargetImpl_AddRef(IN OUT IDropTarget *iface)
82 {
83 IDropTargetImpl *This = impl_from_IDropTarget(iface);
84
85 return InterlockedIncrement(&This->Ref);
86 }
87
88 static HRESULT STDMETHODCALLTYPE
89 IDropTargetImpl_QueryInterface(IN OUT IDropTarget *iface,
90 IN REFIID riid,
91 OUT LPVOID *ppvObj)
92 {
93 IDropTargetImpl *This;
94
95 if (ppvObj == NULL)
96 return E_POINTER;
97
98 This = impl_from_IDropTarget(iface);
99
100 if (IsEqualIID(riid,
101 &IID_IUnknown))
102 {
103 *ppvObj = IUnknown_from_impl(This);
104 }
105 else if (IsEqualIID(riid,
106 &IID_IDropTarget))
107 {
108 *ppvObj = IDropTarget_from_impl(This);
109 }
110 else
111 {
112 *ppvObj = NULL;
113 return E_NOINTERFACE;
114 }
115
116 IDropTargetImpl_AddRef(iface);
117 return S_OK;
118 }
119
120 IDropTarget *
121 CreateDropTarget(IN HWND hwndTarget,
122 IN DWORD nSupportedFormats,
123 IN const FORMATETC *Formats OPTIONAL,
124 IN PVOID Context OPTIONAL,
125 IN const DROPTARGET_CALLBACKS *Callbacks OPTIONAL)
126 {
127 IDropTargetImpl *This;
128 HRESULT hr;
129
130 This = (IDropTargetImpl *)HeapAlloc(hProcessHeap,
131 0,
132 FIELD_OFFSET(IDropTargetImpl,
133 Formats[nSupportedFormats]));
134 if (This != NULL)
135 {
136 ZeroMemory(This,
137 sizeof(*This));
138
139 This->lpVtbl = &IDropTargetImpl_Vtbl;
140 This->Ref = 1;
141 This->hwndTarget = hwndTarget;
142 This->FormatsCount = nSupportedFormats;
143 if (nSupportedFormats != 0)
144 {
145 CopyMemory(This->Formats,
146 Formats,
147 sizeof(Formats[0]) * nSupportedFormats);
148 }
149
150 This->Context = Context;
151 if (Callbacks != NULL)
152 {
153 CopyMemory(&This->Callbacks,
154 Callbacks,
155 sizeof(Callbacks));
156 }
157
158 hr = CoCreateInstance(&CLSID_DragDropHelper,
159 NULL,
160 CLSCTX_INPROC_SERVER,
161 &IID_IDropTargetHelper,
162 (PVOID)&This->DropTargetHelper);
163
164 if (!SUCCEEDED(hr))
165 {
166 HeapFree(hProcessHeap,
167 0,
168 This);
169 return NULL;
170 }
171
172 return IDropTarget_from_impl(This);
173 }
174
175 return NULL;
176 }
177
178 static const FORMATETC *
179 IDropTargetImpl_FindSupportedFormat(IN OUT IDropTargetImpl *This,
180 IN IDataObject *pDataObject)
181 {
182 FORMATETC *Current, *Last;
183 HRESULT hr;
184
185 /* NOTE: we could use IDataObject::EnumFormatEtc(),
186 but this appears to be a lot easier! */
187 Last = This->Formats + This->FormatsCount;
188 for (Current = This->Formats;
189 Current != Last;
190 Current++)
191 {
192 hr = IDataObject_QueryGetData(pDataObject,
193 Current);
194 if (SUCCEEDED(hr))
195 return Current;
196 }
197
198 return NULL;
199 }
200
201 static HRESULT STDMETHODCALLTYPE
202 IDropTargetImpl_DragEnter(IN OUT IDropTarget *iface,
203 IN IDataObject *pDataObject,
204 IN DWORD grfKeyState,
205 IN POINTL pt,
206 IN OUT DWORD *pdwEffect)
207 {
208 IDropTargetImpl *This = impl_from_IDropTarget(iface);
209 const FORMATETC *Format;
210 HRESULT hr;
211
212 if (pDataObject == NULL)
213 return E_INVALIDARG;
214
215 This->CanDrop = FALSE;
216
217 hr = IDropTargetHelper_DragEnter(This->DropTargetHelper,
218 This->hwndTarget,
219 pDataObject,
220 (POINT *)&pt,
221 *pdwEffect);
222
223 if (SUCCEEDED(hr))
224 {
225 Format = IDropTargetImpl_FindSupportedFormat(This,
226 pDataObject);
227 if (Format != NULL)
228 {
229 /* We found a format that we support! */
230 if (This->Callbacks.OnDragEnter != NULL)
231 {
232 hr = This->Callbacks.OnDragEnter(iface,
233 This->Context,
234 Format,
235 grfKeyState,
236 pt,
237 pdwEffect);
238 if (SUCCEEDED(hr))
239 {
240 if (hr == S_OK)
241 This->CanDrop = TRUE;
242 else
243 {
244 /* Special return value by the callback routine,
245 doesn't want to allow dragging */
246 *pdwEffect = DROPEFFECT_NONE;
247 }
248
249 hr = S_OK;
250 }
251 else
252 {
253 *pdwEffect = DROPEFFECT_NONE;
254 hr = S_OK;
255 }
256 }
257 else
258 *pdwEffect = DROPEFFECT_NONE;
259 }
260 else
261 *pdwEffect = DROPEFFECT_NONE;
262 }
263
264 return hr;
265 }
266
267 static HRESULT STDMETHODCALLTYPE
268 IDropTargetImpl_DragOver(IN OUT IDropTarget *iface,
269 IN DWORD grfKeyState,
270 IN POINTL pt,
271 IN OUT DWORD *pdwEffect)
272 {
273 IDropTargetImpl *This = impl_from_IDropTarget(iface);
274 HRESULT hr;
275
276 hr = IDropTargetHelper_DragOver(This->DropTargetHelper,
277 (POINT *)&pt,
278 *pdwEffect);
279
280 if (SUCCEEDED(hr))
281 {
282 if (This->CanDrop)
283 {
284 if (This->Callbacks.OnDragOver != NULL)
285 {
286 hr = This->Callbacks.OnDragOver(iface,
287 This->Context,
288 grfKeyState,
289 pt,
290 pdwEffect);
291 if (SUCCEEDED(hr))
292 {
293 if (hr != S_OK)
294 {
295 /* Special return value by the callback routine,
296 doesn't want to allow dropping here */
297 *pdwEffect = DROPEFFECT_NONE;
298 }
299
300 hr = S_OK;
301 }
302 else
303 {
304 *pdwEffect = DROPEFFECT_NONE;
305 hr = S_OK;
306 }
307 }
308 else
309 *pdwEffect = DROPEFFECT_NONE;
310 }
311 else
312 *pdwEffect = DROPEFFECT_NONE;
313 }
314
315 return hr;
316 }
317
318 static HRESULT STDMETHODCALLTYPE
319 IDropTargetImpl_DragLeave(IN OUT IDropTarget *iface)
320 {
321 IDropTargetImpl *This = impl_from_IDropTarget(iface);
322 HRESULT hr;
323
324 hr = IDropTargetHelper_DragLeave(This->DropTargetHelper);
325 if (SUCCEEDED(hr))
326 {
327 if (This->Callbacks.OnDragLeave != NULL)
328 {
329 hr = This->Callbacks.OnDragLeave(iface,
330 This->Context);
331 }
332 }
333
334 return hr;
335 }
336
337 static HRESULT STDMETHODCALLTYPE
338 IDropTargetImpl_Drop(IN OUT IDropTarget *iface,
339 IN IDataObject *pDataObject,
340 IN DWORD grfKeyState,
341 IN POINTL pt,
342 IN OUT DWORD *pdwEffect)
343 {
344 IDropTargetImpl *This = impl_from_IDropTarget(iface);
345 const FORMATETC *Format;
346 HRESULT hr;
347
348 if (pDataObject == NULL)
349 return E_INVALIDARG;
350
351 hr = IDropTargetHelper_Drop(This->DropTargetHelper,
352 pDataObject,
353 (POINT *)&pt,
354 *pdwEffect);
355
356 if (SUCCEEDED(hr) && This->CanDrop)
357 {
358 Format = IDropTargetImpl_FindSupportedFormat(This,
359 pDataObject);
360 if (Format != NULL)
361 {
362 /* We found a format that we support! */
363 if (This->Callbacks.OnDrop != NULL)
364 {
365 hr = This->Callbacks.OnDrop(iface,
366 This->Context,
367 Format,
368 grfKeyState,
369 pt,
370 pdwEffect);
371 if (SUCCEEDED(hr))
372 {
373 if (hr == S_OK)
374 This->CanDrop = TRUE;
375 else
376 {
377 /* Special return value by the callback routine,
378 doesn't want to allow dragging */
379 *pdwEffect = DROPEFFECT_NONE;
380 }
381
382 hr = S_OK;
383 }
384 else
385 {
386 *pdwEffect = DROPEFFECT_NONE;
387 hr = S_OK;
388 }
389 }
390 else
391 *pdwEffect = DROPEFFECT_NONE;
392 }
393 else
394 *pdwEffect = DROPEFFECT_NONE;
395 }
396
397 return hr;
398 }
399
400 static const IDropTargetVtbl IDropTargetImpl_Vtbl =
401 {
402 /* IUnknown */
403 IDropTargetImpl_QueryInterface,
404 IDropTargetImpl_AddRef,
405 IDropTargetImpl_Release,
406 /* IDropTarget */
407 IDropTargetImpl_DragEnter,
408 IDropTargetImpl_DragOver,
409 IDropTargetImpl_DragLeave,
410 IDropTargetImpl_Drop
411 };