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
25 #define NONAMELESSUNION
34 WINE_DEFAULT_DEBUG_CHANNEL(dragdrop
);
37 static IDataObject
*active_data_object
;
38 static HWND last_droptarget_hwnd
;
43 IDataObject IDataObject_iface
;
49 static inline DragDropDataObject
*impl_from_IDataObject(IDataObject
*iface
)
51 return CONTAINING_RECORD(iface
, DragDropDataObject
, IDataObject_iface
);
55 static HRESULT WINAPI
dddo_QueryInterface(IDataObject
* iface
, REFIID riid
, LPVOID
*ppvObj
)
57 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
59 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppvObj
);
61 if (IsEqualIID(riid
, &IID_IUnknown
) || (IsEqualIID(riid
, &IID_IDataObject
)))
64 IUnknown_AddRef((IUnknown
*)This
);
73 static ULONG WINAPI
dddo_AddRef(IDataObject
* iface
)
75 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
76 ULONG refCount
= InterlockedIncrement(&This
->ref
);
78 TRACE("(%p)->(count=%u)\n", This
, refCount
- 1);
84 static ULONG WINAPI
dddo_Release(IDataObject
* iface
)
86 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
87 ULONG refCount
= InterlockedDecrement(&This
->ref
);
89 TRACE("(%p)->(count=%u)\n", This
, refCount
+ 1);
93 TRACE("-- destroying DragDropDataObject (%p)\n", This
);
94 CFRelease(This
->pasteboard
);
95 HeapFree(GetProcessHeap(), 0, This
);
100 static HRESULT WINAPI
dddo_GetData(IDataObject
* iface
, FORMATETC
* formatEtc
, STGMEDIUM
* medium
)
102 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
105 TRACE("This %p formatEtc %s\n", This
, debugstr_format(formatEtc
->cfFormat
));
107 hr
= IDataObject_QueryGetData(iface
, formatEtc
);
110 medium
->tymed
= TYMED_HGLOBAL
;
111 medium
->u
.hGlobal
= macdrv_get_pasteboard_data(This
->pasteboard
, formatEtc
->cfFormat
);
112 medium
->pUnkForRelease
= NULL
;
113 hr
= medium
->u
.hGlobal
? S_OK
: E_OUTOFMEMORY
;
120 static HRESULT WINAPI
dddo_GetDataHere(IDataObject
* iface
, FORMATETC
* formatEtc
,
123 FIXME("iface %p formatEtc %p medium %p; stub\n", iface
, formatEtc
, medium
);
124 return DATA_E_FORMATETC
;
128 static HRESULT WINAPI
dddo_QueryGetData(IDataObject
* iface
, FORMATETC
* formatEtc
)
130 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
131 HRESULT hr
= DV_E_FORMATETC
;
133 TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s}\n",
134 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
135 debugstr_format(formatEtc
->cfFormat
));
137 if (formatEtc
->tymed
&& !(formatEtc
->tymed
& TYMED_HGLOBAL
))
139 FIXME("only HGLOBAL medium types supported right now\n");
142 if (formatEtc
->dwAspect
!= DVASPECT_CONTENT
)
144 FIXME("only the content aspect is supported right now\n");
148 if (macdrv_pasteboard_has_format(This
->pasteboard
, formatEtc
->cfFormat
))
151 TRACE(" -> 0x%x\n", hr
);
156 static HRESULT WINAPI
dddo_GetConicalFormatEtc(IDataObject
* iface
, FORMATETC
* formatEtc
,
157 FORMATETC
* formatEtcOut
)
159 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
161 TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s}\n",
162 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
163 debugstr_format(formatEtc
->cfFormat
));
165 *formatEtcOut
= *formatEtc
;
166 formatEtcOut
->ptd
= NULL
;
167 return DATA_S_SAMEFORMATETC
;
171 static HRESULT WINAPI
dddo_SetData(IDataObject
* iface
, FORMATETC
* formatEtc
,
172 STGMEDIUM
* medium
, BOOL fRelease
)
174 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
176 TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s} medium %p fRelease %d\n",
177 This
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
,
178 debugstr_format(formatEtc
->cfFormat
), medium
, fRelease
);
184 static HRESULT WINAPI
dddo_EnumFormatEtc(IDataObject
* iface
, DWORD direction
,
185 IEnumFORMATETC
** enumFormatEtc
)
187 DragDropDataObject
*This
= impl_from_IDataObject(iface
);
191 TRACE("This %p direction %u enumFormatEtc %p\n", This
, direction
, enumFormatEtc
);
193 if (direction
!= DATADIR_GET
)
195 WARN("only the get direction is implemented\n");
199 formats
= macdrv_copy_pasteboard_formats(This
->pasteboard
);
202 INT count
= CFArrayGetCount(formats
);
203 FORMATETC
*formatEtcs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
208 for (i
= 0; i
< count
; i
++)
210 formatEtcs
[i
].cfFormat
= (UINT
)CFArrayGetValueAtIndex(formats
, i
);
211 formatEtcs
[i
].ptd
= NULL
;
212 formatEtcs
[i
].dwAspect
= DVASPECT_CONTENT
;
213 formatEtcs
[i
].lindex
= -1;
214 formatEtcs
[i
].tymed
= TYMED_HGLOBAL
;
217 hr
= SHCreateStdEnumFmtEtc(count
, formatEtcs
, enumFormatEtc
);
218 HeapFree(GetProcessHeap(), 0, formatEtcs
);
226 hr
= SHCreateStdEnumFmtEtc(0, NULL
, enumFormatEtc
);
228 TRACE(" -> 0x%x\n", hr
);
233 static HRESULT WINAPI
dddo_DAdvise(IDataObject
* iface
, FORMATETC
* formatEtc
, DWORD advf
,
234 IAdviseSink
* pAdvSink
, DWORD
* pdwConnection
)
236 FIXME("(%p, %p, %u, %p, %p): stub\n", iface
, formatEtc
, advf
,
237 pAdvSink
, pdwConnection
);
238 return OLE_E_ADVISENOTSUPPORTED
;
242 static HRESULT WINAPI
dddo_DUnadvise(IDataObject
* iface
, DWORD dwConnection
)
244 FIXME("(%p, %u): stub\n", iface
, dwConnection
);
245 return OLE_E_ADVISENOTSUPPORTED
;
249 static HRESULT WINAPI
dddo_EnumDAdvise(IDataObject
* iface
, IEnumSTATDATA
** pEnumAdvise
)
251 FIXME("(%p, %p): stub\n", iface
, pEnumAdvise
);
252 return OLE_E_ADVISENOTSUPPORTED
;
256 static const IDataObjectVtbl dovt
=
264 dddo_GetConicalFormatEtc
,
273 static IDataObject
*create_data_object_for_pasteboard(CFTypeRef pasteboard
)
275 DragDropDataObject
*dddo
;
277 dddo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dddo
));
282 dddo
->IDataObject_iface
.lpVtbl
= &dovt
;
283 dddo
->pasteboard
= CFRetain(pasteboard
);
285 return &dddo
->IDataObject_iface
;
289 /**************************************************************************
290 * drag_operations_to_dropeffects
292 static DWORD
drag_operations_to_dropeffects(uint32_t ops
)
294 DWORD effects
= DROPEFFECT_NONE
;
295 if (ops
& (DRAG_OP_COPY
| DRAG_OP_GENERIC
))
296 effects
|= DROPEFFECT_COPY
;
297 if (ops
& DRAG_OP_MOVE
)
298 effects
|= DROPEFFECT_MOVE
;
299 if (ops
& (DRAG_OP_LINK
| DRAG_OP_GENERIC
))
300 effects
|= DROPEFFECT_LINK
;
305 /**************************************************************************
306 * dropeffect_to_drag_operation
308 static uint32_t dropeffect_to_drag_operation(DWORD effect
, uint32_t ops
)
312 case DROPEFFECT_COPY
: return (ops
& DRAG_OP_COPY
) ? DRAG_OP_COPY
: DRAG_OP_GENERIC
;
313 case DROPEFFECT_MOVE
: return DRAG_OP_MOVE
;
314 case DROPEFFECT_LINK
: return (ops
& DRAG_OP_LINK
) ? DRAG_OP_LINK
: DRAG_OP_GENERIC
;
321 /* Based on functions in dlls/ole32/ole2.c */
322 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
324 static const WCHAR prop_marshalleddroptarget
[] =
325 {'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};
327 HANDLE local_handle
= 0;
329 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
335 GetWindowThreadProcessId(hwnd
, &pid
);
336 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
339 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
340 CloseHandle(process
);
347 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
349 HRESULT hr
= E_OUTOFMEMORY
;
352 MEMORY_BASIC_INFORMATION info
;
354 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
357 VirtualQuery(data
, &info
, sizeof(info
));
358 TRACE("size %d\n", (int)info
.RegionSize
);
360 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
363 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
365 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
367 UnmapViewOfFile(data
);
372 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
374 IDropTarget
*droptarget
= NULL
;
378 map
= get_droptarget_local_handle(hwnd
);
379 if(!map
) return NULL
;
381 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
383 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
384 IStream_Release(stream
);
391 /**************************************************************************
394 BOOL
query_drag_drop(macdrv_query
* query
)
397 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
398 struct macdrv_win_data
*data
= get_win_data(hwnd
);
400 IDropTarget
*droptarget
;
402 TRACE("win %p/%p x,y %d,%d op 0x%08x pasteboard %p\n", hwnd
, query
->window
,
403 query
->drag_drop
.x
, query
->drag_drop
.y
, query
->drag_drop
.op
, query
->drag_drop
.pasteboard
);
407 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
411 pt
.x
= query
->drag_drop
.x
+ data
->whole_rect
.left
;
412 pt
.y
= query
->drag_drop
.y
+ data
->whole_rect
.top
;
413 release_win_data(data
);
415 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
420 DWORD effect
= drag_operations_to_dropeffects(query
->drag_drop
.op
);
422 if (!active_data_object
)
424 WARN("shouldn't happen: no active IDataObject\n");
425 active_data_object
= create_data_object_for_pasteboard(query
->drag_drop
.pasteboard
);
430 TRACE("Drop hwnd %p droptarget %p pointl (%d,%d) effect 0x%08x\n", last_droptarget_hwnd
,
431 droptarget
, pointl
.x
, pointl
.y
, effect
);
432 hr
= IDropTarget_Drop(droptarget
, active_data_object
, MK_LBUTTON
, pointl
, &effect
);
435 if (effect
!= DROPEFFECT_NONE
)
437 TRACE("drop succeeded\n");
441 TRACE("the application refused the drop\n");
444 WARN("drop failed, error 0x%08X\n", hr
);
445 IDropTarget_Release(droptarget
);
449 hwnd
= WindowFromPoint(pt
);
450 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
451 hwnd
= GetParent(hwnd
);
454 HDROP hdrop
= macdrv_get_pasteboard_data(query
->drag_drop
.pasteboard
, CF_HDROP
);
455 DROPFILES
*dropfiles
= GlobalLock(hdrop
);
458 dropfiles
->pt
.x
= pt
.x
;
459 dropfiles
->pt
.y
= pt
.y
;
460 dropfiles
->fNC
= TRUE
;
462 TRACE("sending WM_DROPFILES: hwnd %p pt %s %s\n", hwnd
, wine_dbgstr_point(&pt
),
463 debugstr_w((WCHAR
*)((char*)dropfiles
+ dropfiles
->pFiles
)));
467 ret
= PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)hdrop
, 0L);
468 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
475 if (active_data_object
) IDataObject_Release(active_data_object
);
476 active_data_object
= NULL
;
477 last_droptarget_hwnd
= NULL
;
482 /**************************************************************************
485 BOOL
query_drag_exited(macdrv_query
* query
)
487 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
488 IDropTarget
*droptarget
;
490 TRACE("win %p/%p\n", hwnd
, query
->window
);
492 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
497 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, droptarget
);
498 hr
= IDropTarget_DragLeave(droptarget
);
500 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
501 IDropTarget_Release(droptarget
);
504 if (active_data_object
) IDataObject_Release(active_data_object
);
505 active_data_object
= NULL
;
506 last_droptarget_hwnd
= NULL
;
512 /**************************************************************************
513 * query_drag_operation
515 BOOL
query_drag_operation(macdrv_query
* query
)
518 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
519 struct macdrv_win_data
*data
= get_win_data(hwnd
);
522 IDropTarget
*droptarget
;
525 TRACE("win %p/%p x,y %d,%d offered_ops 0x%x pasteboard %p\n", hwnd
, query
->window
,
526 query
->drag_operation
.x
, query
->drag_operation
.y
, query
->drag_operation
.offered_ops
,
527 query
->drag_operation
.pasteboard
);
531 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
535 pt
.x
= query
->drag_operation
.x
+ data
->whole_rect
.left
;
536 pt
.y
= query
->drag_operation
.y
+ data
->whole_rect
.top
;
537 release_win_data(data
);
539 effect
= drag_operations_to_dropeffects(query
->drag_operation
.offered_ops
);
541 /* Instead of the top-level window we got in the query, start with the deepest
542 child under the cursor. Travel up the hierarchy looking for a window that
543 has an associated IDropTarget. */
544 hwnd
= WindowFromPoint(pt
);
547 droptarget
= get_droptarget_pointer(hwnd
);
548 } while (!droptarget
&& (hwnd
= GetParent(hwnd
)));
550 if (last_droptarget_hwnd
!= hwnd
)
552 if (last_droptarget_hwnd
)
554 IDropTarget
*old_droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
557 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, old_droptarget
);
558 hr
= IDropTarget_DragLeave(old_droptarget
);
560 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
561 IDropTarget_Release(old_droptarget
);
565 last_droptarget_hwnd
= hwnd
;
569 POINTL pointl
= { pt
.x
, pt
.y
};
571 if (!active_data_object
)
572 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
574 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd
, droptarget
);
575 hr
= IDropTarget_DragEnter(droptarget
, active_data_object
, MK_LBUTTON
,
579 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
580 query
->drag_operation
.offered_ops
);
581 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
585 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr
);
586 IDropTarget_Release(droptarget
);
591 POINTL pointl
= { pt
.x
, pt
.y
};
593 TRACE("DragOver hwnd %p droptarget %p\n", hwnd
, droptarget
);
594 hr
= IDropTarget_DragOver(droptarget
, MK_LBUTTON
, pointl
, &effect
);
597 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
598 query
->drag_operation
.offered_ops
);
599 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
603 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr
);
604 IDropTarget_Release(droptarget
);
609 hwnd
= WindowFromPoint(pt
);
610 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
611 hwnd
= GetParent(hwnd
);
616 if (!active_data_object
)
617 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
619 formatEtc
.cfFormat
= CF_HDROP
;
620 formatEtc
.ptd
= NULL
;
621 formatEtc
.dwAspect
= DVASPECT_CONTENT
;
622 formatEtc
.lindex
= -1;
623 formatEtc
.tymed
= TYMED_HGLOBAL
;
624 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object
, &formatEtc
)))
626 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd
);
627 query
->drag_operation
.accepted_op
= DRAG_OP_GENERIC
;
633 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");