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
24 #define WIN32_NO_STATUS
25 #include "macdrv_dll.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dragdrop
);
36 static IDataObject
*active_data_object
;
37 static HWND last_droptarget_hwnd
;
42 IDataObject IDataObject_iface
;
48 /**************************************************************************
51 static const char *debugstr_format(UINT id
)
55 if (GetClipboardFormatNameW(id
, buffer
, 256))
56 return wine_dbg_sprintf("0x%04x %s", id
, debugstr_w(buffer
));
60 #define BUILTIN(id) case id: return #id;
63 BUILTIN(CF_METAFILEPICT
)
73 BUILTIN(CF_UNICODETEXT
)
74 BUILTIN(CF_ENHMETAFILE
)
78 BUILTIN(CF_OWNERDISPLAY
)
81 BUILTIN(CF_DSPMETAFILEPICT
)
82 BUILTIN(CF_DSPENHMETAFILE
)
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 };
102 if (!(handle
= GlobalAlloc(GMEM_FIXED
, params
.size
))) return 0;
103 params
.data
= GlobalLock(handle
);
104 status
= MACDRV_CALL(dnd_get_data
, ¶ms
);
105 GlobalUnlock(handle
);
106 if (!status
) return GlobalReAlloc(handle
, params
.size
, GMEM_MOVEABLE
);
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
)))
121 IDataObject_AddRef(iface
);
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);
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);
150 TRACE("-- destroying DragDropDataObject (%p)\n", This
);
151 MACDRV_CALL(dnd_release
, &This
->pasteboard
);
152 HeapFree(GetProcessHeap(), 0, This
);
157 static HRESULT WINAPI
dddo_GetData(IDataObject
* iface
, FORMATETC
* formatEtc
, STGMEDIUM
* medium
)
159 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
162 TRACE("This %p formatEtc %s\n", This
, debugstr_format(formatEtc
->cfFormat
));
164 hr
= IDataObject_QueryGetData(iface
, formatEtc
);
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
;
177 static HRESULT WINAPI
dddo_GetDataHere(IDataObject
* iface
, FORMATETC
* formatEtc
,
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");
200 if (formatEtc
->dwAspect
!= DVASPECT_CONTENT
)
202 FIXME("only the content aspect is supported right now\n");
206 params
.handle
= This
->pasteboard
;
207 params
.format
= formatEtc
->cfFormat
;
208 if (MACDRV_CALL(dnd_have_format
, ¶ms
))
211 TRACE(" -> 0x%lx\n", 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
);
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
;
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");
260 params
.handle
= This
->pasteboard
;
261 count
= MACDRV_CALL(dnd_get_formats
, ¶ms
);
264 FORMATETC
*formatEtcs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
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
);
285 hr
= SHCreateStdEnumFmtEtc(0, NULL
, enumFormatEtc
);
287 TRACE(" -> 0x%lx\n", 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
=
323 dddo_GetConicalFormatEtc
,
332 static IDataObject
*create_data_object_for_pasteboard(UINT64 pasteboard
)
334 DragDropDataObject
*dddo
;
336 dddo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dddo
));
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};
355 HANDLE local_handle
= 0;
357 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
363 GetWindowThreadProcessId(hwnd
, &pid
);
364 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
367 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
368 CloseHandle(process
);
375 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
377 HRESULT hr
= E_OUTOFMEMORY
;
380 MEMORY_BASIC_INFORMATION info
;
382 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
385 VirtualQuery(data
, &info
, sizeof(info
));
386 TRACE("size %d\n", (int)info
.RegionSize
);
388 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
391 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
393 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
395 UnmapViewOfFile(data
);
400 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
402 IDropTarget
*droptarget
= NULL
;
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
);
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
;
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
));
435 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
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
);
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
);
455 if (effect
!= DROPEFFECT_NONE
)
457 TRACE("drop succeeded\n");
461 TRACE("the application refused the drop\n");
464 WARN("drop failed, error 0x%08lx\n", hr
);
465 IDropTarget_Release(droptarget
);
469 HWND hwnd
= WindowFromPoint(pt
);
470 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
471 hwnd
= GetParent(hwnd
);
474 HDROP hdrop
= get_pasteboard_data(params
->handle
, CF_HDROP
);
475 DROPFILES
*dropfiles
= GlobalLock(hdrop
);
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
)));
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
;
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
);
522 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, droptarget
);
523 hr
= IDropTarget_DragLeave(droptarget
);
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
;
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
);
547 IDropTarget
*droptarget
;
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
));
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
);
573 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, old_droptarget
);
574 hr
= IDropTarget_DragLeave(old_droptarget
);
576 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
577 IDropTarget_Release(old_droptarget
);
581 last_droptarget_hwnd
= hwnd
;
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
,
595 TRACE(" effect %ld\n", effect
);
599 WARN("IDropTarget_DragEnter failed, error 0x%08lx\n", hr
);
600 IDropTarget_Release(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
);
611 TRACE(" effect %ld\n", effect
);
615 WARN("IDropTarget_DragOver failed, error 0x%08lx\n", hr
);
616 IDropTarget_Release(droptarget
);
621 hwnd
= WindowFromPoint(pt
);
622 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
623 hwnd
= GetParent(hwnd
);
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
;
645 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");
646 return ret
? effect
: 0;