diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index fe25c6e8fdd..fffb74f1fe7 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -1,10 +1,12 @@ MODULE = winemac.drv -IMPORTS = user32 gdi32 advapi32 +IMPORTS = uuid user32 gdi32 advapi32 +DELAYIMPORTS = ole32 shell32 EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL C_SRCS = \ clipboard.c \ display.c \ + dragdrop.c \ event.c \ gdi.c \ keyboard.c \ diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c index 40445f6bf6e..21d332da264 100644 --- a/dlls/winemac.drv/clipboard.c +++ b/dlls/winemac.drv/clipboard.c @@ -226,7 +226,7 @@ static const CFStringRef registered_name_type_prefix = CFSTR("org.winehq.registe /************************************************************************** * debugstr_format */ -static const char *debugstr_format(UINT id) +const char *debugstr_format(UINT id) { WCHAR buffer[256]; @@ -1259,7 +1259,7 @@ static BOOL release_ownership(void) /************************************************************************** * macdrv_get_pasteboard_data */ -static HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_format) +HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_format) { CFArrayRef types; CFIndex count; @@ -1326,7 +1326,7 @@ static HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_form /************************************************************************** * macdrv_pasteboard_has_format */ -static BOOL macdrv_pasteboard_has_format(CFTypeRef pasteboard, UINT desired_format) +BOOL macdrv_pasteboard_has_format(CFTypeRef pasteboard, UINT desired_format) { CFArrayRef types; int count; @@ -1369,7 +1369,7 @@ static BOOL macdrv_pasteboard_has_format(CFTypeRef pasteboard, UINT desired_form /************************************************************************** * macdrv_copy_pasteboard_formats */ -static CFArrayRef macdrv_copy_pasteboard_formats(CFTypeRef pasteboard) +CFArrayRef macdrv_copy_pasteboard_formats(CFTypeRef pasteboard) { CFArrayRef types; CFIndex count; diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m index c95fac66735..a1a6f9cfd4b 100644 --- a/dlls/winemac.drv/cocoa_event.m +++ b/dlls/winemac.drv/cocoa_event.m @@ -460,8 +460,21 @@ void macdrv_release_query(macdrv_query *query) { if (OSAtomicDecrement32Barrier(&query->refs) <= 0) { - if (query->type == QUERY_PASTEBOARD_DATA && query->pasteboard_data.type) - CFRelease(query->pasteboard_data.type); + switch (query->type) + { + case QUERY_DRAG_OPERATION: + if (query->drag_operation.pasteboard) + CFRelease(query->drag_operation.pasteboard); + break; + case QUERY_DRAG_DROP: + if (query->drag_drop.pasteboard) + CFRelease(query->drag_drop.pasteboard); + break; + case QUERY_PASTEBOARD_DATA: + if (query->pasteboard_data.type) + CFRelease(query->pasteboard_data.type); + break; + } [(WineWindow*)query->window release]; free(query); } diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 7c80c571e34..a2087dafd9d 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -324,6 +324,10 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w window.hwnd = hwnd; window.queue = queue; + [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData, + (NSString*)kUTTypeContent, + nil]]; + contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease]; if (!contentView) return nil; @@ -1189,6 +1193,76 @@ - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type macdrv_release_query(query); } + + /* + * ---------- NSDraggingDestination methods ---------- + */ + - (NSDragOperation) draggingEntered:(id )sender + { + return [self draggingUpdated:sender]; + } + + - (void) draggingExited:(id )sender + { + // This isn't really a query. We don't need any response. However, it + // has to be processed in a similar manner as the other drag-and-drop + // queries in order to maintain the proper order of operations. + macdrv_query* query = macdrv_create_query(); + query->type = QUERY_DRAG_EXITED; + query->window = (macdrv_window)[self retain]; + + [self.queue query:query timeout:0.1]; + macdrv_release_query(query); + } + + - (NSDragOperation) draggingUpdated:(id )sender + { + NSDragOperation ret; + NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil]; + NSPasteboard* pb = [sender draggingPasteboard]; + + macdrv_query* query = macdrv_create_query(); + query->type = QUERY_DRAG_OPERATION; + query->window = (macdrv_window)[self retain]; + query->drag_operation.x = pt.x; + query->drag_operation.y = pt.y; + query->drag_operation.offered_ops = [sender draggingSourceOperationMask]; + query->drag_operation.accepted_op = NSDragOperationNone; + query->drag_operation.pasteboard = (CFTypeRef)[pb retain]; + + [self.queue query:query timeout:3]; + ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone; + macdrv_release_query(query); + + return ret; + } + + - (BOOL) performDragOperation:(id )sender + { + BOOL ret; + NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil]; + NSPasteboard* pb = [sender draggingPasteboard]; + + macdrv_query* query = macdrv_create_query(); + query->type = QUERY_DRAG_DROP; + query->window = (macdrv_window)[self retain]; + query->drag_drop.x = pt.x; + query->drag_drop.y = pt.y; + query->drag_drop.op = [sender draggingSourceOperationMask]; + query->drag_drop.pasteboard = (CFTypeRef)[pb retain]; + + [self.queue query:query timeout:3 * 60 processEvents:YES]; + ret = query->status; + macdrv_release_query(query); + + return ret; + } + + - (BOOL) wantsPeriodicDraggingUpdates + { + return NO; + } + @end diff --git a/dlls/winemac.drv/dragdrop.c b/dlls/winemac.drv/dragdrop.c new file mode 100644 index 00000000000..f7e797bd46f --- /dev/null +++ b/dlls/winemac.drv/dragdrop.c @@ -0,0 +1,635 @@ +/* + * MACDRV Drag and drop code + * + * Copyright 2003 Ulrich Czekalla + * Copyright 2007 Damjan Jovanovic + * Copyright 2013 Ken Thomases for CodeWeavers Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#define NONAMELESSUNION + +#include "macdrv.h" + +#define COBJMACROS +#include "objidl.h" +#include "shellapi.h" +#include "shlobj.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dragdrop); + + +static IDataObject *active_data_object; +static HWND last_droptarget_hwnd; + + +typedef struct +{ + IDataObject IDataObject_iface; + LONG ref; + CFTypeRef pasteboard; +} DragDropDataObject; + + +static inline DragDropDataObject *impl_from_IDataObject(IDataObject *iface) +{ + return CONTAINING_RECORD(iface, DragDropDataObject, IDataObject_iface); +} + + +static HRESULT WINAPI dddo_QueryInterface(IDataObject* iface, REFIID riid, LPVOID *ppvObj) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + + TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppvObj); + + if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IDataObject))) + { + *ppvObj = This; + IUnknown_AddRef((IUnknown*)This); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + + +static ULONG WINAPI dddo_AddRef(IDataObject* iface) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + ULONG refCount = InterlockedIncrement(&This->ref); + + TRACE("(%p)->(count=%u)\n", This, refCount - 1); + + return refCount; +} + + +static ULONG WINAPI dddo_Release(IDataObject* iface) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + ULONG refCount = InterlockedDecrement(&This->ref); + + TRACE("(%p)->(count=%u)\n", This, refCount + 1); + if (refCount) + return refCount; + + TRACE("-- destroying DragDropDataObject (%p)\n", This); + CFRelease(This->pasteboard); + HeapFree(GetProcessHeap(), 0, This); + return 0; +} + + +static HRESULT WINAPI dddo_GetData(IDataObject* iface, FORMATETC* formatEtc, STGMEDIUM* medium) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + HRESULT hr; + + TRACE("This %p formatEtc %s\n", This, debugstr_format(formatEtc->cfFormat)); + + hr = IDataObject_QueryGetData(iface, formatEtc); + if (SUCCEEDED(hr)) + { + medium->tymed = TYMED_HGLOBAL; + medium->u.hGlobal = macdrv_get_pasteboard_data(This->pasteboard, formatEtc->cfFormat); + medium->pUnkForRelease = NULL; + hr = medium->u.hGlobal ? S_OK : E_OUTOFMEMORY; + } + + return hr; +} + + +static HRESULT WINAPI dddo_GetDataHere(IDataObject* iface, FORMATETC* formatEtc, + STGMEDIUM* medium) +{ + FIXME("iface %p formatEtc %p medium %p; stub\n", iface, formatEtc, medium); + return DATA_E_FORMATETC; +} + + +static HRESULT WINAPI dddo_QueryGetData(IDataObject* iface, FORMATETC* formatEtc) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + HRESULT hr = DV_E_FORMATETC; + + TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s}\n", + This, formatEtc, formatEtc->tymed, formatEtc->dwAspect, + debugstr_format(formatEtc->cfFormat)); + + if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL)) + { + FIXME("only HGLOBAL medium types supported right now\n"); + return DV_E_TYMED; + } + if (formatEtc->dwAspect != DVASPECT_CONTENT) + { + FIXME("only the content aspect is supported right now\n"); + return E_NOTIMPL; + } + + if (macdrv_pasteboard_has_format(This->pasteboard, formatEtc->cfFormat)) + hr = S_OK; + + TRACE(" -> 0x%x\n", hr); + return hr; +} + + +static HRESULT WINAPI dddo_GetConicalFormatEtc(IDataObject* iface, FORMATETC* formatEtc, + FORMATETC* formatEtcOut) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + + TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s}\n", + This, formatEtc, formatEtc->tymed, formatEtc->dwAspect, + debugstr_format(formatEtc->cfFormat)); + + *formatEtcOut = *formatEtc; + formatEtcOut->ptd = NULL; + return DATA_S_SAMEFORMATETC; +} + + +static HRESULT WINAPI dddo_SetData(IDataObject* iface, FORMATETC* formatEtc, + STGMEDIUM* medium, BOOL fRelease) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + + TRACE("This %p formatEtc %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%s} medium %p fRelease %d\n", + This, formatEtc, formatEtc->tymed, formatEtc->dwAspect, + debugstr_format(formatEtc->cfFormat), medium, fRelease); + + return E_NOTIMPL; +} + + +static HRESULT WINAPI dddo_EnumFormatEtc(IDataObject* iface, DWORD direction, + IEnumFORMATETC** enumFormatEtc) +{ + DragDropDataObject *This = impl_from_IDataObject(iface); + CFArrayRef formats; + HRESULT hr; + + TRACE("This %p direction %u enumFormatEtc %p\n", This, direction, enumFormatEtc); + + if (direction != DATADIR_GET) + { + WARN("only the get direction is implemented\n"); + return E_NOTIMPL; + } + + formats = macdrv_copy_pasteboard_formats(This->pasteboard); + if (formats) + { + INT count = CFArrayGetCount(formats); + FORMATETC *formatEtcs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC)); + if (formatEtcs) + { + INT i; + + for (i = 0; i < count; i++) + { + formatEtcs[i].cfFormat = (UINT)CFArrayGetValueAtIndex(formats, i); + formatEtcs[i].ptd = NULL; + formatEtcs[i].dwAspect = DVASPECT_CONTENT; + formatEtcs[i].lindex = -1; + formatEtcs[i].tymed = TYMED_HGLOBAL; + } + + hr = SHCreateStdEnumFmtEtc(count, formatEtcs, enumFormatEtc); + HeapFree(GetProcessHeap(), 0, formatEtcs); + } + else + hr = E_OUTOFMEMORY; + + CFRelease(formats); + } + else + hr = SHCreateStdEnumFmtEtc(0, NULL, enumFormatEtc); + + TRACE(" -> 0x%x\n", hr); + return hr; +} + + +static HRESULT WINAPI dddo_DAdvise(IDataObject* iface, FORMATETC* formatEtc, DWORD advf, + IAdviseSink* pAdvSink, DWORD* pdwConnection) +{ + FIXME("(%p, %p, %u, %p, %p): stub\n", iface, formatEtc, advf, + pAdvSink, pdwConnection); + return OLE_E_ADVISENOTSUPPORTED; +} + + +static HRESULT WINAPI dddo_DUnadvise(IDataObject* iface, DWORD dwConnection) +{ + FIXME("(%p, %u): stub\n", iface, dwConnection); + return OLE_E_ADVISENOTSUPPORTED; +} + + +static HRESULT WINAPI dddo_EnumDAdvise(IDataObject* iface, IEnumSTATDATA** pEnumAdvise) +{ + FIXME("(%p, %p): stub\n", iface, pEnumAdvise); + return OLE_E_ADVISENOTSUPPORTED; +} + + +static const IDataObjectVtbl dovt = +{ + dddo_QueryInterface, + dddo_AddRef, + dddo_Release, + dddo_GetData, + dddo_GetDataHere, + dddo_QueryGetData, + dddo_GetConicalFormatEtc, + dddo_SetData, + dddo_EnumFormatEtc, + dddo_DAdvise, + dddo_DUnadvise, + dddo_EnumDAdvise +}; + + +static IDataObject *create_data_object_for_pasteboard(CFTypeRef pasteboard) +{ + DragDropDataObject *dddo; + + dddo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dddo)); + if (!dddo) + return NULL; + + dddo->ref = 1; + dddo->IDataObject_iface.lpVtbl = &dovt; + dddo->pasteboard = CFRetain(pasteboard); + + return &dddo->IDataObject_iface; +} + + +/************************************************************************** + * drag_operations_to_dropeffects + */ +static DWORD drag_operations_to_dropeffects(uint32_t ops) +{ + DWORD effects = DROPEFFECT_NONE; + if (ops & (DRAG_OP_COPY | DRAG_OP_GENERIC)) + effects |= DROPEFFECT_COPY; + if (ops & DRAG_OP_MOVE) + effects |= DROPEFFECT_MOVE; + if (ops & (DRAG_OP_LINK | DRAG_OP_GENERIC)) + effects |= DROPEFFECT_LINK; + return effects; +} + + +/************************************************************************** + * dropeffect_to_drag_operation + */ +static uint32_t dropeffect_to_drag_operation(DWORD effect, uint32_t ops) +{ + switch (effect) + { + case DROPEFFECT_COPY: return (ops & DRAG_OP_COPY) ? DRAG_OP_COPY : DRAG_OP_GENERIC; + case DROPEFFECT_MOVE: return DRAG_OP_MOVE; + case DROPEFFECT_LINK: return (ops & DRAG_OP_LINK) ? DRAG_OP_LINK : DRAG_OP_GENERIC; + } + + return DRAG_OP_NONE; +} + + +/* Based on functions in dlls/ole32/ole2.c */ +static HANDLE get_droptarget_local_handle(HWND hwnd) +{ + static const WCHAR prop_marshalleddroptarget[] = + {'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}; + HANDLE handle; + HANDLE local_handle = 0; + + handle = GetPropW(hwnd, prop_marshalleddroptarget); + if (handle) + { + DWORD pid; + HANDLE process; + + GetWindowThreadProcessId(hwnd, &pid); + process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid); + if (process) + { + DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + CloseHandle(process); + } + } + return local_handle; +} + + +static HRESULT create_stream_from_map(HANDLE map, IStream **stream) +{ + HRESULT hr = E_OUTOFMEMORY; + HGLOBAL hmem; + void *data; + MEMORY_BASIC_INFORMATION info; + + data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if(!data) return hr; + + VirtualQuery(data, &info, sizeof(info)); + TRACE("size %d\n", (int)info.RegionSize); + + hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize); + if(hmem) + { + memcpy(GlobalLock(hmem), data, info.RegionSize); + GlobalUnlock(hmem); + hr = CreateStreamOnHGlobal(hmem, TRUE, stream); + } + UnmapViewOfFile(data); + return hr; +} + + +static IDropTarget* get_droptarget_pointer(HWND hwnd) +{ + IDropTarget *droptarget = NULL; + HANDLE map; + IStream *stream; + + map = get_droptarget_local_handle(hwnd); + if(!map) return NULL; + + if(SUCCEEDED(create_stream_from_map(map, &stream))) + { + CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget); + IStream_Release(stream); + } + CloseHandle(map); + return droptarget; +} + + +/************************************************************************** + * query_drag_drop + */ +BOOL query_drag_drop(macdrv_query* query) +{ + BOOL ret = FALSE; + HWND hwnd = macdrv_get_window_hwnd(query->window); + struct macdrv_win_data *data = get_win_data(hwnd); + POINT pt; + IDropTarget *droptarget; + + TRACE("win %p/%p x,y %d,%d op 0x%08x pasteboard %p\n", hwnd, query->window, + query->drag_drop.x, query->drag_drop.y, query->drag_drop.op, query->drag_drop.pasteboard); + + if (!data) + { + WARN("no win_data for win %p/%p\n", hwnd, query->window); + return FALSE; + } + + pt.x = query->drag_drop.x + data->whole_rect.left; + pt.y = query->drag_drop.y + data->whole_rect.top; + release_win_data(data); + + droptarget = get_droptarget_pointer(last_droptarget_hwnd); + if (droptarget) + { + HRESULT hr; + POINTL pointl; + DWORD effect = drag_operations_to_dropeffects(query->drag_drop.op); + + if (!active_data_object) + { + WARN("shouldn't happen: no active IDataObject\n"); + active_data_object = create_data_object_for_pasteboard(query->drag_drop.pasteboard); + } + + pointl.x = pt.x; + pointl.y = pt.y; + TRACE("Drop hwnd %p droptarget %p pointl (%d,%d) effect 0x%08x\n", last_droptarget_hwnd, + droptarget, pointl.x, pointl.y, effect); + hr = IDropTarget_Drop(droptarget, active_data_object, MK_LBUTTON, pointl, &effect); + if (SUCCEEDED(hr)) + { + if (effect != DROPEFFECT_NONE) + { + TRACE("drop succeeded\n"); + ret = TRUE; + } + else + TRACE("the application refused the drop\n"); + } + else + WARN("drop failed, error 0x%08X\n", hr); + IDropTarget_Release(droptarget); + } + else + { + hwnd = WindowFromPoint(pt); + while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES)) + hwnd = GetParent(hwnd); + if (hwnd) + { + HDROP hdrop = macdrv_get_pasteboard_data(query->drag_drop.pasteboard, CF_HDROP); + DROPFILES *dropfiles = GlobalLock(hdrop); + if (dropfiles) + { + dropfiles->pt.x = pt.x; + dropfiles->pt.y = pt.y; + dropfiles->fNC = TRUE; + + TRACE("sending WM_DROPFILES: hwnd %p pt %s %s\n", hwnd, wine_dbgstr_point(&pt), + debugstr_w((WCHAR*)((char*)dropfiles + dropfiles->pFiles))); + + GlobalUnlock(hdrop); + + ret = PostMessageW(hwnd, WM_DROPFILES, (WPARAM)hdrop, 0L); + /* hdrop is owned by the message and freed when the recipient calls DragFinish(). */ + } + else + GlobalFree(hdrop); + } + } + + if (active_data_object) IDataObject_Release(active_data_object); + active_data_object = NULL; + last_droptarget_hwnd = NULL; + return ret; +} + + +/************************************************************************** + * query_drag_exited + */ +BOOL query_drag_exited(macdrv_query* query) +{ + HWND hwnd = macdrv_get_window_hwnd(query->window); + IDropTarget *droptarget; + + TRACE("win %p/%p\n", hwnd, query->window); + + droptarget = get_droptarget_pointer(last_droptarget_hwnd); + if (droptarget) + { + HRESULT hr; + + TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, droptarget); + hr = IDropTarget_DragLeave(droptarget); + if (FAILED(hr)) + WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr); + IDropTarget_Release(droptarget); + } + + if (active_data_object) IDataObject_Release(active_data_object); + active_data_object = NULL; + last_droptarget_hwnd = NULL; + + return TRUE; +} + + +/************************************************************************** + * query_drag_operation + */ +BOOL query_drag_operation(macdrv_query* query) +{ + BOOL ret = FALSE; + HWND hwnd = macdrv_get_window_hwnd(query->window); + struct macdrv_win_data *data = get_win_data(hwnd); + POINT pt; + DWORD effect; + IDropTarget *droptarget; + HRESULT hr; + + TRACE("win %p/%p x,y %d,%d offered_ops 0x%x pasteboard %p\n", hwnd, query->window, + query->drag_operation.x, query->drag_operation.y, query->drag_operation.offered_ops, + query->drag_operation.pasteboard); + + if (!data) + { + WARN("no win_data for win %p/%p\n", hwnd, query->window); + return FALSE; + } + + pt.x = query->drag_operation.x + data->whole_rect.left; + pt.y = query->drag_operation.y + data->whole_rect.top; + release_win_data(data); + + effect = drag_operations_to_dropeffects(query->drag_operation.offered_ops); + + /* Instead of the top-level window we got in the query, start with the deepest + child under the cursor. Travel up the hierarchy looking for a window that + has an associated IDropTarget. */ + hwnd = WindowFromPoint(pt); + do + { + droptarget = get_droptarget_pointer(hwnd); + } while (!droptarget && (hwnd = GetParent(hwnd))); + + if (last_droptarget_hwnd != hwnd) + { + if (last_droptarget_hwnd) + { + IDropTarget *old_droptarget = get_droptarget_pointer(last_droptarget_hwnd); + if (old_droptarget) + { + TRACE("DragLeave hwnd %p droptarget %p\n", last_droptarget_hwnd, old_droptarget); + hr = IDropTarget_DragLeave(old_droptarget); + if (FAILED(hr)) + WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr); + IDropTarget_Release(old_droptarget); + } + } + + last_droptarget_hwnd = hwnd; + + if (droptarget) + { + POINTL pointl = { pt.x, pt.y }; + + if (!active_data_object) + active_data_object = create_data_object_for_pasteboard(query->drag_operation.pasteboard); + + TRACE("DragEnter hwnd %p droptarget %p\n", hwnd, droptarget); + hr = IDropTarget_DragEnter(droptarget, active_data_object, MK_LBUTTON, + pointl, &effect); + if (SUCCEEDED(hr)) + { + query->drag_operation.accepted_op = dropeffect_to_drag_operation(effect, + query->drag_operation.offered_ops); + TRACE(" effect %d accepted op %d\n", effect, query->drag_operation.accepted_op); + ret = TRUE; + } + else + WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr); + IDropTarget_Release(droptarget); + } + } + else if (droptarget) + { + POINTL pointl = { pt.x, pt.y }; + + TRACE("DragOver hwnd %p droptarget %p\n", hwnd, droptarget); + hr = IDropTarget_DragOver(droptarget, MK_LBUTTON, pointl, &effect); + if (SUCCEEDED(hr)) + { + query->drag_operation.accepted_op = dropeffect_to_drag_operation(effect, + query->drag_operation.offered_ops); + TRACE(" effect %d accepted op %d\n", effect, query->drag_operation.accepted_op); + ret = TRUE; + } + else + WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr); + IDropTarget_Release(droptarget); + } + + if (!ret) + { + hwnd = WindowFromPoint(pt); + while (hwnd && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_ACCEPTFILES)) + hwnd = GetParent(hwnd); + if (hwnd) + { + FORMATETC formatEtc; + + if (!active_data_object) + active_data_object = create_data_object_for_pasteboard(query->drag_operation.pasteboard); + + formatEtc.cfFormat = CF_HDROP; + formatEtc.ptd = NULL; + formatEtc.dwAspect = DVASPECT_CONTENT; + formatEtc.lindex = -1; + formatEtc.tymed = TYMED_HGLOBAL; + if (SUCCEEDED(IDataObject_QueryGetData(active_data_object, &formatEtc))) + { + TRACE("WS_EX_ACCEPTFILES hwnd %p\n", hwnd); + query->drag_operation.accepted_op = DRAG_OP_GENERIC; + ret = TRUE; + } + } + } + + TRACE(" -> %s\n", ret ? "TRUE" : "FALSE"); + return ret; +} diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 1fba68195ae..a4161a4e046 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -116,6 +116,18 @@ static void macdrv_query_event(HWND hwnd, macdrv_event *event) switch (query->type) { + case QUERY_DRAG_DROP: + TRACE("QUERY_DRAG_DROP\n"); + success = query_drag_drop(query); + break; + case QUERY_DRAG_EXITED: + TRACE("QUERY_DRAG_EXITED\n"); + success = query_drag_exited(query); + break; + case QUERY_DRAG_OPERATION: + TRACE("QUERY_DRAG_OPERATION\n"); + success = query_drag_operation(query); + break; case QUERY_PASTEBOARD_DATA: TRACE("QUERY_PASTEBOARD_DATA\n"); success = query_pasteboard_data(hwnd, query->pasteboard_data.type); diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 4c39fc5e6ef..0a2f6b791ca 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -160,6 +160,14 @@ static inline RECT rect_from_cgrect(CGRect cgrect) extern void macdrv_clipboard_process_attach(void) DECLSPEC_HIDDEN; extern BOOL query_pasteboard_data(HWND hwnd, CFStringRef type) DECLSPEC_HIDDEN; +extern const char *debugstr_format(UINT id) DECLSPEC_HIDDEN; +extern HANDLE macdrv_get_pasteboard_data(CFTypeRef pasteboard, UINT desired_format) DECLSPEC_HIDDEN; +extern BOOL CDECL macdrv_pasteboard_has_format(CFTypeRef pasteboard, UINT desired_format) DECLSPEC_HIDDEN; +extern CFArrayRef macdrv_copy_pasteboard_formats(CFTypeRef pasteboard) DECLSPEC_HIDDEN; + +extern BOOL query_drag_operation(macdrv_query* query) DECLSPEC_HIDDEN; +extern BOOL query_drag_exited(macdrv_query* query) DECLSPEC_HIDDEN; +extern BOOL query_drag_drop(macdrv_query* query) DECLSPEC_HIDDEN; extern struct opengl_funcs *macdrv_wine_get_wgl_driver(PHYSDEV dev, UINT version) DECLSPEC_HIDDEN; extern void sync_gl_view(struct macdrv_win_data *data) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 95a372059fb..65b41a821b1 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -99,6 +99,19 @@ #endif +/* Must match the values of Cocoa's NSDragOperation enum. */ +enum { + DRAG_OP_NONE = 0, + DRAG_OP_COPY = 1, + DRAG_OP_LINK = 2, + DRAG_OP_GENERIC = 4, + DRAG_OP_PRIVATE = 8, + DRAG_OP_MOVE = 16, + DRAG_OP_DELETE = 32, + DRAG_OP_EVERY = UINT32_MAX +}; + + typedef struct macdrv_opaque_window* macdrv_window; typedef struct macdrv_opaque_event_queue* macdrv_event_queue; typedef struct macdrv_opaque_view* macdrv_view; @@ -208,6 +221,9 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, } macdrv_event; enum { + QUERY_DRAG_DROP, + QUERY_DRAG_EXITED, + QUERY_DRAG_OPERATION, QUERY_PASTEBOARD_DATA, NUM_QUERY_TYPES }; @@ -219,6 +235,19 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, int status; int done; union { + struct { + int x; + int y; + uint32_t op; + CFTypeRef pasteboard; + } drag_drop; + struct { + int x; + int y; + uint32_t offered_ops; + uint32_t accepted_op; + CFTypeRef pasteboard; + } drag_operation; struct { CFStringRef type; } pasteboard_data;