4 * Copyright 2003 Ulrich Czekalla
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include "shlobj.h" /* DROPFILES */
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
43 /* Maximum wait time for selection notify */
44 #define SELECTION_RETRIES 500 /* wait for .1 seconds */
45 #define SELECTION_WAIT 1000 /* us */
47 typedef struct tagXDNDDATA
53 struct tagXDNDDATA *next;
54 } XDNDDATA, *LPXDNDDATA;
56 static LPXDNDDATA XDNDData = NULL;
57 static POINT XDNDxy = { 0, 0 };
59 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
60 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
61 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
62 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
63 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
64 Atom *types, unsigned long *count);
65 static void X11DRV_XDND_SendDropFiles(HWND hwnd);
66 static void X11DRV_XDND_FreeDragDropOp(void);
67 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
68 static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt);
70 static CRITICAL_SECTION xdnd_cs;
71 static CRITICAL_SECTION_DEBUG critsect_debug =
74 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75 0, 0, { 0, (DWORD)(__FILE__ ": xdnd_cs") }
77 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
79 /**************************************************************************
82 * Entry point for X11 XDND events. Returns FALSE if event is not handled.
84 int X11DRV_XDND_Event(HWND hWnd, XClientMessageEvent *event)
88 TRACE("0x%p\n", hWnd);
90 if (event->message_type == x11drv_atom(XdndEnter))
93 unsigned long count = 0;
95 TRACE("XDNDEnter: ver(%ld) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
96 (event->data.l[1] & 0xFF000000) >> 24, (event->data.l[1] & 1),
97 event->data.l[0], event->data.l[1], event->data.l[2],
98 event->data.l[3], event->data.l[4]);
100 /* If the source supports more than 3 data types we retrieve
101 * the entire list. */
102 if (event->data.l[1] & 1)
106 unsigned long bytesret;
108 /* Request supported formats from source window */
110 XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
111 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
112 &bytesret, (unsigned char**)&xdndtypes);
118 xdndtypes = &event->data.l[2];
126 for (; i < count; i++)
128 if (xdndtypes[i] != 0)
130 char * pn = XGetAtomName(event->display, xdndtypes[i]);
131 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
138 /* Do a one-time data read and cache results */
139 X11DRV_XDND_ResolveProperty(event->display, event->window,
140 event->data.l[1], xdndtypes, &count);
142 if (event->data.l[1] & 1)
145 else if (event->message_type == x11drv_atom(XdndPosition))
147 XClientMessageEvent e;
148 int accept = 0; /* Assume we're not accepting */
150 XDNDxy.x = event->data.l[2] >> 16;
151 XDNDxy.y = event->data.l[2] & 0xFFFF;
153 /* FIXME: Notify OLE of DragEnter. Result determines if we accept */
155 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
158 TRACE("XDNDPosition. action req: %ld accept(%d) at x(%ld),y(%ld)\n",
159 event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
162 * Let source know if we're accepting the drop by
163 * sending a status message.
165 e.type = ClientMessage;
166 e.display = event->display;
167 e.window = event->data.l[0];
168 e.message_type = x11drv_atom(XdndStatus);
170 e.data.l[0] = event->window;
171 e.data.l[1] = accept;
172 e.data.l[2] = 0; /* Empty Rect */
173 e.data.l[3] = 0; /* Empty Rect */
175 e.data.l[4] = event->data.l[4];
179 XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
182 /* FIXME: if drag accepted notify OLE of DragOver */
184 else if (event->message_type == x11drv_atom(XdndDrop))
186 XClientMessageEvent e;
190 /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
191 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
192 X11DRV_XDND_SendDropFiles( hWnd );
194 /* FIXME: Notify OLE of Drop */
195 X11DRV_XDND_FreeDragDropOp();
197 /* Tell the target we are finished. */
198 bzero(&e, sizeof(e));
199 e.type = ClientMessage;
200 e.display = event->display;
201 e.window = event->data.l[0];
202 e.message_type = x11drv_atom(XdndFinished);
204 e.data.l[0] = event->window;
206 XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
209 else if (event->message_type == x11drv_atom(XdndLeave))
211 TRACE("DND Operation canceled\n");
213 X11DRV_XDND_FreeDragDropOp();
215 /* FIXME: Notify OLE of DragLeave */
217 else /* Not an XDND message */
224 /**************************************************************************
225 * X11DRV_XDND_ResolveProperty
227 * Resolve all MIME types to windows clipboard formats. All data is cached.
229 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
230 Atom *types, unsigned long *count)
237 unsigned long bytesret, icount;
239 unsigned char* data = NULL;
241 TRACE("count(%ld)\n", *count);
243 X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
245 for (i = 0; i < *count; i++)
247 TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
253 XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
254 x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
258 * Wait for SelectionNotify
260 for (j = 0; j < SELECTION_RETRIES; j++)
263 res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
265 if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
267 usleep(SELECTION_WAIT);
270 if (xe.xselection.property == None)
274 XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
275 AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
278 entries += X11DRV_XDND_MapFormat(types[i], data, icount * (actfmt / 8));
288 /**************************************************************************
289 * X11DRV_XDND_InsertXDNDData
291 * Cache available XDND property
293 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
295 LPXDNDDATA current = (LPXDNDDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
299 EnterCriticalSection(&xdnd_cs);
300 current->next = XDNDData;
301 current->cf_xdnd = property;
302 current->cf_win = format;
303 current->data = data;
306 LeaveCriticalSection(&xdnd_cs);
311 /**************************************************************************
312 * X11DRV_XDND_MapFormat
314 * Map XDND MIME format to windows clipboard format.
316 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
321 TRACE("%d: %s\n", property, data);
323 /* Always include the raw type */
324 xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
325 memcpy(xdata, data, len);
326 X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
329 if (property == x11drv_atom(text_plain))
330 count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
331 else if (property == x11drv_atom(text_html))
332 count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
338 /**************************************************************************
339 * X11DRV_XDND_DeconstructTextPlain
341 * Interpret text/plain Data and add records to <dndfmt> linked list
343 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
345 char *p = (char*) data;
349 /* Always suppply plain text */
350 X11DRV_XDND_UnixToDos(&dostext, (char*)data, len);
351 X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
354 TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
356 /* Check for additional mappings */
357 while (*p != '\0' && *p != ':') /* Advance to end of protocol */
362 if (!strncasecmp(data, "http", 4))
364 X11DRV_XDND_InsertXDNDData(property, RegisterClipboardFormatA("UniformResourceLocator"),
365 strdup(dostext), strlen(dostext));
368 TRACE("UniformResourceLocator: %s\n", dostext);
370 else if (!strncasecmp(data, "file", 4))
374 pdf = X11DRV_XDND_BuildDropFiles(p+1, len - 5, XDNDxy);
377 unsigned int size = HeapSize(GetProcessHeap(), 0, pdf);
379 X11DRV_XDND_InsertXDNDData(property, CF_HDROP, pdf, size);
383 TRACE("CF_HDROP: %p\n", pdf);
391 /**************************************************************************
392 * X11DRV_XDND_DeconstructTextHTML
394 * Interpret text/html data and add records to <dndfmt> linked list
396 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
400 X11DRV_XDND_UnixToDos(&dostext, data, len);
402 X11DRV_XDND_InsertXDNDData(property,
403 RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
405 TRACE("UniformResourceLocator: %s\n", dostext);
411 /**************************************************************************
412 * X11DRV_XDND_SendDropFiles
414 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
418 EnterCriticalSection(&xdnd_cs);
422 /* Find CF_HDROP type if any */
423 while (current != NULL)
425 if (current->cf_win == CF_HDROP)
427 current = current->next;
432 DROPFILES *lpDrop = (DROPFILES*) current->data;
436 lpDrop->pt.x = XDNDxy.x;
437 lpDrop->pt.y = XDNDxy.y;
439 TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
440 ((char*)lpDrop) + lpDrop->pFiles, ((char*)lpDrop) + lpDrop->pFiles);
442 PostMessageA(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
446 LeaveCriticalSection(&xdnd_cs);
450 /**************************************************************************
451 * X11DRV_XDND_FreeDragDropOp
453 static void X11DRV_XDND_FreeDragDropOp(void)
460 EnterCriticalSection(&xdnd_cs);
464 /** Free data cache */
465 while (current != NULL)
467 next = current->next;
468 HeapFree(GetProcessHeap(), 0, current);
473 XDNDxy.x = XDNDxy.y = 0;
475 LeaveCriticalSection(&xdnd_cs);
480 /**************************************************************************
481 * X11DRV_XDND_BuildDropFiles
483 static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt)
488 DROPFILES *lpDrop = NULL;
490 /* Advance to last starting slash */
492 while (*pfn && (*pfn == '\\' || *pfn =='/'))
498 /* Remove any trailing \r or \n */
501 if (*pfn == '\r' || *pfn == '\n')
509 TRACE("%s\n", filename);
511 pathlen = GetLongPathNameA(filename, path, MAX_PATH);
514 lpDrop = (DROPFILES*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
515 sizeof(DROPFILES) + pathlen + 1);
517 lpDrop->pFiles = sizeof(DROPFILES);
521 lpDrop->fWide = FALSE;
523 strcpy(((char*)lpDrop)+lpDrop->pFiles, path);
526 TRACE("resolved %s\n", lpDrop ? filename : NULL);
532 /**************************************************************************
533 * X11DRV_XDND_UnixToDos
535 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
538 unsigned int destlen, lines;
540 for (i = 0, lines = 0; i <= len; i++)
542 if (lpsrc[i] == '\n')
546 destlen = len + lines + 1;
550 char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
551 for (i = 0, lines = 0; i <= len; i++)
553 if (lpsrc[i] == '\n')
554 lpstr[++lines + i] = '\r';
555 lpstr[lines + i] = lpsrc[i];