Mouse wheel code enhancement (need to check if its correctly implemented)
[wmaker-crm.git] / WINGs / dragsource.c
blob80d20b66a56237a014356d933ba28b3b214ab72c
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 GrabModeSync, 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 XAllowEvents(dpy, SyncPointer, CurrentTime);
743 WMNextEvent(dpy, &ev);
745 switch (ev.type) {
746 case MotionNotify:
747 if (state >= 1 && state <= 4) {
748 while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ;
750 protectBlock(True);
752 oldDragInfo = dragInfo;
754 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
756 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
757 dragInfo.imageLocation.y);
759 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
761 protectBlock(False);
763 /* XXXif entered a different destination, check the operation */
765 switch (state) {
766 case 1:
767 if (oldDragInfo.destinationWindow != None
768 && oldDragInfo.destinationWindow != scr->rootWin
769 && (dragInfo.destinationWindow == None
770 || dragInfo.destinationWindow == scr->rootWin)) {
771 /* left the droppable window */
772 state = 2;
773 action = -1;
775 break;
777 case 2:
778 if (dragInfo.destinationWindow != None
779 && dragInfo.destinationWindow != scr->rootWin) {
781 state = 1;
782 action = -1;
784 break;
786 case 3:
787 case 4:
788 if (oldDragInfo.destinationWindow != None
789 && oldDragInfo.destinationWindow != scr->rootWin
790 && (dragInfo.destinationWindow == None
791 || dragInfo.destinationWindow == scr->rootWin)) {
792 /* left the droppable window */
793 state = 2;
794 action = -1;
796 break;
799 break;
802 case ButtonRelease:
803 /* if (state >= 1 && state <= 4) */ {
805 protectBlock(True);
807 oldDragInfo = dragInfo;
809 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
811 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
812 dragInfo.imageLocation.y);
814 if (state == 4 || state == 1) {
815 dragInfo.destinationWindow = None;
816 dragInfo.destView = NULL;
818 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
820 dragInfo.timestamp = ev.xbutton.time;
822 protectBlock(False);
824 switch (state) {
825 case 1:
826 state = 5;
827 timer = WMAddTimerHandler(3000, timeoutCallback,
828 (void*)dragInfo.destinationWindow);
829 break;
830 case 2:
831 state = 0;
832 break;
833 case 3:
834 state = 6;
835 break;
836 case 4:
837 state = 0;
838 break;
841 break;
843 case ClientMessage:
844 if ((state == 1 || state == 3 || state == 4 || state == 5)
845 && ev.xclient.message_type == scr->xdndStatusAtom
846 && ev.xclient.data.l[0] == dragInfo.destinationWindow) {
848 if (ev.xclient.data.l[1] & 1) {
849 SPIT("got accept msg");
850 /* will accept drop */
851 switch (state) {
852 case 1:
853 case 3:
854 case 4:
855 state = 3;
856 break;
857 case 5:
858 if (timer) {
859 WMDeleteTimerHandler(timer);
860 timer = NULL;
862 state = 6;
863 break;
865 action = actionToOperation(scr, ev.xclient.data.l[4]);
866 } else {
867 SPIT("got reject msg");
868 switch (state) {
869 case 1:
870 case 3:
871 case 4:
872 state = 4;
873 break;
874 case 5:
875 state = 0;
876 if (timer) {
877 WMDeleteTimerHandler(timer);
878 timer = NULL;
880 break;
882 action = 0;
885 if (ev.xclient.data.l[1] & (1<<1)) {
886 rect.pos.x = ev.xclient.data.l[2] >> 16;
887 rect.pos.y = ev.xclient.data.l[2] & 0xffff;
888 rect.size.width = ev.xclient.data.l[3] >> 16;
889 rect.size.height = ev.xclient.data.l[3] & 0xffff;
890 } else {
891 rect.size.width = 0;
894 } else if ((state >= 1 && state <= 5)
895 && ev.xclient.message_type == scr->xdndFinishedAtom
896 && ev.xclient.window == dragInfo.destinationWindow) {
898 wwarning("drag source received unexpected XdndFinished message from %x",
899 (unsigned)dragInfo.destinationWindow);
901 if (state == 3 || state == 4 || state == 5) {
902 state = 0;
903 if (timer) {
904 WMDeleteTimerHandler(timer);
905 timer = NULL;
910 default:
911 WMHandleEvent(&ev);
912 break;
915 if (ostate != state) {
916 if (state == 3) {
917 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
918 } else if (ostate == 3) {
919 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
921 ostate = state;
925 if (timer) {
926 WMDeleteTimerHandler(timer);
927 timer = NULL;
928 } else if (_XErrorOccured) {
929 /* got a timeout, send leave */
930 notifyDragLeave(scr, &dragInfo);
933 XUngrabPointer(dpy, CurrentTime);
935 SPIT("exited main loop");
937 if (_XErrorOccured || state != 6) {
938 goto cancelled;
941 assert(dragInfo.destinationWindow != None);
943 protectBlock(True);
944 notifyDrop(scr, &dragInfo);
945 protectBlock(False);
947 if (_XErrorOccured)
948 goto cancelled;
951 SPIT("dropped");
954 XDestroyWindow(dpy, icon);
956 return;
958 cancelled:
959 scr->dragSourceView = NULL;
961 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
962 event->xmotion.time);
964 if (slideBack) {
965 slideWindow(dpy, icon,
966 dragInfo.imageLocation.x, dragInfo.imageLocation.y,
967 atLocation.x, atLocation.y);
969 XDestroyWindow(dpy, icon);
970 if (view->dragSourceProcs->endedDragImage != NULL) {
971 view->dragSourceProcs->endedDragImage(view, image,
972 dragInfo.imageLocation,
973 False);
988 static Atom
989 operationToAction(WMScreen *scr, WMDragOperationType operation)
991 switch (operation) {
992 case WDOperationNone:
993 return None;
995 case WDOperationCopy:
996 return scr->xdndActionCopy;
998 case WDOperationMove:
999 return scr->xdndActionMove;
1001 case WDOperationLink:
1002 return scr->xdndActionLink;
1004 case WDOperationAsk:
1005 return scr->xdndActionAsk;
1007 case WDOperationPrivate:
1008 return scr->xdndActionPrivate;
1010 default:
1011 return None;
1016 static WMDragOperationType
1017 actionToOperation(WMScreen *scr, Atom action)
1019 if (action == scr->xdndActionCopy) {
1020 return WDOperationCopy;
1022 } else if (action == scr->xdndActionMove) {
1023 return WDOperationMove;
1025 } else if (action == scr->xdndActionLink) {
1026 return WDOperationLink;
1028 } else if (action == scr->xdndActionAsk) {
1029 return WDOperationAsk;
1031 } else if (action == scr->xdndActionPrivate) {
1032 return WDOperationPrivate;
1034 } else if (action == None) {
1036 return WDOperationCopy;
1037 } else {
1038 char *tmp = XGetAtomName(scr->display, action);
1040 wwarning("unknown XDND action %s from 0x%x", tmp,
1041 (unsigned)scr->dragInfo.sourceWindow);
1042 XFree(tmp);
1044 return WDOperationCopy;
1053 static Atom*
1054 getTypeList(Window window, XClientMessageEvent *event)
1056 int i = 0;
1057 Atom *types = NULL;
1059 if (event->data.l[1] & 1) { /* > 3 types */
1061 } else {
1062 types = wmalloc(4 * sizeof(Atom));
1063 if (event->data.l[2] != None)
1064 types[i++] = event->data.l[2];
1065 if (event->data.l[3] != None)
1066 types[i++] = event->data.l[3];
1067 if (event->data.l[4] != None)
1068 types[i++] = event->data.l[4];
1069 types[i] = 0;
1072 if (types[0] == 0) {
1073 wwarning("received invalid drag & drop type list");
1074 /*XXX return;*/
1077 return types;
1082 #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info)
1086 static void
1087 receivedData(WMView *view, Atom selection, Atom target,
1088 Time timestamp, void *cdata, WMData *data)
1090 WMScreen *scr = W_VIEW_SCREEN(view);
1091 WMDraggingInfo *info = (WMDraggingInfo*)cdata;
1092 Bool res;
1094 res = view->dragDestinationProcs->performDragOperation(view, info);
1096 if (res) {
1097 DISPATCH(view, concludeDragOperation, info);
1100 /* send finished message */
1101 sendClientMessage(scr->display, info->sourceWindow,
1102 scr->xdndFinishedAtom,
1103 info->destinationWindow,
1104 0, 0, 0, 0);
1106 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1111 void
1112 W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event)
1114 WMScreen *scr = W_VIEW_SCREEN(toplevel);
1115 WMView *oldView = NULL;
1116 WMView *newView = NULL;
1117 unsigned operation = 0;
1118 int x, y;
1119 enum {
1120 WNothing,
1121 WEnter,
1122 WLeave,
1123 WCross, /* leave one and enter another */
1124 WUpdate,
1125 WDrop
1127 Window source;
1128 int what = WNothing;
1129 Bool sendStatus = False;
1132 source = scr->dragInfo.sourceWindow;
1133 oldView = scr->dragInfo.destView;
1135 if (event->message_type == scr->xdndFinishedAtom) {
1136 WMView *view = scr->dragSourceView;
1138 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
1139 scr->dragInfo.timestamp);
1141 if (view->dragSourceProcs->endedDragImage != NULL) {
1142 view->dragSourceProcs->endedDragImage(view,
1143 scr->dragInfo.image,
1144 scr->dragInfo.imageLocation,
1145 True);
1148 scr->dragSourceView = NULL;
1150 return;
1155 if (event->message_type == scr->xdndEnterAtom) {
1156 Window foo, bar;
1157 int bla;
1158 unsigned ble;
1160 if (scr->dragInfo.sourceWindow != None) {
1161 puts("received Enter event in bad order");
1164 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1167 if ((event->data.l[1] >> 24) > XDND_VERSION) {
1168 wwarning("received drag & drop request with unsupported version %i",
1169 (event->data.l[1] >> 24));
1170 return;
1173 scr->dragInfo.protocolVersion = event->data.l[1] >> 24;
1174 scr->dragInfo.sourceWindow = source = event->data.l[0];
1175 scr->dragInfo.destinationWindow = event->window;
1177 getMouseOffsetHint(scr, source, &scr->dragInfo.mouseOffset);
1179 /* XXX */
1180 scr->dragInfo.image = NULL;
1182 XQueryPointer(scr->display, scr->rootWin, &foo, &bar,
1183 &scr->dragInfo.location.x, &scr->dragInfo.location.y,
1184 &bla, &bla, &ble);
1186 scr->dragInfo.imageLocation = scr->dragInfo.location;
1187 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1188 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1190 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1191 scr->dragInfo.location.x,
1192 scr->dragInfo.location.y, &x, &y);
1195 newView = findViewInToplevel(scr->display,
1196 scr->dragInfo.destinationWindow,
1197 x, y);
1199 } else if (event->message_type == scr->xdndPositionAtom
1200 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1202 scr->dragInfo.location.x = event->data.l[2] >> 16;
1203 scr->dragInfo.location.y = event->data.l[2] & 0xffff;
1205 scr->dragInfo.imageLocation = scr->dragInfo.location;
1206 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1207 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1209 if (scr->dragInfo.protocolVersion >= 1) {
1210 scr->dragInfo.timestamp = event->data.l[3];
1211 scr->dragInfo.sourceOperation = actionToOperation(scr,
1212 event->data.l[4]);
1214 } else {
1215 scr->dragInfo.timestamp = CurrentTime;
1216 scr->dragInfo.sourceOperation = WDOperationCopy;
1219 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1220 scr->dragInfo.location.x,
1221 scr->dragInfo.location.y, &x, &y);
1223 newView = findViewInToplevel(scr->display,
1224 scr->dragInfo.destinationWindow,
1225 x, y);
1227 } else if (event->message_type == scr->xdndLeaveAtom
1228 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1230 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1232 } else if (event->message_type == scr->xdndDropAtom
1233 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1235 /* drop */
1236 if (oldView != NULL)
1237 what = WDrop;
1239 } else {
1240 return;
1245 * Now map the XDND events to WINGs events.
1248 if (what == WNothing) {
1249 if (IS_DROPPABLE(newView)) {
1250 if (!IS_DROPPABLE(oldView)) { /* entered */
1251 what = WEnter;
1252 } else if (oldView == newView) { /* updated */
1253 what = WUpdate;
1254 } else {
1255 what = WCross;
1257 } else {
1258 if (IS_DROPPABLE(oldView)) {
1259 what = WLeave;
1260 } else {
1261 /* just send rejection msg */
1262 sendStatus = True;
1269 switch (what) {
1271 case WEnter:
1272 scr->dragInfo.destView = newView;
1273 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1274 sendStatus = True;
1275 break;
1277 case WLeave:
1278 scr->dragInfo.destView = NULL;
1279 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1280 sendStatus = True;
1281 operation = WDOperationNone;
1282 break;
1284 case WCross:
1285 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1286 scr->dragInfo.destView = newView;
1287 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1288 sendStatus = True;
1289 break;
1291 case WUpdate:
1292 operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo);
1293 sendStatus = True;
1294 break;
1296 case WDrop:
1298 Bool res;
1300 res = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo);
1302 if (res) {
1303 res = DISPATCH(oldView, performDragOperation, &scr->dragInfo);
1306 if (res) {
1310 break;
1312 default:
1313 break;
1318 if (sendStatus) {
1319 Atom action;
1321 action = operationToAction(scr, operation);
1323 sendClientMessage(scr->display, source,
1324 scr->xdndStatusAtom,
1325 scr->dragInfo.destinationWindow,
1326 action != None ? 1 : 0, 0, 0, action);