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
);
188 UINT
*formats
, count
;
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_get_pasteboard_formats(This
->pasteboard
, &count
);
202 FORMATETC
*formatEtcs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
207 for (i
= 0; i
< count
; i
++)
209 formatEtcs
[i
].cfFormat
= formats
[i
];
210 formatEtcs
[i
].ptd
= NULL
;
211 formatEtcs
[i
].dwAspect
= DVASPECT_CONTENT
;
212 formatEtcs
[i
].lindex
= -1;
213 formatEtcs
[i
].tymed
= TYMED_HGLOBAL
;
216 hr
= SHCreateStdEnumFmtEtc(count
, formatEtcs
, enumFormatEtc
);
217 HeapFree(GetProcessHeap(), 0, formatEtcs
);
222 HeapFree(GetProcessHeap(), 0, formats
);
225 hr
= SHCreateStdEnumFmtEtc(0, NULL
, enumFormatEtc
);
227 TRACE(" -> 0x%x\n", hr
);
232 static HRESULT WINAPI
dddo_DAdvise(IDataObject
* iface
, FORMATETC
* formatEtc
, DWORD advf
,
233 IAdviseSink
* pAdvSink
, DWORD
* pdwConnection
)
235 FIXME("(%p, %p, %u, %p, %p): stub\n", iface
, formatEtc
, advf
,
236 pAdvSink
, pdwConnection
);
237 return OLE_E_ADVISENOTSUPPORTED
;
241 static HRESULT WINAPI
dddo_DUnadvise(IDataObject
* iface
, DWORD dwConnection
)
243 FIXME("(%p, %u): stub\n", iface
, dwConnection
);
244 return OLE_E_ADVISENOTSUPPORTED
;
248 static HRESULT WINAPI
dddo_EnumDAdvise(IDataObject
* iface
, IEnumSTATDATA
** pEnumAdvise
)
250 FIXME("(%p, %p): stub\n", iface
, pEnumAdvise
);
251 return OLE_E_ADVISENOTSUPPORTED
;
255 static const IDataObjectVtbl dovt
=
263 dddo_GetConicalFormatEtc
,
272 static IDataObject
*create_data_object_for_pasteboard(CFTypeRef pasteboard
)
274 DragDropDataObject
*dddo
;
276 dddo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dddo
));
281 dddo
->IDataObject_iface
.lpVtbl
= &dovt
;
282 dddo
->pasteboard
= CFRetain(pasteboard
);
284 return &dddo
->IDataObject_iface
;
288 /**************************************************************************
289 * drag_operations_to_dropeffects
291 static DWORD
drag_operations_to_dropeffects(uint32_t ops
)
293 DWORD effects
= DROPEFFECT_NONE
;
294 if (ops
& (DRAG_OP_COPY
| DRAG_OP_GENERIC
))
295 effects
|= DROPEFFECT_COPY
;
296 if (ops
& DRAG_OP_MOVE
)
297 effects
|= DROPEFFECT_MOVE
;
298 if (ops
& (DRAG_OP_LINK
| DRAG_OP_GENERIC
))
299 effects
|= DROPEFFECT_LINK
;
304 /**************************************************************************
305 * dropeffect_to_drag_operation
307 static uint32_t dropeffect_to_drag_operation(DWORD effect
, uint32_t ops
)
309 if (effect
& DROPEFFECT_LINK
&& ops
& DRAG_OP_LINK
) return DRAG_OP_LINK
;
310 if (effect
& DROPEFFECT_COPY
&& ops
& DRAG_OP_COPY
) return DRAG_OP_COPY
;
311 if (effect
& DROPEFFECT_MOVE
&& ops
& DRAG_OP_MOVE
) return DRAG_OP_MOVE
;
312 if (effect
& DROPEFFECT_LINK
&& ops
& DRAG_OP_GENERIC
) return DRAG_OP_GENERIC
;
313 if (effect
& DROPEFFECT_COPY
&& ops
& DRAG_OP_GENERIC
) return DRAG_OP_GENERIC
;
319 /* Based on functions in dlls/ole32/ole2.c */
320 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
322 static const WCHAR prop_marshalleddroptarget
[] =
323 {'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};
325 HANDLE local_handle
= 0;
327 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
333 GetWindowThreadProcessId(hwnd
, &pid
);
334 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
337 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
338 CloseHandle(process
);
345 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
347 HRESULT hr
= E_OUTOFMEMORY
;
350 MEMORY_BASIC_INFORMATION info
;
352 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
355 VirtualQuery(data
, &info
, sizeof(info
));
356 TRACE("size %d\n", (int)info
.RegionSize
);
358 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
361 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
363 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
365 UnmapViewOfFile(data
);
370 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
372 IDropTarget
*droptarget
= NULL
;
376 map
= get_droptarget_local_handle(hwnd
);
377 if(!map
) return NULL
;
379 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
381 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
382 IStream_Release(stream
);
389 /**************************************************************************
392 BOOL
query_drag_drop(macdrv_query
* query
)
395 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
396 struct macdrv_win_data
*data
= get_win_data(hwnd
);
398 IDropTarget
*droptarget
;
400 TRACE("win %p/%p x,y %d,%d op 0x%08x pasteboard %p\n", hwnd
, query
->window
,
401 query
->drag_drop
.x
, query
->drag_drop
.y
, query
->drag_drop
.op
, query
->drag_drop
.pasteboard
);
405 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
409 pt
.x
= query
->drag_drop
.x
+ data
->whole_rect
.left
;
410 pt
.y
= query
->drag_drop
.y
+ data
->whole_rect
.top
;
411 release_win_data(data
);
413 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
418 DWORD effect
= drag_operations_to_dropeffects(query
->drag_drop
.op
);
420 if (!active_data_object
)
422 WARN("shouldn't happen: no active IDataObject\n");
423 active_data_object
= create_data_object_for_pasteboard(query
->drag_drop
.pasteboard
);
428 TRACE("Drop hwnd %p droptarget %p pointl (%d,%d) effect 0x%08x\n", last_droptarget_hwnd
,
429 droptarget
, pointl
.x
, pointl
.y
, effect
);
430 hr
= IDropTarget_Drop(droptarget
, active_data_object
, MK_LBUTTON
, pointl
, &effect
);
433 if (effect
!= DROPEFFECT_NONE
)
435 TRACE("drop succeeded\n");
439 TRACE("the application refused the drop\n");
442 WARN("drop failed, error 0x%08X\n", hr
);
443 IDropTarget_Release(droptarget
);
447 hwnd
= WindowFromPoint(pt
);
448 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
449 hwnd
= GetParent(hwnd
);
452 HDROP hdrop
= macdrv_get_pasteboard_data(query
->drag_drop
.pasteboard
, CF_HDROP
);
453 DROPFILES
*dropfiles
= GlobalLock(hdrop
);
456 dropfiles
->pt
.x
= pt
.x
;
457 dropfiles
->pt
.y
= pt
.y
;
458 dropfiles
->fNC
= TRUE
;
460 TRACE("sending WM_DROPFILES: hwnd %p pt %s %s\n", hwnd
, wine_dbgstr_point(&pt
),
461 debugstr_w((WCHAR
*)((char*)dropfiles
+ dropfiles
->pFiles
)));
465 ret
= PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)hdrop
, 0L);
466 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
469 if (!ret
) GlobalFree(hdrop
);
473 if (active_data_object
) IDataObject_Release(active_data_object
);
474 active_data_object
= NULL
;
475 last_droptarget_hwnd
= NULL
;
480 /**************************************************************************
483 BOOL
query_drag_exited(macdrv_query
* query
)
485 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
486 IDropTarget
*droptarget
;
488 TRACE("win %p/%p\n", hwnd
, query
->window
);
490 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
495 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, droptarget
);
496 hr
= IDropTarget_DragLeave(droptarget
);
498 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
499 IDropTarget_Release(droptarget
);
502 if (active_data_object
) IDataObject_Release(active_data_object
);
503 active_data_object
= NULL
;
504 last_droptarget_hwnd
= NULL
;
510 /**************************************************************************
511 * query_drag_operation
513 BOOL
query_drag_operation(macdrv_query
* query
)
516 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
517 struct macdrv_win_data
*data
= get_win_data(hwnd
);
520 IDropTarget
*droptarget
;
523 TRACE("win %p/%p x,y %d,%d offered_ops 0x%x pasteboard %p\n", hwnd
, query
->window
,
524 query
->drag_operation
.x
, query
->drag_operation
.y
, query
->drag_operation
.offered_ops
,
525 query
->drag_operation
.pasteboard
);
529 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
533 pt
.x
= query
->drag_operation
.x
+ data
->whole_rect
.left
;
534 pt
.y
= query
->drag_operation
.y
+ data
->whole_rect
.top
;
535 release_win_data(data
);
537 effect
= drag_operations_to_dropeffects(query
->drag_operation
.offered_ops
);
539 /* Instead of the top-level window we got in the query, start with the deepest
540 child under the cursor. Travel up the hierarchy looking for a window that
541 has an associated IDropTarget. */
542 hwnd
= WindowFromPoint(pt
);
545 droptarget
= get_droptarget_pointer(hwnd
);
546 } while (!droptarget
&& (hwnd
= GetParent(hwnd
)));
548 if (last_droptarget_hwnd
!= hwnd
)
550 if (last_droptarget_hwnd
)
552 IDropTarget
*old_droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
555 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, old_droptarget
);
556 hr
= IDropTarget_DragLeave(old_droptarget
);
558 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
559 IDropTarget_Release(old_droptarget
);
563 last_droptarget_hwnd
= hwnd
;
567 POINTL pointl
= { pt
.x
, pt
.y
};
569 if (!active_data_object
)
570 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
572 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd
, droptarget
);
573 hr
= IDropTarget_DragEnter(droptarget
, active_data_object
, MK_LBUTTON
,
577 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
578 query
->drag_operation
.offered_ops
);
579 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
583 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr
);
584 IDropTarget_Release(droptarget
);
589 POINTL pointl
= { pt
.x
, pt
.y
};
591 TRACE("DragOver hwnd %p droptarget %p\n", hwnd
, droptarget
);
592 hr
= IDropTarget_DragOver(droptarget
, MK_LBUTTON
, pointl
, &effect
);
595 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
596 query
->drag_operation
.offered_ops
);
597 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
601 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr
);
602 IDropTarget_Release(droptarget
);
607 hwnd
= WindowFromPoint(pt
);
608 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
609 hwnd
= GetParent(hwnd
);
614 if (!active_data_object
)
615 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
617 formatEtc
.cfFormat
= CF_HDROP
;
618 formatEtc
.ptd
= NULL
;
619 formatEtc
.dwAspect
= DVASPECT_CONTENT
;
620 formatEtc
.lindex
= -1;
621 formatEtc
.tymed
= TYMED_HGLOBAL
;
622 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object
, &formatEtc
)))
624 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd
);
625 query
->drag_operation
.accepted_op
= DRAG_OP_GENERIC
;
631 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");