winex11.drv: Implement a framework for dragging from X and dropping to OLE.
authorDamjan Jovanovic <damjan.jov@gmail.com>
Mon, 16 Aug 2010 19:10:05 +0000 (21:10 +0200)
committerAlexandre Julliard <julliard@winehq.org>
Tue, 17 Aug 2010 12:48:02 +0000 (14:48 +0200)
dlls/winex11.drv/Makefile.in
dlls/winex11.drv/xdnd.c

index e381b95ac179dd0dc5913cc8e7ad8158f1fb923b..e9152c6238701e4b9ff430c275480d4164e9bcee 100644 (file)
@@ -3,8 +3,8 @@ TOPOBJDIR = ../..
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = winex11.drv
-IMPORTS   = user32 gdi32 advapi32 imm32
-DELAYIMPORTS = comctl32
+IMPORTS   = uuid user32 gdi32 advapi32 imm32
+DELAYIMPORTS = comctl32 ole32
 EXTRAINCL = @X_CFLAGS@
 EXTRALIBS = @X_LIBS@ @X_PRE_LIBS@ @XLIB@ @X_EXTRA_LIBS@
 
index f0704da3f35b497763b8a42baa319d8951f343d8..9dda6be5e001c8a94a9339f0a57fe213cc7efde7 100644 (file)
 #include "wingdi.h"
 #include "winuser.h"
 
+#define COBJMACROS
 #include "x11drv.h"
 #include "shlobj.h"  /* DROPFILES */
+#include "oleidl.h"
+#include "objidl.h"
 
 #include "wine/unicode.h"
 #include "wine/debug.h"
@@ -58,6 +61,13 @@ typedef struct tagXDNDDATA
 
 static struct list xdndData = LIST_INIT(xdndData);
 static POINT XDNDxy = { 0, 0 };
+static IDataObject XDNDDataObject;
+static BOOL XDNDAccepted = FALSE;
+static DWORD XDNDDropEffect = DROPEFFECT_NONE;
+/* the last window the mouse was over */
+static HWND XDNDLastTargetWnd;
+/* might be a ancestor of XDNDLastTargetWnd */
+static HWND XDNDLastDropTargetWnd;
 
 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len);
@@ -81,6 +91,109 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
 
 
+/* 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;
+}
+
+/**************************************************************************
+ * X11DRV_XDND_XdndActionToDROPEFFECT
+ */
+static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
+{
+    /* In Windows, nothing but the given effects is allowed.
+     * In X the given action is just a hint, and you can always
+     * XdndActionCopy and XdndActionPrivate, so be more permissive. */
+    if (action == x11drv_atom(XdndActionCopy))
+        return DROPEFFECT_COPY;
+    else if (action == x11drv_atom(XdndActionMove))
+        return DROPEFFECT_MOVE | DROPEFFECT_COPY;
+    else if (action == x11drv_atom(XdndActionLink))
+        return DROPEFFECT_LINK | DROPEFFECT_COPY;
+    else if (action == x11drv_atom(XdndActionAsk))
+        /* FIXME: should we somehow ask the user what to do here? */
+        return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
+    FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
+    return DROPEFFECT_COPY;
+}
+
+/**************************************************************************
+ * X11DRV_XDND_DROPEFFECTToXdndAction
+ */
+static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
+{
+    if (effect == DROPEFFECT_COPY)
+        return x11drv_atom(XdndActionCopy);
+    else if (effect == DROPEFFECT_MOVE)
+        return x11drv_atom(XdndActionMove);
+    else if (effect == DROPEFFECT_LINK)
+        return x11drv_atom(XdndActionLink);
+    FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
+    return x11drv_atom(XdndActionCopy);
+}
+
 /**************************************************************************
  * X11DRV_XDND_EnterEvent
  *
@@ -104,6 +217,8 @@ void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
         return;
     }
 
+    XDNDAccepted = FALSE;
+
     /* If the source supports more than 3 data types we retrieve
      * the entire list. */
     if (event->data.l[1] & 1)
