gdi32: Fix leak in GdiDeleteSpoolFileHandle.
[wine.git] / dlls / winemac.drv / dragdrop.c
blobbbf74ac80c2da9552e23bdbd2a3919357b29d429
1 /*
2 * MACDRV Drag and drop code
4 * Copyright 2003 Ulrich Czekalla
5 * Copyright 2007 Damjan Jovanovic
6 * Copyright 2013 Ken Thomases for CodeWeavers Inc.
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.
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.
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
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "macdrv_dll.h"
27 #define COBJMACROS
28 #include "objidl.h"
29 #include "shellapi.h"
30 #include "shlobj.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dragdrop);
36 static IDataObject *active_data_object;
37 static HWND last_droptarget_hwnd;
40 typedef struct
42 IDataObject IDataObject_iface;
43 LONG ref;
44 UINT64 pasteboard;
45 } DragDropDataObject;
48 /**************************************************************************
49 * debugstr_format
51 static const char *debugstr_format(UINT id)
53 WCHAR buffer[256];
55 if (GetClipboardFormatNameW(id, buffer, 256))
56 return wine_dbg_sprintf("0x%04x %s", id, debugstr_w(buffer));
58 switch (id)
60 #define BUILTIN(id) case id: return #id;
61 BUILTIN(CF_TEXT)
62 BUILTIN(CF_BITMAP)
63 BUILTIN(CF_METAFILEPICT)
64 BUILTIN(CF_SYLK)
65 BUILTIN(CF_DIF)
66 BUILTIN(CF_TIFF)
67 BUILTIN(CF_OEMTEXT)
68 BUILTIN(CF_DIB)
69 BUILTIN(CF_PALETTE)
70 BUILTIN(CF_PENDATA)
71 BUILTIN(CF_RIFF)
72 BUILTIN(CF_WAVE)
73 BUILTIN(CF_UNICODETEXT)
74 BUILTIN(CF_ENHMETAFILE)
75 BUILTIN(CF_HDROP)
76 BUILTIN(CF_LOCALE)
77 BUILTIN(CF_DIBV5)
78 BUILTIN(CF_OWNERDISPLAY)
79 BUILTIN(CF_DSPTEXT)
80 BUILTIN(CF_DSPBITMAP)
81 BUILTIN(CF_DSPMETAFILEPICT)
82 BUILTIN(CF_DSPENHMETAFILE)
83 #undef BUILTIN
84 default: return wine_dbg_sprintf("0x%04x", id);
88 static inline DragDropDataObject *impl_from_IDataObject(IDataObject *iface)
90 return CONTAINING_RECORD(iface, DragDropDataObject, IDataObject_iface);
94 static HANDLE get_pasteboard_data(UINT64 pasteboard, UINT desired_format)
96 struct dnd_get_data_params params = { .handle = pasteboard, .format = desired_format, .size = 2048 };
97 HANDLE handle;
98 NTSTATUS status;
100 for (;;)
102 if (!(handle = GlobalAlloc(GMEM_FIXED, params.size))) return 0;
103 params.data = GlobalLock(handle);
104 status = MACDRV_CALL(dnd_get_data, &params);
105 GlobalUnlock(handle);
106 if (!status) return GlobalReAlloc(handle, params.size, GMEM_MOVEABLE);
107 GlobalFree(handle);
108 if (status != STATUS_BUFFER_OVERFLOW) return 0;
112 static HRESULT WINAPI dddo_QueryInterface(IDataObject* iface, REFIID riid, LPVOID *ppvObj)
114 DragDropDataObject *This = impl_from_IDataObject(iface);
116 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppvObj);
118 if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IDataObject)))
120 *ppvObj = iface;
121 IDataObject_AddRef(iface);
122 return S_OK;
125 *ppvObj = NULL;
126 return E_NOINTERFACE;
130 static ULONG WINAPI dddo_AddRef(IDataObject* iface)
132 DragDropDataObject *This = impl_from_IDataObject(iface);
133 ULONG refCount = InterlockedIncrement(&This->ref);
135 TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
137 return refCount;
141 static ULONG WINAPI dddo_Release(IDataObject* iface)
143 DragDropDataObject *This = impl_from_IDataObject(iface);
144 ULONG refCount = InterlockedDecrement(&This->ref);
146 TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
147 if (refCount)
148 return refCount;
150 TRACE("-- destroying DragDropDataObject (%p)\n", This);
151 MACDRV_CALL(dnd_release, &This->pasteboard);
152 HeapFree(GetProcessHeap(), 0, This);
153 return 0;
157 static HRESULT WINAPI dddo_GetData(IDataObject* iface, FORMATETC* formatEtc, STGMEDIUM* medium)
159 DragDropDataObject *This = impl_from_IDataObject(iface);
160 HRESULT hr;
162 TRACE("This %p formatEtc %s\n", This, debugstr_format(formatEtc->cfFormat));
164 hr = IDataObject_QueryGetData(iface, formatEtc);
165 if (SUCCEEDED(hr))
167 medium->tymed = TYMED_HGLOBAL;
168 medium->hGlobal = get_pasteboard_data(This->pasteboard, formatEtc->cfFormat);
169 medium->pUnkForRelease = NULL;
170 hr = medium->hGlobal ? S_OK : E_OUTOFMEMORY;
173 return hr;
177 static HRESULT WINAPI dddo_GetDataHere(IDataObject* iface, FORMATETC* formatEtc,
178 STGMEDIUM* medium)
180 FIXME("iface %p formatEtc %p medium %p; stub\n", iface, formatEtc, medium);
181 return DATA_E_FORMATETC;
185 static HRESULT WINAPI dddo_QueryGetData(IDataObject* iface, FORMATETC* formatEtc)
187 DragDropDataObject *This = impl_from_IDataObject(iface);
188 struct dnd_have_format_params params;
189 HRESULT hr = DV_E_FORMATETC;
191 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
192 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
193 debugstr_format(formatEtc->cfFormat));
195 if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL))
197 FIXME("only HGLOBAL medium types supported right now\n");
198 return DV_E_TYMED;
200 if (formatEtc->dwAspect != DVASPECT_CONTENT)
202 FIXME("only the content aspect is supported right now\n");
203 return E_NOTIMPL;
206 params.handle = This->pasteboard;
207 params.format = formatEtc->cfFormat;
208 if (MACDRV_CALL(dnd_have_format, &params))
209 hr = S_OK;
211 TRACE(" -> 0x%lx\n", hr);
212 return hr;
216 static HRESULT WINAPI dddo_GetConicalFormatEtc(IDataObject* iface, FORMATETC* formatEtc,
217 FORMATETC* formatEtcOut)
219 DragDropDataObject *This = impl_from_IDataObject(iface);
221 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
222 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
223 debugstr_format(formatEtc->cfFormat));
225 *formatEtcOut = *formatEtc;
226 formatEtcOut->ptd = NULL;
227 return DATA_S_SAMEFORMATETC;
231 static HRESULT WINAPI dddo_SetData(IDataObject* iface, FORMATETC* formatEtc,
232 STGMEDIUM* medium, BOOL fRelease)
234 DragDropDataObject *This = impl_from_IDataObject(iface);
236 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s} medium %p fRelease %d\n",
237 This, formatEtc, formatEtc->tymed, formatEtc->dwAspect,
238 debugstr_format(formatEtc->cfFormat), medium, fRelease);
240 return E_NOTIMPL;
244 static HRESULT WINAPI dddo_EnumFormatEtc(IDataObject* iface, DWORD direction,
245 IEnumFORMATETC** enumFormatEtc)
247 DragDropDataObject *This = impl_from_IDataObject(iface);
248 struct dnd_get_formats_params params;
249 UINT count;
250 HRESULT hr;
252 TRACE("This %p direction %lu enumFormatEtc %p\n", This, direction, enumFormatEtc);
254 if (direction != DATADIR_GET)
256 WARN("only the get direction is implemented\n");
257 return E_NOTIMPL;
260 params.handle = This->pasteboard;
261 count = MACDRV_CALL(dnd_get_formats, &params);
262 if (count)
264 FORMATETC *formatEtcs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC));
265 if (formatEtcs)
267 INT i;
269 for (i = 0; i < count; i++)
271 formatEtcs[i].cfFormat = params.formats[i];
272 formatEtcs[i].ptd = NULL;
273 formatEtcs[i].dwAspect = DVASPECT_CONTENT;
274 formatEtcs[i].lindex = -1;
275 formatEtcs[i].tymed = TYMED_HGLOBAL;
278 hr = SHCreateStdEnumFmtEtc(count, formatEtcs, enumFormatEtc);
279 HeapFree(GetProcessHeap(), 0, formatEtcs);
281 else
282 hr = E_OUTOFMEMORY;
284 else
285 hr = SHCreateStdEnumFmtEtc(0, NULL, enumFormatEtc);
287 TRACE(" -> 0x%lx\n", hr);
288 return hr;
292 static HRESULT WINAPI dddo_DAdvise(IDataObject* iface, FORMATETC* formatEtc, DWORD advf,
293 IAdviseSink* pAdvSink, DWORD* pdwConnection)
295 FIXME("(%p, %p, %lu, %p, %p): stub\n", iface, formatEtc, advf,
296 pAdvSink, pdwConnection);
297 return OLE_E_ADVISENOTSUPPORTED;
301 static HRESULT WINAPI dddo_DUnadvise(IDataObject* iface, DWORD dwConnection)
303 FIXME("(%p, %lu): stub\n", iface, dwConnection);
304 return OLE_E_ADVISENOTSUPPORTED;
308 static HRESULT WINAPI dddo_EnumDAdvise(IDataObject* iface, IEnumSTATDATA** pEnumAdvise)
310 FIXME("(%p, %p): stub\n", iface, pEnumAdvise);
311 return OLE_E_ADVISENOTSUPPORTED;
315 static const IDataObjectVtbl dovt =
317 dddo_QueryInterface,
318 dddo_AddRef,
319 dddo_Release,
320 dddo_GetData,
321 dddo_GetDataHere,
322 dddo_QueryGetData,
323 dddo_GetConicalFormatEtc,
324 dddo_SetData,
325 dddo_EnumFormatEtc,
326 dddo_DAdvise,
327 dddo_DUnadvise,
328 dddo_EnumDAdvise
332 static IDataObject *create_data_object_for_pasteboard(UINT64 pasteboard)
334 DragDropDataObject *dddo;
336 dddo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dddo));
337 if (!dddo)
338 return NULL;
340 dddo->ref = 1;
341 dddo->IDataObject_iface.lpVtbl = &dovt;
342 dddo->pasteboard = pasteboard;
343 MACDRV_CALL(dnd_retain, &dddo->pasteboard);
345 return &dddo->IDataObject_iface;
349 /* Based on functions in dlls/ole32/ole2.c */
350 static HANDLE get_droptarget_local_handle(HWND hwnd)
352 static const WCHAR prop_marshalleddroptarget[] =
353 {'W','i','n','e','M','a','r','s','h','a','l','l','e','d','D','r','o','p','T','a','r','g','e','t',0};
354 HANDLE handle;
355 HANDLE local_handle = 0;
357 handle = GetPropW(hwnd, prop_marshalleddroptarget);
358 if (handle)
360 DWORD pid;
361 HANDLE process;
363 GetWindowThreadProcessId(hwnd, &pid);
364 process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
365 if (process)
367 DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
368 CloseHandle(process);
371 return local_handle;
375 static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
377 HRESULT hr = E_OUTOFMEMORY;
378 HGLOBAL hmem;
379 void *data;
380 MEMORY_BASIC_INFORMATION info;
382 data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
383 if(!data) return hr;
385 VirtualQuery(data, &info, sizeof(info));
386 TRACE("size %d\n", (int)info.RegionSize);
388 hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
389 if(hmem)
391 memcpy(GlobalLock(hmem), data, info.RegionSize);
392 GlobalUnlock(hmem);
393 hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
395 UnmapViewOfFile(data);
396 return hr;
400 static IDropTarget* get_droptarget_pointer(HWND hwnd)
402 IDropTarget *droptarget = NULL;
403 HANDLE map;
404 IStream *stream;
406 map = get_droptarget_local_handle(hwnd);
407 if(!map) return NULL;
409 if(SUCCEEDED(create_stream_from_map(map, &stream)))
411 CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
412 IStream_Release(stream);
414 CloseHandle(map);
415 return droptarget;
419 /**************************************************************************
420 * macdrv_dnd_query_drop
422 NTSTATUS WINAPI macdrv_dnd_query_drop(void *arg, ULONG size)
424 struct dnd_query_drop_params *params = arg;
425 IDropTarget *droptarget;
426 BOOL ret = FALSE;
427 POINT pt;
429 TRACE("win %x x,y %d,%d effect %x pasteboard %s\n", params->hwnd, params->x, params->y,
430 params->effect, wine_dbgstr_longlong(params->handle));
432 pt.x = params->x;
433 pt.y = params->y;
435 droptarget = get_droptarget_pointer(last_droptarget_hwnd);
436 if (droptarget)
438 HRESULT hr;
439 POINTL pointl;
440 DWORD effect = params->effect;
442 if (!active_data_object)
444 WARN("shouldn't happen: no active IDataObject\n");
445 active_data_object = create_data_object_for_pasteboard(params->handle);
448 pointl.x = pt.x;
449 pointl.y = pt.y;
450 TRACE("Drop hwnd %p droptarget %p pointl (%ld,%ld) effect 0x%08lx\n", last_droptarget_hwnd,
451 droptarget, pointl.x, pointl.y, effect);
452 hr = IDropTarget_Drop(droptarget, active_data_object, MK_LBUTTON, pointl, &effect);
453 if (SUCCEEDED(hr))
455 if (effect != DROPEFFECT_NONE)
457 TRACE("drop succeeded\n");
458 ret = TRUE;
460 else
461 TRACE("the application refused the drop\n");
463 else
464 WARN("drop failed, error 0x%08lx\n", hr);
465 IDropTarget_Release(droptarget);
467 else
469 HWND hwnd = WindowFromPoint(pt);
470 while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
471 hwnd = GetParent(hwnd);
472 if (hwnd)
474 HDROP hdrop = get_pasteboard_data(params->handle, CF_HDROP);
475 DROPFILES *dropfiles = GlobalLock(hdrop);
476 if (dropfiles)
478 RECT rect;
479 dropfiles->pt.x = pt.x;
480 dropfiles->pt.y = pt.y;
481 dropfiles->fNC = !(ScreenToClient(hwnd, &dropfiles->pt) &&
482 GetClientRect(hwnd, &rect) &&
483 PtInRect(&rect, dropfiles->pt));
485 TRACE("sending WM_DROPFILES: hwnd %p nc %d pt %s %s\n", hwnd,
486 dropfiles->fNC, wine_dbgstr_point(&dropfiles->pt),
487 debugstr_w((WCHAR*)((char*)dropfiles + dropfiles->pFiles)));
489 GlobalUnlock(hdrop);
491 ret = PostMessageW(hwnd, WM_DROPFILES, (WPARAM)hdrop, 0L);
492 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
495 if (!ret) GlobalFree(hdrop);
499 if (active_data_object) IDataObject_Release(active_data_object);
500 active_data_object = NULL;
501 last_droptarget_hwnd = NULL;
502 return ret;
506 /**************************************************************************
507 * macdrv_dnd_query_exited
509 NTSTATUS WINAPI macdrv_dnd_query_exited(void *arg, ULONG size)
511 struct dnd_query_exited_params *params = arg;
512 HWND hwnd = UlongToHandle(params->hwnd);
513 IDropTarget *droptarget;
515 TRACE("win %p\n", hwnd);
517 droptarget = get_droptarget_pointer(last_droptarget_hwnd);
518 if (droptarget)
520 HRESULT hr;
522 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, droptarget);
523 hr = IDropTarget_DragLeave(droptarget);
524 if (FAILED(hr))
525 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr);
526 IDropTarget_Release(droptarget);
529 if (active_data_object) IDataObject_Release(active_data_object);
530 active_data_object = NULL;
531 last_droptarget_hwnd = NULL;
533 return TRUE;
537 /**************************************************************************
538 * query_drag_operation
540 NTSTATUS WINAPI macdrv_dnd_query_drag(void *arg, ULONG size)
542 struct dnd_query_drag_params *params = arg;
543 HWND hwnd = UlongToHandle(params->hwnd);
544 BOOL ret = FALSE;
545 POINT pt;
546 DWORD effect;
547 IDropTarget *droptarget;
548 HRESULT hr;
550 TRACE("win %p x,y %d,%d effect %x pasteboard %s\n", hwnd, params->x, params->y,
551 params->effect, wine_dbgstr_longlong(params->handle));
553 pt.x = params->x;
554 pt.y = params->y;
555 effect = params->effect;
557 /* Instead of the top-level window we got in the query, start with the deepest
558 child under the cursor. Travel up the hierarchy looking for a window that
559 has an associated IDropTarget. */
560 hwnd = WindowFromPoint(pt);
563 droptarget = get_droptarget_pointer(hwnd);
564 } while (!droptarget && (hwnd = GetParent(hwnd)));
566 if (last_droptarget_hwnd != hwnd)
568 if (last_droptarget_hwnd)
570 IDropTarget *old_droptarget = get_droptarget_pointer(last_droptarget_hwnd);
571 if (old_droptarget)
573 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, old_droptarget);
574 hr = IDropTarget_DragLeave(old_droptarget);
575 if (FAILED(hr))
576 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr);
577 IDropTarget_Release(old_droptarget);
581 last_droptarget_hwnd = hwnd;
583 if (droptarget)
585 POINTL pointl = { pt.x, pt.y };
587 if (!active_data_object)
588 active_data_object = create_data_object_for_pasteboard(params->handle);
590 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd, droptarget);
591 hr = IDropTarget_DragEnter(droptarget, active_data_object, MK_LBUTTON,
592 pointl, &effect);
593 if (SUCCEEDED(hr))
595 TRACE(" effect %ld\n", effect);
596 ret = TRUE;
598 else
599 WARN("IDropTarget_DragEnter failed, error 0x%08lx\n", hr);
600 IDropTarget_Release(droptarget);
603 else if (droptarget)
605 POINTL pointl = { pt.x, pt.y };
607 TRACE("DragOver hwnd %p droptarget %p\n", hwnd, droptarget);
608 hr = IDropTarget_DragOver(droptarget, MK_LBUTTON, pointl, &effect);
609 if (SUCCEEDED(hr))
611 TRACE(" effect %ld\n", effect);
612 ret = TRUE;
614 else
615 WARN("IDropTarget_DragOver failed, error 0x%08lx\n", hr);
616 IDropTarget_Release(droptarget);
619 if (!ret)
621 hwnd = WindowFromPoint(pt);
622 while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
623 hwnd = GetParent(hwnd);
624 if (hwnd)
626 FORMATETC formatEtc;
628 if (!active_data_object)
629 active_data_object = create_data_object_for_pasteboard(params->handle);
631 formatEtc.cfFormat = CF_HDROP;
632 formatEtc.ptd = NULL;
633 formatEtc.dwAspect = DVASPECT_CONTENT;
634 formatEtc.lindex = -1;
635 formatEtc.tymed = TYMED_HGLOBAL;
636 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object, &formatEtc)))
638 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd);
639 effect = DROPEFFECT_COPY | DROPEFFECT_LINK;
640 ret = TRUE;
645 TRACE(" -> %s\n", ret ? "TRUE" : "FALSE");
646 return ret ? effect : 0;