- added WMGetLabelText()
[wmaker-crm.git] / WINGs / dragsource.c
blobfc3a7eeedebba04c2f0c3184404636b0e0e37ea1
2 #include "../src/config.h"
4 #include <X11/Xlib.h>
5 #include <X11/Xutil.h>
6 #include <X11/Xatom.h>
8 #ifdef SHAPE
9 #include <X11/extensions/shape.h>
10 #endif
12 #include <math.h>
15 #include "WINGsP.h"
20 #define SPIT(a)
23 #define IS_DROPPABLE(view) (view!=NULL && view->droppableTypes!=NULL && \
24 view->dragDestinationProcs!=NULL)
27 static Atom operationToAction(WMScreen *scr, WMDragOperationType operation);
28 static WMDragOperationType actionToOperation(WMScreen *scr, Atom action);
30 static Bool _XErrorOccured = False;
34 static unsigned defDraggingSourceOperation(WMView *self, Bool local)
36 return WDOperationCopy;
40 static void defBeganDragImage(WMView *self, WMPixmap *image, WMPoint point)
45 static void defEndedDragImage(WMView *self, WMPixmap *image, WMPoint point,
46 Bool deposited)
50 static WMData* defFetchDragData(WMView *self, char *type)
52 return NULL;
56 void
57 WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
59 if (view->dragSourceProcs)
60 free(view->dragSourceProcs);
61 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
63 *view->dragSourceProcs = *procs;
65 if (procs->draggingSourceOperation == NULL) {
66 view->dragSourceProcs->draggingSourceOperation = defDraggingSourceOperation;
68 if (procs->beganDragImage == NULL) {
69 view->dragSourceProcs->beganDragImage = defBeganDragImage;
71 if (procs->endedDragImage == NULL) {
72 view->dragSourceProcs->endedDragImage = defEndedDragImage;
74 if (procs->fetchDragData == NULL) {
75 view->dragSourceProcs->fetchDragData = defFetchDragData;
80 /***********************************************************************/
83 static int
84 handleXError(Display *dpy, XErrorEvent *ev)
86 _XErrorOccured = True;
88 return 1;
92 static void
93 protectBlock(Bool begin)
95 static void *oldHandler = NULL;
97 if (begin) {
98 oldHandler = XSetErrorHandler(handleXError);
99 } else {
100 XSetErrorHandler(oldHandler);
107 static Window
108 makeDragIcon(WMScreen *scr, WMPixmap *pixmap)
110 Window window;
111 WMSize size;
112 unsigned long flags;
113 XSetWindowAttributes attribs;
114 Pixmap pix, mask;
116 if (!pixmap) {
117 pixmap = scr->defaultObjectIcon;
119 size = WMGetPixmapSize(pixmap);
120 pix = pixmap->pixmap;
121 mask = pixmap->mask;
123 flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap;
124 attribs.save_under = True;
125 attribs.background_pixmap = pix;
126 attribs.override_redirect = True;
127 attribs.colormap = scr->colormap;
128 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
129 size.height, 0, scr->depth, InputOutput,
130 scr->visual, flags, &attribs);
132 #ifdef SHAPE
133 if (mask) {
134 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask,
135 ShapeSet);
137 #endif
139 return window;
143 static void
144 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
146 double x, y, dx, dy;
147 int i;
148 int iterations;
150 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
152 x = srcX;
153 y = srcY;
155 dx = (double)(dstX-srcX)/iterations;
156 dy = (double)(dstY-srcY)/iterations;
158 for (i = 0; i <= iterations; i++) {
159 XMoveWindow(dpy, win, x, y);
160 XFlush(dpy);
162 wusleep(800);
164 x += dx;
165 y += dy;
170 static Window
171 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
173 Window foo, bar;
174 Window *children;
175 unsigned nchildren;
176 int i;
178 if (!XQueryTree(dpy, toplevel, &foo, &bar,
179 &children, &nchildren) || children == NULL) {
180 return None;
183 /* first window that contains the point is the one */
184 for (i = nchildren-1; i >= 0; i--) {
185 XWindowAttributes attr;
187 if (XGetWindowAttributes(dpy, children[i], &attr)
188 && attr.map_state == IsViewable
189 && x >= attr.x && y >= attr.y
190 && x < attr.x + attr.width && y < attr.y + attr.height) {
191 Window child, tmp;
193 tmp = children[i];
195 child = findChildInWindow(dpy, tmp, x - attr.x, y - attr.y);
197 XFree(children);
199 if (child == None)
200 return tmp;
201 else
202 return child;
206 XFree(children);
207 return None;
211 static WMView*
212 findViewInToplevel(Display *dpy, Window toplevel, int x, int y)
214 Window child;
216 child = findChildInWindow(dpy, toplevel, x, y);
218 if (child != None) {
219 return W_GetViewForXWindow(dpy, child);
220 } else {
221 return NULL;
227 static Window
228 lookForToplevel(WMScreen *scr, Window window, Bool *isAware)
230 Window toplevel = None;
231 Atom *atoms;
232 int j, count;
234 *isAware = False;
236 atoms = XListProperties(scr->display, window, &count);
237 for (j = 0; j < count; j++) {
238 if (atoms[j] == scr->wmStateAtom) {
239 toplevel = window;
240 } else if (atoms[j] == scr->xdndAwareAtom) {
241 *isAware = True;
244 if (atoms)
245 XFree(atoms);
247 if (toplevel == None) {
248 Window *children;
249 Window foo, bar;
250 unsigned nchildren;
252 if (!XQueryTree(scr->display, window, &foo, &bar,
253 &children, &nchildren) || children == NULL) {
254 return None;
257 for (j = 0; j < nchildren; j++) {
258 toplevel = lookForToplevel(scr, children[j], isAware);
259 if (toplevel != None)
260 break;
263 XFree(children);
266 return toplevel;
271 static Window
272 findToplevelUnderDragPointer(WMScreen *scr, int x, int y, Window iconWindow)
274 Window foo, bar;
275 Window *children;
276 unsigned nchildren;
277 Bool overSomething = False;
278 int i;
280 if (!XQueryTree(scr->display, scr->rootWin, &foo, &bar,
281 &children, &nchildren) || children == NULL) {
282 SPIT("couldnt query tree!");
283 return None;
286 /* try to find the window below the iconWindow by traversing
287 * the whole window list */
289 /* first find the position of the iconWindow */
290 for (i = nchildren-1; i >= 0; i--) {
291 if (children[i] == iconWindow) {
292 i--;
293 break;
296 if (i <= 0) {
297 XFree(children);
298 return scr->rootWin;
301 /* first window that contains the point is the one */
302 for (; i >= 0; i--) {
303 XWindowAttributes attr;
305 if (XGetWindowAttributes(scr->display, children[i], &attr)
306 && attr.map_state == IsViewable
307 && x >= attr.x && y >= attr.y
308 && x < attr.x + attr.width && y < attr.y + attr.height) {
309 Window toplevel;
310 Bool isaware;
312 overSomething = True;
314 toplevel = lookForToplevel(scr, children[i], &isaware);
316 XFree(children);
318 if (isaware)
319 return toplevel;
320 else
321 return None;
325 XFree(children);
326 if (!overSomething)
327 return scr->rootWin;
328 else
329 return None;
337 static void
338 sendClientMessage(Display *dpy, Window win, Atom message,
339 unsigned data1, unsigned data2, unsigned data3,
340 unsigned data4, unsigned data5)
342 XEvent ev;
344 ev.type = ClientMessage;
345 ev.xclient.message_type = message;
346 ev.xclient.format = 32;
347 ev.xclient.window = win;
348 ev.xclient.data.l[0] = data1;
349 ev.xclient.data.l[1] = data2;
350 ev.xclient.data.l[2] = data3;
351 ev.xclient.data.l[3] = data4;
352 ev.xclient.data.l[4] = data5;
354 XSendEvent(dpy, win, False, 0, &ev);
355 XFlush(dpy);
361 static unsigned
362 notifyPosition(WMScreen *scr, WMDraggingInfo *info)
364 Atom action = operationToAction(scr, info->sourceOperation);
366 sendClientMessage(scr->display, info->destinationWindow,
367 scr->xdndPositionAtom,
368 info->sourceWindow,
369 0, /* reserved */
370 info->location.x<<16|info->location.y,
371 info->timestamp,
372 action/* operation */);
374 return 0;
379 static void
380 notifyDrop(WMScreen *scr, WMDraggingInfo *info)
382 sendClientMessage(scr->display, info->destinationWindow,
383 scr->xdndDropAtom,
384 info->sourceWindow,
385 0, /* reserved */
386 info->timestamp,
387 0, 0);
392 static void
393 notifyDragLeave(WMScreen *scr, WMDraggingInfo *info)
395 sendClientMessage(scr->display, info->destinationWindow,
396 scr->xdndLeaveAtom,
397 info->sourceWindow, 0, 0, 0, 0);
402 static unsigned
403 notifyDragEnter(WMScreen *scr, WMDraggingInfo *info)
405 unsigned d;
407 d = XDND_VERSION << 24;
409 sendClientMessage(scr->display, info->destinationWindow,
410 scr->xdndEnterAtom,
411 info->sourceWindow, d, 0, 0, 0);
413 return 0;
417 static void
418 translateCoordinates(WMScreen *scr, Window target, int fromX, int fromY,
419 int *x, int *y)
421 Window child;
423 XTranslateCoordinates(scr->display, scr->rootWin, target,
424 fromX, fromY, x, y, &child);
428 static void
429 updateDraggingInfo(WMScreen *scr, WMDraggingInfo *info, WMSize offset,
430 XEvent *event, Window iconWindow)
432 Window toplevel;
434 if (event->type == MotionNotify) {
435 info->imageLocation.x = event->xmotion.x_root-offset.width;
436 info->imageLocation.y = event->xmotion.y_root-offset.height;
438 info->location.x = event->xmotion.x_root;
439 info->location.y = event->xmotion.y_root;
440 /* info->timestamp = event->xmotion.time;*/
442 } else if (event->type == ButtonRelease) {
443 info->imageLocation.x = event->xbutton.x_root-offset.width;
444 info->imageLocation.y = event->xbutton.y_root-offset.height;
446 info->location.x = event->xbutton.x_root;
447 info->location.y = event->xbutton.y_root;
448 /* info->timestamp = event->xbutton.time;*/
451 toplevel = findToplevelUnderDragPointer(scr,
452 info->location.x,
453 info->location.y,
454 iconWindow);
455 info->destinationWindow = toplevel;
461 static void
462 processMotion(WMScreen *scr, WMDraggingInfo *info, WMDraggingInfo *oldInfo,
463 WMRect *rect, unsigned currentAction)
465 unsigned action;
467 if (info->destinationWindow == None) { /* entered an unsupporeted window */
469 if (oldInfo->destinationWindow != None
470 && oldInfo->destinationWindow != scr->rootWin) {
471 SPIT("left window");
473 notifyDragLeave(scr, oldInfo);
476 } else if (info->destinationWindow == scr->rootWin) {
478 if (oldInfo->destinationWindow != None
479 && oldInfo->destinationWindow != scr->rootWin) {
480 SPIT("left window to root");
482 notifyDragLeave(scr, oldInfo);
483 } else {
484 /* nothing */
487 } else if (oldInfo->destinationWindow != info->destinationWindow) {
489 if (oldInfo->destinationWindow != None
490 && oldInfo->destinationWindow != scr->rootWin) {
491 notifyDragLeave(scr, oldInfo);
492 SPIT("crossed");
493 } else {
494 SPIT("entered window");
497 action = notifyDragEnter(scr, info);
499 } else {
501 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
502 || X >= r->pos.x + r->size.width \
503 || Y >= r->pos.y + r->size.height)
505 if (rect->size.width == 0 ||
506 (LEFT_RECT(rect, info->location.x, info->location.y))) {
508 action = notifyPosition(scr, info);
510 rect->size.width = 0;
517 static WMData*
518 convertSelection(WMView *view, Atom selection, Atom target,
519 void *cdata, Atom *type)
521 WMScreen *scr = W_VIEW_SCREEN(view);
522 WMData *data;
523 char *typeName = XGetAtomName(scr->display, target);
525 *type = target;
527 data = view->dragSourceProcs->fetchDragData(view, typeName);
529 if (typeName != NULL)
530 XFree(typeName);
532 return data;
536 static void
537 selectionLost(WMView *view, Atom selection, void *cdata)
539 if (W_VIEW_SCREEN(view)->dragSourceView == view) {
540 wwarning("DND selection lost during drag operation...");
541 W_VIEW_SCREEN(view)->dragSourceView = NULL;
546 static void
547 selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
556 static void
557 setMouseOffsetHint(WMView *view, WMSize mouseOffset)
559 WMScreen *scr = W_VIEW_SCREEN(view);
560 long hint[2];
563 * Tell the offset from the topleft corner of the icon being
564 * dragged. Not from XDND, but it's backwards compatible.
567 hint[0] = mouseOffset.width;
568 hint[1] = mouseOffset.height;
570 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
571 scr->wmIconDragOffsetAtom, XA_INTEGER, 32,
572 PropModeReplace, (unsigned char*)hint, 2);
576 static Bool
577 getMouseOffsetHint(WMScreen *scr, Window source, WMSize *mouseOffset)
579 long *hint;
580 Atom type_ret;
581 int fmt_ret;
582 unsigned long nitems_ret, bytes_after_ret;
583 Bool ok = False;
586 hint = NULL;
588 XGetWindowProperty(scr->display, source,
589 scr->wmIconDragOffsetAtom, 0, 2, False, XA_INTEGER,
590 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
591 (unsigned char **)&hint);
593 if (hint && nitems_ret == 2) {
594 mouseOffset->width = hint[0];
595 mouseOffset->height = hint[1];
596 ok = True;
598 if (hint)
599 XFree(hint);
601 return ok;
606 static void
607 timeoutCallback(void *data)
609 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
610 (unsigned)data);
611 _XErrorOccured = 2;
615 * State Machine For Drag Source:
616 * ------------------------------
617 * Events
618 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
619 * 0) idle 1bu - - - - - - - - -
620 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
621 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
622 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
623 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
624 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
625 * 6) dropped - - - - - - - - 0 0w
627 * Events:
628 * Call - called WMDragImageFromView()
629 * Mtn - Motion
630 * Ent - Enter droppable window
631 * Lea - Leave droppable window (or rectangle)
632 * Crs - Leave droppable window (or rectangle) and enter another
633 * BUp - Button Released
634 * StA - XdndStatus client msg with Accept drop
635 * StR - XdndStatus client msg with Reject drop
636 * Fin - XdndFinish client msg
637 * TO - timeout
639 * Actions:
640 * a - send update message
641 * b - send enter message
642 * c - send leave message
643 * d - release drag section info
644 * e - send drop message
645 * f - setup timeout
646 * u - update dragInfo
648 * X - ignore
649 * w - warn about unexpected reply
650 * z - abort operation.. unexpected reply
651 * - shouldnt happen
653 void
654 WMDragImageFromView(WMView *view, WMPixmap *image, char *dataTypes[],
655 WMPoint atLocation, WMSize mouseOffset, XEvent *event,
656 Bool slideBack)
658 WMScreen *scr = view->screen;
659 Display *dpy = scr->display;
660 WMView *toplevel = W_TopLevelOfView(view);
661 Window icon;
662 XEvent ev;
663 WMRect rect = {{0,0},{0,0}};
664 int ostate = -1;
665 int state;
666 int action = -1;
667 XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
668 XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
669 XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
670 WMDraggingInfo dragInfo;
671 WMDraggingInfo oldDragInfo;
672 WMHandlerID timer = NULL;
673 static WMSelectionProcs handler = {
674 convertSelection,
675 selectionLost,
676 selectionDone
680 if (scr->dragSourceView != NULL)
681 return;
683 wassertr(view->dragSourceProcs != NULL);
686 /* prepare icon to be dragged */
687 if (image == NULL)
688 image = scr->defaultObjectIcon;
690 icon = makeDragIcon(scr, image);
692 XMoveWindow(dpy, icon, atLocation.x, atLocation.y);
693 XMapRaised(dpy, icon);
696 /* init dragging info */
698 scr->dragSourceView = view;
700 memset(&dragInfo, 0, sizeof(WMDraggingInfo));
701 memset(&oldDragInfo, 0, sizeof(WMDraggingInfo));
702 dragInfo.image = image;
703 dragInfo.sourceView = view;
704 dragInfo.sourceWindow = W_VIEW_DRAWABLE(toplevel);
706 dragInfo.destinationWindow = dragInfo.sourceWindow;
708 dragInfo.location.x = atLocation.x + mouseOffset.width;
709 dragInfo.location.y = atLocation.y + mouseOffset.height;
710 dragInfo.imageLocation = atLocation;
713 /* start pointer grab */
714 XGrabPointer(dpy, scr->rootWin, False,
715 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
716 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
717 CurrentTime);
719 XFlush(dpy);
721 _XErrorOccured = False;
723 /* take ownership of XdndSelection */
724 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
725 event->xmotion.time,
726 &handler, NULL)) {
727 wwarning("could not get ownership or DND selection");
728 return;
731 setMouseOffsetHint(toplevel, mouseOffset);
733 if (view->dragSourceProcs->beganDragImage != NULL) {
734 view->dragSourceProcs->beganDragImage(view, image, atLocation);
737 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
739 state = 1;
741 while (state != 6 && state != 0 && !_XErrorOccured) {
742 WMNextEvent(dpy, &ev);
744 switch (ev.type) {
745 case MotionNotify:
746 if (state >= 1 && state <= 4) {
747 while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ;
749 protectBlock(True);
751 oldDragInfo = dragInfo;
753 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
755 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
756 dragInfo.imageLocation.y);
758 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
760 protectBlock(False);
762 /* XXXif entered a different destination, check the operation */
764 switch (state) {
765 case 1:
766 if (oldDragInfo.destinationWindow != None
767 && oldDragInfo.destinationWindow != scr->rootWin
768 && (dragInfo.destinationWindow == None
769 || dragInfo.destinationWindow == scr->rootWin)) {
770 /* left the droppable window */
771 state = 2;
772 action = -1;
774 break;
776 case 2:
777 if (dragInfo.destinationWindow != None
778 && dragInfo.destinationWindow != scr->rootWin) {
780 state = 1;
781 action = -1;
783 break;
785 case 3:
786 case 4:
787 if (oldDragInfo.destinationWindow != None
788 && oldDragInfo.destinationWindow != scr->rootWin
789 && (dragInfo.destinationWindow == None
790 || dragInfo.destinationWindow == scr->rootWin)) {
791 /* left the droppable window */
792 state = 2;
793 action = -1;
795 break;
798 break;
801 case ButtonRelease:
802 /* if (state >= 1 && state <= 4) */ {
804 protectBlock(True);
806 oldDragInfo = dragInfo;
808 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
810 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
811 dragInfo.imageLocation.y);
813 if (state == 4 || state == 1) {
814 dragInfo.destinationWindow = None;
815 dragInfo.destView = NULL;
817 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
819 dragInfo.timestamp = ev.xbutton.time;
821 protectBlock(False);
823 switch (state) {
824 case 1:
825 state = 5;
826 timer = WMAddTimerHandler(3000, timeoutCallback,
827 (void*)dragInfo.destinationWindow);
828 break;
829 case 2:
830 state = 0;
831 break;
832 case 3:
833 state = 6;
834 break;
835 case 4:
836 state = 0;
837 break;
840 break;
842 case ClientMessage:
843 if ((state == 1 || state == 3 || state == 4 || state == 5)
844 && ev.xclient.message_type == scr->xdndStatusAtom
845 && ev.xclient.data.l[0] == dragInfo.destinationWindow) {
847 if (ev.xclient.data.l[1] & 1) {
848 SPIT("got accept msg");
849 /* will accept drop */
850 switch (state) {
851 case 1:
852 case 3:
853 case 4:
854 state = 3;
855 break;
856 case 5:
857 if (timer) {
858 WMDeleteTimerHandler(timer);
859 timer = NULL;
861 state = 6;
862 break;
864 action = actionToOperation(scr, ev.xclient.data.l[4]);
865 } else {
866 SPIT("got reject msg");
867 switch (state) {
868 case 1:
869 case 3:
870 case 4:
871 state = 4;
872 break;
873 case 5:
874 state = 0;
875 if (timer) {
876 WMDeleteTimerHandler(timer);
877 timer = NULL;
879 break;
881 action = 0;
884 if (ev.xclient.data.l[1] & (1<<1)) {
885 rect.pos.x = ev.xclient.data.l[2] >> 16;
886 rect.pos.y = ev.xclient.data.l[2] & 0xffff;
887 rect.size.width = ev.xclient.data.l[3] >> 16;
888 rect.size.height = ev.xclient.data.l[3] & 0xffff;
889 } else {
890 rect.size.width = 0;
893 } else if ((state >= 1 && state <= 5)
894 && ev.xclient.message_type == scr->xdndFinishedAtom
895 && ev.xclient.window == dragInfo.destinationWindow) {
897 wwarning("drag source received unexpected XdndFinished message from %x",
898 (unsigned)dragInfo.destinationWindow);
900 if (state == 3 || state == 4 || state == 5) {
901 state = 0;
902 if (timer) {
903 WMDeleteTimerHandler(timer);
904 timer = NULL;
909 default:
910 WMHandleEvent(&ev);
911 break;
914 if (ostate != state) {
915 if (state == 3) {
916 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
917 } else if (ostate == 3) {
918 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
920 ostate = state;
924 if (timer) {
925 WMDeleteTimerHandler(timer);
926 timer = NULL;
927 } else if (_XErrorOccured) {
928 /* got a timeout, send leave */
929 notifyDragLeave(scr, &dragInfo);
932 XUngrabPointer(dpy, CurrentTime);
934 SPIT("exited main loop");
936 if (_XErrorOccured || state != 6) {
937 goto cancelled;
940 assert(dragInfo.destinationWindow != None);
942 protectBlock(True);
943 notifyDrop(scr, &dragInfo);
944 protectBlock(False);
946 if (_XErrorOccured)
947 goto cancelled;
950 SPIT("dropped");
953 XDestroyWindow(dpy, icon);
955 return;
957 cancelled:
958 scr->dragSourceView = NULL;
960 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
961 event->xmotion.time);
963 if (slideBack) {
964 slideWindow(dpy, icon,
965 dragInfo.imageLocation.x, dragInfo.imageLocation.y,
966 atLocation.x, atLocation.y);
968 XDestroyWindow(dpy, icon);
969 if (view->dragSourceProcs->endedDragImage != NULL) {
970 view->dragSourceProcs->endedDragImage(view, image,
971 dragInfo.imageLocation,
972 False);
987 static Atom
988 operationToAction(WMScreen *scr, WMDragOperationType operation)
990 switch (operation) {
991 case WDOperationNone:
992 return None;
994 case WDOperationCopy:
995 return scr->xdndActionCopy;
997 case WDOperationMove:
998 return scr->xdndActionMove;
1000 case WDOperationLink:
1001 return scr->xdndActionLink;
1003 case WDOperationAsk:
1004 return scr->xdndActionAsk;
1006 case WDOperationPrivate:
1007 return scr->xdndActionPrivate;
1009 default:
1010 return None;
1015 static WMDragOperationType
1016 actionToOperation(WMScreen *scr, Atom action)
1018 if (action == scr->xdndActionCopy) {
1019 return WDOperationCopy;
1021 } else if (action == scr->xdndActionMove) {
1022 return WDOperationMove;
1024 } else if (action == scr->xdndActionLink) {
1025 return WDOperationLink;
1027 } else if (action == scr->xdndActionAsk) {
1028 return WDOperationAsk;
1030 } else if (action == scr->xdndActionPrivate) {
1031 return WDOperationPrivate;
1033 } else if (action == None) {
1035 return WDOperationCopy;
1036 } else {
1037 char *tmp = XGetAtomName(scr->display, action);
1039 wwarning("unknown XDND action %s from 0x%x", tmp,
1040 (unsigned)scr->dragInfo.sourceWindow);
1041 XFree(tmp);
1043 return WDOperationCopy;
1052 static Atom*
1053 getTypeList(Window window, XClientMessageEvent *event)
1055 int i = 0;
1056 Atom *types = NULL;
1058 if (event->data.l[1] & 1) { /* > 3 types */
1060 } else {
1061 types = wmalloc(4 * sizeof(Atom));
1062 if (event->data.l[2] != None)
1063 types[i++] = event->data.l[2];
1064 if (event->data.l[3] != None)
1065 types[i++] = event->data.l[3];
1066 if (event->data.l[4] != None)
1067 types[i++] = event->data.l[4];
1068 types[i] = 0;
1071 if (types[0] == 0) {
1072 wwarning("received invalid drag & drop type list");
1073 /*XXX return;*/
1076 return types;
1081 #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info)
1085 static void
1086 receivedData(WMView *view, Atom selection, Atom target,
1087 Time timestamp, void *cdata, WMData *data)
1089 WMScreen *scr = W_VIEW_SCREEN(view);
1090 WMDraggingInfo *info = (WMDraggingInfo*)cdata;
1091 Bool res;
1093 res = view->dragDestinationProcs->performDragOperation(view, info);
1095 if (res) {
1096 DISPATCH(view, concludeDragOperation, info);
1099 /* send finished message */
1100 sendClientMessage(scr->display, info->sourceWindow,
1101 scr->xdndFinishedAtom,
1102 info->destinationWindow,
1103 0, 0, 0, 0);
1105 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1110 void
1111 W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event)
1113 WMScreen *scr = W_VIEW_SCREEN(toplevel);
1114 WMView *oldView = NULL;
1115 WMView *newView = NULL;
1116 unsigned operation = 0;
1117 int x, y;
1118 enum {
1119 WNothing,
1120 WEnter,
1121 WLeave,
1122 WCross, /* leave one and enter another */
1123 WUpdate,
1124 WDrop
1126 Window source;
1127 int what = WNothing;
1128 Bool sendStatus = False;
1131 source = scr->dragInfo.sourceWindow;
1132 oldView = scr->dragInfo.destView;
1134 if (event->message_type == scr->xdndFinishedAtom) {
1135 WMView *view = scr->dragSourceView;
1137 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
1138 scr->dragInfo.timestamp);
1140 if (view->dragSourceProcs->endedDragImage != NULL) {
1141 view->dragSourceProcs->endedDragImage(view,
1142 scr->dragInfo.image,
1143 scr->dragInfo.imageLocation,
1144 True);
1147 scr->dragSourceView = NULL;
1149 return;
1154 if (event->message_type == scr->xdndEnterAtom) {
1155 Window foo, bar;
1156 int bla;
1157 unsigned ble;
1159 if (scr->dragInfo.sourceWindow != None) {
1160 puts("received Enter event in bad order");
1163 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1166 if ((event->data.l[1] >> 24) > XDND_VERSION) {
1167 wwarning("received drag & drop request with unsupported version %i",
1168 (event->data.l[1] >> 24));
1169 return;
1172 scr->dragInfo.protocolVersion = event->data.l[1] >> 24;
1173 scr->dragInfo.sourceWindow = source = event->data.l[0];
1174 scr->dragInfo.destinationWindow = event->window;
1176 getMouseOffsetHint(scr, source, &scr->dragInfo.mouseOffset);
1178 /* XXX */
1179 scr->dragInfo.image = NULL;
1181 XQueryPointer(scr->display, scr->rootWin, &foo, &bar,
1182 &scr->dragInfo.location.x, &scr->dragInfo.location.y,
1183 &bla, &bla, &ble);
1185 scr->dragInfo.imageLocation = scr->dragInfo.location;
1186 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1187 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1189 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1190 scr->dragInfo.location.x,
1191 scr->dragInfo.location.y, &x, &y);
1194 newView = findViewInToplevel(scr->display,
1195 scr->dragInfo.destinationWindow,
1196 x, y);
1198 } else if (event->message_type == scr->xdndPositionAtom
1199 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1201 scr->dragInfo.location.x = event->data.l[2] >> 16;
1202 scr->dragInfo.location.y = event->data.l[2] & 0xffff;
1204 scr->dragInfo.imageLocation = scr->dragInfo.location;
1205 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1206 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1208 if (scr->dragInfo.protocolVersion >= 1) {
1209 scr->dragInfo.timestamp = event->data.l[3];
1210 scr->dragInfo.sourceOperation = actionToOperation(scr,
1211 event->data.l[4]);
1213 } else {
1214 scr->dragInfo.timestamp = CurrentTime;
1215 scr->dragInfo.sourceOperation = WDOperationCopy;
1218 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1219 scr->dragInfo.location.x,
1220 scr->dragInfo.location.y, &x, &y);
1222 newView = findViewInToplevel(scr->display,
1223 scr->dragInfo.destinationWindow,
1224 x, y);
1226 } else if (event->message_type == scr->xdndLeaveAtom
1227 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1229 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1231 } else if (event->message_type == scr->xdndDropAtom
1232 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1234 /* drop */
1235 if (oldView != NULL)
1236 what = WDrop;
1238 } else {
1239 return;
1244 * Now map the XDND events to WINGs events.
1247 if (what == WNothing) {
1248 if (IS_DROPPABLE(newView)) {
1249 if (!IS_DROPPABLE(oldView)) { /* entered */
1250 what = WEnter;
1251 } else if (oldView == newView) { /* updated */
1252 what = WUpdate;
1253 } else {
1254 what = WCross;
1256 } else {
1257 if (IS_DROPPABLE(oldView)) {
1258 what = WLeave;
1259 } else {
1260 /* just send rejection msg */
1261 sendStatus = True;
1268 switch (what) {
1270 case WEnter:
1271 scr->dragInfo.destView = newView;
1272 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1273 sendStatus = True;
1274 break;
1276 case WLeave:
1277 scr->dragInfo.destView = NULL;
1278 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1279 sendStatus = True;
1280 operation = WDOperationNone;
1281 break;
1283 case WCross:
1284 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1285 scr->dragInfo.destView = newView;
1286 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1287 sendStatus = True;
1288 break;
1290 case WUpdate:
1291 operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo);
1292 sendStatus = True;
1293 break;
1295 case WDrop:
1297 Bool res;
1299 res = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo);
1301 if (res) {
1302 res = DISPATCH(oldView, performDragOperation, &scr->dragInfo);
1305 if (res) {
1309 break;
1311 default:
1312 break;
1317 if (sendStatus) {
1318 Atom action;
1320 action = operationToAction(scr, operation);
1322 sendClientMessage(scr->display, source,
1323 scr->xdndStatusAtom,
1324 scr->dragInfo.destinationWindow,
1325 action != None ? 1 : 0, 0, 0, action);