@@ -159,12 +274,79 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
 {
     XClientMessageEvent e;
     int accept = 0; /* Assume we're not accepting */
+    IDropTarget *dropTarget = NULL;
+    DWORD effect;
+    POINTL pointl;
+    HWND targetWindow;
+    HRESULT hr;
 
     XDNDxy.x = event->data.l[2] >> 16;
     XDNDxy.y = event->data.l[2] & 0xFFFF;
+    targetWindow = WindowFromPoint(XDNDxy);
 
-    /* FIXME: Notify OLE of DragEnter. Result determines if we accept */
+    pointl.x = XDNDxy.x;
+    pointl.y = XDNDxy.y;
+    effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
 
+    if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
+    {
+        /* Notify OLE of DragEnter. Result determines if we accept */
+        HWND dropTargetWindow;
+
+        if (XDNDLastDropTargetWnd)
+        {
+            dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
+            if (dropTarget)
+            {
+                hr = IDropTarget_DragLeave(dropTarget);
+                if (FAILED(hr))
+                    WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
+                IDropTarget_Release(dropTarget);
+            }
+        }
+        dropTargetWindow = targetWindow;
+        do
+        {
+            dropTarget = get_droptarget_pointer(dropTargetWindow);
+        } while (dropTarget == NULL && (dropTargetWindow = GetParent(dropTargetWindow)) != NULL);
+        XDNDLastTargetWnd = targetWindow;
+        XDNDLastDropTargetWnd = dropTargetWindow;
+        if (dropTarget)
+        {
+            hr = IDropTarget_DragEnter(dropTarget, &XDNDDataObject,
+                                       MK_LBUTTON, pointl, &effect);
+            if (SUCCEEDED(hr))
+            {
+                if (effect != DROPEFFECT_NONE)
+                {
+                    XDNDAccepted = TRUE;
+                    TRACE("the application accepted the drop\n");
+                }
+                else
+                    TRACE("the application refused the drop\n");
+            }
+            else
+                WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr);
+            IDropTarget_Release(dropTarget);
+        }
+    }
+    if (XDNDAccepted && XDNDLastTargetWnd == targetWindow)
+    {
+        /* If drag accepted notify OLE of DragOver */
+        dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
+        if (dropTarget)
+        {
+            hr = IDropTarget_DragOver(dropTarget, MK_LBUTTON, pointl, &effect);
+            if (SUCCEEDED(hr))
+                XDNDDropEffect = effect;
+            else
+                WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr);
+            IDropTarget_Release(dropTarget);
+        }
+    }
+
+    if (XDNDAccepted)
+        accept = 1;
     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
         accept = 1;
 
@@ -185,14 +367,12 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
     e.data.l[2] = 0; /* Empty Rect */
     e.data.l[3] = 0; /* Empty Rect */
     if (accept)
-        e.data.l[4] = event->data.l[4];
+        e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
     else
         e.data.l[4] = None;
     wine_tsx11_lock();
     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
     wine_tsx11_unlock();
-
-    /* FIXME: if drag accepted notify OLE of DragOver */
 }
 
 /**************************************************************************
@@ -203,6 +383,7 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
 void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
 {
     XClientMessageEvent e;
+    IDropTarget *dropTarget;
 
     TRACE("\n");
 
@@ -210,7 +391,30 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
         X11DRV_XDND_SendDropFiles( hWnd );
 
-    /* FIXME: Notify OLE of Drop */
+    /* Notify OLE of Drop */
+    dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
+    if (dropTarget)
+    {
+        HRESULT hr;
+        POINTL pointl;
+        DWORD effect = XDNDDropEffect;
+
+        pointl.x = XDNDxy.x;
+        pointl.y = XDNDxy.y;
+        hr = IDropTarget_Drop(dropTarget, &XDNDDataObject, MK_LBUTTON,
+                              pointl, &effect);
+        if (SUCCEEDED(hr))
+        {
+            if (effect != DROPEFFECT_NONE)
+                TRACE("drop succeeded\n");
+            else
+                TRACE("the application refused the drop\n");
+        }
+        else
+            WARN("drop failed, error 0x%08X\n", hr);
+        IDropTarget_Release(dropTarget);
+    }
+
     X11DRV_XDND_FreeDragDropOp();
 
     /* Tell the target we are finished. */
@@ -233,11 +437,21 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
  */
 void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
 {
+    IDropTarget *dropTarget;
+
     TRACE("DND Operation canceled\n");
 
-    X11DRV_XDND_FreeDragDropOp();
+    /* Notify OLE of DragLeave */
+    dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
+    if (dropTarget)
+    {
+        HRESULT hr = IDropTarget_DragLeave(dropTarget);
+        if (FAILED(hr))
+            WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
+        IDropTarget_Release(dropTarget);
+    }
 
-    /* FIXME: Notify OLE of DragLeave */
+    X11DRV_XDND_FreeDragDropOp();
 }
 
 
