1 #include "../src/config.h"
4 #include <X11/cursorfont.h>
6 #include <X11/extensions/shape.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
);
51 stateName(W_DndState
*state
)
54 return "no state defined";
56 if (state
== idleState
)
59 if (state
== dropAllowedState
)
60 return "dropAllowedState";
62 if (state
== finishDropState
)
63 return "finishDropState";
65 return "unknown state";
71 sourceScreen(WMDraggingInfo
*info
)
73 return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
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
,
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
),
101 /* clear remaining draggging infos */
102 wfree(XDND_SOURCE_INFO(info
));
103 XDND_SOURCE_INFO(info
) = NULL
;
107 /* ----- drag cursor ----- */
109 initDragCursor(WMDraggingInfo
*info
)
111 WMScreen
*scr
= sourceScreen(info
);
112 XColor cursorFgColor
, cursorBgColor
;
115 cursorFgColor
.red
= 0x4500;
116 cursorFgColor
.green
= 0xb000;
117 cursorFgColor
.blue
= 0x4500;
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
),
130 XFlush(scr
->display
);
134 recolorCursor(WMDraggingInfo
*info
, Bool dropIsAllowed
)
136 WMScreen
*scr
= sourceScreen(info
);
139 XDefineCursor(scr
->display
,
141 XDND_DRAG_CURSOR(info
));
143 XDefineCursor(scr
->display
,
148 XFlush(scr
->display
);
150 /* ----- end of drag cursor ----- */
153 /* ----- selection procs ----- */
155 convertSelection(WMView
*view
, Atom selection
, Atom target
,
156 void *cdata
, Atom
*type
)
162 scr
= W_VIEW_SCREEN(view
);
163 typeName
= XGetAtomName(scr
->display
, target
);
167 if (view
->dragSourceProcs
->fetchDragData
!= NULL
) {
168 data
= view
->dragSourceProcs
->fetchDragData(
175 if (typeName
!= NULL
)
183 selectionLost(WMView
*view
, Atom selection
, void *cdata
)
185 wwarning("DND selection lost during drag operation...");
190 selectionDone(WMView
*view
, Atom selection
, Atom target
, void *cdata
)
193 printf("selection done\n");
196 /* ----- end of selection procs ----- */
199 /* ----- visual part ----- */
202 makeDragIcon(WMScreen
*scr
, WMPixmap
*pixmap
)
207 XSetWindowAttributes attribs
;
211 pixmap
= scr
->defaultObjectIcon
;
214 size
= WMGetPixmapSize(pixmap
);
215 pix
= pixmap
->pixmap
;
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
);
231 XShapeCombineMask(scr
->display
, window
, ShapeBounding
, 0, 0, mask
,
241 slideWindow(Display
*dpy
, Window win
, int srcX
, int srcY
, int dstX
, int dstY
)
247 iterations
= WMIN(MAX_SLIDEBACK_ITER
,
248 WMAX(abs(dstX
-srcX
), abs(dstY
-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
);
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;
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 */
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
;
291 return mouseCoord
- iconSize
/2;
297 initDragImagePos(WMView
*view
, WMDraggingInfo
*info
, XEvent
*event
)
299 WMSize iconSize
= WMGetPixmapSize(view
->dragImage
);
300 WMSize viewSize
= WMGetViewSize(view
);
304 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
305 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
306 0, 0, &(viewPos
.x
), &(viewPos
.y
),
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
;
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
);
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
);
352 endDragImage(WMDraggingInfo
*info
, Bool slideBack
)
354 WMView
*view
= XDND_SOURCE_VIEW(info
);
355 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
361 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
362 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
363 0, 0, &(toLocation
.x
), &(toLocation
.y
),
366 slideWindow(dpy
, XDND_DRAG_ICON(info
),
367 XDND_DRAG_ICON_POS(info
).x
,
368 XDND_DRAG_ICON_POS(info
).y
,
373 XDestroyWindow(dpy
, XDND_DRAG_ICON(info
));
376 /* ----- end of visual part ----- */
379 /* ----- messages ----- */
381 /* send a DnD message to the destination window */
383 sendDnDClientMessage(WMDraggingInfo
*info
, Atom message
,
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
,
402 recolorCursor(info
, False
);
403 endDragImage(info
, True
);
404 endDragProcess(info
, False
);
413 sendEnterMessage(WMDraggingInfo
*info
)
415 WMScreen
*scr
= sourceScreen(info
);
418 data1
= (VERSION_INFO(info
) << 24)|1; /* 1: support of type list */
420 return sendDnDClientMessage(info
, scr
->xdndEnterAtom
,
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 */
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
));
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
));
461 sendLeaveMessage(WMDraggingInfo
*info
)
463 WMScreen
*scr
= sourceScreen(info
);
465 return sendDnDClientMessage(info
, scr
->xdndLeaveAtom
,
471 sendDropMessage(WMDraggingInfo
*info
)
473 WMScreen
*scr
= sourceScreen(info
);
475 return sendDnDClientMessage(info
,
478 XDND_TIMESTAMP(info
),
482 /* ----- end of messages ----- */
486 getTypeAtomList(WMScreen
*scr
, WMView
*view
, int* count
)
492 types
= view
->dragSourceProcs
->dropDataTypes(view
);
495 *count
= WMGetArrayItemCount(types
);
497 typeAtoms
= wmalloc((*count
)*sizeof(Atom
));
498 for (i
=0; i
< *count
; i
++) {
499 typeAtoms
[i
] = XInternAtom(scr
->display
,
500 WMGetFromArray(types
, i
),
504 /* WMFreeArray(types); */
508 /* WMFreeArray(types); */
512 typeAtoms
= wmalloc(sizeof(Atom
));
520 registerDropTypes(WMScreen
*scr
, WMView
*view
, WMDraggingInfo
*info
)
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
];
532 XDND_3_TYPES(info
)[i
] = None
;
535 /* store the entire type list */
536 XChangeProperty(scr
->display
,
538 scr
->xdndTypeListAtom
,
540 XDND_PROPERTY_FORMAT
,
542 (unsigned char*) typeList
,
548 registerOperationList(WMScreen
*scr
, WMView
*view
, WMArray
* operationArray
)
551 WMDragOperationType operation
;
552 int count
= WMGetArrayItemCount(operationArray
);
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
,
565 scr
->xdndActionListAtom
,
567 XDND_PROPERTY_FORMAT
,
569 (unsigned char*) actionList
,
574 registerDescriptionList(WMScreen
*scr
, WMView
*view
, WMArray
* operationArray
)
576 char *text
, *textListItem
, *textList
;
577 int count
= WMGetArrayItemCount(operationArray
);
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
,
601 scr
->xdndActionDescriptionAtom
,
603 XDND_ACTION_DESCRIPTION_FORMAT
,
605 (unsigned char*) textList
,
609 /* called if wanted operation is WDOperationAsk */
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); */
626 initSourceDragInfo(WMView
*sourceView
, WMDraggingInfo
*info
)
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
654 defDropDataTypes(WMView
*self
)
660 static WMDragOperationType
661 defWantedDropOperation(WMView
*self
)
663 return WDOperationNone
;
668 Must be defined if wantedDropOperation return WDOperationAsk
670 Return a WMDragOperationItem array (destroyed after call).
671 A WMDragOperationItem links a label to an operation.
673 defAskedOperations(WMView *self); */
677 defAcceptDropOperation(WMView
*self
, WMDragOperationType allowedOperation
)
684 defBeganDrag(WMView
*self
, WMPoint
*point
)
690 defEndedDrag(WMView
*self
, WMPoint
*point
, Bool deposited
)
696 Returned data is not destroyed
699 defFetchDragData(WMView
*self
, char *type
)
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
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
;
740 isXdndAware(WMScreen
*scr
, Window win
)
744 unsigned long count
, remain
;
745 unsigned char *winXdndVersion
;
750 XGetWindowProperty(scr
->display
, win
, scr
->xdndAwareAtom
,
751 0, 1, False
, XA_ATOM
, &type
, &format
,
752 &count
, &remain
, &winXdndVersion
);
755 || format
!= XDND_PROPERTY_FORMAT
756 || count
== 0 || !winXdndVersion
) {
758 XFree(winXdndVersion
);
762 XFree(winXdndVersion
);
763 return (count
== 1); /* xdnd version is set */
768 windowChildren(Display
*dpy
, Window win
, unsigned *nchildren
)
773 if (!XQueryTree(dpy
, win
, &foo
, &bar
, &children
, nchildren
)) {
781 lookForAwareWindow(WMScreen
*scr
, WMPoint
*mousePos
, Window win
)
786 /* Since xdnd v3, only the toplevel window should be aware */
787 if (isXdndAware(scr
, win
))
790 /* inspect child under pointer */
791 if (XTranslateCoordinates(scr
->display
, scr
->rootWin
, win
,
792 mousePos
->x
, mousePos
->y
, &tmpx
, &tmpy
,
797 return lookForAwareWindow(scr
, mousePos
, child
);
805 findDestination(WMDraggingInfo
*info
, WMPoint
*mousePos
)
807 WMScreen
*scr
= sourceScreen(info
);
809 Window
*children
= windowChildren(scr
->display
, scr
->rootWin
, &nchildren
);
811 XWindowAttributes attr
;
813 if (isXdndAware(scr
, 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
)) {
825 /* root window has no child under drag icon, and is not xdnd aware. */
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 */
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
,
862 XDND_SELECTION_PROCS(info
), NULL
)) {
863 wwarning("could not get ownership or DND selection");
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
);
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
;
907 W_DragSourceStartTimer(info
);
910 if (XDND_DEST_WIN(info
) != None
) {
911 if (! sendPositionMessage(info
, mousePos
)) {
912 XDND_DEST_WIN(info
) = None
;
916 W_DragSourceStartTimer(info
);
923 processButtonRelease(WMDraggingInfo
*info
)
925 if (XDND_SOURCE_STATE(info
) == dropAllowedState
) {
927 W_DragSourceStopTimer();
929 if (! sendDropMessage(info
))
932 W_DragSourceStartTimer(info
);
935 if (XDND_DEST_WIN(info
) != None
)
936 sendLeaveMessage(info
);
938 W_DragSourceStopTimer();
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; */
956 WMDragImageFromView(WMView
*view
, XEvent
*event
)
958 WMDraggingInfo
*info
= &W_VIEW_SCREEN(view
)->dragInfo
;
959 WMPoint mouseLocation
;
961 switch(event
->type
) {
963 if (event
->xbutton
.button
== Button1
) {
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
);
977 if (WMIsDraggingFromView(view
)) {
978 Bool dropBegan
= processButtonRelease(info
);
980 recolorCursor(info
, False
);
982 endDragImage(info
, False
);
983 XDND_SOURCE_STATE(info
) = finishDropState
;
986 endDragImage(info
, True
);
987 endDragProcess(info
,False
);
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
) >=
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
);
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
);
1012 event
->xmotion
.window
,
1022 /* Minimal mouse events handler: no right or double-click detection,
1023 only drag is supported */
1025 dragImageHandler(XEvent
*event
, void *cdata
)
1027 WMView
*view
= (WMView
*)cdata
;
1029 WMDragImageFromView(view
, event
);
1033 /* ----- source states ----- */
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");
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",
1054 if (statusEvent
->data
.l
[1] & 0x1L
)
1055 printf("allowed action: %s\n",
1056 XGetAtomName(dpy
, statusEvent
->data
.l
[4]));
1058 printf("no action allowed\n");
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(
1072 W_ActionToOperation(scr
, destAction
)))
1073 XDND_DEST_ACTION(info
) = destAction
;
1075 XDND_DEST_ACTION(info
) = None
;
1077 XDND_DEST_ACTION(info
) = destAction
;
1083 storeStatusMessageInfos(WMDraggingInfo
*info
, XClientMessageEvent
*statusEvent
)
1085 WMRect
* noPosZone
= &(XDND_NO_POS_ZONE(info
));
1089 traceStatusMsg(sourceScreen(info
)->display
, statusEvent
);
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;
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]);
1108 XDND_DEST_ACTION(info
) = None
;
1114 idleState(WMView
*view
, XClientMessageEvent
*event
, WMDraggingInfo
*info
)
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
;
1130 recolorCursor(info
, False
);
1135 if (destMsg
== scr
->xdndFinishedAtom
) {
1136 wwarning("received xdndFinishedAtom before drop began");
1139 W_DragSourceStartTimer(info
);
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
) {
1155 recolorCursor(info
, False
);
1160 W_DragSourceStartTimer(info
);
1161 return dropAllowedState
;
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
);
1176 W_DragSourceStartTimer(info
);
1177 return finishDropState
;
1179 /* ----- end of source states ----- */
1182 /* ----- source timer ----- */
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
) {
1195 endDragImage(info
, True
);
1196 endDragProcess(info
, False
);
1198 XDND_SOURCE_STATE(info
) = idleState
;
1203 W_DragSourceStopTimer()
1205 if (dndSourceTimer
!= NULL
) {
1206 WMDeleteTimerHandler(dndSourceTimer
);
1207 dndSourceTimer
= NULL
;
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 ----- */
1226 W_DragSourceStateHandler(WMDraggingInfo
*info
, XClientMessageEvent
*event
)
1229 W_DndState
* newState
;
1231 if (XDND_SOURCE_VIEW_STORED(info
)) {
1232 view
= XDND_SOURCE_VIEW(info
);
1235 printf("current source state: %s\n",
1236 stateName(XDND_SOURCE_STATE(info
)));
1239 newState
= (W_DndState
*) XDND_SOURCE_STATE(info
)(view
, event
, info
);
1243 printf("new source state: %s\n", stateName(newState
));
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 */
1271 WMCreateDragHandler(WMView
*view
, WMEventProc
*dragEventProc
, void *clientData
)
1273 WMCreateEventHandler(view
,
1274 ButtonPressMask
|ButtonReleaseMask
|Button1MotionMask
,
1275 dragEventProc
, clientData
);
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 */
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
);
1303 WMUnsetViewDraggable(WMView
*view
)
1305 if (view
->dragSourceProcs
) {
1306 wfree(view
->dragSourceProcs
);
1307 view
->dragSourceProcs
= NULL
;
1310 WMReleaseViewDragImage(view
);
1312 WMDeleteDragHandler(view
, dragImageHandler
, view
);