- Fixed all // comments
[wmaker-crm.git] / WINGs / dragsource.c
blob7614f46309658e3c8abb41afce8755794e4b96ab
1 #include "../src/config.h"
3 #include <X11/Xatom.h>
4 #include <X11/cursorfont.h>
5 #ifdef SHAPE
6 #include <X11/extensions/shape.h>
7 #endif
9 #include "WINGsP.h"
12 #define XDND_DESTINATION_RESPONSE_MAX_DELAY 10000
13 #define MIN_X_MOVE_OFFSET 5
14 #define MIN_Y_MOVE_OFFSET 5
15 #define MAX_SLIDEBACK_ITER 15
17 #define VERSION_INFO(dragInfo) dragInfo->protocolVersion
18 #define XDND_PROPERTY_FORMAT 32
19 #define XDND_ACTION_DESCRIPTION_FORMAT 8
21 #define XDND_SOURCE_INFO(dragInfo) dragInfo->sourceInfo
22 #define XDND_DEST_WIN(dragInfo) dragInfo->sourceInfo->destinationWindow
23 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
24 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
25 #define XDND_SOURCE_VIEW(dragInfo) dragInfo->sourceInfo->sourceView
26 #define XDND_SOURCE_STATE(dragInfo) dragInfo->sourceInfo->state
27 #define XDND_SELECTION_PROCS(dragInfo) dragInfo->sourceInfo->selectionProcs
28 #define XDND_DRAG_ICON(dragInfo) dragInfo->sourceInfo->icon
29 #define XDND_MOUSE_OFFSET(dragInfo) dragInfo->sourceInfo->mouseOffset
30 #define XDND_DRAG_CURSOR(dragInfo) dragInfo->sourceInfo->dragCursor
31 #define XDND_DRAG_ICON_POS(dragInfo) dragInfo->sourceInfo->imageLocation
32 #define XDND_NO_POS_ZONE(dragInfo) dragInfo->sourceInfo->noPositionMessageZone
33 #define XDND_TIMESTAMP(dragInfo) dragInfo->timestamp
34 #define XDND_3_TYPES(dragInfo) dragInfo->sourceInfo->firstThreeTypes
35 #define XDND_SOURCE_VIEW_STORED(dragInfo) dragInfo->sourceInfo != NULL \
36 && dragInfo->sourceInfo->sourceView != NULL
39 static WMHandlerID dndSourceTimer = NULL;
42 static void* idleState(WMView *srcView, XClientMessageEvent *event,
43 WMDraggingInfo *info);
44 static void* dropAllowedState(WMView *srcView, XClientMessageEvent *event,
45 WMDraggingInfo *info);
46 static void* finishDropState(WMView *srcView, XClientMessageEvent *event,
47 WMDraggingInfo *info);
49 #ifdef XDND_DEBUG
50 static const char*
51 stateName(W_DndState *state)
53 if (state == NULL)
54 return "no state defined";
56 if (state == idleState)
57 return "idleState";
59 if (state == dropAllowedState)
60 return "dropAllowedState";
62 if (state == finishDropState)
63 return "finishDropState";
65 return "unknown state";
67 #endif
70 static WMScreen*
71 sourceScreen(WMDraggingInfo *info)
73 return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
77 static void
78 endDragProcess(WMDraggingInfo *info, Bool deposited)
80 WMView *view = XDND_SOURCE_VIEW(info);
81 WMScreen *scr = W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
83 /* free selection handler while view exists */
84 WMDeleteSelectionHandler(view,
85 scr->xdndSelectionAtom,
86 CurrentTime);
87 wfree(XDND_SELECTION_PROCS(info));
89 if (XDND_DRAG_CURSOR(info) != None) {
90 XFreeCursor(scr->display,
91 XDND_DRAG_CURSOR(info));
92 XDND_DRAG_CURSOR(info) = None;
95 if (view->dragSourceProcs->endedDrag != NULL) {
96 /* this can destroy source view (with a "move" action for example) */
97 view->dragSourceProcs->endedDrag(view, &XDND_DRAG_ICON_POS(info),
98 deposited);
101 /* clear remaining draggging infos */
102 wfree(XDND_SOURCE_INFO(info));
103 XDND_SOURCE_INFO(info) = NULL;
107 /* ----- drag cursor ----- */
108 static void
109 initDragCursor(WMDraggingInfo *info)
111 WMScreen *scr = sourceScreen(info);
112 XColor cursorFgColor, cursorBgColor;
114 /* green */
115 cursorFgColor.red = 0x4500;
116 cursorFgColor.green = 0xb000;
117 cursorFgColor.blue = 0x4500;
119 /* white */
120 cursorBgColor.red = 0xffff;
121 cursorBgColor.green = 0xffff;
122 cursorBgColor.blue = 0xffff;
124 XDND_DRAG_CURSOR(info) = XCreateFontCursor(scr->display, XC_left_ptr);
125 XRecolorCursor(scr->display,
126 XDND_DRAG_CURSOR(info),
127 &cursorFgColor,
128 &cursorBgColor);
130 XFlush(scr->display);
133 static void
134 recolorCursor(WMDraggingInfo *info, Bool dropIsAllowed)
136 WMScreen *scr = sourceScreen(info);
138 if (dropIsAllowed) {
139 XDefineCursor(scr->display,
140 scr->rootWin,
141 XDND_DRAG_CURSOR(info));
142 } else {
143 XDefineCursor(scr->display,
144 scr->rootWin,
145 scr->defaultCursor);
148 XFlush(scr->display);
150 /* ----- end of drag cursor ----- */
153 /* ----- selection procs ----- */
154 static WMData*
155 convertSelection(WMView *view, Atom selection, Atom target,
156 void *cdata, Atom *type)
158 WMScreen *scr;
159 WMData *data;
160 char *typeName;
162 scr = W_VIEW_SCREEN(view);
163 typeName = XGetAtomName(scr->display, target);
165 *type = target;
167 if (view->dragSourceProcs->fetchDragData != NULL) {
168 data = view->dragSourceProcs->fetchDragData(
169 view,
170 typeName);
171 } else {
172 data = NULL;
175 if (typeName != NULL)
176 XFree(typeName);
178 return data;
182 static void
183 selectionLost(WMView *view, Atom selection, void *cdata)
185 wwarning("DND selection lost during drag operation...");
189 static void
190 selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
192 #ifdef XDND_DEBUG
193 printf("selection done\n");
194 #endif
196 /* ----- end of selection procs ----- */
199 /* ----- visual part ----- */
201 static Window
202 makeDragIcon(WMScreen *scr, WMPixmap *pixmap)
204 Window window;
205 WMSize size;
206 unsigned long flags;
207 XSetWindowAttributes attribs;
208 Pixmap pix, mask;
210 if (!pixmap) {
211 pixmap = scr->defaultObjectIcon;
214 size = WMGetPixmapSize(pixmap);
215 pix = pixmap->pixmap;
216 mask = pixmap->mask;
218 flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap;
219 attribs.save_under = True;
220 attribs.background_pixmap = pix;
221 attribs.override_redirect = True;
222 attribs.colormap = scr->colormap;
224 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
225 size.height, 0, scr->depth, InputOutput,
226 scr->visual, flags, &attribs);
228 #ifdef SHAPE
230 if (mask) {
231 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask,
232 ShapeSet);
234 #endif
236 return window;
240 static void
241 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
243 double x, y, dx, dy;
244 int i;
245 int iterations;
247 iterations = WMIN(MAX_SLIDEBACK_ITER,
248 WMAX(abs(dstX-srcX), abs(dstY-srcY)));
250 x = srcX;
251 y = srcY;
253 dx = (double)(dstX-srcX)/iterations;
254 dy = (double)(dstY-srcY)/iterations;
256 for (i = 0; i <= iterations; i++) {
257 XMoveWindow(dpy, win, x, y);
258 XFlush(dpy);
260 wusleep(800);
262 x += dx;
263 y += dy;
268 static int
269 getInitialDragImageCoord(int viewCoord, int mouseCoord,
270 int viewSize, int iconSize)
272 if (iconSize >= viewSize) {
273 /* center icon coord on view */
274 return viewCoord - iconSize/2;
275 } else {
276 /* try to center icon on mouse pos */
278 if (mouseCoord - iconSize/2 <= viewCoord)
279 /* if icon was centered on mouse, it would be off view
280 thus, put icon left (resp. top) side
281 at view (resp. top) side */
282 return viewCoord;
284 else if (mouseCoord + iconSize/2 >= viewCoord + viewSize)
285 /* if icon was centered on mouse, it would be off view
286 thus, put icon right (resp. bottom) side
287 at view right (resp. bottom) side */
288 return viewCoord + viewSize - iconSize;
290 else
291 return mouseCoord - iconSize/2;
296 static void
297 initDragImagePos(WMView *view, WMDraggingInfo *info, XEvent *event)
299 WMSize iconSize = WMGetPixmapSize(view->dragImage);
300 WMSize viewSize = WMGetViewSize(view);
301 WMPoint viewPos;
302 Window foo;
304 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
305 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
306 0, 0, &(viewPos.x), &(viewPos.y),
307 &foo);
309 /* set icon pos */
310 XDND_DRAG_ICON_POS(info).x =
311 getInitialDragImageCoord(viewPos.x, event->xmotion.x_root,
312 viewSize.width, iconSize.width);
314 XDND_DRAG_ICON_POS(info).y =
315 getInitialDragImageCoord(viewPos.y, event->xmotion.y_root,
316 viewSize.height, iconSize.height);
318 /* set mouse offset relative to icon */
319 XDND_MOUSE_OFFSET(info).x =
320 event->xmotion.x_root - XDND_DRAG_ICON_POS(info).x;
321 XDND_MOUSE_OFFSET(info).y =
322 event->xmotion.y_root - XDND_DRAG_ICON_POS(info).y;
326 static void
327 refreshDragImage(WMView *view, WMDraggingInfo *info)
329 WMScreen *scr = W_VIEW_SCREEN(view);
331 XMoveWindow(scr->display, XDND_DRAG_ICON(info),
332 XDND_DRAG_ICON_POS(info).x,
333 XDND_DRAG_ICON_POS(info).y);
337 static void
338 startDragImage(WMView *view, WMDraggingInfo *info, XEvent* event)
340 WMScreen *scr = W_VIEW_SCREEN(view);
342 XDND_DRAG_ICON(info) = makeDragIcon(scr, view->dragImage);
343 initDragImagePos(view, info, event);
344 refreshDragImage(view, info);
345 XMapRaised(scr->display, XDND_DRAG_ICON(info));
347 initDragCursor(info);
351 static void
352 endDragImage(WMDraggingInfo *info, Bool slideBack)
354 WMView *view = XDND_SOURCE_VIEW(info);
355 Display *dpy = W_VIEW_SCREEN(view)->display;
357 if (slideBack) {
358 WMPoint toLocation;
359 Window foo;
361 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
362 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
363 0, 0, &(toLocation.x), &(toLocation.y),
364 &foo);
366 slideWindow(dpy, XDND_DRAG_ICON(info),
367 XDND_DRAG_ICON_POS(info).x,
368 XDND_DRAG_ICON_POS(info).y,
369 toLocation.x,
370 toLocation.y);
373 XDestroyWindow(dpy, XDND_DRAG_ICON(info));
376 /* ----- end of visual part ----- */
379 /* ----- messages ----- */
381 /* send a DnD message to the destination window */
382 static Bool
383 sendDnDClientMessage(WMDraggingInfo *info, Atom message,
384 unsigned long data1,
385 unsigned long data2,
386 unsigned long data3,
387 unsigned long data4)
389 Display *dpy = sourceScreen(info)->display;
390 Window srcWin = WMViewXID(XDND_SOURCE_VIEW(info));
391 Window destWin = XDND_DEST_WIN(info);
393 if (! W_SendDnDClientMessage(dpy,
394 destWin,
395 message,
396 srcWin,
397 data1,
398 data2,
399 data3,
400 data4)) {
401 /* drop failed */
402 recolorCursor(info, False);
403 endDragImage(info, True);
404 endDragProcess(info, False);
405 return False;
408 return True;
412 static Bool
413 sendEnterMessage(WMDraggingInfo *info)
415 WMScreen *scr = sourceScreen(info);
416 unsigned long data1;
418 data1 = (VERSION_INFO(info) << 24)|1; /* 1: support of type list */
420 return sendDnDClientMessage(info, scr->xdndEnterAtom,
421 data1,
422 XDND_3_TYPES(info)[0],
423 XDND_3_TYPES(info)[1],
424 XDND_3_TYPES(info)[2]);
429 // this functon doesn't return something in all cases.
430 // control reaches end of non-void function. fix this -Dan */
431 static Bool
432 sendPositionMessage(WMDraggingInfo *info, WMPoint *mousePos)
434 WMScreen *scr = sourceScreen(info);
435 WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
437 if (noPosZone->size.width != 0 || noPosZone->size.height != 0) {
438 if (mousePos->x < noPosZone->pos.x
439 || mousePos->x > (noPosZone->pos.x + noPosZone->size.width)
440 || mousePos->y < noPosZone->pos.y
441 || mousePos->y > (noPosZone->pos.y + noPosZone->size.width)) {
442 /* send position if out of zone defined by destination */
443 return sendDnDClientMessage(info, scr->xdndPositionAtom,
445 mousePos->x<<16|mousePos->y,
446 XDND_TIMESTAMP(info),
447 XDND_SOURCE_ACTION(info));
449 } else {
450 /* send position on each move */
451 return sendDnDClientMessage(info, scr->xdndPositionAtom,
453 mousePos->x<<16|mousePos->y,
454 XDND_TIMESTAMP(info),
455 XDND_SOURCE_ACTION(info));
460 static Bool
461 sendLeaveMessage(WMDraggingInfo *info)
463 WMScreen *scr = sourceScreen(info);
465 return sendDnDClientMessage(info, scr->xdndLeaveAtom,
466 0, 0, 0, 0);
470 static Bool
471 sendDropMessage(WMDraggingInfo *info)
473 WMScreen *scr = sourceScreen(info);
475 return sendDnDClientMessage(info,
476 scr->xdndDropAtom,
478 XDND_TIMESTAMP(info),
479 0, 0);
482 /* ----- end of messages ----- */
485 static Atom*
486 getTypeAtomList(WMScreen *scr, WMView *view, int* count)
488 WMArray* types;
489 Atom* typeAtoms;
490 int i;
492 types = view->dragSourceProcs->dropDataTypes(view);
494 if (types != NULL) {
495 *count = WMGetArrayItemCount(types);
496 if (*count > 0) {
497 typeAtoms = wmalloc((*count)*sizeof(Atom));
498 for (i=0; i < *count; i++) {
499 typeAtoms[i] = XInternAtom(scr->display,
500 WMGetFromArray(types, i),
501 False);
504 /* WMFreeArray(types); */
505 return typeAtoms;
508 /* WMFreeArray(types); */
511 *count = 1;
512 typeAtoms = wmalloc(sizeof(Atom));
513 *typeAtoms = None;
515 return typeAtoms;
519 static void
520 registerDropTypes(WMScreen *scr, WMView *view, WMDraggingInfo *info)
522 Atom* typeList;
523 int i, count;
525 typeList = getTypeAtomList(scr, view, &count);
527 /* store the first 3 types */
528 for(i=0; i < 3 && i < count; i++)
529 XDND_3_TYPES(info)[i] = typeList[i];
531 for(; i < 3; i++)
532 XDND_3_TYPES(info)[i] = None;
535 /* store the entire type list */
536 XChangeProperty(scr->display,
537 WMViewXID(view),
538 scr->xdndTypeListAtom,
539 XA_ATOM,
540 XDND_PROPERTY_FORMAT,
541 PropModeReplace,
542 (unsigned char*) typeList,
543 count);
547 static void
548 registerOperationList(WMScreen *scr, WMView *view, WMArray* operationArray)
550 Atom* actionList;
551 WMDragOperationType operation;
552 int count = WMGetArrayItemCount(operationArray);
553 int i;
555 actionList = wmalloc(sizeof(Atom)*count);
557 for(i=0; i < count; i++) {
558 operation = WMGetDragOperationItemType(
559 WMGetFromArray(operationArray, i));
560 actionList[i] = W_OperationToAction(scr, operation);
563 XChangeProperty(scr->display,
564 WMViewXID(view),
565 scr->xdndActionListAtom,
566 XA_ATOM,
567 XDND_PROPERTY_FORMAT,
568 PropModeReplace,
569 (unsigned char*) actionList,
570 count);
573 static void
574 registerDescriptionList(WMScreen *scr, WMView *view, WMArray* operationArray)
576 char *text, *textListItem, *textList;
577 int count = WMGetArrayItemCount(operationArray);
578 int i;
579 int size = 0;
581 /* size of XA_STRING info */
582 for(i=0; i < count; i++) {
583 size += strlen(WMGetDragOperationItemText(
584 WMGetFromArray(operationArray, i))) + 1; /* +1 = +NULL */
587 /* create text list */
588 textList = wmalloc(size);
589 textListItem = textList;
591 for(i=0; i < count; i++) {
592 text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
593 strcpy(textListItem, text);
595 /* to next text offset */
596 textListItem = &(textListItem[strlen(textListItem) + 1]);
599 XChangeProperty(scr->display,
600 WMViewXID(view),
601 scr->xdndActionDescriptionAtom,
602 XA_STRING,
603 XDND_ACTION_DESCRIPTION_FORMAT,
604 PropModeReplace,
605 (unsigned char*) textList,
606 size);
609 /* called if wanted operation is WDOperationAsk */
610 static void
611 registerSupportedOperations(WMView *view)
613 WMScreen *scr = W_VIEW_SCREEN(view);
614 WMArray* operationArray;
616 operationArray = view->dragSourceProcs->askedOperations(view);
618 registerOperationList(scr, view, operationArray);
619 registerDescriptionList(scr, view, operationArray);
621 /* WMFreeArray(operationArray); */
625 static void
626 initSourceDragInfo(WMView *sourceView, WMDraggingInfo *info)
628 WMRect emptyZone;
630 XDND_SOURCE_INFO(info) = (W_DragSourceInfo*) wmalloc(sizeof(W_DragSourceInfo));
632 XDND_SOURCE_VIEW(info) = sourceView;
633 XDND_DEST_WIN(info) = None;
634 XDND_DRAG_ICON(info) = None;
636 XDND_SOURCE_ACTION(info) = W_OperationToAction(
637 W_VIEW_SCREEN(sourceView),
638 sourceView->dragSourceProcs->wantedDropOperation(sourceView));
640 XDND_DEST_ACTION(info) = None;
642 XDND_SOURCE_STATE(info) = idleState;
644 emptyZone.pos = wmkpoint(0, 0);
645 emptyZone.size = wmksize(0, 0);
646 XDND_NO_POS_ZONE(info) = emptyZone;
651 Returned array is destroyed after dropDataTypes call
653 static WMArray*
654 defDropDataTypes(WMView *self)
656 return NULL;
660 static WMDragOperationType
661 defWantedDropOperation(WMView *self)
663 return WDOperationNone;
668 Must be defined if wantedDropOperation return WDOperationAsk
669 (useless otherwise).
670 Return a WMDragOperationItem array (destroyed after call).
671 A WMDragOperationItem links a label to an operation.
672 static WMArray*
673 defAskedOperations(WMView *self); */
676 static Bool
677 defAcceptDropOperation(WMView *self, WMDragOperationType allowedOperation)
679 return False;
683 static void
684 defBeganDrag(WMView *self, WMPoint *point)
689 static void
690 defEndedDrag(WMView *self, WMPoint *point, Bool deposited)
696 Returned data is not destroyed
698 static WMData*
699 defFetchDragData(WMView *self, char *type)
701 return NULL;
705 void
706 WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
708 if (view->dragSourceProcs)
709 wfree(view->dragSourceProcs);
710 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
712 *view->dragSourceProcs = *procs;
714 if (procs->dropDataTypes == NULL)
715 view->dragSourceProcs->dropDataTypes = defDropDataTypes;
717 if (procs->wantedDropOperation == NULL)
718 view->dragSourceProcs->wantedDropOperation = defWantedDropOperation;
721 Note: askedOperations can be NULL, if wantedDropOperation never returns
722 WDOperationAsk.
725 if (procs->acceptDropOperation == NULL)
726 view->dragSourceProcs->acceptDropOperation = defAcceptDropOperation;
728 if (procs->beganDrag == NULL)
729 view->dragSourceProcs->beganDrag = defBeganDrag;
731 if (procs->endedDrag == NULL)
732 view->dragSourceProcs->endedDrag = defEndedDrag;
734 if (procs->fetchDragData == NULL)
735 view->dragSourceProcs->fetchDragData = defFetchDragData;
739 static Bool
740 isXdndAware(WMScreen *scr, Window win)
742 Atom type;
743 int format;
744 unsigned long count, remain;
745 unsigned char *winXdndVersion;
747 if (win == None)
748 return False;
750 XGetWindowProperty(scr->display, win, scr->xdndAwareAtom,
751 0, 1, False, XA_ATOM, &type, &format,
752 &count, &remain, &winXdndVersion);
754 if (type != XA_ATOM
755 || format != XDND_PROPERTY_FORMAT
756 || count == 0 || !winXdndVersion) {
757 if (winXdndVersion)
758 XFree(winXdndVersion);
759 return False;
762 XFree(winXdndVersion);
763 return (count == 1); /* xdnd version is set */
767 static Window*
768 windowChildren(Display *dpy, Window win, unsigned *nchildren)
770 Window *children;
771 Window foo, bar;
773 if (!XQueryTree(dpy, win, &foo, &bar, &children, nchildren)) {
774 *nchildren = 0;
775 return NULL;
776 } else
777 return children;
780 static Window
781 lookForAwareWindow(WMScreen *scr, WMPoint *mousePos, Window win)
783 int tmpx, tmpy;
784 Window child;
786 /* Since xdnd v3, only the toplevel window should be aware */
787 if (isXdndAware(scr, win))
788 return win;
790 /* inspect child under pointer */
791 if (XTranslateCoordinates(scr->display, scr->rootWin, win,
792 mousePos->x, mousePos->y, &tmpx, &tmpy,
793 &child)) {
794 if (child == None)
795 return None;
796 else
797 return lookForAwareWindow(scr, mousePos, child);
800 return None;
804 static Window
805 findDestination(WMDraggingInfo *info, WMPoint *mousePos)
807 WMScreen *scr = sourceScreen(info);
808 unsigned nchildren;
809 Window *children = windowChildren(scr->display, scr->rootWin, &nchildren);
810 int i;
811 XWindowAttributes attr;
813 if (isXdndAware(scr, scr->rootWin))
814 return scr->rootWin;
816 /* exclude drag icon (and upper) from search */
817 for (i = nchildren-1; i >= 0; i--) {
818 if (children[i] == XDND_DRAG_ICON(info)) {
819 i--;
820 break;
824 if (i < 0) {
825 /* root window has no child under drag icon, and is not xdnd aware. */
826 return None;
829 /* inspecting children, from upper to lower */
830 for (; i >= 0; i--) {
831 if (XGetWindowAttributes(scr->display, children[i], &attr)
832 && attr.map_state == IsViewable
833 && mousePos->x >= attr.x
834 && mousePos->y >= attr.y
835 && mousePos->x < attr.x + attr.width
836 && mousePos->y < attr.y + attr.height) {
837 return lookForAwareWindow(scr, mousePos, children[i]);
841 /* No child window under drag pointer */
842 return None;
846 static void
847 initMotionProcess(WMView *view, WMDraggingInfo *info,
848 XEvent *event, WMPoint *startLocation)
850 WMScreen *scr = W_VIEW_SCREEN(view);
852 /* take ownership of XdndSelection */
853 XDND_SELECTION_PROCS(info) =
854 (WMSelectionProcs*) wmalloc(sizeof(WMSelectionProcs));
855 XDND_SELECTION_PROCS(info)->convertSelection = convertSelection;
856 XDND_SELECTION_PROCS(info)->selectionLost = selectionLost;
857 XDND_SELECTION_PROCS(info)->selectionDone = selectionDone;
858 XDND_TIMESTAMP(info) = event->xmotion.time;
860 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
861 CurrentTime,
862 XDND_SELECTION_PROCS(info), NULL)) {
863 wwarning("could not get ownership or DND selection");
864 return;
867 registerDropTypes(scr, view, info);
869 if (XDND_SOURCE_ACTION(info) == W_VIEW_SCREEN(view)->xdndActionAsk)
870 registerSupportedOperations(view);
872 if (view->dragSourceProcs->beganDrag != NULL) {
873 view->dragSourceProcs->beganDrag(view, startLocation);
878 static void
879 processMotion(WMDraggingInfo *info, Window windowUnderDrag, WMPoint *mousePos)
881 /* // WMScreen *scr = sourceScreen(info); */
882 Window newDestination = findDestination(info, mousePos);
884 W_DragSourceStopTimer();
886 if (newDestination != XDND_DEST_WIN(info)) {
887 recolorCursor(info, False);
889 if (XDND_DEST_WIN(info) != None) {
890 /* leaving a xdnd window */
891 sendLeaveMessage(info);
894 XDND_DEST_WIN(info) = newDestination;
895 XDND_SOURCE_STATE(info) = idleState;
896 XDND_DEST_ACTION(info) = None;
897 XDND_NO_POS_ZONE(info).size.width = 0;
898 XDND_NO_POS_ZONE(info).size.height = 0;
900 if (newDestination != None) {
901 /* entering a xdnd window */
902 if (! sendEnterMessage(info)) {
903 XDND_DEST_WIN(info) = None;
904 return;
907 W_DragSourceStartTimer(info);
909 } else {
910 if (XDND_DEST_WIN(info) != None) {
911 if (! sendPositionMessage(info, mousePos)) {
912 XDND_DEST_WIN(info) = None;
913 return;
916 W_DragSourceStartTimer(info);
922 static Bool
923 processButtonRelease(WMDraggingInfo *info)
925 if (XDND_SOURCE_STATE(info) == dropAllowedState) {
926 /* begin drop */
927 W_DragSourceStopTimer();
929 if (! sendDropMessage(info))
930 return False;
932 W_DragSourceStartTimer(info);
933 return True;
934 } else {
935 if (XDND_DEST_WIN(info) != None)
936 sendLeaveMessage(info);
938 W_DragSourceStopTimer();
939 return False;
944 Bool
945 WMIsDraggingFromView(WMView *view)
947 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
949 return ( XDND_SOURCE_INFO(info) != NULL
950 && XDND_SOURCE_STATE(info) != finishDropState);
951 /* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
955 void
956 WMDragImageFromView(WMView *view, XEvent *event)
958 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
959 WMPoint mouseLocation;
961 switch(event->type) {
962 case ButtonPress:
963 if (event->xbutton.button == Button1) {
964 XEvent nextEvent;
966 XPeekEvent(event->xbutton.display, &nextEvent);
968 /* Initialize only if a drag really begins (avoid clicks) */
969 if (nextEvent.type == MotionNotify) {
970 initSourceDragInfo(view, info);
974 break;
976 case ButtonRelease:
977 if (WMIsDraggingFromView(view)) {
978 Bool dropBegan = processButtonRelease(info);
980 recolorCursor(info, False);
981 if (dropBegan) {
982 endDragImage(info, False);
983 XDND_SOURCE_STATE(info) = finishDropState;
984 } else {
985 /* drop failed */
986 endDragImage(info, True);
987 endDragProcess(info,False);
990 break;
992 case MotionNotify:
993 if (WMIsDraggingFromView(view)) {
994 mouseLocation = wmkpoint(event->xmotion.x_root,
995 event->xmotion.y_root);
997 if (abs(XDND_DRAG_ICON_POS(info).x - mouseLocation.x) >=
998 MIN_X_MOVE_OFFSET
999 || abs(XDND_DRAG_ICON_POS(info).y - mouseLocation.y) >=
1000 MIN_Y_MOVE_OFFSET) {
1001 if (XDND_DRAG_ICON(info) == None) {
1002 initMotionProcess(view, info, event, &mouseLocation);
1003 startDragImage(view, info, event);
1004 } else {
1005 XDND_DRAG_ICON_POS(info).x =
1006 mouseLocation.x - XDND_MOUSE_OFFSET(info).x;
1007 XDND_DRAG_ICON_POS(info).y =
1008 mouseLocation.y - XDND_MOUSE_OFFSET(info).y;
1010 refreshDragImage(view, info);
1011 processMotion(info,
1012 event->xmotion.window,
1013 &mouseLocation);
1017 break;
1022 /* Minimal mouse events handler: no right or double-click detection,
1023 only drag is supported */
1024 static void
1025 dragImageHandler(XEvent *event, void *cdata)
1027 WMView *view = (WMView*)cdata;
1029 WMDragImageFromView(view, event);
1033 /* ----- source states ----- */
1035 #ifdef XDND_DEBUG
1036 static void
1037 traceStatusMsg(Display *dpy, XClientMessageEvent *statusEvent)
1039 printf("Xdnd status message:\n");
1041 if (statusEvent->data.l[1] & 0x2UL)
1042 printf("send position on every move\n");
1043 else {
1044 int x, y, w, h;
1045 x = statusEvent->data.l[2] >> 16;
1046 y = statusEvent->data.l[2] & 0xFFFFL;
1047 w = statusEvent->data.l[3] >> 16;
1048 h = statusEvent->data.l[3] & 0xFFFFL;
1050 printf("send position out of ((%d,%d) , (%d,%d))\n",
1051 x, y, x+w, y+h);
1054 if (statusEvent->data.l[1] & 0x1L)
1055 printf("allowed action: %s\n",
1056 XGetAtomName(dpy, statusEvent->data.l[4]));
1057 else
1058 printf("no action allowed\n");
1060 #endif
1063 static void
1064 storeDropAction(WMDraggingInfo *info, Atom destAction)
1066 WMView* sourceView = XDND_SOURCE_VIEW(info);
1067 WMScreen *scr = W_VIEW_SCREEN(sourceView);
1069 if (sourceView->dragSourceProcs->acceptDropOperation != NULL) {
1070 if (sourceView->dragSourceProcs->acceptDropOperation(
1071 sourceView,
1072 W_ActionToOperation(scr, destAction)))
1073 XDND_DEST_ACTION(info) = destAction;
1074 else
1075 XDND_DEST_ACTION(info) = None;
1076 } else {
1077 XDND_DEST_ACTION(info) = destAction;
1082 static void
1083 storeStatusMessageInfos(WMDraggingInfo *info, XClientMessageEvent *statusEvent)
1085 WMRect* noPosZone = &(XDND_NO_POS_ZONE(info));
1087 #ifdef XDND_DEBUG
1089 traceStatusMsg(sourceScreen(info)->display, statusEvent);
1090 #endif
1092 if (statusEvent->data.l[1] & 0x2UL) {
1093 /* bit 1 set: destination wants position messages on every move */
1094 noPosZone->size.width = 0;
1095 noPosZone->size.height = 0;
1096 } else {
1097 /* don't send another position message while in given rectangle */
1098 noPosZone->pos.x = statusEvent->data.l[2] >> 16;
1099 noPosZone->pos.y = statusEvent->data.l[2] & 0xFFFFL;
1100 noPosZone->size.width = statusEvent->data.l[3] >> 16;
1101 noPosZone->size.height = statusEvent->data.l[3] & 0xFFFFL;
1104 if ((statusEvent->data.l[1] & 0x1L) || statusEvent->data.l[4] != None) {
1105 /* destination accept drop */
1106 storeDropAction(info, statusEvent->data.l[4]);
1107 } else {
1108 XDND_DEST_ACTION(info) = None;
1113 static void*
1114 idleState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
1116 WMScreen *scr;
1117 Atom destMsg = event->message_type;
1119 scr = W_VIEW_SCREEN(view);
1121 if (destMsg == scr->xdndStatusAtom) {
1122 storeStatusMessageInfos(info, event);
1124 if (XDND_DEST_ACTION(info) != None) {
1125 recolorCursor(info, True);
1126 W_DragSourceStartTimer(info);
1127 return dropAllowedState;
1128 } else {
1129 /* drop denied */
1130 recolorCursor(info, False);
1131 return idleState;
1135 if (destMsg == scr->xdndFinishedAtom) {
1136 wwarning("received xdndFinishedAtom before drop began");
1139 W_DragSourceStartTimer(info);
1140 return idleState;
1144 static void*
1145 dropAllowedState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
1147 WMScreen *scr = W_VIEW_SCREEN(view);
1148 Atom destMsg = event->message_type;
1150 if (destMsg == scr->xdndStatusAtom) {
1151 storeStatusMessageInfos(info, event);
1153 if (XDND_DEST_ACTION(info) == None) {
1154 /* drop denied */
1155 recolorCursor(info, False);
1156 return idleState;
1160 W_DragSourceStartTimer(info);
1161 return dropAllowedState;
1165 static void*
1166 finishDropState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
1168 WMScreen *scr = W_VIEW_SCREEN(view);
1169 Atom destMsg = event->message_type;
1171 if (destMsg == scr->xdndFinishedAtom) {
1172 endDragProcess(info, True);
1173 return NULL;
1176 W_DragSourceStartTimer(info);
1177 return finishDropState;
1179 /* ----- end of source states ----- */
1182 /* ----- source timer ----- */
1183 static void
1184 dragSourceResponseTimeOut(void *source)
1186 WMView *view = (WMView*)source;
1187 WMDraggingInfo *info = &(W_VIEW_SCREEN(view)->dragInfo);
1189 wwarning("delay for drag destination response expired");
1190 sendLeaveMessage(info);
1192 recolorCursor(info, False);
1193 if (XDND_SOURCE_STATE(info) == finishDropState) {
1194 /* drop failed */
1195 endDragImage(info, True);
1196 endDragProcess(info, False);
1197 } else {
1198 XDND_SOURCE_STATE(info) = idleState;
1202 void
1203 W_DragSourceStopTimer()
1205 if (dndSourceTimer != NULL) {
1206 WMDeleteTimerHandler(dndSourceTimer);
1207 dndSourceTimer = NULL;
1211 void
1212 W_DragSourceStartTimer(WMDraggingInfo *info)
1214 W_DragSourceStopTimer();
1216 dndSourceTimer = WMAddTimerHandler(
1217 XDND_DESTINATION_RESPONSE_MAX_DELAY,
1218 dragSourceResponseTimeOut,
1219 XDND_SOURCE_VIEW(info));
1222 /* ----- End of Destination timer ----- */
1225 void
1226 W_DragSourceStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
1228 WMView *view;
1229 W_DndState* newState;
1231 if (XDND_SOURCE_VIEW_STORED(info)) {
1232 view = XDND_SOURCE_VIEW(info);
1233 #ifdef XDND_DEBUG
1235 printf("current source state: %s\n",
1236 stateName(XDND_SOURCE_STATE(info)));
1237 #endif
1239 newState = (W_DndState*) XDND_SOURCE_STATE(info)(view, event, info);
1241 #ifdef XDND_DEBUG
1243 printf("new source state: %s\n", stateName(newState));
1244 #endif
1246 if (newState != NULL)
1247 XDND_SOURCE_STATE(info) = newState;
1248 /* else drop finished, and info has been flushed */
1253 void WMSetViewDragImage(WMView* view, WMPixmap *dragImage)
1255 if (view->dragImage != NULL)
1256 WMReleasePixmap(view->dragImage);
1258 view->dragImage = WMRetainPixmap(dragImage);
1262 void WMReleaseViewDragImage(WMView* view)
1264 if (view->dragImage != NULL)
1265 WMReleasePixmap(view->dragImage);
1269 /* Create a drag handler, associating drag event masks with dragEventProc */
1270 void
1271 WMCreateDragHandler(WMView *view, WMEventProc *dragEventProc, void *clientData)
1273 WMCreateEventHandler(view,
1274 ButtonPressMask|ButtonReleaseMask|Button1MotionMask,
1275 dragEventProc, clientData);
1279 void
1280 WMDeleteDragHandler(WMView *view, WMEventProc *dragEventProc, void *clientData)
1282 WMDeleteEventHandler(view,
1283 ButtonPressMask|ButtonReleaseMask|Button1MotionMask,
1284 dragEventProc, clientData);
1288 /* set default drag handler for view */
1289 void
1290 WMSetViewDraggable(WMView *view, WMDragSourceProcs *dragSourceProcs,
1291 WMPixmap *dragImage)
1293 wassertr(dragImage != NULL);
1294 view->dragImage = WMRetainPixmap(dragImage);
1296 WMSetViewDragSourceProcs(view, dragSourceProcs);
1298 WMCreateDragHandler(view, dragImageHandler, view);
1302 void
1303 WMUnsetViewDraggable(WMView *view)
1305 if (view->dragSourceProcs) {
1306 wfree(view->dragSourceProcs);
1307 view->dragSourceProcs = NULL;
1310 WMReleaseViewDragImage(view);
1312 WMDeleteDragHandler(view, dragImageHandler, view);