6 #include <X11/cursorfont.h>
8 #include <X11/extensions/shape.h>
11 #define XDND_DESTINATION_RESPONSE_MAX_DELAY 10000
12 #define MIN_X_MOVE_OFFSET 5
13 #define MIN_Y_MOVE_OFFSET 5
14 #define MAX_SLIDEBACK_ITER 15
16 #define XDND_PROPERTY_FORMAT 32
17 #define XDND_ACTION_DESCRIPTION_FORMAT 8
19 #define XDND_DEST_VERSION(dragInfo) dragInfo->protocolVersion
20 #define XDND_SOURCE_INFO(dragInfo) dragInfo->sourceInfo
21 #define XDND_DEST_WIN(dragInfo) dragInfo->sourceInfo->destinationWindow
22 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
23 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
24 #define XDND_SOURCE_VIEW(dragInfo) dragInfo->sourceInfo->sourceView
25 #define XDND_SOURCE_STATE(dragInfo) dragInfo->sourceInfo->state
26 #define XDND_SELECTION_PROCS(dragInfo) dragInfo->sourceInfo->selectionProcs
27 #define XDND_DRAG_ICON(dragInfo) dragInfo->sourceInfo->icon
28 #define XDND_MOUSE_OFFSET(dragInfo) dragInfo->sourceInfo->mouseOffset
29 #define XDND_DRAG_CURSOR(dragInfo) dragInfo->sourceInfo->dragCursor
30 #define XDND_DRAG_ICON_POS(dragInfo) dragInfo->sourceInfo->imageLocation
31 #define XDND_NO_POS_ZONE(dragInfo) dragInfo->sourceInfo->noPositionMessageZone
32 #define XDND_TIMESTAMP(dragInfo) dragInfo->timestamp
33 #define XDND_3_TYPES(dragInfo) dragInfo->sourceInfo->firstThreeTypes
34 #define XDND_SOURCE_VIEW_STORED(dragInfo) dragInfo->sourceInfo != NULL \
35 && dragInfo->sourceInfo->sourceView != NULL
37 static WMHandlerID dndSourceTimer
= NULL
;
39 static void *idleState(WMView
* srcView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
40 static void *dropAllowedState(WMView
* srcView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
41 static void *finishDropState(WMView
* srcView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
44 static const char *stateName(W_DndState
* state
)
47 return "no state defined";
49 if (state
== idleState
)
52 if (state
== dropAllowedState
)
53 return "dropAllowedState";
55 if (state
== finishDropState
)
56 return "finishDropState";
58 return "unknown state";
62 static WMScreen
*sourceScreen(WMDraggingInfo
* info
)
64 return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
67 static void endDragProcess(WMDraggingInfo
* info
, Bool deposited
)
69 WMView
*view
= XDND_SOURCE_VIEW(info
);
70 WMScreen
*scr
= W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
72 /* free selection handler while view exists */
73 WMDeleteSelectionHandler(view
, scr
->xdndSelectionAtom
, CurrentTime
);
74 wfree(XDND_SELECTION_PROCS(info
));
76 if (XDND_DRAG_CURSOR(info
) != None
) {
77 XFreeCursor(scr
->display
, XDND_DRAG_CURSOR(info
));
78 XDND_DRAG_CURSOR(info
) = None
;
81 if (view
->dragSourceProcs
->endedDrag
!= NULL
) {
82 /* this can destroy source view (with a "move" action for example) */
83 view
->dragSourceProcs
->endedDrag(view
, &XDND_DRAG_ICON_POS(info
), deposited
);
86 /* clear remaining draggging infos */
87 wfree(XDND_SOURCE_INFO(info
));
88 XDND_SOURCE_INFO(info
) = NULL
;
91 /* ----- drag cursor ----- */
92 static void initDragCursor(WMDraggingInfo
* info
)
94 WMScreen
*scr
= sourceScreen(info
);
95 XColor cursorFgColor
, cursorBgColor
;
98 cursorFgColor
.red
= 0x4500;
99 cursorFgColor
.green
= 0xb000;
100 cursorFgColor
.blue
= 0x4500;
103 cursorBgColor
.red
= 0xffff;
104 cursorBgColor
.green
= 0xffff;
105 cursorBgColor
.blue
= 0xffff;
107 XDND_DRAG_CURSOR(info
) = XCreateFontCursor(scr
->display
, XC_left_ptr
);
108 XRecolorCursor(scr
->display
, XDND_DRAG_CURSOR(info
), &cursorFgColor
, &cursorBgColor
);
110 XFlush(scr
->display
);
113 static void recolorCursor(WMDraggingInfo
* info
, Bool dropIsAllowed
)
115 WMScreen
*scr
= sourceScreen(info
);
118 XDefineCursor(scr
->display
, scr
->rootWin
, XDND_DRAG_CURSOR(info
));
120 XDefineCursor(scr
->display
, scr
->rootWin
, scr
->defaultCursor
);
123 XFlush(scr
->display
);
126 /* ----- end of drag cursor ----- */
128 /* ----- selection procs ----- */
129 static WMData
*convertSelection(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
)
135 /* Parameter not used, but tell the compiler that it is ok */
139 scr
= W_VIEW_SCREEN(view
);
140 typeName
= XGetAtomName(scr
->display
, target
);
144 if (view
->dragSourceProcs
->fetchDragData
!= NULL
) {
145 data
= view
->dragSourceProcs
->fetchDragData(view
, typeName
);
150 if (typeName
!= NULL
)
156 static void selectionLost(WMView
* view
, Atom selection
, void *cdata
)
158 /* Parameter not used, but tell the compiler that it is ok */
163 wwarning(_("XDND selection lost during drag operation..."));
166 static void selectionDone(WMView
* view
, Atom selection
, Atom target
, void *cdata
)
168 /* Parameter not used, but tell the compiler that it is ok */
175 printf("selection done\n");
179 /* ----- end of selection procs ----- */
181 /* ----- visual part ----- */
183 static Window
makeDragIcon(WMScreen
* scr
, WMPixmap
* pixmap
)
188 XSetWindowAttributes attribs
;
191 pixmap
= scr
->defaultObjectIcon
;
194 size
= WMGetPixmapSize(pixmap
);
196 flags
= CWSaveUnder
| CWBackPixmap
| CWOverrideRedirect
| CWColormap
;
197 attribs
.save_under
= True
;
198 attribs
.background_pixmap
= pixmap
->pixmap
;
199 attribs
.override_redirect
= True
;
200 attribs
.colormap
= scr
->colormap
;
202 window
= XCreateWindow(scr
->display
, scr
->rootWin
, 0, 0, size
.width
,
203 size
.height
, 0, scr
->depth
, InputOutput
, scr
->visual
, flags
, &attribs
);
207 XShapeCombineMask(scr
->display
, window
, ShapeBounding
, 0, 0, pixmap
->mask
, ShapeSet
);
214 static void slideWindow(Display
* dpy
, Window win
, int srcX
, int srcY
, int dstX
, int dstY
)
220 iterations
= WMIN(MAX_SLIDEBACK_ITER
, WMAX(abs(dstX
- srcX
), abs(dstY
- srcY
)));
225 dx
= (double)(dstX
- srcX
) / iterations
;
226 dy
= (double)(dstY
- srcY
) / iterations
;
228 for (i
= 0; i
<= iterations
; i
++) {
229 XMoveWindow(dpy
, win
, x
, y
);
239 static int getInitialDragImageCoord(int viewCoord
, int mouseCoord
, int viewSize
, int iconSize
)
241 if (iconSize
>= viewSize
) {
242 /* center icon coord on view */
243 return viewCoord
- iconSize
/ 2;
245 /* try to center icon on mouse pos */
247 if (mouseCoord
- iconSize
/ 2 <= viewCoord
)
248 /* if icon was centered on mouse, it would be off view
249 thus, put icon left (resp. top) side
250 at view (resp. top) side */
253 else if (mouseCoord
+ iconSize
/ 2 >= viewCoord
+ viewSize
)
254 /* if icon was centered on mouse, it would be off view
255 thus, put icon right (resp. bottom) side
256 at view right (resp. bottom) side */
257 return viewCoord
+ viewSize
- iconSize
;
260 return mouseCoord
- iconSize
/ 2;
265 static void initDragImagePos(WMView
* view
, WMDraggingInfo
* info
, XEvent
* event
)
267 WMSize iconSize
= WMGetPixmapSize(view
->dragImage
);
268 WMSize viewSize
= WMGetViewSize(view
);
272 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
273 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
274 0, 0, &(viewPos
.x
), &(viewPos
.y
), &foo
);
277 XDND_DRAG_ICON_POS(info
).x
=
278 getInitialDragImageCoord(viewPos
.x
, event
->xmotion
.x_root
, viewSize
.width
, iconSize
.width
);
280 XDND_DRAG_ICON_POS(info
).y
=
281 getInitialDragImageCoord(viewPos
.y
, event
->xmotion
.y_root
, viewSize
.height
, iconSize
.height
);
283 /* set mouse offset relative to icon */
284 XDND_MOUSE_OFFSET(info
).x
= event
->xmotion
.x_root
- XDND_DRAG_ICON_POS(info
).x
;
285 XDND_MOUSE_OFFSET(info
).y
= event
->xmotion
.y_root
- XDND_DRAG_ICON_POS(info
).y
;
288 static void refreshDragImage(WMView
* view
, WMDraggingInfo
* info
)
290 WMScreen
*scr
= W_VIEW_SCREEN(view
);
292 XMoveWindow(scr
->display
, XDND_DRAG_ICON(info
), XDND_DRAG_ICON_POS(info
).x
, XDND_DRAG_ICON_POS(info
).y
);
295 static void startDragImage(WMView
* view
, WMDraggingInfo
* info
, XEvent
* event
)
297 WMScreen
*scr
= W_VIEW_SCREEN(view
);
299 XDND_DRAG_ICON(info
) = makeDragIcon(scr
, view
->dragImage
);
300 initDragImagePos(view
, info
, event
);
301 refreshDragImage(view
, info
);
302 XMapRaised(scr
->display
, XDND_DRAG_ICON(info
));
304 initDragCursor(info
);
307 static void endDragImage(WMDraggingInfo
* info
, Bool slideBack
)
309 WMView
*view
= XDND_SOURCE_VIEW(info
);
310 Display
*dpy
= W_VIEW_SCREEN(view
)->display
;
316 XTranslateCoordinates(W_VIEW_SCREEN(view
)->display
,
317 WMViewXID(view
), W_VIEW_SCREEN(view
)->rootWin
,
318 0, 0, &(toLocation
.x
), &(toLocation
.y
), &foo
);
320 slideWindow(dpy
, XDND_DRAG_ICON(info
),
321 XDND_DRAG_ICON_POS(info
).x
, XDND_DRAG_ICON_POS(info
).y
, toLocation
.x
, toLocation
.y
);
324 XDestroyWindow(dpy
, XDND_DRAG_ICON(info
));
327 /* ----- end of visual part ----- */
329 /* ----- messages ----- */
331 /* send a DnD message to the destination window */
333 sendDnDClientMessage(WMDraggingInfo
* info
, Atom message
,
334 unsigned long data1
, unsigned long data2
, unsigned long data3
, unsigned long data4
)
336 Display
*dpy
= sourceScreen(info
)->display
;
337 Window srcWin
= WMViewXID(XDND_SOURCE_VIEW(info
));
338 Window destWin
= XDND_DEST_WIN(info
);
340 if (!W_SendDnDClientMessage(dpy
, destWin
, message
, srcWin
, data1
, data2
, data3
, data4
)) {
342 recolorCursor(info
, False
);
343 endDragImage(info
, True
);
344 endDragProcess(info
, False
);
351 static Bool
sendEnterMessage(WMDraggingInfo
* info
)
353 WMScreen
*scr
= sourceScreen(info
);
354 unsigned long version
;
356 if (XDND_DEST_VERSION(info
) > 2) {
357 if (XDND_DEST_VERSION(info
) < XDND_VERSION
)
358 version
= XDND_DEST_VERSION(info
);
360 version
= XDND_VERSION
;
365 return sendDnDClientMessage(info
, scr
->xdndEnterAtom
, (version
<< 24) | 1, /* 1: support of type list */
366 XDND_3_TYPES(info
)[0], XDND_3_TYPES(info
)[1], XDND_3_TYPES(info
)[2]);
369 static Bool
sendPositionMessage(WMDraggingInfo
* info
, WMPoint
* mousePos
)
371 WMScreen
*scr
= sourceScreen(info
);
372 WMRect
*noPosZone
= &(XDND_NO_POS_ZONE(info
));
374 if (noPosZone
->size
.width
!= 0 || noPosZone
->size
.height
!= 0) {
375 if (mousePos
->x
< noPosZone
->pos
.x
|| mousePos
->x
> (noPosZone
->pos
.x
+ noPosZone
->size
.width
)
376 || mousePos
->y
< noPosZone
->pos
.y
|| mousePos
->y
> (noPosZone
->pos
.y
+ noPosZone
->size
.height
)) {
377 /* send position if out of zone defined by destination */
378 return sendDnDClientMessage(info
, scr
->xdndPositionAtom
,
380 mousePos
->x
<< 16 | mousePos
->y
,
381 XDND_TIMESTAMP(info
), XDND_SOURCE_ACTION(info
));
384 /* Nothing to send, always succeed */
389 /* send position on each move */
390 return sendDnDClientMessage(info
, scr
->xdndPositionAtom
,
392 mousePos
->x
<< 16 | mousePos
->y
,
393 XDND_TIMESTAMP(info
), XDND_SOURCE_ACTION(info
));
396 static Bool
sendLeaveMessage(WMDraggingInfo
* info
)
398 WMScreen
*scr
= sourceScreen(info
);
400 return sendDnDClientMessage(info
, scr
->xdndLeaveAtom
, 0, 0, 0, 0);
403 static Bool
sendDropMessage(WMDraggingInfo
* info
)
405 WMScreen
*scr
= sourceScreen(info
);
407 return sendDnDClientMessage(info
, scr
->xdndDropAtom
, 0, XDND_TIMESTAMP(info
), 0, 0);
410 /* ----- end of messages ----- */
412 static Atom
*getTypeAtomList(WMScreen
* scr
, WMView
* view
, int *count
)
418 types
= view
->dragSourceProcs
->dropDataTypes(view
);
421 *count
= WMGetArrayItemCount(types
);
423 typeAtoms
= wmalloc((*count
) * sizeof(Atom
));
424 for (i
= 0; i
< *count
; i
++) {
425 typeAtoms
[i
] = XInternAtom(scr
->display
, WMGetFromArray(types
, i
), False
);
428 /* WMFreeArray(types); */
432 /* WMFreeArray(types); */
436 typeAtoms
= wmalloc(sizeof(Atom
));
442 static void registerDropTypes(WMScreen
* scr
, WMView
* view
, WMDraggingInfo
* info
)
447 typeList
= getTypeAtomList(scr
, view
, &count
);
449 /* store the first 3 types */
450 for (i
= 0; i
< 3 && i
< count
; i
++)
451 XDND_3_TYPES(info
)[i
] = typeList
[i
];
454 XDND_3_TYPES(info
)[i
] = None
;
456 /* store the entire type list */
457 XChangeProperty(scr
->display
,
459 scr
->xdndTypeListAtom
,
460 XA_ATOM
, XDND_PROPERTY_FORMAT
, PropModeReplace
, (unsigned char *)typeList
, count
);
463 static void registerOperationList(WMScreen
* scr
, WMView
* view
, WMArray
* operationArray
)
466 WMDragOperationType operation
;
467 int count
= WMGetArrayItemCount(operationArray
);
470 actionList
= wmalloc(sizeof(Atom
) * count
);
472 for (i
= 0; i
< count
; i
++) {
473 operation
= WMGetDragOperationItemType(WMGetFromArray(operationArray
, i
));
474 actionList
[i
] = W_OperationToAction(scr
, operation
);
477 XChangeProperty(scr
->display
,
479 scr
->xdndActionListAtom
,
480 XA_ATOM
, XDND_PROPERTY_FORMAT
, PropModeReplace
, (unsigned char *)actionList
, count
);
483 static void registerDescriptionList(WMScreen
* scr
, WMView
* view
, WMArray
* operationArray
)
485 char *text
, *textListItem
, *textList
;
486 int count
= WMGetArrayItemCount(operationArray
);
490 /* size of XA_STRING info */
491 for (i
= 0; i
< count
; i
++) {
492 size
+= strlen(WMGetDragOperationItemText(WMGetFromArray(operationArray
, i
))) + 1 /* NULL */;
495 /* create text list */
496 textList
= wmalloc(size
);
497 textListItem
= textList
;
499 for (i
= 0; i
< count
; i
++) {
500 text
= WMGetDragOperationItemText(WMGetFromArray(operationArray
, i
));
501 wstrlcpy(textListItem
, text
, size
);
503 /* to next text offset */
504 textListItem
= &(textListItem
[strlen(textListItem
) + 1]);
507 XChangeProperty(scr
->display
,
509 scr
->xdndActionDescriptionAtom
,
511 XDND_ACTION_DESCRIPTION_FORMAT
, PropModeReplace
, (unsigned char *)textList
, size
);
514 /* called if wanted operation is WDOperationAsk */
515 static void registerSupportedOperations(WMView
* view
)
517 WMScreen
*scr
= W_VIEW_SCREEN(view
);
518 WMArray
*operationArray
;
520 operationArray
= view
->dragSourceProcs
->askedOperations(view
);
522 registerOperationList(scr
, view
, operationArray
);
523 registerDescriptionList(scr
, view
, operationArray
);
525 /* WMFreeArray(operationArray); */
528 static void initSourceDragInfo(WMView
* sourceView
, WMDraggingInfo
* info
)
532 XDND_SOURCE_INFO(info
) = (W_DragSourceInfo
*) wmalloc(sizeof(W_DragSourceInfo
));
534 XDND_SOURCE_VIEW(info
) = sourceView
;
535 XDND_DEST_WIN(info
) = None
;
536 XDND_DRAG_ICON(info
) = None
;
538 XDND_SOURCE_ACTION(info
) = W_OperationToAction(W_VIEW_SCREEN(sourceView
),
539 sourceView
->dragSourceProcs
->
540 wantedDropOperation(sourceView
));
542 XDND_DEST_ACTION(info
) = None
;
544 XDND_SOURCE_STATE(info
) = idleState
;
546 emptyZone
.pos
= wmkpoint(0, 0);
547 emptyZone
.size
= wmksize(0, 0);
548 XDND_NO_POS_ZONE(info
) = emptyZone
;
552 Returned array is destroyed after dropDataTypes call
554 static WMArray
*defDropDataTypes(WMView
* self
)
556 /* Parameter not used, but tell the compiler that it is ok */
562 static WMDragOperationType
defWantedDropOperation(WMView
* self
)
564 /* Parameter not used, but tell the compiler that it is ok */
567 return WDOperationNone
;
571 Must be defined if wantedDropOperation return WDOperationAsk
573 Return a WMDragOperationItem array (destroyed after call).
574 A WMDragOperationItem links a label to an operation.
576 defAskedOperations(WMView *self); */
578 static Bool
defAcceptDropOperation(WMView
* self
, WMDragOperationType allowedOperation
)
580 /* Parameter not used, but tell the compiler that it is ok */
582 (void) allowedOperation
;
587 static void defBeganDrag(WMView
* self
, WMPoint
* point
)
589 /* Parameter not used, but tell the compiler that it is ok */
594 static void defEndedDrag(WMView
* self
, WMPoint
* point
, Bool deposited
)
596 /* Parameter not used, but tell the compiler that it is ok */
603 Returned data is not destroyed
605 static WMData
*defFetchDragData(WMView
* self
, char *type
)
607 /* Parameter not used, but tell the compiler that it is ok */
614 void WMSetViewDragSourceProcs(WMView
* view
, WMDragSourceProcs
* procs
)
616 if (view
->dragSourceProcs
)
617 wfree(view
->dragSourceProcs
);
618 view
->dragSourceProcs
= wmalloc(sizeof(WMDragSourceProcs
));
620 *view
->dragSourceProcs
= *procs
;
622 if (procs
->dropDataTypes
== NULL
)
623 view
->dragSourceProcs
->dropDataTypes
= defDropDataTypes
;
625 if (procs
->wantedDropOperation
== NULL
)
626 view
->dragSourceProcs
->wantedDropOperation
= defWantedDropOperation
;
629 Note: askedOperations can be NULL, if wantedDropOperation never returns
633 if (procs
->acceptDropOperation
== NULL
)
634 view
->dragSourceProcs
->acceptDropOperation
= defAcceptDropOperation
;
636 if (procs
->beganDrag
== NULL
)
637 view
->dragSourceProcs
->beganDrag
= defBeganDrag
;
639 if (procs
->endedDrag
== NULL
)
640 view
->dragSourceProcs
->endedDrag
= defEndedDrag
;
642 if (procs
->fetchDragData
== NULL
)
643 view
->dragSourceProcs
->fetchDragData
= defFetchDragData
;
646 static Bool
isXdndAware(WMScreen
* scr
, Window win
)
650 unsigned long count
, remain
;
651 unsigned char *winXdndVersion
;
656 XGetWindowProperty(scr
->display
, win
, scr
->xdndAwareAtom
,
657 0, 1, False
, XA_ATOM
, &type
, &format
, &count
, &remain
, &winXdndVersion
);
659 if (type
!= XA_ATOM
|| format
!= XDND_PROPERTY_FORMAT
|| count
== 0 || !winXdndVersion
) {
661 XFree(winXdndVersion
);
665 XFree(winXdndVersion
);
666 return (count
== 1); /* xdnd version is set */
669 static Window
*windowChildren(Display
* dpy
, Window win
, unsigned *nchildren
)
674 if (!XQueryTree(dpy
, win
, &foo
, &bar
, &children
, nchildren
)) {
681 static Window
lookForAwareWindow(WMScreen
* scr
, WMPoint
* mousePos
, Window win
)
686 /* Since xdnd v3, only the toplevel window should be aware */
687 if (isXdndAware(scr
, win
))
690 /* inspect child under pointer */
691 if (XTranslateCoordinates(scr
->display
, scr
->rootWin
, win
, mousePos
->x
, mousePos
->y
, &tmpx
, &tmpy
, &child
)) {
695 return lookForAwareWindow(scr
, mousePos
, child
);
701 static Window
findDestination(WMDraggingInfo
* info
, WMPoint
* mousePos
)
703 WMScreen
*scr
= sourceScreen(info
);
705 Window
*children
= windowChildren(scr
->display
, scr
->rootWin
, &nchildren
);
707 XWindowAttributes attr
;
709 if (isXdndAware(scr
, scr
->rootWin
))
712 /* exclude drag icon (and upper) from search */
713 for (i
= nchildren
- 1; i
>= 0; i
--) {
714 if (children
[i
] == XDND_DRAG_ICON(info
)) {
721 /* root window has no child under drag icon, and is not xdnd aware. */
725 /* inspecting children, from upper to lower */
726 for (; i
>= 0; i
--) {
727 if (XGetWindowAttributes(scr
->display
, children
[i
], &attr
)
728 && attr
.map_state
== IsViewable
729 && mousePos
->x
>= attr
.x
730 && mousePos
->y
>= attr
.y
731 && mousePos
->x
< attr
.x
+ attr
.width
&& mousePos
->y
< attr
.y
+ attr
.height
) {
732 return lookForAwareWindow(scr
, mousePos
, children
[i
]);
736 /* No child window under drag pointer */
740 static void storeDestinationProtocolVersion(WMDraggingInfo
* info
)
744 unsigned long count
, remain
;
745 unsigned char *winXdndVersion
;
746 WMScreen
*scr
= W_VIEW_SCREEN(XDND_SOURCE_VIEW(info
));
748 wassertr(XDND_DEST_WIN(info
) != None
);
750 if (XGetWindowProperty(scr
->display
, XDND_DEST_WIN(info
),
752 0, 1, False
, XA_ATOM
, &type
, &format
,
753 &count
, &remain
, &winXdndVersion
) == Success
) {
754 XDND_DEST_VERSION(info
) = *winXdndVersion
;
755 XFree(winXdndVersion
);
757 XDND_DEST_VERSION(info
) = 0;
758 wwarning(_("could not get XDND version for target of drop"));
762 static void initMotionProcess(WMView
* view
, WMDraggingInfo
* info
, XEvent
* event
, WMPoint
* startLocation
)
764 WMScreen
*scr
= W_VIEW_SCREEN(view
);
766 /* take ownership of XdndSelection */
767 XDND_SELECTION_PROCS(info
) = (WMSelectionProcs
*) wmalloc(sizeof(WMSelectionProcs
));
768 XDND_SELECTION_PROCS(info
)->convertSelection
= convertSelection
;
769 XDND_SELECTION_PROCS(info
)->selectionLost
= selectionLost
;
770 XDND_SELECTION_PROCS(info
)->selectionDone
= selectionDone
;
771 XDND_TIMESTAMP(info
) = event
->xmotion
.time
;
773 if (!WMCreateSelectionHandler(view
, scr
->xdndSelectionAtom
, CurrentTime
, XDND_SELECTION_PROCS(info
), NULL
)) {
774 wwarning(_("could not get ownership of XDND selection"));
778 registerDropTypes(scr
, view
, info
);
780 if (XDND_SOURCE_ACTION(info
) == W_VIEW_SCREEN(view
)->xdndActionAsk
)
781 registerSupportedOperations(view
);
783 if (view
->dragSourceProcs
->beganDrag
!= NULL
) {
784 view
->dragSourceProcs
->beganDrag(view
, startLocation
);
788 static void processMotion(WMDraggingInfo
* info
, WMPoint
* mousePos
)
790 Window newDestination
= findDestination(info
, mousePos
);
792 W_DragSourceStopTimer();
794 if (newDestination
!= XDND_DEST_WIN(info
)) {
795 recolorCursor(info
, False
);
797 if (XDND_DEST_WIN(info
) != None
) {
798 /* leaving a xdnd window */
799 sendLeaveMessage(info
);
802 XDND_DEST_WIN(info
) = newDestination
;
803 XDND_DEST_ACTION(info
) = None
;
804 XDND_NO_POS_ZONE(info
).size
.width
= 0;
805 XDND_NO_POS_ZONE(info
).size
.height
= 0;
807 if (newDestination
!= None
) {
808 /* entering a xdnd window */
809 XDND_SOURCE_STATE(info
) = idleState
;
810 storeDestinationProtocolVersion(info
);
812 if (!sendEnterMessage(info
)) {
813 XDND_DEST_WIN(info
) = None
;
817 W_DragSourceStartTimer(info
);
819 XDND_SOURCE_STATE(info
) = NULL
;
822 if (XDND_DEST_WIN(info
) != None
) {
823 if (!sendPositionMessage(info
, mousePos
)) {
824 XDND_DEST_WIN(info
) = None
;
828 W_DragSourceStartTimer(info
);
833 static Bool
processButtonRelease(WMDraggingInfo
* info
)
835 if (XDND_SOURCE_STATE(info
) == dropAllowedState
) {
837 W_DragSourceStopTimer();
839 if (!sendDropMessage(info
))
842 W_DragSourceStartTimer(info
);
845 if (XDND_DEST_WIN(info
) != None
)
846 sendLeaveMessage(info
);
848 W_DragSourceStopTimer();
853 Bool
WMIsDraggingFromView(WMView
* view
)
855 WMDraggingInfo
*info
= &W_VIEW_SCREEN(view
)->dragInfo
;
857 return (XDND_SOURCE_INFO(info
) != NULL
&& XDND_SOURCE_STATE(info
) != finishDropState
);
858 /* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
861 void WMDragImageFromView(WMView
* view
, XEvent
* event
)
863 WMDraggingInfo
*info
= &W_VIEW_SCREEN(view
)->dragInfo
;
864 WMPoint mouseLocation
;
866 switch (event
->type
) {
868 if (event
->xbutton
.button
== Button1
) {
871 XPeekEvent(event
->xbutton
.display
, &nextEvent
);
873 /* Initialize only if a drag really begins (avoid clicks) */
874 if (nextEvent
.type
== MotionNotify
) {
875 initSourceDragInfo(view
, info
);
882 if (WMIsDraggingFromView(view
)) {
883 Bool dropBegan
= processButtonRelease(info
);
885 recolorCursor(info
, False
);
887 endDragImage(info
, False
);
888 XDND_SOURCE_STATE(info
) = finishDropState
;
891 endDragImage(info
, True
);
892 endDragProcess(info
, False
);
898 if (WMIsDraggingFromView(view
)) {
899 mouseLocation
= wmkpoint(event
->xmotion
.x_root
, event
->xmotion
.y_root
);
901 if (abs(XDND_DRAG_ICON_POS(info
).x
- mouseLocation
.x
) >=
903 || abs(XDND_DRAG_ICON_POS(info
).y
- mouseLocation
.y
) >= MIN_Y_MOVE_OFFSET
) {
904 if (XDND_DRAG_ICON(info
) == None
) {
905 initMotionProcess(view
, info
, event
, &mouseLocation
);
906 startDragImage(view
, info
, event
);
908 XDND_DRAG_ICON_POS(info
).x
= mouseLocation
.x
- XDND_MOUSE_OFFSET(info
).x
;
909 XDND_DRAG_ICON_POS(info
).y
= mouseLocation
.y
- XDND_MOUSE_OFFSET(info
).y
;
911 refreshDragImage(view
, info
);
912 processMotion(info
, &mouseLocation
);
920 /* Minimal mouse events handler: no right or double-click detection,
921 only drag is supported */
922 static void dragImageHandler(XEvent
* event
, void *cdata
)
924 WMView
*view
= (WMView
*) cdata
;
926 WMDragImageFromView(view
, event
);
929 /* ----- source states ----- */
932 static void traceStatusMsg(Display
* dpy
, XClientMessageEvent
* statusEvent
)
934 printf("Xdnd status message:\n");
936 if (statusEvent
->data
.l
[1] & 0x2UL
)
937 printf("\tsend position on every move\n");
940 x
= statusEvent
->data
.l
[2] >> 16;
941 y
= statusEvent
->data
.l
[2] & 0xFFFFL
;
942 w
= statusEvent
->data
.l
[3] >> 16;
943 h
= statusEvent
->data
.l
[3] & 0xFFFFL
;
945 printf("\tsend position out of ((%d,%d) , (%d,%d))\n", x
, y
, x
+ w
, y
+ h
);
948 if (statusEvent
->data
.l
[1] & 0x1L
)
949 printf("\tallowed action: %s\n", XGetAtomName(dpy
, statusEvent
->data
.l
[4]));
951 printf("\tno action allowed\n");
955 static void storeDropAction(WMDraggingInfo
* info
, Atom destAction
)
957 WMView
*sourceView
= XDND_SOURCE_VIEW(info
);
958 WMScreen
*scr
= W_VIEW_SCREEN(sourceView
);
960 if (sourceView
->dragSourceProcs
->acceptDropOperation
!= NULL
) {
961 if (sourceView
->dragSourceProcs
->acceptDropOperation(sourceView
,
962 W_ActionToOperation(scr
, destAction
)))
963 XDND_DEST_ACTION(info
) = destAction
;
965 XDND_DEST_ACTION(info
) = None
;
967 XDND_DEST_ACTION(info
) = destAction
;
971 static void storeStatusMessageInfos(WMDraggingInfo
* info
, XClientMessageEvent
* statusEvent
)
973 WMRect
*noPosZone
= &(XDND_NO_POS_ZONE(info
));
977 traceStatusMsg(sourceScreen(info
)->display
, statusEvent
);
980 if (statusEvent
->data
.l
[1] & 0x2UL
) {
981 /* bit 1 set: destination wants position messages on every move */
982 noPosZone
->size
.width
= 0;
983 noPosZone
->size
.height
= 0;
985 /* don't send another position message while in given rectangle */
986 noPosZone
->pos
.x
= statusEvent
->data
.l
[2] >> 16;
987 noPosZone
->pos
.y
= statusEvent
->data
.l
[2] & 0xFFFFL
;
988 noPosZone
->size
.width
= statusEvent
->data
.l
[3] >> 16;
989 noPosZone
->size
.height
= statusEvent
->data
.l
[3] & 0xFFFFL
;
992 if ((statusEvent
->data
.l
[1] & 0x1L
) || statusEvent
->data
.l
[4] != None
) {
993 /* destination accept drop */
994 storeDropAction(info
, statusEvent
->data
.l
[4]);
996 XDND_DEST_ACTION(info
) = None
;
1000 static void *idleState(WMView
* view
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
1003 Atom destMsg
= event
->message_type
;
1005 scr
= W_VIEW_SCREEN(view
);
1007 if (destMsg
== scr
->xdndStatusAtom
) {
1008 storeStatusMessageInfos(info
, event
);
1010 if (XDND_DEST_ACTION(info
) != None
) {
1011 recolorCursor(info
, True
);
1012 W_DragSourceStartTimer(info
);
1013 return dropAllowedState
;
1016 recolorCursor(info
, False
);
1021 if (destMsg
== scr
->xdndFinishedAtom
) {
1022 wwarning("received xdndFinishedAtom before drop began");
1025 W_DragSourceStartTimer(info
);
1029 static void *dropAllowedState(WMView
* view
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
1031 WMScreen
*scr
= W_VIEW_SCREEN(view
);
1032 Atom destMsg
= event
->message_type
;
1034 if (destMsg
== scr
->xdndStatusAtom
) {
1035 storeStatusMessageInfos(info
, event
);
1037 if (XDND_DEST_ACTION(info
) == None
) {
1039 recolorCursor(info
, False
);
1044 W_DragSourceStartTimer(info
);
1045 return dropAllowedState
;
1048 static void *finishDropState(WMView
* view
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
1050 WMScreen
*scr
= W_VIEW_SCREEN(view
);
1051 Atom destMsg
= event
->message_type
;
1053 if (destMsg
== scr
->xdndFinishedAtom
) {
1054 endDragProcess(info
, True
);
1058 W_DragSourceStartTimer(info
);
1059 return finishDropState
;
1062 /* ----- end of source states ----- */
1064 /* ----- source timer ----- */
1065 static void dragSourceResponseTimeOut(void *source
)
1067 WMView
*view
= (WMView
*) source
;
1068 WMDraggingInfo
*info
= &(W_VIEW_SCREEN(view
)->dragInfo
);
1070 wwarning(_("delay for drag destination response expired"));
1071 sendLeaveMessage(info
);
1073 recolorCursor(info
, False
);
1074 if (XDND_SOURCE_STATE(info
) == finishDropState
) {
1076 endDragImage(info
, True
);
1077 endDragProcess(info
, False
);
1079 XDND_SOURCE_STATE(info
) = idleState
;
1083 void W_DragSourceStopTimer()
1085 if (dndSourceTimer
!= NULL
) {
1086 WMDeleteTimerHandler(dndSourceTimer
);
1087 dndSourceTimer
= NULL
;
1091 void W_DragSourceStartTimer(WMDraggingInfo
* info
)
1093 W_DragSourceStopTimer();
1095 dndSourceTimer
= WMAddTimerHandler(XDND_DESTINATION_RESPONSE_MAX_DELAY
,
1096 dragSourceResponseTimeOut
, XDND_SOURCE_VIEW(info
));
1099 /* ----- End of Destination timer ----- */
1101 void W_DragSourceStateHandler(WMDraggingInfo
* info
, XClientMessageEvent
* event
)
1104 W_DndState
*newState
;
1106 if (XDND_SOURCE_VIEW_STORED(info
)) {
1107 if (XDND_SOURCE_STATE(info
) != NULL
) {
1108 view
= XDND_SOURCE_VIEW(info
);
1111 printf("current source state: %s\n", stateName(XDND_SOURCE_STATE(info
)));
1114 newState
= (W_DndState
*) XDND_SOURCE_STATE(info
) (view
, event
, info
);
1118 printf("new source state: %s\n", stateName(newState
));
1121 if (newState
!= NULL
)
1122 XDND_SOURCE_STATE(info
) = newState
;
1123 /* else drop finished, and info has been flushed */
1127 wwarning("received DnD message without having a target");
1131 void WMSetViewDragImage(WMView
* view
, WMPixmap
* dragImage
)
1133 if (view
->dragImage
!= NULL
)
1134 WMReleasePixmap(view
->dragImage
);
1136 view
->dragImage
= WMRetainPixmap(dragImage
);
1139 void WMReleaseViewDragImage(WMView
* view
)
1141 if (view
->dragImage
!= NULL
)
1142 WMReleasePixmap(view
->dragImage
);
1145 /* Create a drag handler, associating drag event masks with dragEventProc */
1146 void WMCreateDragHandler(WMView
* view
, WMEventProc
* dragEventProc
, void *clientData
)
1148 WMCreateEventHandler(view
,
1149 ButtonPressMask
| ButtonReleaseMask
| Button1MotionMask
, dragEventProc
, clientData
);
1152 void WMDeleteDragHandler(WMView
* view
, WMEventProc
* dragEventProc
, void *clientData
)
1154 WMDeleteEventHandler(view
,
1155 ButtonPressMask
| ButtonReleaseMask
| Button1MotionMask
, dragEventProc
, clientData
);
1158 /* set default drag handler for view */
1159 void WMSetViewDraggable(WMView
* view
, WMDragSourceProcs
* dragSourceProcs
, WMPixmap
* dragImage
)
1161 wassertr(dragImage
!= NULL
);
1162 view
->dragImage
= WMRetainPixmap(dragImage
);
1164 WMSetViewDragSourceProcs(view
, dragSourceProcs
);
1166 WMCreateDragHandler(view
, dragImageHandler
, view
);
1169 void WMUnsetViewDraggable(WMView
* view
)
1171 if (view
->dragSourceProcs
) {
1172 wfree(view
->dragSourceProcs
);
1173 view
->dragSourceProcs
= NULL
;
1176 WMReleaseViewDragImage(view
);
1178 WMDeleteDragHandler(view
, dragImageHandler
, view
);