@@ -566,6 +780,9 @@ static void X11DRV_XDND_FreeDragDropOp(void)
     }
 
     XDNDxy.x = XDNDxy.y = 0;
+    XDNDLastTargetWnd = NULL;
+    XDNDLastDropTargetWnd = NULL;
+    XDNDAccepted = FALSE;
 
     LeaveCriticalSection(&xdnd_cs);
 }
@@ -685,3 +902,122 @@ static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI)
     HeapFree(GetProcessHeap(), 0, uri);
     return ret;
 }
+
+/* The IDataObject singleton we feed to OLE follows */
+
+static HRESULT WINAPI XDNDDATAOBJECT_QueryInterface(IDataObject *dataObject,
+                                                    REFIID riid, void **ppvObject)
+{
+    TRACE("(%p, %s, %p)\n", dataObject, debugstr_guid(riid), ppvObject);
+    if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDataObject))
+    {
+        *ppvObject = dataObject;
+        IDataObject_AddRef(dataObject);
+        return S_OK;
+    }
+    *ppvObject = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI XDNDDATAOBJECT_AddRef(IDataObject *dataObject)
+{
+    TRACE("(%p)\n", dataObject);
+    return 2;
+}
+
+static ULONG WINAPI XDNDDATAOBJECT_Release(IDataObject *dataObject)
+{
+    TRACE("(%p)\n", dataObject);
+    return 1;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_GetData(IDataObject *dataObject,
+                                             FORMATETC *formatEtc,
+                                             STGMEDIUM *pMedium)
+{
+    FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, pMedium);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_GetDataHere(IDataObject *dataObject,
+                                                 FORMATETC *formatEtc,
+                                                 STGMEDIUM *pMedium)
+{
+    FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, pMedium);
+    return DATA_E_FORMATETC;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject,
+                                                  FORMATETC *formatEtc)
+{
+    FIXME("(%p, %p): stub\n", dataObject, formatEtc);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject *dataObject,
+                                                           FORMATETC *formatEtc,
+                                                           FORMATETC *formatEtcOut)
+{
+    FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, formatEtcOut);
+    formatEtcOut->ptd = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_SetData(IDataObject *dataObject,
+                                             FORMATETC *formatEtc,
+                                             STGMEDIUM *pMedium, BOOL fRelease)
+{
+    FIXME("(%p, %p, %p, %s): stub\n", dataObject, formatEtc,
+        pMedium, fRelease?"TRUE":"FALSE");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject,
+                                                   DWORD dwDirection,
+                                                   IEnumFORMATETC **ppEnumFormatEtc)
+{
+    FIXME("(%p, %u, %p): stub\n", dataObject, dwDirection, ppEnumFormatEtc);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_DAdvise(IDataObject *dataObject,
+                                             FORMATETC *formatEtc, DWORD advf,
+                                             IAdviseSink *adviseSink,
+                                             DWORD *pdwConnection)
+{
+    FIXME("(%p, %p, %u, %p, %p): stub\n", dataObject, formatEtc, advf,
+        adviseSink, pdwConnection);
+    return OLE_E_ADVISENOTSUPPORTED;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_DUnadvise(IDataObject *dataObject,
+                                               DWORD dwConnection)
+{
+    FIXME("(%p, %u): stub\n", dataObject, dwConnection);
+    return OLE_E_ADVISENOTSUPPORTED;
+}
+
+static HRESULT WINAPI XDNDDATAOBJECT_EnumDAdvise(IDataObject *dataObject,
+                                                 IEnumSTATDATA **pEnumAdvise)
+{
+    FIXME("(%p, %p): stub\n", dataObject, pEnumAdvise);
+    return OLE_E_ADVISENOTSUPPORTED;
+}
+
+static IDataObjectVtbl xdndDataObjectVtbl =
+{
+    XDNDDATAOBJECT_QueryInterface,
+    XDNDDATAOBJECT_AddRef,
+    XDNDDATAOBJECT_Release,
+    XDNDDATAOBJECT_GetData,
+    XDNDDATAOBJECT_GetDataHere,
+    XDNDDATAOBJECT_QueryGetData,
+    XDNDDATAOBJECT_GetCanonicalFormatEtc,
+    XDNDDATAOBJECT_SetData,
+    XDNDDATAOBJECT_EnumFormatEtc,
+    XDNDDATAOBJECT_DAdvise,
+    XDNDDATAOBJECT_DUnadvise,
+    XDNDDATAOBJECT_EnumDAdvise
+};
+
+static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };