drag and drop!
[wmaker-crm.git] / WINGs / dragsource.c
blob742cdd3bfecba9d52b0094dbf4a64a51a19780cf
3 #include <X11/Xatom.h>
4 #include <math.h>
6 #include "WINGsP.h"
11 #define SPIT(a)
14 #define IS_DROPPABLE(view) (view!=NULL && view->droppableTypes!=NULL && \
15 view->dragDestinationProcs!=NULL)
18 static Atom operationToAction(WMScreen *scr, WMDragOperationType operation);
19 static WMDragOperationType actionToOperation(WMScreen *scr, Atom action);
21 static Bool _XErrorOccured = False;
26 void
27 WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
29 if (view->dragSourceProcs)
30 free(view->dragSourceProcs);
31 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
33 *view->dragSourceProcs = *procs;
35 /* XXX fill in non-implemented stuffs */
39 /***********************************************************************/
42 static int
43 handleXError(Display *dpy, XErrorEvent *ev)
45 _XErrorOccured = True;
47 return 1;
51 static void
52 protectBlock(Bool begin)
54 static void *oldHandler = NULL;
56 if (begin) {
57 oldHandler = XSetErrorHandler(handleXError);
58 } else {
59 XSetErrorHandler(oldHandler);
66 static Window
67 makeDragIcon(WMScreen *scr, WMPixmap *pixmap)
69 Window window;
70 WMSize size;
71 unsigned long flags;
72 XSetWindowAttributes attribs;
73 Pixmap pix, mask;
75 if (!pixmap) {
76 pixmap = scr->defaultObjectIcon;
78 size = WMGetPixmapSize(pixmap);
79 pix = pixmap->pixmap;
80 mask = pixmap->mask;
82 flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap;
83 attribs.save_under = True;
84 attribs.background_pixmap = pix;
85 attribs.override_redirect = True;
86 attribs.colormap = scr->colormap;
87 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
88 size.height, 0, scr->depth, InputOutput,
89 scr->visual, flags, &attribs);
91 #ifdef SHAPE
92 if (mask) {
93 XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask,
94 ShapeSet);
96 #endif
98 return window;
102 static void
103 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
105 double x, y, dx, dy;
106 int i;
107 int iterations;
109 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
111 x = srcX;
112 y = srcY;
114 dx = (double)(dstX-srcX)/iterations;
115 dy = (double)(dstY-srcY)/iterations;
117 for (i = 0; i <= iterations; i++) {
118 XMoveWindow(dpy, win, x, y);
119 XFlush(dpy);
121 wusleep(800);
123 x += dx;
124 y += dy;
129 static Window
130 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
132 Window foo, bar;
133 Window *children;
134 unsigned nchildren;
135 int i;
137 if (!XQueryTree(dpy, toplevel, &foo, &bar,
138 &children, &nchildren) || children == NULL) {
139 return None;
142 /* first window that contains the point is the one */
143 for (i = nchildren-1; i >= 0; i--) {
144 XWindowAttributes attr;
146 if (XGetWindowAttributes(dpy, children[i], &attr)
147 && attr.map_state == IsViewable
148 && x >= attr.x && y >= attr.y
149 && x < attr.x + attr.width && y < attr.y + attr.height) {
150 Window child;
152 child = findChildInWindow(dpy, children[i],
153 x - attr.x, y - attr.y);
155 XFree(children);
157 if (child == None)
158 return toplevel;
159 else
160 return child;
164 XFree(children);
165 return None;
169 static WMView*
170 findViewInToplevel(Display *dpy, Window toplevel, int x, int y)
172 Window child;
174 child = findChildInWindow(dpy, toplevel, x, y);
176 if (child != None)
177 return W_GetViewForXWindow(dpy, child);
178 else
179 return NULL;
184 static Window
185 lookForToplevel(WMScreen *scr, Window window, Bool *isAware)
187 Window toplevel = None;
188 Atom *atoms;
189 int j, count;
191 *isAware = False;
193 atoms = XListProperties(scr->display, window, &count);
194 for (j = 0; j < count; j++) {
195 if (atoms[j] == scr->wmStateAtom) {
196 toplevel = window;
197 } else if (atoms[j] == scr->xdndAwareAtom) {
198 *isAware = True;
201 if (atoms)
202 XFree(atoms);
204 if (toplevel == None) {
205 Window *children;
206 Window foo, bar;
207 unsigned nchildren;
209 if (!XQueryTree(scr->display, window, &foo, &bar,
210 &children, &nchildren) || children == NULL) {
211 return None;
214 for (j = 0; j < nchildren; j++) {
215 toplevel = lookForToplevel(scr, children[j], isAware);
216 if (toplevel != None)
217 break;
220 XFree(children);
225 return toplevel;
230 static Window
231 findToplevelUnderDragPointer(WMScreen *scr, int x, int y, Window iconWindow)
233 Window foo, bar;
234 Window *children;
235 unsigned nchildren;
236 Bool overSomething = False;
237 int i;
239 if (!XQueryTree(scr->display, scr->rootWin, &foo, &bar,
240 &children, &nchildren) || children == NULL) {
241 SPIT("couldnt query tree!");
242 return None;
245 /* try to find the window below the iconWindow by traversing
246 * the whole window list */
248 /* first find the position of the iconWindow */
249 for (i = nchildren-1; i >= 0; i--) {
250 if (children[i] == iconWindow) {
251 i--;
252 break;
255 if (i <= 0) {
256 XFree(children);
257 return scr->rootWin;
260 /* first window that contains the point is the one */
261 for (; i >= 0; i--) {
262 XWindowAttributes attr;
264 if (XGetWindowAttributes(scr->display, children[i], &attr)
265 && attr.map_state == IsViewable
266 && x >= attr.x && y >= attr.y
267 && x < attr.x + attr.width && y < attr.y + attr.height) {
268 Window toplevel;
269 Bool isaware;
271 overSomething = True;
273 toplevel = lookForToplevel(scr, children[i], &isaware);
275 XFree(children);
277 if (isaware)
278 return toplevel;
279 else
280 return None;
284 XFree(children);
285 if (!overSomething)
286 return scr->rootWin;
287 else
288 return None;
296 static void
297 sendClientMessage(Display *dpy, Window win, Atom message,
298 unsigned data1, unsigned data2, unsigned data3,
299 unsigned data4, unsigned data5)
301 XEvent ev;
303 ev.type = ClientMessage;
304 ev.xclient.message_type = message;
305 ev.xclient.format = 32;
306 ev.xclient.window = win;
307 ev.xclient.data.l[0] = data1;
308 ev.xclient.data.l[1] = data2;
309 ev.xclient.data.l[2] = data3;
310 ev.xclient.data.l[3] = data4;
311 ev.xclient.data.l[4] = data5;
313 XSendEvent(dpy, win, False, 0, &ev);
314 XFlush(dpy);
320 static unsigned
321 notifyPosition(WMScreen *scr, WMDraggingInfo *info)
323 Atom action = operationToAction(scr, info->sourceOperation);
325 sendClientMessage(scr->display, info->destinationWindow,
326 scr->xdndPositionAtom,
327 info->sourceWindow,
328 0, /* reserved */
329 info->location.x<<16|info->location.y,
330 info->timestamp,
331 action/* operation */);
333 return 0;
338 static void
339 notifyDrop(WMScreen *scr, WMDraggingInfo *info)
341 sendClientMessage(scr->display, info->destinationWindow,
342 scr->xdndDropAtom,
343 info->sourceWindow,
344 0, /* reserved */
345 info->timestamp,
346 0, 0);
351 static void
352 notifyDragLeave(WMScreen *scr, WMDraggingInfo *info)
354 sendClientMessage(scr->display, info->destinationWindow,
355 scr->xdndLeaveAtom,
356 info->sourceWindow, 0, 0, 0, 0);
361 static unsigned
362 notifyDragEnter(WMScreen *scr, WMDraggingInfo *info)
364 unsigned d;
366 d = XDND_VERSION << 24;
368 sendClientMessage(scr->display, info->destinationWindow,
369 scr->xdndEnterAtom,
370 info->sourceWindow, d, 0, 0, 0);
372 return 0;
376 static void
377 translateCoordinates(WMScreen *scr, Window target, int fromX, int fromY,
378 int *x, int *y)
380 Window child;
382 XTranslateCoordinates(scr->display, scr->rootWin, target,
383 fromX, fromY, x, y, &child);
387 static void
388 updateDraggingInfo(WMScreen *scr, WMDraggingInfo *info,
389 XEvent *event, Window iconWindow)
391 Window toplevel;
392 WMSize size;
394 size = WMGetPixmapSize(info->image);
396 if (event->type == MotionNotify) {
397 info->imageLocation.x = event->xmotion.x_root-(int)size.width/2;
398 info->imageLocation.y = event->xmotion.y_root-(int)size.height/2;
400 info->location.x = event->xmotion.x_root;
401 info->location.y = event->xmotion.y_root;
402 info->timestamp = event->xmotion.time;
404 } else if (event->type == ButtonRelease) {
405 info->imageLocation.x = event->xbutton.x_root-(int)size.width/2;
406 info->imageLocation.y = event->xbutton.y_root-(int)size.height/2;
408 info->location.x = event->xbutton.x_root;
409 info->location.y = event->xbutton.y_root;
410 info->timestamp = event->xbutton.time;
413 toplevel = findToplevelUnderDragPointer(scr,
414 info->location.x,
415 info->location.y,
416 iconWindow);
417 info->destinationWindow = toplevel;
423 static void
424 processMotion(WMScreen *scr, WMDraggingInfo *info, WMDraggingInfo *oldInfo,
425 WMRect *rect, unsigned currentAction)
427 unsigned action;
429 if (info->destinationWindow == None) { /* entered an unsupporeted window */
431 if (oldInfo->destinationWindow != None
432 && oldInfo->destinationWindow != scr->rootWin) {
433 SPIT("left window");
435 notifyDragLeave(scr, oldInfo);
438 } else if (info->destinationWindow == scr->rootWin) {
440 if (oldInfo->destinationWindow != None
441 && oldInfo->destinationWindow != scr->rootWin) {
442 SPIT("left window to root");
444 notifyDragLeave(scr, oldInfo);
445 } else {
446 /* nothing */
449 } else if (oldInfo->destinationWindow != info->destinationWindow) {
451 if (oldInfo->destinationWindow != None
452 && oldInfo->destinationWindow != scr->rootWin) {
453 notifyDragLeave(scr, oldInfo);
454 SPIT("crossed");
455 } else {
456 SPIT("entered window");
459 action = notifyDragEnter(scr, info);
461 } else {
463 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
464 || X >= r->pos.x + r->size.width \
465 || Y >= r->pos.y + r->size.height)
467 if (rect->size.width == 0 ||
468 (LEFT_RECT(rect, info->location.x, info->location.y))) {
470 action = notifyPosition(scr, info);
472 rect->size.width = 0;
479 static WMData*
480 convertSelection(WMView *view, Atom selection, Atom target,
481 void *cdata, Atom *type)
483 WMScreen *scr = W_VIEW_SCREEN(view);
484 WMData *data;
485 char *typeName = XGetAtomName(scr->display, target);
487 *type = target;
489 data = view->dragSourceProcs->fetchDragData(view, typeName);
491 if (typeName != NULL)
492 XFree(typeName);
494 return data;
498 static void
499 selectionLost(WMView *view, Atom selection, void *cdata)
501 if (W_VIEW_SCREEN(view)->dragSourceView == view) {
502 wwarning("DND selection lost during drag operation...");
507 static void
508 selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
520 static void
521 timeoutCallback(void *data)
523 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
524 (unsigned)data);
525 _XErrorOccured = 2;
529 * State Machine For Drag Source:
530 * ------------------------------
531 * Events
532 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
533 * 0) idle 1bu - - - - - - - - -
534 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
535 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
536 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
537 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
538 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
539 * 6) dropped - - - - - - - - 0 0w
541 * Events:
542 * Call - called WMDragImageFromView()
543 * Mtn - Motion
544 * Ent - Enter droppable window
545 * Lea - Leave droppable window (or rectangle)
546 * Crs - Leave droppable window (or rectangle) and enter another
547 * BUp - Button Released
548 * StA - XdndStatus client msg with Accept drop
549 * StR - XdndStatus client msg with Reject drop
550 * Fin - XdndFinish client msg
551 * TO - timeout
553 * Actions:
554 * a - send update message
555 * b - send enter message
556 * c - send leave message
557 * d - release drag section info
558 * e - send drop message
559 * f - setup timeout
560 * u - update dragInfo
562 * X - ignore
563 * w - warn about unexpected reply
564 * z - abort operation.. unexpected reply
565 * - shouldnt happen
567 void
568 WMDragImageFromView(WMView *view, WMPixmap *image, char *dataTypes[],
569 WMPoint atLocation, WMSize mouseOffset, XEvent *event,
570 Bool slideBack)
572 WMScreen *scr = view->screen;
573 Display *dpy = scr->display;
574 Window icon;
575 XEvent ev;
576 WMSize size;
577 WMRect rect = {{0,0},{0,0}};
578 int ostate = -1;
579 int state;
580 int action = -1;
581 XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
582 XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
583 XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
584 WMDraggingInfo dragInfo;
585 WMDraggingInfo oldDragInfo;
586 WMHandlerID timer = NULL;
587 static WMSelectionProcs handler = {
588 convertSelection,
589 selectionLost,
590 selectionDone
594 wassertr(scr->dragSourceView == NULL);
596 wassertr(view->dragSourceProcs != NULL);
599 /* prepare icon to be dragged */
600 if (image == NULL)
601 image = scr->defaultObjectIcon;
603 size = WMGetPixmapSize(image);
605 icon = makeDragIcon(scr, image);
607 XMoveWindow(dpy, icon, event->xmotion.x_root-(int)size.width/2,
608 event->xmotion.y_root-(int)size.height/2);
609 XMapRaised(dpy, icon);
612 /* init dragging info */
614 scr->dragSourceView = view;
616 memset(&dragInfo, 0, sizeof(WMDraggingInfo));
617 memset(&oldDragInfo, 0, sizeof(WMDraggingInfo));
618 dragInfo.image = image;
619 dragInfo.sourceView = view;
620 dragInfo.sourceWindow = W_VIEW_DRAWABLE(W_TopLevelOfView(view));
622 dragInfo.destinationWindow = dragInfo.sourceWindow;
624 dragInfo.location.x = event->xmotion.x_root;
625 dragInfo.location.y = event->xmotion.y_root;
626 dragInfo.imageLocation = atLocation;
629 /* start pointer grab */
630 XGrabPointer(dpy, scr->rootWin, False,
631 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
632 GrabModeSync, GrabModeAsync, None, scr->defaultCursor,
633 CurrentTime);
635 XFlush(dpy);
637 _XErrorOccured = False;
639 /* take ownership of XdndSelection */
640 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
641 event->xmotion.time,
642 &handler, NULL)) {
643 wwarning("could not get ownership or DND selection");
644 return;
647 if (view->dragSourceProcs->beganDragImage != NULL) {
648 view->dragSourceProcs->beganDragImage(view, image, atLocation);
651 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
653 state = 1;
655 while (state != 6 && state != 0 && !_XErrorOccured) {
656 XAllowEvents(dpy, SyncPointer, CurrentTime);
657 WMNextEvent(dpy, &ev);
659 switch (ev.type) {
660 case MotionNotify:
661 if (state >= 1 && state <= 4) {
662 while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ;
664 protectBlock(True);
666 oldDragInfo = dragInfo;
668 updateDraggingInfo(scr, &dragInfo, &ev, icon);
670 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
671 dragInfo.imageLocation.y);
673 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
675 protectBlock(False);
677 /* XXXif entered a different destination, check the operation */
679 switch (state) {
680 case 1:
681 if (oldDragInfo.destinationWindow != None
682 && oldDragInfo.destinationWindow != scr->rootWin
683 && (dragInfo.destinationWindow == None
684 || dragInfo.destinationWindow == scr->rootWin)) {
685 /* left the droppable window */
686 state = 2;
687 action = -1;
689 break;
691 case 2:
692 if (dragInfo.destinationWindow != None
693 && dragInfo.destinationWindow != scr->rootWin) {
695 state = 1;
696 action = -1;
698 break;
700 case 3:
701 case 4:
702 if (oldDragInfo.destinationWindow != None
703 && oldDragInfo.destinationWindow != scr->rootWin
704 && (dragInfo.destinationWindow == None
705 || dragInfo.destinationWindow == scr->rootWin)) {
706 /* left the droppable window */
707 state = 2;
708 action = -1;
710 break;
713 break;
716 case ButtonRelease:
717 /* if (state >= 1 && state <= 4) */ {
719 protectBlock(True);
721 oldDragInfo = dragInfo;
723 updateDraggingInfo(scr, &dragInfo, &ev, icon);
725 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
726 dragInfo.imageLocation.y);
728 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
730 dragInfo.timestamp = ev.xbutton.time;
732 protectBlock(False);
734 switch (state) {
735 case 1:
736 state = 5;
737 timer = WMAddTimerHandler(3000, timeoutCallback,
738 (void*)dragInfo.destinationWindow);
739 break;
740 case 2:
741 state = 0;
742 break;
743 case 3:
744 state = 6;
745 break;
746 case 4:
747 state = 0;
748 break;
751 break;
753 case ClientMessage:
754 if ((state == 1 || state == 3 || state == 4 || state == 5)
755 && ev.xclient.message_type == scr->xdndStatusAtom
756 && ev.xclient.data.l[0] == dragInfo.destinationWindow) {
758 if (ev.xclient.data.l[1] & 1) {
759 SPIT("got accept msg");
760 /* will accept drop */
761 switch (state) {
762 case 1:
763 case 3:
764 case 4:
765 state = 3;
766 break;
767 case 5:
768 if (timer) {
769 WMDeleteTimerHandler(timer);
770 timer = NULL;
772 state = 6;
773 break;
775 action = actionToOperation(scr, ev.xclient.data.l[4]);
776 } else {
777 SPIT("got reject msg");
778 switch (state) {
779 case 1:
780 case 3:
781 case 4:
782 state = 4;
783 break;
784 case 5:
785 state = 0;
786 if (timer) {
787 WMDeleteTimerHandler(timer);
788 timer = NULL;
790 break;
792 action = 0;
795 if (ev.xclient.data.l[1] & (1<<1)) {
796 rect.pos.x = ev.xclient.data.l[2] >> 16;
797 rect.pos.y = ev.xclient.data.l[2] & 0xffff;
798 rect.size.width = ev.xclient.data.l[3] >> 16;
799 rect.size.height = ev.xclient.data.l[3] & 0xffff;
800 } else {
801 rect.size.width = 0;
804 } else if ((state >= 1 && state <= 5)
805 && ev.xclient.message_type == scr->xdndFinishedAtom
806 && ev.xclient.window == dragInfo.destinationWindow) {
808 wwarning("drag source received unexpected XdndFinished message from %x",
809 (unsigned)dragInfo.destinationWindow);
811 if (state == 3 || state == 4 || state == 5) {
812 state = 0;
813 if (timer) {
814 WMDeleteTimerHandler(timer);
815 timer = NULL;
820 default:
821 WMHandleEvent(&ev);
822 break;
825 if (ostate != state) {
826 if (state == 3) {
827 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
828 } else if (ostate == 3) {
829 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
831 ostate = state;
835 if (timer) {
836 WMDeleteTimerHandler(timer);
837 timer = NULL;
838 } else if (_XErrorOccured) {
839 /* got a timeout, send leave */
840 notifyDragLeave(scr, &dragInfo);
843 XUngrabPointer(dpy, CurrentTime);
845 SPIT("exited main loop");
847 if (_XErrorOccured || state != 6) {
848 goto cancelled;
851 assert(dragInfo.destinationWindow != None);
853 protectBlock(True);
854 notifyDrop(scr, &dragInfo);
855 protectBlock(False);
857 if (_XErrorOccured)
858 goto cancelled;
861 SPIT("dropped");
864 XDestroyWindow(dpy, icon);
866 return;
868 cancelled:
869 scr->dragSourceView = NULL;
871 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
872 event->xmotion.time);
874 if (slideBack) {
875 slideWindow(dpy, icon,
876 dragInfo.imageLocation.x, dragInfo.imageLocation.y,
877 atLocation.x, atLocation.y);
879 XDestroyWindow(dpy, icon);
880 if (view->dragSourceProcs->endedDragImage != NULL) {
881 view->dragSourceProcs->endedDragImage(view, image,
882 dragInfo.imageLocation,
883 False);
898 static Atom
899 operationToAction(WMScreen *scr, WMDragOperationType operation)
901 switch (operation) {
902 case WDOperationNone:
903 return None;
905 case WDOperationCopy:
906 return scr->xdndActionCopy;
908 case WDOperationMove:
909 return scr->xdndActionMove;
911 case WDOperationLink:
912 return scr->xdndActionLink;
914 case WDOperationAsk:
915 return scr->xdndActionAsk;
917 case WDOperationPrivate:
918 return scr->xdndActionPrivate;
920 default:
921 return None;
926 static WMDragOperationType
927 actionToOperation(WMScreen *scr, Atom action)
929 if (action == scr->xdndActionCopy) {
930 return WDOperationCopy;
932 } else if (action == scr->xdndActionMove) {
933 return WDOperationMove;
935 } else if (action == scr->xdndActionLink) {
936 return WDOperationLink;
938 } else if (action == scr->xdndActionAsk) {
939 return WDOperationAsk;
941 } else if (action == scr->xdndActionPrivate) {
942 return WDOperationPrivate;
944 } else if (action == None) {
946 return WDOperationCopy;
947 } else {
948 char *tmp = XGetAtomName(scr->display, action);
950 wwarning("unknown XDND action %s from 0x%x", tmp,
951 (unsigned)scr->dragInfo.sourceWindow);
952 XFree(tmp);
954 return WDOperationCopy;
963 static Atom*
964 getTypeList(Window window, XClientMessageEvent *event)
966 int i = 0;
967 Atom *types = NULL;
969 if (event->data.l[1] & 1) { /* > 3 types */
971 } else {
972 types = wmalloc(4 * sizeof(Atom));
973 if (event->data.l[2] != None)
974 types[i++] = event->data.l[2];
975 if (event->data.l[3] != None)
976 types[i++] = event->data.l[3];
977 if (event->data.l[4] != None)
978 types[i++] = event->data.l[4];
979 types[i] = 0;
982 if (types[0] == 0) {
983 wwarning("received invalid drag & drop type list");
984 /*XXX return;*/
987 return types;
992 #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info)
996 static void
997 receivedData(WMView *view, Atom selection, Atom target,
998 Time timestamp, void *cdata, WMData *data)
1000 WMScreen *scr = W_VIEW_SCREEN(view);
1001 WMDraggingInfo *info = (WMDraggingInfo*)cdata;
1002 Bool res;
1004 res = view->dragDestinationProcs->performDragOperation(view, info, data);
1006 if (res) {
1007 DISPATCH(view, concludeDragOperation, info);
1010 /* send finished message */
1011 sendClientMessage(scr->display, info->sourceWindow,
1012 scr->xdndFinishedAtom,
1013 info->destinationWindow,
1014 0, 0, 0, 0);
1016 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1021 void
1022 W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event)
1024 WMScreen *scr = W_VIEW_SCREEN(toplevel);
1025 WMView *oldView = NULL;
1026 WMView *newView = NULL;
1027 unsigned operation = 0;
1028 int x, y;
1029 enum {
1030 WNothing,
1031 WEnter,
1032 WLeave,
1033 WCross, /* leave one and enter another */
1034 WUpdate,
1035 WDrop
1037 Window source;
1038 int what = WNothing;
1039 Bool sendStatus = False;
1042 source = scr->dragInfo.sourceWindow;
1043 oldView = scr->dragInfo.destView;
1046 if (event->message_type == scr->xdndFinishedAtom) {
1047 WMView *view = scr->dragSourceView;
1049 if (view->dragSourceProcs->endedDragImage != NULL) {
1050 view->dragSourceProcs->endedDragImage(view,
1051 scr->dragInfo.image,
1052 scr->dragInfo.imageLocation,
1053 True);
1056 scr->dragSourceView = NULL;
1058 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
1059 scr->dragInfo.timestamp);
1061 return;
1067 if (event->message_type == scr->xdndEnterAtom) {
1068 Window foo, bar;
1069 int bla;
1070 unsigned ble;
1072 if (scr->dragInfo.sourceWindow != None) {
1073 puts("received Enter event in bad order");
1076 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1079 if ((event->data.l[1] >> 24) > XDND_VERSION) {
1080 wwarning("received drag & drop request with unsupported version %i",
1081 (event->data.l[1] >> 24));
1082 return;
1085 scr->dragInfo.protocolVersion = event->data.l[1] >> 24;
1086 scr->dragInfo.sourceWindow = source = event->data.l[0];
1087 scr->dragInfo.destinationWindow = event->window;
1089 /* XXX */
1090 scr->dragInfo.image = NULL;
1092 XQueryPointer(scr->display, scr->rootWin, &foo, &bar,
1093 &scr->dragInfo.location.x, &scr->dragInfo.location.y,
1094 &bla, &bla, &ble);
1096 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1097 scr->dragInfo.location.x,
1098 scr->dragInfo.location.y, &x, &y);
1101 newView = findViewInToplevel(scr->display,
1102 scr->dragInfo.destinationWindow,
1103 x, y);
1105 } else if (event->message_type == scr->xdndPositionAtom
1106 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1108 scr->dragInfo.location.x = event->data.l[2] >> 16;
1109 scr->dragInfo.location.y = event->data.l[2] & 0xffff;
1111 if (scr->dragInfo.protocolVersion >= 1) {
1112 scr->dragInfo.timestamp = event->data.l[3];
1113 scr->dragInfo.sourceOperation = actionToOperation(scr,
1114 event->data.l[4]);
1116 } else {
1117 scr->dragInfo.timestamp = CurrentTime;
1118 scr->dragInfo.sourceOperation = WDOperationCopy;
1121 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1122 scr->dragInfo.location.x,
1123 scr->dragInfo.location.y, &x, &y);
1125 newView = findViewInToplevel(scr->display,
1126 scr->dragInfo.destinationWindow,
1127 x, y);
1129 } else if (event->message_type == scr->xdndLeaveAtom
1130 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1132 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1134 } else if (event->message_type == scr->xdndDropAtom
1135 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1137 /* drop */
1138 what = WDrop;
1140 } else {
1141 return;
1146 * Now map the XDND events to WINGs events.
1149 if (what == WNothing) {
1150 if (IS_DROPPABLE(newView)) {
1151 if (oldView == NULL) { /* entered */
1152 what = WEnter;
1153 } else if (oldView == newView) { /* updated */
1154 what = WUpdate;
1155 } else {
1156 what = WCross;
1158 } else {
1159 if (oldView != NULL) {
1160 what = WLeave;
1161 } else {
1162 /* just send rejection msg */
1163 sendStatus = True;
1170 switch (what) {
1172 case WEnter:
1173 scr->dragInfo.destView = newView;
1174 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1175 sendStatus = True;
1176 break;
1178 case WLeave:
1179 scr->dragInfo.destView = NULL;
1180 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1181 sendStatus = True;
1182 operation = WDOperationNone;
1183 break;
1185 case WCross:
1186 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1187 scr->dragInfo.destView = newView;
1188 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1189 sendStatus = True;
1190 break;
1192 case WUpdate:
1193 operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo);
1194 sendStatus = True;
1195 break;
1197 case WDrop:
1199 char *type;
1201 type = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo);
1203 if (type != NULL) {
1204 if (!WMRequestSelection(scr->dragInfo.destView,
1205 scr->xdndSelectionAtom,
1206 XInternAtom(scr->display, type, False),
1207 scr->dragInfo.timestamp,
1208 receivedData, &scr->dragInfo)) {
1209 wwarning("could not request data for dropped data");
1211 /* send finished message */
1212 sendClientMessage(scr->display, source,
1213 scr->xdndFinishedAtom,
1214 scr->dragInfo.destinationWindow,
1215 0, 0, 0, 0);
1217 } else {
1218 /* send finished message */
1219 sendClientMessage(scr->display, source,
1220 scr->xdndFinishedAtom,
1221 scr->dragInfo.destinationWindow,
1222 0, 0, 0, 0);
1225 break;
1227 default:
1228 break;
1233 if (sendStatus) {
1234 Atom action;
1236 action = operationToAction(scr, operation);
1238 sendClientMessage(scr->display, source,
1239 scr->xdndStatusAtom,
1240 scr->dragInfo.destinationWindow,
1241 action != None ? 1 : 0, 0, 0, action);