7 #include <X11/cursorfont.h>
9 #include <X11/extensions/shape.h>
14 #define XDND_DESTINATION_RESPONSE_MAX_DELAY 10000
15 #define MIN_X_MOVE_OFFSET 5
16 #define MIN_Y_MOVE_OFFSET 5
17 #define MAX_SLIDEBACK_ITER 15
19 #define XDND_PROPERTY_FORMAT 32
20 #define XDND_ACTION_DESCRIPTION_FORMAT 8
22 #define XDND_DEST_VERSION(dragInfo) dragInfo->protocolVersion
23 #define XDND_SOURCE_INFO(dragInfo) dragInfo->sourceInfo
24 #define XDND_DEST_WIN(dragInfo) dragInfo->sourceInfo->destinationWindow
25 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
26 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
27 #define XDND_SOURCE_VIEW(dragInfo) dragInfo->sourceInfo->sourceView
28 #define XDND_SOURCE_STATE(dragInfo) dragInfo->sourceInfo->state
29 #define XDND_SELECTION_PROCS(dragInfo) dragInfo->sourceInfo->selectionProcs
30 #define XDND_DRAG_ICON(dragInfo) dragInfo->sourceInfo->icon
31 #define XDND_MOUSE_OFFSET(dragInfo) dragInfo->sourceInfo->mouseOffset
32 #define XDND_DRAG_CURSOR(dragInfo) dragInfo->sourceInfo->dragCursor
33 #define XDND_DRAG_ICON_POS(dragInfo) dragInfo->sourceInfo->imageLocation
34 #define XDND_NO_POS_ZONE(dragInfo) dragInfo->sourceInfo->noPositionMessageZone
35 #define XDND_TIMESTAMP(dragInfo) dragInfo->timestamp
36 #define XDND_3_TYPES(dragInfo) dragInfo->sourceInfo->firstThreeTypes
37 #define XDND_SOURCE_VIEW_STORED(dragInfo) dragInfo->sourceInfo != NULL \
38 && dragInfo->sourceInfo->sourceView != NULL
41 static WMHandlerID dndSourceTimer
= NULL
;
44 static void* idleState(WMView
*srcView
, XClientMessageEvent
*event
,
45 WMDraggingInfo
*info
);
46 static void* dropAllowedState(WMView
*srcView
, XClientMessageEvent
*event
,
47 WMDraggingInfo
*info
);
48 static void* finishDropState(WMView
*srcView
, XClientMessageEvent
*event
,
49 WMDraggingInfo
*info
);
53 stateName(W_DndState
*state
)
56 return "no state defined";
58 if (state
== idleState
)
61 if (state
== dropAllowedState
)
62 return "dropAllowedState";
64 if (state
== finishDropState
)
65 return "finishDropState";
67 return "unknown state";
73 sourceScreen(WMDraggingInfo
*info
)
75 return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
80 endDragProcess(WMDraggingInfo
*info
, Bool deposited
)
82 WMView
*view
= XDND_SOURCE_VIEW(info
);
83 WMScreen
*scr
= W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
85 /* free selection handler while view exists */
86 WMDeleteSelectionHandler(view
,
87 scr
->xdndSelectionAtom
,
89 wfree(XDND_SELECTION_PROCS(info
));
91 if (XDND_DRAG_CURSOR(info
) != None
) {
92 XFreeCursor(scr
->display
,
93 XDND_DRAG_CURSOR(info
));
94 XDND_DRAG_CURSOR(info
) = None
;
97 if (view
->dragSourceProcs
->endedDrag
!= NULL
) {
98 /* this can destroy source view (with a "move" action for example) */
99 view
->dragSourceProcs
->endedDrag(view
, &XDND_DRAG_ICON_POS(info
),
103 /* clear remaining draggging infos */
104 wfree(XDND_SOURCE_INFO(info
));
105 XDND_SOURCE_INFO(info
) = NULL
;
109 /* ----- drag cursor ----- */
111 initDragCursor(WMDraggingInfo
*info
)
113 WMScreen
*scr
= sourceScreen(info
);
114 XColor cursorFgColor
, cursorBgColor
;
117 cursorFgColor
.red
= 0x4500;
118 cursorFgColor
.green
= 0xb000;
119 cursorFgColor
.blue
= 0x4500;
122 cursorBgColor
.red
= 0xffff;
123 cursorBgColor
.green
= 0xffff;
124 cursorBgColor
.blue
= 0xffff;
126 XDND_DRAG_CURSOR(info
) = XCreateFontCursor(scr
->display
, XC_left_ptr
);
127 XRecolorCursor(scr
->display
,
128 XDND_DRAG_CURSOR(info
),
132 XFlush(scr
->display
);
136 recolorCursor(WMDraggingInfo
*info
, Bool dropIsAllowed
)
138 WMScreen
*scr
= sourceScreen(info
);
141 XDefineCursor(scr
->display
,
143 XDND_DRAG_CURSOR(info
));
145 XDefineCursor(scr
->display
,
150 XFlush(scr
->display
);
152 /* ----- end of drag cursor ----- */
155 /* ----- selection procs ----- */
157 convertSelection(WMView
*view
, Atom selection
, Atom target
,
158 void *cdata
, Atom
*type
)
164 scr
= W_VIEW_SCREEN(view
);
165 typeName
= XGetAtomName(scr
->display
, target
);
169 if (view
->dragSourceProcs
->fetchDragData
!= NULL
) {
170 data
= view
->dragSourceProcs
->fetchDragData(
177 if (typeName
!= NULL
)
185 selectionLost(WMView
*view
, Atom selection
, void *cdata
)
187 wwarning("DND selection lost during drag operation...");
192 selectionDone(WMView
*view
, Atom selection
, Atom target
, void *cdata
)
195 printf("selection done\n");
198 /* ----- end of selection procs ----- */
201 /* ----- visual part ----- */
204 makeDragIcon(WMScreen
*scr
, WMPixmap
*pixmap
)
209 XSetWindowAttributes attribs
;
213 pixmap
= scr
->defaultObjectIcon
;
216 size
= WMGetPixmapSize(pixmap
);
217 pix
= pixmap
->pixmap
;
220 flags
= CWSaveUnder
|CWBackPixmap
|CWOverrideRedirect
|CWColormap
;
221 attribs
.save_under
= True
;
222 attribs
.background_pixmap
= pix
;
223 attribs
.override_redirect
= True
;
224 attribs
.colormap
= scr
->colormap
;
226 window
= XCreateWindow(scr
->display
, scr
->rootWin
, 0, 0, size
.width
,
227 size
.height
, 0, scr
->depth
, InputOutput
,
228 scr
->visual
, flags
, &attribs
);
233 XShapeCombineMask(scr
->display
, window
, ShapeBounding
, 0, 0, mask
,
243 slideWindow(Display
*dpy
, Window win
, int srcX
, int srcY
, int dstX
, int dstY
)
249 iterations
= WMIN(MAX_SLIDEBACK_ITER
,
250 WMAX(abs(dstX
-srcX
), abs(dstY
-srcY
)));
255 dx
= (double)(dstX
-srcX
)/iterations
;
256 dy
= (double)(dstY
-srcY
)/iterations
;
258 for (i
= 0; i
<= iterations
; i
++) {
259 XMoveWindow(dpy
, win
, x
, y
);
271 getInitialDragImageCoord(int viewCoord
, int mouseCoord
,
272 int viewSize
, int iconSize
)
274 if (iconSize
>= viewSize
) {
275 /* center icon coord on view */
276 return viewCoord
- iconSize
/2;
278 /* try to center icon on mouse pos */
280 if (mouseCoord
- iconSize
/2 <= viewCoord
)
281 /* if icon was centered on mouse, it would be off view
282 thus, put icon left (resp. top) side
283 at view (resp. top) side */
286 else if (mouseCoord
+ iconSize
/2 >= viewCoord
+ viewSize
)
287 /* if icon was centered on mouse, it would be off view
288 thus, put icon right (resp. bottom) side
289 at view right (resp. bottom) side */
290 return viewCoord
+ viewSize
- iconSize
;
293 return mouseCoord
- iconSize
/2;
299 initDragImagePos(WMView
*view
, WMDraggingInfo
*info
, XEvent
*event
)
301 WMSize iconSize
= WMGetPixmapSize(view
->dragImage
);
302 WMSize viewSize
= WMGetViewSize(view
);
306 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
307 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
308 0, 0, &(viewPos
.x
), &(viewPos
.y
),
312 XDND_DRAG_ICON_POS(info
).x
=
313 getInitialDragImageCoord(viewPos
.x
, event
->xmotion
.x_root
,
314 viewSize
.width
, iconSize
.width
);
316 XDND_DRAG_ICON_POS(info
).y
=
317 getInitialDragImageCoord(viewPos
.y
, event
->xmotion
.y_root
,
318 viewSize
.height
, iconSize
.height
);
320 /* set mouse offset relative to icon */
321 XDND_MOUSE_OFFSET(info
).x
=
322 event
->xmotion
.x_root
- XDND_DRAG_ICON_POS(info
).x
;
323 XDND_MOUSE_OFFSET(info
).y
=
324 event
->xmotion
.y_root
- XDND_DRAG_ICON_POS(info
).y
;
329 refreshDragImage(WMView
*view
, WMDraggingInfo
*info
)
331 WMScreen
*scr
= W_VIEW_SCREEN(view
);
333 XMoveWindow(scr
->display
, XDND_DRAG_ICON(info
),
334 XDND_DRAG_ICON_POS(info
).x
,
335 XDND_DRAG_ICON_POS(info
).y
);
340 startDragImage(WMView
*view
, WMDraggingInfo
*info
, XEvent
* event
)
342 WMScreen
*scr
= W_VIEW_SCREEN(view
);
344 XDND_DRAG_ICON(info
) = makeDragIcon(scr
, view
->dragImage
);
345 initDragImagePos(view
, info
, event
);
346 refreshDragImage(view
, info
);
347 XMapRaised(scr
->display
, XDND_DRAG_ICON(info
));
349 initDragCursor(info
);
354 endDragImage(WMDraggingInfo
*info
, Bool slideBack
)
356 WMView
*view
= XDND_SOURCE_VIEW(info
);
357 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
363 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
364 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
365 0, 0, &(toLocation
.x
), &(toLocation
.y
),
368 slideWindow(dpy
, XDND_DRAG_ICON(info
),
369 XDND_DRAG_ICON_POS(info
).x
,
370 XDND_DRAG_ICON_POS(info
).y
,
375 XDestroyWindow(dpy
, XDND_DRAG_ICON(info
));
378 /* ----- end of visual part ----- */
381 /* ----- messages ----- */
383 /* send a DnD message to the destination window */
385 sendDnDClientMessage(WMDraggingInfo
*info
, Atom message
,
391 Display
*dpy
= sourceScreen(info
)->display
;
392 Window srcWin
= WMViewXID(XDND_SOURCE_VIEW(info
));
393 Window destWin
= XDND_DEST_WIN(info
);
395 if (! W_SendDnDClientMessage(dpy
,
404 recolorCursor(info
, False
);
405 endDragImage(info
, True
);
406 endDragProcess(info
, False
);
415 sendEnterMessage(WMDraggingInfo
*info
)
417 WMScreen
*scr
= sourceScreen(info
);
418 unsigned long version
;
420 if (XDND_DEST_VERSION(info
) > 2) {
421 if (XDND_DEST_VERSION(info
) < XDND_VERSION
)
422 version
= XDND_DEST_VERSION(info
);
424 version
= XDND_VERSION
;
429 return sendDnDClientMessage(info
, scr
->xdndEnterAtom
,
430 (version
<< 24) | 1, /* 1: support of type list */
431 XDND_3_TYPES(info
)[0],
432 XDND_3_TYPES(info
)[1],
433 XDND_3_TYPES(info
)[2]);
438 sendPositionMessage(WMDraggingInfo
*info
, WMPoint
*mousePos
)
440 WMScreen
*scr
= sourceScreen(info
);
441 WMRect
*noPosZone
= &(XDND_NO_POS_ZONE(info
));
443 if (noPosZone
->size
.width
!= 0 || noPosZone
->size
.height
!= 0) {
444 if (mousePos
->x
< noPosZone
->pos
.x
445 || mousePos
->x
> (noPosZone
->pos
.x
+ noPosZone
->size
.width
)
446 || mousePos
->y
< noPosZone
->pos
.y
447 || mousePos
->y
> (noPosZone
->pos
.y
+ noPosZone
->size
.height
)) {
448 /* send position if out of zone defined by destination */
449 return sendDnDClientMessage(info
, scr
->xdndPositionAtom
,
451 mousePos
->x
<<16|mousePos
->y
,
452 XDND_TIMESTAMP(info
),
453 XDND_SOURCE_ACTION(info
));
456 /* Nothing to send, always succeed */
461 /* send position on each move */
462 return sendDnDClientMessage(info
, scr
->xdndPositionAtom
,
464 mousePos
->x
<<16|mousePos
->y
,
465 XDND_TIMESTAMP(info
),
466 XDND_SOURCE_ACTION(info
));
471 sendLeaveMessage(WMDraggingInfo
*info
)
473 WMScreen
*scr
= sourceScreen(info
);
475 return sendDnDClientMessage(info
, scr
->xdndLeaveAtom
,
481 sendDropMessage(WMDraggingInfo
*info
)
483 WMScreen
*scr
= sourceScreen(info
);
485 return sendDnDClientMessage(info
,
488 XDND_TIMESTAMP(info
),
492 /* ----- end of messages ----- */
496 getTypeAtomList(WMScreen
*scr
, WMView
*view
, int* count
)
502 types
= view
->dragSourceProcs
->dropDataTypes(view
);
505 *count
= WMGetArrayItemCount(types
);
507 typeAtoms
= wmalloc((*count
)*sizeof(Atom
));
508 for (i
=0; i
< *count
; i
++) {
509 typeAtoms
[i
] = XInternAtom(scr
->display
,
510 WMGetFromArray(types
, i
),
514 /* WMFreeArray(types); */
518 /* WMFreeArray(types); */
522 typeAtoms
= wmalloc(sizeof(Atom
));
530 registerDropTypes(WMScreen
*scr
, WMView
*view
, WMDraggingInfo
*info
)
535 typeList
= getTypeAtomList(scr
, view
, &count
);
537 /* store the first 3 types */
538 for(i
=0; i
< 3 && i
< count
; i
++)
539 XDND_3_TYPES(info
)[i
] = typeList
[i
];
542 XDND_3_TYPES(info
)[i
] = None
;
545 /* store the entire type list */
546 XChangeProperty(scr
->display
,
548 scr
->xdndTypeListAtom
,
550 XDND_PROPERTY_FORMAT
,
552 (unsigned char*) typeList
,
558 registerOperationList(WMScreen
*scr
, WMView
*view
, WMArray
* operationArray
)
561 WMDragOperationType operation
;
562 int count
= WMGetArrayItemCount(operationArray
);
565 actionList
= wmalloc(sizeof(Atom
)*count
);
567 for(i
=0; i
< count
; i
++) {
568 operation
= WMGetDragOperationItemType(
569 WMGetFromArray(operationArray
, i
));
570 actionList
[i
] = W_OperationToAction(scr
, operation
);
573 XChangeProperty(scr
->display
,
575 scr
->xdndActionListAtom
,
577 XDND_PROPERTY_FORMAT
,
579 (unsigned char*) actionList
,
584 registerDescriptionList(WMScreen
*scr
, WMView
*view
, WMArray
* operationArray
)
586 char *text
, *textListItem
, *textList
;
587 int count
= WMGetArrayItemCount(operationArray
);
591 /* size of XA_STRING info */
592 for(i
=0; i
< count
; i
++) {
593 size
+= strlen(WMGetDragOperationItemText(
594 WMGetFromArray(operationArray
, i
))) + 1; /* +1 = +NULL */
597 /* create text list */
598 textList
= wmalloc(size
);
599 textListItem
= textList
;
601 for(i
=0; i
< count
; i
++) {
602 text
= WMGetDragOperationItemText(WMGetFromArray(operationArray
, i
));
603 strcpy(textListItem
, text
);
605 /* to next text offset */
606 textListItem
= &(textListItem
[strlen(textListItem
) + 1]);
609 XChangeProperty(scr
->display
,
611 scr
->xdndActionDescriptionAtom
,
613 XDND_ACTION_DESCRIPTION_FORMAT
,
615 (unsigned char*) textList
,
619 /* called if wanted operation is WDOperationAsk */
621 registerSupportedOperations(WMView
*view
)
623 WMScreen
*scr
= W_VIEW_SCREEN(view
);
624 WMArray
* operationArray
;
626 operationArray
= view
->dragSourceProcs
->askedOperations(view
);
628 registerOperationList(scr
, view
, operationArray
);
629 registerDescriptionList(scr
, view
, operationArray
);
631 /* WMFreeArray(operationArray); */
636 initSourceDragInfo(WMView
*sourceView
, WMDraggingInfo
*info
)
640 XDND_SOURCE_INFO(info
) = (W_DragSourceInfo
*) wmalloc(sizeof(W_DragSourceInfo
));
642 XDND_SOURCE_VIEW(info
) = sourceView
;
643 XDND_DEST_WIN(info
) = None
;
644 XDND_DRAG_ICON(info
) = None
;
646 XDND_SOURCE_ACTION(info
) = W_OperationToAction(
647 W_VIEW_SCREEN(sourceView
),
648 sourceView
->dragSourceProcs
->wantedDropOperation(sourceView
));
650 XDND_DEST_ACTION(info
) = None
;
652 XDND_SOURCE_STATE(info
) = idleState
;
654 emptyZone
.pos
= wmkpoint(0, 0);
655 emptyZone
.size
= wmksize(0, 0);
656 XDND_NO_POS_ZONE(info
) = emptyZone
;
661 Returned array is destroyed after dropDataTypes call
664 defDropDataTypes(WMView
*self
)
670 static WMDragOperationType
671 defWantedDropOperation(WMView
*self
)
673 return WDOperationNone
;
678 Must be defined if wantedDropOperation return WDOperationAsk
680 Return a WMDragOperationItem array (destroyed after call).
681 A WMDragOperationItem links a label to an operation.
683 defAskedOperations(WMView *self); */
687 defAcceptDropOperation(WMView
*self
, WMDragOperationType allowedOperation
)
694 defBeganDrag(WMView
*self
, WMPoint
*point
)
700 defEndedDrag(WMView
*self
, WMPoint
*point
, Bool deposited
)
706 Returned data is not destroyed
709 defFetchDragData(WMView
*self
, char *type
)
716 WMSetViewDragSourceProcs(WMView
*view
, WMDragSourceProcs
*procs
)
718 if (view
->dragSourceProcs
)
719 wfree(view
->dragSourceProcs
);
720 view
->dragSourceProcs
= wmalloc(sizeof(WMDragSourceProcs
));
722 *view
->dragSourceProcs
= *procs
;
724 if (procs
->dropDataTypes
== NULL
)
725 view
->dragSourceProcs
->dropDataTypes
= defDropDataTypes
;
727 if (procs
->wantedDropOperation
== NULL
)
728 view
->dragSourceProcs
->wantedDropOperation
= defWantedDropOperation
;
731 Note: askedOperations can be NULL, if wantedDropOperation never returns
735 if (procs
->acceptDropOperation
== NULL
)
736 view
->dragSourceProcs
->acceptDropOperation
= defAcceptDropOperation
;
738 if (procs
->beganDrag
== NULL
)
739 view
->dragSourceProcs
->beganDrag
= defBeganDrag
;
741 if (procs
->endedDrag
== NULL
)
742 view
->dragSourceProcs
->endedDrag
= defEndedDrag
;
744 if (procs
->fetchDragData
== NULL
)
745 view
->dragSourceProcs
->fetchDragData
= defFetchDragData
;
750 isXdndAware(WMScreen
*scr
, Window win
)
754 unsigned long count
, remain
;
755 unsigned char *winXdndVersion
;
760 XGetWindowProperty(scr
->display
, win
, scr
->xdndAwareAtom
,
761 0, 1, False
, XA_ATOM
, &type
, &format
,
762 &count
, &remain
, &winXdndVersion
);
765 || format
!= XDND_PROPERTY_FORMAT
766 || count
== 0 || !winXdndVersion
) {
768 XFree(winXdndVersion
);
772 XFree(winXdndVersion
);
773 return (count
== 1); /* xdnd version is set */
778 windowChildren(Display
*dpy
, Window win
, unsigned *nchildren
)
783 if (!XQueryTree(dpy
, win
, &foo
, &bar
, &children
, nchildren
)) {
791 lookForAwareWindow(WMScreen
*scr
, WMPoint
*mousePos
, Window win
)
796 /* Since xdnd v3, only the toplevel window should be aware */
797 if (isXdndAware(scr
, win
))
800 /* inspect child under pointer */
801 if (XTranslateCoordinates(scr
->display
, scr
->rootWin
, win
,
802 mousePos
->x
, mousePos
->y
, &tmpx
, &tmpy
,
807 return lookForAwareWindow(scr
, mousePos
, child
);
815 findDestination(WMDraggingInfo
*info
, WMPoint
*mousePos
)
817 WMScreen
*scr
= sourceScreen(info
);
819 Window
*children
= windowChildren(scr
->display
, scr
->rootWin
, &nchildren
);
821 XWindowAttributes attr
;
823 if (isXdndAware(scr
, scr
->rootWin
))
826 /* exclude drag icon (and upper) from search */
827 for (i
= nchildren
-1; i
>= 0; i
--) {
828 if (children
[i
] == XDND_DRAG_ICON(info
)) {
835 /* root window has no child under drag icon, and is not xdnd aware. */
839 /* inspecting children, from upper to lower */
840 for (; i
>= 0; i
--) {
841 if (XGetWindowAttributes(scr
->display
, children
[i
], &attr
)
842 && attr
.map_state
== IsViewable
843 && mousePos
->x
>= attr
.x
844 && mousePos
->y
>= attr
.y
845 && mousePos
->x
< attr
.x
+ attr
.width
846 && mousePos
->y
< attr
.y
+ attr
.height
) {
847 return lookForAwareWindow(scr
, mousePos
, children
[i
]);
851 /* No child window under drag pointer */
857 storeDestinationProtocolVersion(WMDraggingInfo
*info
)
861 unsigned long count
, remain
;
862 unsigned char *winXdndVersion
;
863 WMScreen
*scr
= W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
865 wassertr(XDND_DEST_WIN(info
) != None
);
867 if (XGetWindowProperty(scr
->display
, XDND_DEST_WIN(info
),
869 0, 1, False
, XA_ATOM
, &type
, &format
,
870 &count
, &remain
, &winXdndVersion
) == Success
) {
871 XDND_DEST_VERSION(info
) = *winXdndVersion
;
872 XFree(winXdndVersion
);
874 XDND_DEST_VERSION(info
) = 0;
875 wwarning("failed to read XDND version of drop target");
881 initMotionProcess(WMView
*view
, WMDraggingInfo
*info
,
882 XEvent
*event
, WMPoint
*startLocation
)
884 WMScreen
*scr
= W_VIEW_SCREEN(view
);
886 /* take ownership of XdndSelection */
887 XDND_SELECTION_PROCS(info
) =
888 (WMSelectionProcs
*) wmalloc(sizeof(WMSelectionProcs
));
889 XDND_SELECTION_PROCS(info
)->convertSelection
= convertSelection
;
890 XDND_SELECTION_PROCS(info
)->selectionLost
= selectionLost
;
891 XDND_SELECTION_PROCS(info
)->selectionDone
= selectionDone
;
892 XDND_TIMESTAMP(info
) = event
->xmotion
.time
;
894 if (!WMCreateSelectionHandler(view
, scr
->xdndSelectionAtom
,
896 XDND_SELECTION_PROCS(info
), NULL
)) {
897 wwarning("could not get ownership or DND selection");
901 registerDropTypes(scr
, view
, info
);
903 if (XDND_SOURCE_ACTION(info
) == W_VIEW_SCREEN(view
)->xdndActionAsk
)
904 registerSupportedOperations(view
);
906 if (view
->dragSourceProcs
->beganDrag
!= NULL
) {
907 view
->dragSourceProcs
->beganDrag(view
, startLocation
);
913 processMotion(WMDraggingInfo
*info
, WMPoint
*mousePos
)
915 Window newDestination
= findDestination(info
, mousePos
);
917 W_DragSourceStopTimer();
919 if (newDestination
!= XDND_DEST_WIN(info
)) {
920 recolorCursor(info
, False
);
922 if (XDND_DEST_WIN(info
) != None
) {
923 /* leaving a xdnd window */
924 sendLeaveMessage(info
);
927 XDND_DEST_WIN(info
) = newDestination
;
928 XDND_DEST_ACTION(info
) = None
;
929 XDND_NO_POS_ZONE(info
).size
.width
= 0;
930 XDND_NO_POS_ZONE(info
).size
.height
= 0;
932 if (newDestination
!= None
) {
933 /* entering a xdnd window */
934 XDND_SOURCE_STATE(info
) = idleState
;
935 storeDestinationProtocolVersion(info
);
937 if (! sendEnterMessage(info
)) {
938 XDND_DEST_WIN(info
) = None
;
942 W_DragSourceStartTimer(info
);
944 XDND_SOURCE_STATE(info
) = NULL
;
947 if (XDND_DEST_WIN(info
) != None
) {
948 if (! sendPositionMessage(info
, mousePos
)) {
949 XDND_DEST_WIN(info
) = None
;
953 W_DragSourceStartTimer(info
);
960 processButtonRelease(WMDraggingInfo
*info
)
962 if (XDND_SOURCE_STATE(info
) == dropAllowedState
) {
964 W_DragSourceStopTimer();
966 if (! sendDropMessage(info
))
969 W_DragSourceStartTimer(info
);
972 if (XDND_DEST_WIN(info
) != None
)
973 sendLeaveMessage(info
);
975 W_DragSourceStopTimer();
982 WMIsDraggingFromView(WMView
*view
)
984 WMDraggingInfo
*info
= &W_VIEW_SCREEN(view
)->dragInfo
;
986 return ( XDND_SOURCE_INFO(info
) != NULL
987 && XDND_SOURCE_STATE(info
) != finishDropState
);
988 /* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
993 WMDragImageFromView(WMView
*view
, XEvent
*event
)
995 WMDraggingInfo
*info
= &W_VIEW_SCREEN(view
)->dragInfo
;
996 WMPoint mouseLocation
;
998 switch(event
->type
) {
1000 if (event
->xbutton
.button
== Button1
) {
1003 XPeekEvent(event
->xbutton
.display
, &nextEvent
);
1005 /* Initialize only if a drag really begins (avoid clicks) */
1006 if (nextEvent
.type
== MotionNotify
) {
1007 initSourceDragInfo(view
, info
);
1014 if (WMIsDraggingFromView(view
)) {
1015 Bool dropBegan
= processButtonRelease(info
);
1017 recolorCursor(info
, False
);
1019 endDragImage(info
, False
);
1020 XDND_SOURCE_STATE(info
) = finishDropState
;
1023 endDragImage(info
, True
);
1024 endDragProcess(info
,False
);
1030 if (WMIsDraggingFromView(view
)) {
1031 mouseLocation
= wmkpoint(event
->xmotion
.x_root
,
1032 event
->xmotion
.y_root
);
1034 if (abs(XDND_DRAG_ICON_POS(info
).x
- mouseLocation
.x
) >=
1036 || abs(XDND_DRAG_ICON_POS(info
).y
- mouseLocation
.y
) >=
1037 MIN_Y_MOVE_OFFSET
) {
1038 if (XDND_DRAG_ICON(info
) == None
) {
1039 initMotionProcess(view
, info
, event
, &mouseLocation
);
1040 startDragImage(view
, info
, event
);
1042 XDND_DRAG_ICON_POS(info
).x
=
1043 mouseLocation
.x
- XDND_MOUSE_OFFSET(info
).x
;
1044 XDND_DRAG_ICON_POS(info
).y
=
1045 mouseLocation
.y
- XDND_MOUSE_OFFSET(info
).y
;
1047 refreshDragImage(view
, info
);
1048 processMotion(info
, &mouseLocation
);
1057 /* Minimal mouse events handler: no right or double-click detection,
1058 only drag is supported */
1060 dragImageHandler(XEvent
*event
, void *cdata
)
1062 WMView
*view
= (WMView
*)cdata
;
1064 WMDragImageFromView(view
, event
);
1068 /* ----- source states ----- */
1072 traceStatusMsg(Display
*dpy
, XClientMessageEvent
*statusEvent
)
1074 printf("Xdnd status message:\n");
1076 if (statusEvent
->data
.l
[1] & 0x2UL
)
1077 printf("\tsend position on every move\n");
1080 x
= statusEvent
->data
.l
[2] >> 16;
1081 y
= statusEvent
->data
.l
[2] & 0xFFFFL
;
1082 w
= statusEvent
->data
.l
[3] >> 16;
1083 h
= statusEvent
->data
.l
[3] & 0xFFFFL
;
1085 printf("\tsend position out of ((%d,%d) , (%d,%d))\n",
1089 if (statusEvent
->data
.l
[1] & 0x1L
)
1090 printf("\tallowed action: %s\n",
1091 XGetAtomName(dpy
, statusEvent
->data
.l
[4]));
1093 printf("\tno action allowed\n");
1099 storeDropAction(WMDraggingInfo
*info
, Atom destAction
)
1101 WMView
* sourceView
= XDND_SOURCE_VIEW(info
);
1102 WMScreen
*scr
= W_VIEW_SCREEN(sourceView
);
1104 if (sourceView
->dragSourceProcs
->acceptDropOperation
!= NULL
) {
1105 if (sourceView
->dragSourceProcs
->acceptDropOperation(
1107 W_ActionToOperation(scr
, destAction
)))
1108 XDND_DEST_ACTION(info
) = destAction
;
1110 XDND_DEST_ACTION(info
) = None
;
1112 XDND_DEST_ACTION(info
) = destAction
;
1118 storeStatusMessageInfos(WMDraggingInfo
*info
, XClientMessageEvent
*statusEvent
)
1120 WMRect
* noPosZone
= &(XDND_NO_POS_ZONE(info
));
1124 traceStatusMsg(sourceScreen(info
)->display
, statusEvent
);
1127 if (statusEvent
->data
.l
[1] & 0x2UL
) {
1128 /* bit 1 set: destination wants position messages on every move */
1129 noPosZone
->size
.width
= 0;
1130 noPosZone
->size
.height
= 0;
1132 /* don't send another position message while in given rectangle */
1133 noPosZone
->pos
.x
= statusEvent
->data
.l
[2] >> 16;
1134 noPosZone
->pos
.y
= statusEvent
->data
.l
[2] & 0xFFFFL
;
1135 noPosZone
->size
.width
= statusEvent
->data
.l
[3] >> 16;
1136 noPosZone
->size
.height
= statusEvent
->data
.l
[3] & 0xFFFFL
;
1139 if ((statusEvent
->data
.l
[1] & 0x1L
) || statusEvent
->data
.l
[4] != None
) {
1140 /* destination accept drop */
1141 storeDropAction(info
, statusEvent
->data
.l
[4]);
1143 XDND_DEST_ACTION(info
) = None
;
1149 idleState(WMView
*view
, XClientMessageEvent
*event
, WMDraggingInfo
*info
)
1152 Atom destMsg
= event
->message_type
;
1154 scr
= W_VIEW_SCREEN(view
);
1156 if (destMsg
== scr
->xdndStatusAtom
) {
1157 storeStatusMessageInfos(info
, event
);
1159 if (XDND_DEST_ACTION(info
) != None
) {
1160 recolorCursor(info
, True
);
1161 W_DragSourceStartTimer(info
);
1162 return dropAllowedState
;
1165 recolorCursor(info
, False
);
1170 if (destMsg
== scr
->xdndFinishedAtom
) {
1171 wwarning("received xdndFinishedAtom before drop began");
1174 W_DragSourceStartTimer(info
);
1180 dropAllowedState(WMView
*view
, XClientMessageEvent
*event
, WMDraggingInfo
*info
)
1182 WMScreen
*scr
= W_VIEW_SCREEN(view
);
1183 Atom destMsg
= event
->message_type
;
1185 if (destMsg
== scr
->xdndStatusAtom
) {
1186 storeStatusMessageInfos(info
, event
);
1188 if (XDND_DEST_ACTION(info
) == None
) {
1190 recolorCursor(info
, False
);
1195 W_DragSourceStartTimer(info
);
1196 return dropAllowedState
;
1201 finishDropState(WMView
*view
, XClientMessageEvent
*event
, WMDraggingInfo
*info
)
1203 WMScreen
*scr
= W_VIEW_SCREEN(view
);
1204 Atom destMsg
= event
->message_type
;
1206 if (destMsg
== scr
->xdndFinishedAtom
) {
1207 endDragProcess(info
, True
);
1211 W_DragSourceStartTimer(info
);
1212 return finishDropState
;
1214 /* ----- end of source states ----- */
1217 /* ----- source timer ----- */
1219 dragSourceResponseTimeOut(void *source
)
1221 WMView
*view
= (WMView
*)source
;
1222 WMDraggingInfo
*info
= &(W_VIEW_SCREEN(view
)->dragInfo
);
1224 wwarning("delay for drag destination response expired");
1225 sendLeaveMessage(info
);
1227 recolorCursor(info
, False
);
1228 if (XDND_SOURCE_STATE(info
) == finishDropState
) {
1230 endDragImage(info
, True
);
1231 endDragProcess(info
, False
);
1233 XDND_SOURCE_STATE(info
) = idleState
;
1238 W_DragSourceStopTimer()
1240 if (dndSourceTimer
!= NULL
) {
1241 WMDeleteTimerHandler(dndSourceTimer
);
1242 dndSourceTimer
= NULL
;
1247 W_DragSourceStartTimer(WMDraggingInfo
*info
)
1249 W_DragSourceStopTimer();
1251 dndSourceTimer
= WMAddTimerHandler(
1252 XDND_DESTINATION_RESPONSE_MAX_DELAY
,
1253 dragSourceResponseTimeOut
,
1254 XDND_SOURCE_VIEW(info
));
1257 /* ----- End of Destination timer ----- */
1261 W_DragSourceStateHandler(WMDraggingInfo
*info
, XClientMessageEvent
*event
)
1264 W_DndState
* newState
;
1266 if (XDND_SOURCE_VIEW_STORED(info
)) {
1267 if (XDND_SOURCE_STATE(info
) != NULL
) {
1268 view
= XDND_SOURCE_VIEW(info
);
1271 printf("current source state: %s\n",
1272 stateName(XDND_SOURCE_STATE(info
)));
1275 newState
= (W_DndState
*) XDND_SOURCE_STATE(info
)(view
, event
, info
);
1279 printf("new source state: %s\n", stateName(newState
));
1282 if (newState
!= NULL
)
1283 XDND_SOURCE_STATE(info
) = newState
;
1284 /* else drop finished, and info has been flushed */
1288 wwarning("received DnD message without having a target");
1293 void WMSetViewDragImage(WMView
* view
, WMPixmap
*dragImage
)
1295 if (view
->dragImage
!= NULL
)
1296 WMReleasePixmap(view
->dragImage
);
1298 view
->dragImage
= WMRetainPixmap(dragImage
);
1302 void WMReleaseViewDragImage(WMView
* view
)
1304 if (view
->dragImage
!= NULL
)
1305 WMReleasePixmap(view
->dragImage
);
1309 /* Create a drag handler, associating drag event masks with dragEventProc */
1311 WMCreateDragHandler(WMView
*view
, WMEventProc
*dragEventProc
, void *clientData
)
1313 WMCreateEventHandler(view
,
1314 ButtonPressMask
|ButtonReleaseMask
|Button1MotionMask
,
1315 dragEventProc
, clientData
);
1320 WMDeleteDragHandler(WMView
*view
, WMEventProc
*dragEventProc
, void *clientData
)
1322 WMDeleteEventHandler(view
,
1323 ButtonPressMask
|ButtonReleaseMask
|Button1MotionMask
,
1324 dragEventProc
, clientData
);
1328 /* set default drag handler for view */
1330 WMSetViewDraggable(WMView
*view
, WMDragSourceProcs
*dragSourceProcs
,
1331 WMPixmap
*dragImage
)
1333 wassertr(dragImage
!= NULL
);
1334 view
->dragImage
= WMRetainPixmap(dragImage
);
1336 WMSetViewDragSourceProcs(view
, dragSourceProcs
);
1338 WMCreateDragHandler(view
, dragImageHandler
, view
);
1343 WMUnsetViewDraggable(WMView
*view
)
1345 if (view
->dragSourceProcs
) {
1346 wfree(view
->dragSourceProcs
);
1347 view
->dragSourceProcs
= NULL
;
1350 WMReleaseViewDragImage(view
);
1352 WMDeleteDragHandler(view
, dragImageHandler
, view
);