shell32: Indentation fix.
[wine] / dlls / winex11.drv / xdnd.c
1 /*
2  * XDND handler code
3  *
4  * Copyright 2003 Ulrich Czekalla
5  * Copyright 2007 Damjan Jovanovic
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <string.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #define NONAMELESSUNION
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38
39 #define COBJMACROS
40 #include "x11drv.h"
41 #include "shlobj.h"  /* DROPFILES */
42 #include "oleidl.h"
43 #include "objidl.h"
44
45 #include "wine/unicode.h"
46 #include "wine/debug.h"
47 #include "wine/list.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
50
51 /* Maximum wait time for selection notify */
52 #define SELECTION_RETRIES 500  /* wait for .1 seconds */
53 #define SELECTION_WAIT    1000 /* us */
54
55 typedef struct tagXDNDDATA
56 {
57     int cf_win;
58     Atom cf_xdnd;
59     void *data;
60     unsigned int size;
61     struct list entry;
62 } XDNDDATA, *LPXDNDDATA;
63
64 static struct list xdndData = LIST_INIT(xdndData);
65 static POINT XDNDxy = { 0, 0 };
66 static IDataObject XDNDDataObject;
67 static BOOL XDNDAccepted = FALSE;
68 static DWORD XDNDDropEffect = DROPEFFECT_NONE;
69 /* the last window the mouse was over */
70 static HWND XDNDLastTargetWnd;
71 /* might be a ancestor of XDNDLastTargetWnd */
72 static HWND XDNDLastDropTargetWnd;
73
74 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
75 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len);
76 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
77 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
78 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
79 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
80     Atom *types, unsigned long *count);
81 static void X11DRV_XDND_SendDropFiles(HWND hwnd);
82 static void X11DRV_XDND_FreeDragDropOp(void);
83 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
84 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI);
85
86 static CRITICAL_SECTION xdnd_cs;
87 static CRITICAL_SECTION_DEBUG critsect_debug =
88 {
89     0, 0, &xdnd_cs,
90     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
91       0, 0, { (DWORD_PTR)(__FILE__ ": xdnd_cs") }
92 };
93 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
94
95
96 /* Based on functions in dlls/ole32/ole2.c */
97 static HANDLE get_droptarget_local_handle(HWND hwnd)
98 {
99     static const WCHAR prop_marshalleddroptarget[] =
100         {'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};
101     HANDLE handle;
102     HANDLE local_handle = 0;
103
104     handle = GetPropW(hwnd, prop_marshalleddroptarget);
105     if (handle)
106     {
107         DWORD pid;
108         HANDLE process;
109
110         GetWindowThreadProcessId(hwnd, &pid);
111         process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
112         if (process)
113         {
114             DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
115             CloseHandle(process);
116         }
117     }
118     return local_handle;
119 }
120
121 static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
122 {
123     HRESULT hr = E_OUTOFMEMORY;
124     HGLOBAL hmem;
125     void *data;
126     MEMORY_BASIC_INFORMATION info;
127
128     data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
129     if(!data) return hr;
130
131     VirtualQuery(data, &info, sizeof(info));
132     TRACE("size %d\n", (int)info.RegionSize);
133
134     hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
135     if(hmem)
136     {
137         memcpy(GlobalLock(hmem), data, info.RegionSize);
138         GlobalUnlock(hmem);
139         hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
140     }
141     UnmapViewOfFile(data);
142     return hr;
143 }
144
145 static IDropTarget* get_droptarget_pointer(HWND hwnd)
146 {
147     IDropTarget *droptarget = NULL;
148     HANDLE map;
149     IStream *stream;
150
151     map = get_droptarget_local_handle(hwnd);
152     if(!map) return NULL;
153
154     if(SUCCEEDED(create_stream_from_map(map, &stream)))
155     {
156         CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
157         IStream_Release(stream);
158     }
159     CloseHandle(map);
160     return droptarget;
161 }
162
163 /**************************************************************************
164  * X11DRV_XDND_XdndActionToDROPEFFECT
165  */
166 static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
167 {
168     /* In Windows, nothing but the given effects is allowed.
169      * In X the given action is just a hint, and you can always
170      * XdndActionCopy and XdndActionPrivate, so be more permissive. */
171     if (action == x11drv_atom(XdndActionCopy))
172         return DROPEFFECT_COPY;
173     else if (action == x11drv_atom(XdndActionMove))
174         return DROPEFFECT_MOVE | DROPEFFECT_COPY;
175     else if (action == x11drv_atom(XdndActionLink))
176         return DROPEFFECT_LINK | DROPEFFECT_COPY;
177     else if (action == x11drv_atom(XdndActionAsk))
178         /* FIXME: should we somehow ask the user what to do here? */
179         return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
180     FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
181     return DROPEFFECT_COPY;
182 }
183
184 /**************************************************************************
185  * X11DRV_XDND_DROPEFFECTToXdndAction
186  */
187 static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
188 {
189     if (effect == DROPEFFECT_COPY)
190         return x11drv_atom(XdndActionCopy);
191     else if (effect == DROPEFFECT_MOVE)
192         return x11drv_atom(XdndActionMove);
193     else if (effect == DROPEFFECT_LINK)
194         return x11drv_atom(XdndActionLink);
195     FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
196     return x11drv_atom(XdndActionCopy);
197 }
198
199 /**************************************************************************
200  * X11DRV_XDND_EnterEvent
201  *
202  * Handle an XdndEnter event.
203  */
204 void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
205 {
206     int version;
207     Atom *xdndtypes;
208     unsigned long count = 0;
209
210     version = (event->data.l[1] & 0xFF000000) >> 24;
211     TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
212           version, (event->data.l[1] & 1),
213           event->data.l[0], event->data.l[1], event->data.l[2],
214           event->data.l[3], event->data.l[4]);
215
216     if (version > WINE_XDND_VERSION)
217     {
218         TRACE("Ignores unsupported version\n");
219         return;
220     }
221
222     XDNDAccepted = FALSE;
223
224     /* If the source supports more than 3 data types we retrieve
225      * the entire list. */
226     if (event->data.l[1] & 1)
227     {
228         Atom acttype;
229         int actfmt;
230         unsigned long bytesret;
231
232         /* Request supported formats from source window */
233         XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
234                            0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
235                            &bytesret, (unsigned char**)&xdndtypes);
236     }
237     else
238     {
239         count = 3;
240         xdndtypes = (Atom*) &event->data.l[2];
241     }
242
243     if (TRACE_ON(xdnd))
244     {
245         unsigned int i;
246
247         for (i = 0; i < count; i++)
248         {
249             if (xdndtypes[i] != 0)
250             {
251                 char * pn = XGetAtomName(event->display, xdndtypes[i]);
252                 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
253                 XFree(pn);
254             }
255         }
256     }
257
258     /* Do a one-time data read and cache results */
259     X11DRV_XDND_ResolveProperty(event->display, event->window,
260                                 event->data.l[1], xdndtypes, &count);
261
262     if (event->data.l[1] & 1)
263         XFree(xdndtypes);
264 }
265
266 /**************************************************************************
267  * X11DRV_XDND_PositionEvent
268  *
269  * Handle an XdndPosition event.
270  */
271 void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
272 {
273     XClientMessageEvent e;
274     int accept = 0; /* Assume we're not accepting */
275     IDropTarget *dropTarget = NULL;
276     DWORD effect;
277     POINTL pointl;
278     HWND targetWindow;
279     HRESULT hr;
280
281     XDNDxy.x = event->data.l[2] >> 16;
282     XDNDxy.y = event->data.l[2] & 0xFFFF;
283     XDNDxy.x += virtual_screen_rect.left;
284     XDNDxy.y += virtual_screen_rect.top;
285     targetWindow = WindowFromPoint(XDNDxy);
286
287     pointl.x = XDNDxy.x;
288     pointl.y = XDNDxy.y;
289     effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
290
291     if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
292     {
293         /* Notify OLE of DragEnter. Result determines if we accept */
294         HWND dropTargetWindow;
295
296         if (XDNDLastDropTargetWnd)
297         {
298             dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
299             if (dropTarget)
300             {
301                 hr = IDropTarget_DragLeave(dropTarget);
302                 if (FAILED(hr))
303                     WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
304                 IDropTarget_Release(dropTarget);
305             }
306         }
307         dropTargetWindow = targetWindow;
308         do
309         {
310             dropTarget = get_droptarget_pointer(dropTargetWindow);
311         } while (dropTarget == NULL && (dropTargetWindow = GetParent(dropTargetWindow)) != NULL);
312         XDNDLastTargetWnd = targetWindow;
313         XDNDLastDropTargetWnd = dropTargetWindow;
314         if (dropTarget)
315         {
316             hr = IDropTarget_DragEnter(dropTarget, &XDNDDataObject,
317                                        MK_LBUTTON, pointl, &effect);
318             if (SUCCEEDED(hr))
319             {
320                 if (effect != DROPEFFECT_NONE)
321                 {
322                     XDNDAccepted = TRUE;
323                     TRACE("the application accepted the drop\n");
324                 }
325                 else
326                     TRACE("the application refused the drop\n");
327             }
328             else
329                 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr);
330             IDropTarget_Release(dropTarget);
331         }
332     }
333     if (XDNDAccepted && XDNDLastTargetWnd == targetWindow)
334     {
335         /* If drag accepted notify OLE of DragOver */
336         dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
337         if (dropTarget)
338         {
339             hr = IDropTarget_DragOver(dropTarget, MK_LBUTTON, pointl, &effect);
340             if (SUCCEEDED(hr))
341                 XDNDDropEffect = effect;
342             else
343                 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr);
344             IDropTarget_Release(dropTarget);
345         }
346     }
347
348     if (XDNDAccepted)
349         accept = 1;
350     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
351         accept = 1;
352
353     TRACE("action req: %ld accept(%d) at x(%d),y(%d)\n",
354           event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
355
356     /*
357      * Let source know if we're accepting the drop by
358      * sending a status message.
359      */
360     e.type = ClientMessage;
361     e.display = event->display;
362     e.window = event->data.l[0];
363     e.message_type = x11drv_atom(XdndStatus);
364     e.format = 32;
365     e.data.l[0] = event->window;
366     e.data.l[1] = accept;
367     e.data.l[2] = 0; /* Empty Rect */
368     e.data.l[3] = 0; /* Empty Rect */
369     if (accept)
370         e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
371     else
372         e.data.l[4] = None;
373     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
374 }
375
376 /**************************************************************************
377  * X11DRV_XDND_DropEvent
378  *
379  * Handle an XdndDrop event.
380  */
381 void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
382 {
383     XClientMessageEvent e;
384     IDropTarget *dropTarget;
385
386     TRACE("\n");
387
388     /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
389     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
390         X11DRV_XDND_SendDropFiles( hWnd );
391
392     /* Notify OLE of Drop */
393     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
394     if (dropTarget)
395     {
396         HRESULT hr;
397         POINTL pointl;
398         DWORD effect = XDNDDropEffect;
399
400         pointl.x = XDNDxy.x;
401         pointl.y = XDNDxy.y;
402         hr = IDropTarget_Drop(dropTarget, &XDNDDataObject, MK_LBUTTON,
403                               pointl, &effect);
404         if (SUCCEEDED(hr))
405         {
406             if (effect != DROPEFFECT_NONE)
407                 TRACE("drop succeeded\n");
408             else
409                 TRACE("the application refused the drop\n");
410         }
411         else
412             WARN("drop failed, error 0x%08X\n", hr);
413         IDropTarget_Release(dropTarget);
414     }
415
416     X11DRV_XDND_FreeDragDropOp();
417
418     /* Tell the target we are finished. */
419     memset(&e, 0, sizeof(e));
420     e.type = ClientMessage;
421     e.display = event->display;
422     e.window = event->data.l[0];
423     e.message_type = x11drv_atom(XdndFinished);
424     e.format = 32;
425     e.data.l[0] = event->window;
426     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
427 }
428
429 /**************************************************************************
430  * X11DRV_XDND_LeaveEvent
431  *
432  * Handle an XdndLeave event.
433  */
434 void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
435 {
436     IDropTarget *dropTarget;
437
438     TRACE("DND Operation canceled\n");
439
440     /* Notify OLE of DragLeave */
441     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
442     if (dropTarget)
443     {
444         HRESULT hr = IDropTarget_DragLeave(dropTarget);
445         if (FAILED(hr))
446             WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
447         IDropTarget_Release(dropTarget);
448     }
449
450     X11DRV_XDND_FreeDragDropOp();
451 }
452
453
454 /**************************************************************************
455  * X11DRV_XDND_ResolveProperty
456  *
457  * Resolve all MIME types to windows clipboard formats. All data is cached.
458  */
459 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
460                                         Atom *types, unsigned long *count)
461 {
462     unsigned int i, j;
463     BOOL res;
464     XEvent xe;
465     Atom acttype;
466     int actfmt;
467     unsigned long bytesret, icount;
468     int entries = 0;
469     unsigned char* data = NULL;
470     XDNDDATA *current, *next;
471     BOOL haveHDROP = FALSE;
472
473     TRACE("count(%ld)\n", *count);
474
475     X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
476
477     for (i = 0; i < *count; i++)
478     {
479         TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
480
481         if (types[i] == 0)
482             continue;
483
484         XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
485                           x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
486
487         /*
488          * Wait for SelectionNotify
489          */
490         for (j = 0; j < SELECTION_RETRIES; j++)
491         {
492             res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
493             if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
494
495             usleep(SELECTION_WAIT);
496         }
497
498         if (xe.xselection.property == None)
499             continue;
500
501         XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
502             AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
503
504         entries += X11DRV_XDND_MapFormat(types[i], data, get_property_size( actfmt, icount ));
505         XFree(data);
506     }
507
508     /* On Windows when there is a CF_HDROP, there are no other CF_ formats.
509      * foobar2000 relies on this (spaces -> %20's without it).
510      */
511     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
512     {
513         if (current->cf_win == CF_HDROP)
514         {
515             haveHDROP = TRUE;
516             break;
517         }
518     }
519     if (haveHDROP)
520     {
521         LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
522         {
523             if (current->cf_win != CF_HDROP && current->cf_win < CF_MAX)
524             {
525                 list_remove(&current->entry);
526                 HeapFree(GetProcessHeap(), 0, current->data);
527                 HeapFree(GetProcessHeap(), 0, current);
528                 --entries;
529             }
530         }
531     }
532
533     *count = entries;
534 }
535
536
537 /**************************************************************************
538  * X11DRV_XDND_InsertXDNDData
539  *
540  * Cache available XDND property
541  */
542 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
543 {
544     LPXDNDDATA current = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
545
546     if (current)
547     {
548         EnterCriticalSection(&xdnd_cs);
549         current->cf_xdnd = property;
550         current->cf_win = format;
551         current->data = data;
552         current->size = len;
553         list_add_tail(&xdndData, &current->entry);
554         LeaveCriticalSection(&xdnd_cs);
555     }
556 }
557
558
559 /**************************************************************************
560  * X11DRV_XDND_MapFormat
561  *
562  * Map XDND MIME format to windows clipboard format.
563  */
564 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
565 {
566     void* xdata;
567     int count = 0;
568
569     TRACE("%d: %s\n", property, data);
570
571     /* Always include the raw type */
572     xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
573     memcpy(xdata, data, len);
574     X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
575     count++;
576
577     if (property == x11drv_atom(text_uri_list))
578         count += X11DRV_XDND_DeconstructTextURIList(property, data, len);
579     else if (property == x11drv_atom(text_plain))
580         count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
581     else if (property == x11drv_atom(text_html))
582         count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
583
584     return count;
585 }
586
587
588 /**************************************************************************
589  * X11DRV_XDND_DeconstructTextURIList
590  *
591  * Interpret text/uri-list data and add records to <dndfmt> linked list
592  */
593 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len)
594 {
595     char *uriList = data;
596     char *uri;
597     WCHAR *path;
598
599     WCHAR *out = NULL;
600     int size = 0;
601     int capacity = 4096;
602
603     int count = 0;
604     int start = 0;
605     int end = 0;
606
607     out = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(WCHAR));
608     if (out == NULL)
609         return 0;
610
611     while (end < len)
612     {
613         while (end < len && uriList[end] != '\r')
614             ++end;
615         if (end < (len - 1) && uriList[end+1] != '\n')
616         {
617             WARN("URI list line doesn't end in \\r\\n\n");
618             break;
619         }
620
621         uri = HeapAlloc(GetProcessHeap(), 0, end - start + 1);
622         if (uri == NULL)
623             break;
624         lstrcpynA(uri, &uriList[start], end - start + 1);
625         path = X11DRV_XDND_URIToDOS(uri);
626         TRACE("converted URI %s to DOS path %s\n", debugstr_a(uri), debugstr_w(path));
627         HeapFree(GetProcessHeap(), 0, uri);
628
629         if (path)
630         {
631             int pathSize = strlenW(path) + 1;
632             if (pathSize > capacity-size)
633             {
634                 capacity = 2*capacity + pathSize;
635                 out = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, out, (capacity + 1)*sizeof(WCHAR));
636                 if (out == NULL)
637                     goto done;
638             }
639             memcpy(&out[size], path, pathSize * sizeof(WCHAR));
640             size += pathSize;
641         done:
642             HeapFree(GetProcessHeap(), 0, path);
643             if (out == NULL)
644                 break;
645         }
646
647         start = end + 2;
648         end = start;
649     }
650     if (out && end >= len)
651     {
652         DROPFILES *dropFiles;
653         dropFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
654         if (dropFiles)
655         {
656             dropFiles->pFiles = sizeof(DROPFILES);
657             dropFiles->pt.x = XDNDxy.x;
658             dropFiles->pt.y = XDNDxy.y;
659             dropFiles->fNC = 0;
660             dropFiles->fWide = TRUE;
661             out[size] = '\0';
662             memcpy(((char*)dropFiles) + dropFiles->pFiles, out, (size + 1)*sizeof(WCHAR));
663             X11DRV_XDND_InsertXDNDData(property, CF_HDROP, dropFiles, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
664             count = 1;
665         }
666     }
667     HeapFree(GetProcessHeap(), 0, out);
668     return count;
669 }
670
671
672 /**************************************************************************
673  * X11DRV_XDND_DeconstructTextPlain
674  *
675  * Interpret text/plain Data and add records to <dndfmt> linked list
676  */
677 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
678 {
679     char* dostext;
680
681     /* Always supply plain text */
682     X11DRV_XDND_UnixToDos(&dostext, data, len);
683     X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
684
685     TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
686
687     return 1;
688 }
689
690
691 /**************************************************************************
692  * X11DRV_XDND_DeconstructTextHTML
693  *
694  * Interpret text/html data and add records to <dndfmt> linked list
695  */
696 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
697 {
698     char* dostext;
699
700     X11DRV_XDND_UnixToDos(&dostext, data, len);
701
702     X11DRV_XDND_InsertXDNDData(property,
703         RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
704
705     TRACE("UniformResourceLocator: %s\n", dostext);
706
707     return 1;
708 }
709
710
711 /**************************************************************************
712  * X11DRV_XDND_SendDropFiles
713  */
714 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
715 {
716     LPXDNDDATA current = NULL;
717     BOOL found = FALSE;
718
719     EnterCriticalSection(&xdnd_cs);
720
721     /* Find CF_HDROP type if any */
722     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
723     {
724         if (current->cf_win == CF_HDROP)
725         {
726             found = TRUE;
727             break;
728         }
729     }
730
731     if (found)
732     {
733         DROPFILES *lpDrop = current->data;
734
735         if (lpDrop)
736         {
737             lpDrop->pt.x = XDNDxy.x;
738             lpDrop->pt.y = XDNDxy.y;
739
740             TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
741                 ((char*)lpDrop) + lpDrop->pFiles, debugstr_w((WCHAR*)(((char*)lpDrop) + lpDrop->pFiles)));
742
743             PostMessageW(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
744         }
745     }
746
747     LeaveCriticalSection(&xdnd_cs);
748 }
749
750
751 /**************************************************************************
752  * X11DRV_XDND_FreeDragDropOp
753  */
754 static void X11DRV_XDND_FreeDragDropOp(void)
755 {
756     LPXDNDDATA next;
757     LPXDNDDATA current;
758
759     TRACE("\n");
760
761     EnterCriticalSection(&xdnd_cs);
762
763     /** Free data cache */
764     LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
765     {
766         list_remove(&current->entry);
767         HeapFree(GetProcessHeap(), 0, current);
768     }
769
770     XDNDxy.x = XDNDxy.y = 0;
771     XDNDLastTargetWnd = NULL;
772     XDNDLastDropTargetWnd = NULL;
773     XDNDAccepted = FALSE;
774
775     LeaveCriticalSection(&xdnd_cs);
776 }
777
778
779
780 /**************************************************************************
781  * X11DRV_XDND_UnixToDos
782  */
783 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
784 {
785     int i;
786     unsigned int destlen, lines;
787
788     for (i = 0, lines = 0; i <= len; i++)
789     {
790         if (lpsrc[i] == '\n')
791             lines++;
792     }
793
794     destlen = len + lines + 1;
795
796     if (lpdest)
797     {
798         char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
799         for (i = 0, lines = 0; i <= len; i++)
800         {
801             if (lpsrc[i] == '\n')
802                 lpstr[++lines + i] = '\r';
803             lpstr[lines + i] = lpsrc[i];
804         }
805
806         *lpdest = lpstr;
807     }
808
809     return lines;
810 }
811
812
813 /**************************************************************************
814  * X11DRV_XDND_URIToDOS
815  */
816 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI)
817 {
818     WCHAR *ret = NULL;
819     int i;
820     int j = 0;
821     char *uri = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(encodedURI) + 1);
822     if (uri == NULL)
823         return NULL;
824     for (i = 0; encodedURI[i]; ++i)
825     {
826         if (encodedURI[i] == '%')
827         { 
828             if (encodedURI[i+1] && encodedURI[i+2])
829             {
830                 char buffer[3];
831                 int number;
832                 buffer[0] = encodedURI[i+1];
833                 buffer[1] = encodedURI[i+2];
834                 buffer[2] = '\0';
835                 sscanf(buffer, "%x", &number);
836                 uri[j++] = number;
837                 i += 2;
838             }
839             else
840             {
841                 WARN("invalid URI encoding in %s\n", debugstr_a(encodedURI));
842                 HeapFree(GetProcessHeap(), 0, uri);
843                 return NULL;
844             }
845         }
846         else
847             uri[j++] = encodedURI[i];
848     }
849
850     /* Read http://www.freedesktop.org/wiki/Draganddropwarts and cry... */
851     if (strncmp(uri, "file:/", 6) == 0)
852     {
853         if (uri[6] == '/')
854         {
855             if (uri[7] == '/')
856             {
857                 /* file:///path/to/file (nautilus, thunar) */
858                 ret = wine_get_dos_file_name(&uri[7]);
859             }
860             else if (uri[7])
861             {
862                 /* file://hostname/path/to/file (X file drag spec) */
863                 char hostname[256];
864                 char *path = strchr(&uri[7], '/');
865                 if (path)
866                 {
867                     *path = '\0';
868                     if (strcmp(&uri[7], "localhost") == 0)
869                     {
870                         *path = '/';
871                         ret = wine_get_dos_file_name(path);
872                     }
873                     else if (gethostname(hostname, sizeof(hostname)) == 0)
874                     {
875                         if (strcmp(hostname, &uri[7]) == 0)
876                         {
877                             *path = '/';
878                             ret = wine_get_dos_file_name(path);
879                         }
880                     }
881                 }
882             }
883         }
884         else if (uri[6])
885         {
886             /* file:/path/to/file (konqueror) */
887             ret = wine_get_dos_file_name(&uri[5]);
888         }
889     }
890     HeapFree(GetProcessHeap(), 0, uri);
891     return ret;
892 }
893
894
895 /**************************************************************************
896  * X11DRV_XDND_DescribeClipboardFormat
897  */
898 static void X11DRV_XDND_DescribeClipboardFormat(int cfFormat, char *buffer, int size)
899 {
900 #define D(x) case x: lstrcpynA(buffer, #x, size); return;
901     switch (cfFormat)
902     {
903         D(CF_TEXT)
904         D(CF_BITMAP)
905         D(CF_METAFILEPICT)
906         D(CF_SYLK)
907         D(CF_DIF)
908         D(CF_TIFF)
909         D(CF_OEMTEXT)
910         D(CF_DIB)
911         D(CF_PALETTE)
912         D(CF_PENDATA)
913         D(CF_RIFF)
914         D(CF_WAVE)
915         D(CF_UNICODETEXT)
916         D(CF_ENHMETAFILE)
917         D(CF_HDROP)
918         D(CF_LOCALE)
919         D(CF_DIBV5)
920     }
921 #undef D
922
923     if (CF_PRIVATEFIRST <= cfFormat && cfFormat <= CF_PRIVATELAST)
924     {
925         lstrcpynA(buffer, "some private object", size);
926         return;
927     }
928     if (CF_GDIOBJFIRST <= cfFormat && cfFormat <= CF_GDIOBJLAST)
929     {
930         lstrcpynA(buffer, "some GDI object", size);
931         return;
932     }
933
934     GetClipboardFormatNameA(cfFormat, buffer, size);
935 }
936
937
938 /* The IDataObject singleton we feed to OLE follows */
939
940 static HRESULT WINAPI XDNDDATAOBJECT_QueryInterface(IDataObject *dataObject,
941                                                     REFIID riid, void **ppvObject)
942 {
943     TRACE("(%p, %s, %p)\n", dataObject, debugstr_guid(riid), ppvObject);
944     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDataObject))
945     {
946         *ppvObject = dataObject;
947         IDataObject_AddRef(dataObject);
948         return S_OK;
949     }
950     *ppvObject = NULL;
951     return E_NOINTERFACE;
952 }
953
954 static ULONG WINAPI XDNDDATAOBJECT_AddRef(IDataObject *dataObject)
955 {
956     TRACE("(%p)\n", dataObject);
957     return 2;
958 }
959
960 static ULONG WINAPI XDNDDATAOBJECT_Release(IDataObject *dataObject)
961 {
962     TRACE("(%p)\n", dataObject);
963     return 1;
964 }
965
966 static HRESULT WINAPI XDNDDATAOBJECT_GetData(IDataObject *dataObject,
967                                              FORMATETC *formatEtc,
968                                              STGMEDIUM *pMedium)
969 {
970     HRESULT hr;
971     char formatDesc[1024];
972
973     TRACE("(%p, %p, %p)\n", dataObject, formatEtc, pMedium);
974     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat,
975         formatDesc, sizeof(formatDesc));
976     TRACE("application is looking for %s\n", formatDesc);
977
978     hr = IDataObject_QueryGetData(dataObject, formatEtc);
979     if (SUCCEEDED(hr))
980     {
981         XDNDDATA *current;
982         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
983         {
984             if (current->cf_win == formatEtc->cfFormat)
985             {
986                 pMedium->tymed = TYMED_HGLOBAL;
987                 pMedium->u.hGlobal = HeapAlloc(GetProcessHeap(), 0, current->size);
988                 if (pMedium->u.hGlobal == NULL)
989                     return E_OUTOFMEMORY;
990                 memcpy(pMedium->u.hGlobal, current->data, current->size);
991                 pMedium->pUnkForRelease = 0;
992                 return S_OK;
993             }
994         }
995     }
996     return hr;
997 }
998
999 static HRESULT WINAPI XDNDDATAOBJECT_GetDataHere(IDataObject *dataObject,
1000                                                  FORMATETC *formatEtc,
1001                                                  STGMEDIUM *pMedium)
1002 {
1003     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, pMedium);
1004     return DATA_E_FORMATETC;
1005 }
1006
1007 static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject,
1008                                                   FORMATETC *formatEtc)
1009 {
1010     char formatDesc[1024];
1011     XDNDDATA *current;
1012
1013     TRACE("(%p, %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%d}\n",
1014         dataObject, formatEtc, formatEtc->tymed, formatEtc->dwAspect, formatEtc->cfFormat);
1015     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat, formatDesc, sizeof(formatDesc));
1016
1017     if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL))
1018     {
1019         FIXME("only HGLOBAL medium types supported right now\n");
1020         return DV_E_TYMED;
1021     }
1022     if (formatEtc->dwAspect != DVASPECT_CONTENT)
1023     {
1024         FIXME("only the content aspect is supported right now\n");
1025         return E_NOTIMPL;
1026     }
1027
1028     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1029     {
1030         if (current->cf_win == formatEtc->cfFormat)
1031         {
1032             TRACE("application found %s\n", formatDesc);
1033             return S_OK;
1034         }
1035     }
1036     TRACE("application didn't find %s\n", formatDesc);
1037     return DV_E_FORMATETC;
1038 }
1039
1040 static HRESULT WINAPI XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject *dataObject,
1041                                                            FORMATETC *formatEtc,
1042                                                            FORMATETC *formatEtcOut)
1043 {
1044     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, formatEtcOut);
1045     formatEtcOut->ptd = NULL;
1046     return E_NOTIMPL;
1047 }
1048
1049 static HRESULT WINAPI XDNDDATAOBJECT_SetData(IDataObject *dataObject,
1050                                              FORMATETC *formatEtc,
1051                                              STGMEDIUM *pMedium, BOOL fRelease)
1052 {
1053     FIXME("(%p, %p, %p, %s): stub\n", dataObject, formatEtc,
1054         pMedium, fRelease?"TRUE":"FALSE");
1055     return E_NOTIMPL;
1056 }
1057
1058 static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject,
1059                                                    DWORD dwDirection,
1060                                                    IEnumFORMATETC **ppEnumFormatEtc)
1061 {
1062     DWORD count;
1063     FORMATETC *formats;
1064
1065     TRACE("(%p, %u, %p)\n", dataObject, dwDirection, ppEnumFormatEtc);
1066
1067     if (dwDirection != DATADIR_GET)
1068     {
1069         FIXME("only the get direction is implemented\n");
1070         return E_NOTIMPL;
1071     }
1072
1073     count = list_count(&xdndData);
1074     formats = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC));
1075     if (formats)
1076     {
1077         XDNDDATA *current;
1078         DWORD i = 0;
1079         HRESULT hr;
1080         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1081         {
1082             formats[i].cfFormat = current->cf_win;
1083             formats[i].ptd = NULL;
1084             formats[i].dwAspect = DVASPECT_CONTENT;
1085             formats[i].lindex = -1;
1086             formats[i].tymed = TYMED_HGLOBAL;
1087             i++;
1088         }
1089         hr = SHCreateStdEnumFmtEtc(count, formats, ppEnumFormatEtc);
1090         HeapFree(GetProcessHeap(), 0, formats);
1091         return hr;
1092     }
1093     else
1094         return E_OUTOFMEMORY;
1095 }
1096
1097 static HRESULT WINAPI XDNDDATAOBJECT_DAdvise(IDataObject *dataObject,
1098                                              FORMATETC *formatEtc, DWORD advf,
1099                                              IAdviseSink *adviseSink,
1100                                              DWORD *pdwConnection)
1101 {
1102     FIXME("(%p, %p, %u, %p, %p): stub\n", dataObject, formatEtc, advf,
1103         adviseSink, pdwConnection);
1104     return OLE_E_ADVISENOTSUPPORTED;
1105 }
1106
1107 static HRESULT WINAPI XDNDDATAOBJECT_DUnadvise(IDataObject *dataObject,
1108                                                DWORD dwConnection)
1109 {
1110     FIXME("(%p, %u): stub\n", dataObject, dwConnection);
1111     return OLE_E_ADVISENOTSUPPORTED;
1112 }
1113
1114 static HRESULT WINAPI XDNDDATAOBJECT_EnumDAdvise(IDataObject *dataObject,
1115                                                  IEnumSTATDATA **pEnumAdvise)
1116 {
1117     FIXME("(%p, %p): stub\n", dataObject, pEnumAdvise);
1118     return OLE_E_ADVISENOTSUPPORTED;
1119 }
1120
1121 static IDataObjectVtbl xdndDataObjectVtbl =
1122 {
1123     XDNDDATAOBJECT_QueryInterface,
1124     XDNDDATAOBJECT_AddRef,
1125     XDNDDATAOBJECT_Release,
1126     XDNDDATAOBJECT_GetData,
1127     XDNDDATAOBJECT_GetDataHere,
1128     XDNDDATAOBJECT_QueryGetData,
1129     XDNDDATAOBJECT_GetCanonicalFormatEtc,
1130     XDNDDATAOBJECT_SetData,
1131     XDNDDATAOBJECT_EnumFormatEtc,
1132     XDNDDATAOBJECT_DAdvise,
1133     XDNDDATAOBJECT_DUnadvise,
1134     XDNDDATAOBJECT_EnumDAdvise
1135 };
1136
1137 static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };