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 #define NONAMELESSUNION
26 #define WIN32_NO_STATUS
27 #include "macdrv_dll.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dragdrop
);
38 static IDataObject
*active_data_object
;
39 static HWND last_droptarget_hwnd
;
44 IDataObject IDataObject_iface
;
50 /**************************************************************************
53 static const char *debugstr_format(UINT id
)
57 if (GetClipboardFormatNameW(id
, buffer
, 256))
58 return wine_dbg_sprintf("0x%04x %s", id
, debugstr_w(buffer
));
62 #define BUILTIN(id) case id: return #id;
65 BUILTIN(CF_METAFILEPICT
)
75 BUILTIN(CF_UNICODETEXT
)
76 BUILTIN(CF_ENHMETAFILE
)
80 BUILTIN(CF_OWNERDISPLAY
)
83 BUILTIN(CF_DSPMETAFILEPICT
)
84 BUILTIN(CF_DSPENHMETAFILE
)
86 default: return wine_dbg_sprintf("0x%04x", id
);
90 static inline DragDropDataObject
*impl_from_IDataObject(IDataObject
*iface
)
92 return CONTAINING_RECORD(iface
, DragDropDataObject
, IDataObject_iface
);
96 static HANDLE
get_pasteboard_data(UINT64 pasteboard
, UINT desired_format
)
98 struct dnd_get_data_params params
= { .handle
= pasteboard
, .format
= desired_format
, .size
= 2048 };
104 if (!(handle
= GlobalAlloc(GMEM_FIXED
, params
.size
))) return 0;
105 params
.data
= GlobalLock(handle
);
106 status
= MACDRV_CALL(dnd_get_data
, ¶ms
);
107 GlobalUnlock(handle
);
108 if (!status
) return GlobalReAlloc(handle
, params
.size
, GMEM_MOVEABLE
);
110 if (status
!= STATUS_BUFFER_OVERFLOW
) return 0;
114 static HRESULT WINAPI
dddo_QueryInterface(IDataObject
* iface
, REFIID riid
, LPVOID
*ppvObj
)
116 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
118 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppvObj
);
120 if (IsEqualIID(riid
, &IID_IUnknown
) || (IsEqualIID(riid
, &IID_IDataObject
)))
123 IDataObject_AddRef(iface
);
128 return E_NOINTERFACE
;
132 static ULONG WINAPI
dddo_AddRef(IDataObject
* iface
)
134 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
135 ULONG refCount
= InterlockedIncrement(&This
->ref
);
137 TRACE("(%p)->(count=%lu)\n", This
, refCount
- 1);
143 static ULONG WINAPI
dddo_Release(IDataObject
* iface
)
145 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
146 ULONG refCount
= InterlockedDecrement(&This
->ref
);
148 TRACE("(%p)->(count=%lu)\n", This
, refCount
+ 1);
152 TRACE("-- destroying DragDropDataObject (%p)\n", This
);
153 MACDRV_CALL(dnd_release
, &This
->pasteboard
);
154 HeapFree(GetProcessHeap(), 0, This
);
159 static HRESULT WINAPI
dddo_GetData(IDataObject
* iface
, FORMATETC
* formatEtc
, STGMEDIUM
* medium
)
161 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
164 TRACE("This %p formatEtc %s\n", This
, debugstr_format(formatEtc
->cfFormat
));
166 hr
= IDataObject_QueryGetData(iface
, formatEtc
);
169 medium
->tymed
= TYMED_HGLOBAL
;
170 medium
->u
.hGlobal
= get_pasteboard_data(This
->pasteboard
, formatEtc
->cfFormat
);
171 medium
->pUnkForRelease
= NULL
;
172 hr
= medium
->u
.hGlobal
? S_OK
: E_OUTOFMEMORY
;
179 static HRESULT WINAPI
dddo_GetDataHere(IDataObject
* iface
, FORMATETC
* formatEtc
,
182 FIXME("iface %p formatEtc %p medium %p; stub\n", iface
, formatEtc
, medium
);
183 return DATA_E_FORMATETC
;
187 static HRESULT WINAPI
dddo_QueryGetData(IDataObject
* iface
, FORMATETC
* formatEtc
)
189 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
190 struct dnd_have_format_params params
;
191 HRESULT hr
= DV_E_FORMATETC
;
193 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
194 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
195 debugstr_format(formatEtc
->cfFormat
));
197 if (formatEtc
->tymed
&& !(formatEtc
->tymed
& TYMED_HGLOBAL
))
199 FIXME("only HGLOBAL medium types supported right now\n");
202 if (formatEtc
->dwAspect
!= DVASPECT_CONTENT
)
204 FIXME("only the content aspect is supported right now\n");
208 params
.handle
= This
->pasteboard
;
209 params
.format
= formatEtc
->cfFormat
;
210 if (MACDRV_CALL(dnd_have_format
, ¶ms
))
213 TRACE(" -> 0x%lx\n", hr
);
218 static HRESULT WINAPI
dddo_GetConicalFormatEtc(IDataObject
* iface
, FORMATETC
* formatEtc
,
219 FORMATETC
* formatEtcOut
)
221 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
223 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s}\n",
224 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
225 debugstr_format(formatEtc
->cfFormat
));
227 *formatEtcOut
= *formatEtc
;
228 formatEtcOut
->ptd
= NULL
;
229 return DATA_S_SAMEFORMATETC
;
233 static HRESULT WINAPI
dddo_SetData(IDataObject
* iface
, FORMATETC
* formatEtc
,
234 STGMEDIUM
* medium
, BOOL fRelease
)
236 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
238 TRACE("This %p formatEtc %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%s} medium %p fRelease %d\n",
239 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
240 debugstr_format(formatEtc
->cfFormat
), medium
, fRelease
);
246 static HRESULT WINAPI
dddo_EnumFormatEtc(IDataObject
* iface
, DWORD direction
,
247 IEnumFORMATETC
** enumFormatEtc
)
249 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
250 struct dnd_get_formats_params params
;
254 TRACE("This %p direction %lu enumFormatEtc %p\n", This
, direction
, enumFormatEtc
);
256 if (direction
!= DATADIR_GET
)
258 WARN("only the get direction is implemented\n");
262 params
.handle
= This
->pasteboard
;
263 count
= MACDRV_CALL(dnd_get_formats
, ¶ms
);
266 FORMATETC
*formatEtcs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
271 for (i
= 0; i
< count
; i
++)
273 formatEtcs
[i
].cfFormat
= params
.formats
[i
];
274 formatEtcs
[i
].ptd
= NULL
;
275 formatEtcs
[i
].dwAspect
= DVASPECT_CONTENT
;
276 formatEtcs
[i
].lindex
= -1;
277 formatEtcs
[i
].tymed
= TYMED_HGLOBAL
;
280 hr
= SHCreateStdEnumFmtEtc(count
, formatEtcs
, enumFormatEtc
);
281 HeapFree(GetProcessHeap(), 0, formatEtcs
);
287 hr
= SHCreateStdEnumFmtEtc(0, NULL
, enumFormatEtc
);
289 TRACE(" -> 0x%lx\n", hr
);
294 static HRESULT WINAPI
dddo_DAdvise(IDataObject
* iface
, FORMATETC
* formatEtc
, DWORD advf
,
295 IAdviseSink
* pAdvSink
, DWORD
* pdwConnection
)
297 FIXME("(%p, %p, %lu, %p, %p): stub\n", iface
, formatEtc
, advf
,
298 pAdvSink
, pdwConnection
);
299 return OLE_E_ADVISENOTSUPPORTED
;
303 static HRESULT WINAPI
dddo_DUnadvise(IDataObject
* iface
, DWORD dwConnection
)
305 FIXME("(%p, %lu): stub\n", iface
, dwConnection
);
306 return OLE_E_ADVISENOTSUPPORTED
;
310 static HRESULT WINAPI
dddo_EnumDAdvise(IDataObject
* iface
, IEnumSTATDATA
** pEnumAdvise
)
312 FIXME("(%p, %p): stub\n", iface
, pEnumAdvise
);
313 return OLE_E_ADVISENOTSUPPORTED
;
317 static const IDataObjectVtbl dovt
=
325 dddo_GetConicalFormatEtc
,
334 static IDataObject
*create_data_object_for_pasteboard(UINT64 pasteboard
)
336 DragDropDataObject
*dddo
;
338 dddo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dddo
));
343 dddo
->IDataObject_iface
.lpVtbl
= &dovt
;
344 dddo
->pasteboard
= pasteboard
;
345 MACDRV_CALL(dnd_retain
, &dddo
->pasteboard
);
347 return &dddo
->IDataObject_iface
;
351 /* Based on functions in dlls/ole32/ole2.c */
352 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
354 static const WCHAR prop_marshalleddroptarget
[] =
355 {'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};
357 HANDLE local_handle
= 0;
359 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
365 GetWindowThreadProcessId(hwnd
, &pid
);
366 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
369 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
370 CloseHandle(process
);
377 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
379 HRESULT hr
= E_OUTOFMEMORY
;
382 MEMORY_BASIC_INFORMATION info
;
384 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
387 VirtualQuery(data
, &info
, sizeof(info
));
388 TRACE("size %d\n", (int)info
.RegionSize
);
390 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
393 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
395 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
397 UnmapViewOfFile(data
);
402 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
404 IDropTarget
*droptarget
= NULL
;
408 map
= get_droptarget_local_handle(hwnd
);
409 if(!map
) return NULL
;
411 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
413 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
414 IStream_Release(stream
);
421 /**************************************************************************
422 * macdrv_dnd_query_drop
424 NTSTATUS WINAPI
macdrv_dnd_query_drop(void *arg
, ULONG size
)
426 struct dnd_query_drop_params
*params
= arg
;
427 IDropTarget
*droptarget
;
431 TRACE("win %x x,y %d,%d effect %x pasteboard %s\n", params
->hwnd
, params
->x
, params
->y
,
432 params
->effect
, wine_dbgstr_longlong(params
->handle
));
437 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
442 DWORD effect
= params
->effect
;
444 if (!active_data_object
)
446 WARN("shouldn't happen: no active IDataObject\n");
447 active_data_object
= create_data_object_for_pasteboard(params
->handle
);
452 TRACE("Drop hwnd %p droptarget %p pointl (%ld,%ld) effect 0x%08lx\n", last_droptarget_hwnd
,
453 droptarget
, pointl
.x
, pointl
.y
, effect
);
454 hr
= IDropTarget_Drop(droptarget
, active_data_object
, MK_LBUTTON
, pointl
, &effect
);
457 if (effect
!= DROPEFFECT_NONE
)
459 TRACE("drop succeeded\n");
463 TRACE("the application refused the drop\n");
466 WARN("drop failed, error 0x%08lx\n", hr
);
467 IDropTarget_Release(droptarget
);
471 HWND hwnd
= WindowFromPoint(pt
);
472 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
473 hwnd
= GetParent(hwnd
);
476 HDROP hdrop
= get_pasteboard_data(params
->handle
, CF_HDROP
);
477 DROPFILES
*dropfiles
= GlobalLock(hdrop
);
481 dropfiles
->pt
.x
= pt
.x
;
482 dropfiles
->pt
.y
= pt
.y
;
483 dropfiles
->fNC
= !(ScreenToClient(hwnd
, &dropfiles
->pt
) &&
484 GetClientRect(hwnd
, &rect
) &&
485 PtInRect(&rect
, dropfiles
->pt
));
487 TRACE("sending WM_DROPFILES: hwnd %p nc %d pt %s %s\n", hwnd
,
488 dropfiles
->fNC
, wine_dbgstr_point(&dropfiles
->pt
),
489 debugstr_w((WCHAR
*)((char*)dropfiles
+ dropfiles
->pFiles
)));
493 ret
= PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)hdrop
, 0L);
494 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
497 if (!ret
) GlobalFree(hdrop
);
501 if (active_data_object
) IDataObject_Release(active_data_object
);
502 active_data_object
= NULL
;
503 last_droptarget_hwnd
= NULL
;
508 /**************************************************************************
509 * macdrv_dnd_query_exited
511 NTSTATUS WINAPI
macdrv_dnd_query_exited(void *arg
, ULONG size
)
513 struct dnd_query_exited_params
*params
= arg
;
514 HWND hwnd
= UlongToHandle(params
->hwnd
);
515 IDropTarget
*droptarget
;
517 TRACE("win %p\n", hwnd
);
519 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
524 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, droptarget
);
525 hr
= IDropTarget_DragLeave(droptarget
);
527 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
528 IDropTarget_Release(droptarget
);
531 if (active_data_object
) IDataObject_Release(active_data_object
);
532 active_data_object
= NULL
;
533 last_droptarget_hwnd
= NULL
;
539 /**************************************************************************
540 * query_drag_operation
542 NTSTATUS WINAPI
macdrv_dnd_query_drag(void *arg
, ULONG size
)
544 struct dnd_query_drag_params
*params
= arg
;
545 HWND hwnd
= UlongToHandle(params
->hwnd
);
549 IDropTarget
*droptarget
;
552 TRACE("win %p x,y %d,%d effect %x pasteboard %s\n", hwnd
, params
->x
, params
->y
,
553 params
->effect
, wine_dbgstr_longlong(params
->handle
));
557 effect
= params
->effect
;
559 /* Instead of the top-level window we got in the query, start with the deepest
560 child under the cursor. Travel up the hierarchy looking for a window that
561 has an associated IDropTarget. */
562 hwnd
= WindowFromPoint(pt
);
565 droptarget
= get_droptarget_pointer(hwnd
);
566 } while (!droptarget
&& (hwnd
= GetParent(hwnd
)));
568 if (last_droptarget_hwnd
!= hwnd
)
570 if (last_droptarget_hwnd
)
572 IDropTarget
*old_droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
575 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, old_droptarget
);
576 hr
= IDropTarget_DragLeave(old_droptarget
);
578 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
579 IDropTarget_Release(old_droptarget
);
583 last_droptarget_hwnd
= hwnd
;
587 POINTL pointl
= { pt
.x
, pt
.y
};
589 if (!active_data_object
)
590 active_data_object
= create_data_object_for_pasteboard(params
->handle
);
592 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd
, droptarget
);
593 hr
= IDropTarget_DragEnter(droptarget
, active_data_object
, MK_LBUTTON
,
597 TRACE(" effect %ld\n", effect
);
601 WARN("IDropTarget_DragEnter failed, error 0x%08lx\n", hr
);
602 IDropTarget_Release(droptarget
);
607 POINTL pointl
= { pt
.x
, pt
.y
};
609 TRACE("DragOver hwnd %p droptarget %p\n", hwnd
, droptarget
);
610 hr
= IDropTarget_DragOver(droptarget
, MK_LBUTTON
, pointl
, &effect
);
613 TRACE(" effect %ld\n", effect
);
617 WARN("IDropTarget_DragOver failed, error 0x%08lx\n", hr
);
618 IDropTarget_Release(droptarget
);
623 hwnd
= WindowFromPoint(pt
);
624 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
625 hwnd
= GetParent(hwnd
);
630 if (!active_data_object
)
631 active_data_object
= create_data_object_for_pasteboard(params
->handle
);
633 formatEtc
.cfFormat
= CF_HDROP
;
634 formatEtc
.ptd
= NULL
;
635 formatEtc
.dwAspect
= DVASPECT_CONTENT
;
636 formatEtc
.lindex
= -1;
637 formatEtc
.tymed
= TYMED_HGLOBAL
;
638 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object
, &formatEtc
)))
640 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd
);
641 effect
= DROPEFFECT_COPY
| DROPEFFECT_LINK
;
647 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");
648 return ret
? effect
: 0;