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
)
310 if (effect
& DROPEFFECT_LINK
&& ops
& DRAG_OP_LINK
) return DRAG_OP_LINK
;
311 if (effect
& DROPEFFECT_COPY
&& ops
& DRAG_OP_COPY
) return DRAG_OP_COPY
;
312 if (effect
& DROPEFFECT_MOVE
&& ops
& DRAG_OP_MOVE
) return DRAG_OP_MOVE
;
313 if (effect
& DROPEFFECT_LINK
&& ops
& DRAG_OP_GENERIC
) return DRAG_OP_GENERIC
;
314 if (effect
& DROPEFFECT_COPY
&& ops
& DRAG_OP_GENERIC
) return DRAG_OP_GENERIC
;
320 /* Based on functions in dlls/ole32/ole2.c */
321 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
323 static const WCHAR prop_marshalleddroptarget
[] =
324 {'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};
326 HANDLE local_handle
= 0;
328 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
334 GetWindowThreadProcessId(hwnd
, &pid
);
335 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
338 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
339 CloseHandle(process
);
346 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
348 HRESULT hr
= E_OUTOFMEMORY
;
351 MEMORY_BASIC_INFORMATION info
;
353 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
356 VirtualQuery(data
, &info
, sizeof(info
));
357 TRACE("size %d\n", (int)info
.RegionSize
);
359 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
362 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
364 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
366 UnmapViewOfFile(data
);
371 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
373 IDropTarget
*droptarget
= NULL
;
377 map
= get_droptarget_local_handle(hwnd
);
378 if(!map
) return NULL
;
380 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
382 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
383 IStream_Release(stream
);
390 /**************************************************************************
393 BOOL
query_drag_drop(macdrv_query
* query
)
396 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
397 struct macdrv_win_data
*data
= get_win_data(hwnd
);
399 IDropTarget
*droptarget
;
401 TRACE("win %p/%p x,y %d,%d op 0x%08x pasteboard %p\n", hwnd
, query
->window
,
402 query
->drag_drop
.x
, query
->drag_drop
.y
, query
->drag_drop
.op
, query
->drag_drop
.pasteboard
);
406 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
410 pt
.x
= query
->drag_drop
.x
+ data
->whole_rect
.left
;
411 pt
.y
= query
->drag_drop
.y
+ data
->whole_rect
.top
;
412 release_win_data(data
);
414 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
419 DWORD effect
= drag_operations_to_dropeffects(query
->drag_drop
.op
);
421 if (!active_data_object
)
423 WARN("shouldn't happen: no active IDataObject\n");
424 active_data_object
= create_data_object_for_pasteboard(query
->drag_drop
.pasteboard
);
429 TRACE("Drop hwnd %p droptarget %p pointl (%d,%d) effect 0x%08x\n", last_droptarget_hwnd
,
430 droptarget
, pointl
.x
, pointl
.y
, effect
);
431 hr
= IDropTarget_Drop(droptarget
, active_data_object
, MK_LBUTTON
, pointl
, &effect
);
434 if (effect
!= DROPEFFECT_NONE
)
436 TRACE("drop succeeded\n");
440 TRACE("the application refused the drop\n");
443 WARN("drop failed, error 0x%08X\n", hr
);
444 IDropTarget_Release(droptarget
);
448 hwnd
= WindowFromPoint(pt
);
449 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
450 hwnd
= GetParent(hwnd
);
453 HDROP hdrop
= macdrv_get_pasteboard_data(query
->drag_drop
.pasteboard
, CF_HDROP
);
454 DROPFILES
*dropfiles
= GlobalLock(hdrop
);
457 dropfiles
->pt
.x
= pt
.x
;
458 dropfiles
->pt
.y
= pt
.y
;
459 dropfiles
->fNC
= TRUE
;
461 TRACE("sending WM_DROPFILES: hwnd %p pt %s %s\n", hwnd
, wine_dbgstr_point(&pt
),
462 debugstr_w((WCHAR
*)((char*)dropfiles
+ dropfiles
->pFiles
)));
466 ret
= PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)hdrop
, 0L);
467 /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */
474 if (active_data_object
) IDataObject_Release(active_data_object
);
475 active_data_object
= NULL
;
476 last_droptarget_hwnd
= NULL
;
481 /**************************************************************************
484 BOOL
query_drag_exited(macdrv_query
* query
)
486 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
487 IDropTarget
*droptarget
;
489 TRACE("win %p/%p\n", hwnd
, query
->window
);
491 droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
496 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, droptarget
);
497 hr
= IDropTarget_DragLeave(droptarget
);
499 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
500 IDropTarget_Release(droptarget
);
503 if (active_data_object
) IDataObject_Release(active_data_object
);
504 active_data_object
= NULL
;
505 last_droptarget_hwnd
= NULL
;
511 /**************************************************************************
512 * query_drag_operation
514 BOOL
query_drag_operation(macdrv_query
* query
)
517 HWND hwnd
= macdrv_get_window_hwnd(query
->window
);
518 struct macdrv_win_data
*data
= get_win_data(hwnd
);
521 IDropTarget
*droptarget
;
524 TRACE("win %p/%p x,y %d,%d offered_ops 0x%x pasteboard %p\n", hwnd
, query
->window
,
525 query
->drag_operation
.x
, query
->drag_operation
.y
, query
->drag_operation
.offered_ops
,
526 query
->drag_operation
.pasteboard
);
530 WARN("no win_data for win %p/%p\n", hwnd
, query
->window
);
534 pt
.x
= query
->drag_operation
.x
+ data
->whole_rect
.left
;
535 pt
.y
= query
->drag_operation
.y
+ data
->whole_rect
.top
;
536 release_win_data(data
);
538 effect
= drag_operations_to_dropeffects(query
->drag_operation
.offered_ops
);
540 /* Instead of the top-level window we got in the query, start with the deepest
541 child under the cursor. Travel up the hierarchy looking for a window that
542 has an associated IDropTarget. */
543 hwnd
= WindowFromPoint(pt
);
546 droptarget
= get_droptarget_pointer(hwnd
);
547 } while (!droptarget
&& (hwnd
= GetParent(hwnd
)));
549 if (last_droptarget_hwnd
!= hwnd
)
551 if (last_droptarget_hwnd
)
553 IDropTarget
*old_droptarget
= get_droptarget_pointer(last_droptarget_hwnd
);
556 TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd
, old_droptarget
);
557 hr
= IDropTarget_DragLeave(old_droptarget
);
559 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
560 IDropTarget_Release(old_droptarget
);
564 last_droptarget_hwnd
= hwnd
;
568 POINTL pointl
= { pt
.x
, pt
.y
};
570 if (!active_data_object
)
571 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
573 TRACE("DragEnter hwnd %p droptarget %p\n", hwnd
, droptarget
);
574 hr
= IDropTarget_DragEnter(droptarget
, active_data_object
, MK_LBUTTON
,
578 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
579 query
->drag_operation
.offered_ops
);
580 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
584 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr
);
585 IDropTarget_Release(droptarget
);
590 POINTL pointl
= { pt
.x
, pt
.y
};
592 TRACE("DragOver hwnd %p droptarget %p\n", hwnd
, droptarget
);
593 hr
= IDropTarget_DragOver(droptarget
, MK_LBUTTON
, pointl
, &effect
);
596 query
->drag_operation
.accepted_op
= dropeffect_to_drag_operation(effect
,
597 query
->drag_operation
.offered_ops
);
598 TRACE(" effect %d accepted op %d\n", effect
, query
->drag_operation
.accepted_op
);
602 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr
);
603 IDropTarget_Release(droptarget
);
608 hwnd
= WindowFromPoint(pt
);
609 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
610 hwnd
= GetParent(hwnd
);
615 if (!active_data_object
)
616 active_data_object
= create_data_object_for_pasteboard(query
->drag_operation
.pasteboard
);
618 formatEtc
.cfFormat
= CF_HDROP
;
619 formatEtc
.ptd
= NULL
;
620 formatEtc
.dwAspect
= DVASPECT_CONTENT
;
621 formatEtc
.lindex
= -1;
622 formatEtc
.tymed
= TYMED_HGLOBAL
;
623 if (SUCCEEDED(IDataObject_QueryGetData(active_data_object
, &formatEtc
)))
625 TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd
);
626 query
->drag_operation
.accepted_op
= DRAG_OP_GENERIC
;
632 TRACE(" -> %s\n", ret
? "TRUE" : "FALSE");