WPrefs: Removed unused Screen argument to the 'Init*' functions
[wmaker-crm.git] / WINGs / dragsource.c
blob05d2c6a5e65932d035419cbab1a4eb7b6320a8c3
2 #include "wconfig.h"
3 #include "WINGsP.h"
5 #include <X11/Xatom.h>
6 #include <X11/cursorfont.h>
7 #ifdef SHAPE
8 #include <X11/extensions/shape.h>
9 #endif
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);
43 #ifdef XDND_DEBUG
44 static const char *stateName(W_DndState * state)
46 if (state == NULL)
47 return "no state defined";
49 if (state == idleState)
50 return "idleState";
52 if (state == dropAllowedState)
53 return "dropAllowedState";
55 if (state == finishDropState)
56 return "finishDropState";
58 return "unknown state";
60 #endif
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;
97 /* green */
98 cursorFgColor.red = 0x4500;
99 cursorFgColor.green = 0xb000;
100 cursorFgColor.blue = 0x4500;
102 /* white */
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);
117 if (dropIsAllowed) {
118 XDefineCursor(scr->display, scr->rootWin, XDND_DRAG_CURSOR(info));
119 } else {
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)
131 WMScreen *scr;
132 WMData *data;
133 char *typeName;
135 /* Parameter not used, but tell the compiler that it is ok */
136 (void) selection;
137 (void) cdata;
139 scr = W_VIEW_SCREEN(view);
140 typeName = XGetAtomName(scr->display, target);
142 *type = target;
144 if (view->dragSourceProcs->fetchDragData != NULL) {
145 data = view->dragSourceProcs->fetchDragData(view, typeName);
146 } else {
147 data = NULL;
150 if (typeName != NULL)
151 XFree(typeName);
153 return data;
156 static void selectionLost(WMView * view, Atom selection, void *cdata)
158 /* Parameter not used, but tell the compiler that it is ok */
159 (void) view;
160 (void) selection;
161 (void) cdata;
163 wwarning("DND 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 */
169 (void) view;
170 (void) selection;
171 (void) target;
172 (void) cdata;
174 #ifdef XDND_DEBUG
175 printf("selection done\n");
176 #endif
179 /* ----- end of selection procs ----- */
181 /* ----- visual part ----- */
183 static Window makeDragIcon(WMScreen * scr, WMPixmap * pixmap)
185 Window window;
186 WMSize size;
187 unsigned long flags;
188 XSetWindowAttributes attribs;
189 Pixmap pix, mask;
191 if (!pixmap) {
192 pixmap = scr->defaultObjectIcon;
195 size = WMGetPixmapSize(pixmap);
196 pix = pixmap->pixmap;
197 mask = pixmap->mask;
199 flags = CWSaveUnder | CWBackPixmap | CWOverrideRedirect | CWColormap;
200 attribs.save_under = True;
201 attribs.background_pixmap = pix;
202 attribs.override_redirect = True;
203 attribs.colormap = scr->colormap;
205 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
206 size.height, 0, scr->depth, InputOutput, scr->visual, flags, &attribs);
208 #ifdef SHAPE
210 if (mask) {
211 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask, ShapeSet);
213 #endif
215 return window;
218 static void slideWindow(Display * dpy, Window win, int srcX, int srcY, int dstX, int dstY)
220 double x, y, dx, dy;
221 int i;
222 int iterations;
224 iterations = WMIN(MAX_SLIDEBACK_ITER, WMAX(abs(dstX - srcX), abs(dstY - srcY)));
226 x = srcX;
227 y = srcY;
229 dx = (double)(dstX - srcX) / iterations;
230 dy = (double)(dstY - srcY) / iterations;
232 for (i = 0; i <= iterations; i++) {
233 XMoveWindow(dpy, win, x, y);
234 XFlush(dpy);
236 wusleep(800);
238 x += dx;
239 y += dy;
243 static int getInitialDragImageCoord(int viewCoord, int mouseCoord, int viewSize, int iconSize)
245 if (iconSize >= viewSize) {
246 /* center icon coord on view */
247 return viewCoord - iconSize / 2;
248 } else {
249 /* try to center icon on mouse pos */
251 if (mouseCoord - iconSize / 2 <= viewCoord)
252 /* if icon was centered on mouse, it would be off view
253 thus, put icon left (resp. top) side
254 at view (resp. top) side */
255 return viewCoord;
257 else if (mouseCoord + iconSize / 2 >= viewCoord + viewSize)
258 /* if icon was centered on mouse, it would be off view
259 thus, put icon right (resp. bottom) side
260 at view right (resp. bottom) side */
261 return viewCoord + viewSize - iconSize;
263 else
264 return mouseCoord - iconSize / 2;
269 static void initDragImagePos(WMView * view, WMDraggingInfo * info, XEvent * event)
271 WMSize iconSize = WMGetPixmapSize(view->dragImage);
272 WMSize viewSize = WMGetViewSize(view);
273 WMPoint viewPos;
274 Window foo;
276 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
277 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
278 0, 0, &(viewPos.x), &(viewPos.y), &foo);
280 /* set icon pos */
281 XDND_DRAG_ICON_POS(info).x =
282 getInitialDragImageCoord(viewPos.x, event->xmotion.x_root, viewSize.width, iconSize.width);
284 XDND_DRAG_ICON_POS(info).y =
285 getInitialDragImageCoord(viewPos.y, event->xmotion.y_root, viewSize.height, iconSize.height);
287 /* set mouse offset relative to icon */
288 XDND_MOUSE_OFFSET(info).x = event->xmotion.x_root - XDND_DRAG_ICON_POS(info).x;
289 XDND_MOUSE_OFFSET(info).y = event->xmotion.y_root - XDND_DRAG_ICON_POS(info).y;
292 static void refreshDragImage(WMView * view, WMDraggingInfo * info)
294 WMScreen *scr = W_VIEW_SCREEN(view);
296 XMoveWindow(scr->display, XDND_DRAG_ICON(info), XDND_DRAG_ICON_POS(info).x, XDND_DRAG_ICON_POS(info).y);
299 static void startDragImage(WMView * view, WMDraggingInfo * info, XEvent * event)
301 WMScreen *scr = W_VIEW_SCREEN(view);
303 XDND_DRAG_ICON(info) = makeDragIcon(scr, view->dragImage);
304 initDragImagePos(view, info, event);
305 refreshDragImage(view, info);
306 XMapRaised(scr->display, XDND_DRAG_ICON(info));
308 initDragCursor(info);
311 static void endDragImage(WMDraggingInfo * info, Bool slideBack)
313 WMView *view = XDND_SOURCE_VIEW(info);
314 Display *dpy = W_VIEW_SCREEN(view)->display;
316 if (slideBack) {
317 WMPoint toLocation;
318 Window foo;
320 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
321 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
322 0, 0, &(toLocation.x), &(toLocation.y), &foo);
324 slideWindow(dpy, XDND_DRAG_ICON(info),
325 XDND_DRAG_ICON_POS(info).x, XDND_DRAG_ICON_POS(info).y, toLocation.x, toLocation.y);
328 XDestroyWindow(dpy, XDND_DRAG_ICON(info));
331 /* ----- end of visual part ----- */
333 /* ----- messages ----- */
335 /* send a DnD message to the destination window */
336 static Bool
337 sendDnDClientMessage(WMDraggingInfo * info, Atom message,
338 unsigned long data1, unsigned long data2, unsigned long data3, unsigned long data4)
340 Display *dpy = sourceScreen(info)->display;
341 Window srcWin = WMViewXID(XDND_SOURCE_VIEW(info));
342 Window destWin = XDND_DEST_WIN(info);
344 if (!W_SendDnDClientMessage(dpy, destWin, message, srcWin, data1, data2, data3, data4)) {
345 /* drop failed */
346 recolorCursor(info, False);
347 endDragImage(info, True);
348 endDragProcess(info, False);
349 return False;
352 return True;
355 static Bool sendEnterMessage(WMDraggingInfo * info)
357 WMScreen *scr = sourceScreen(info);
358 unsigned long version;
360 if (XDND_DEST_VERSION(info) > 2) {
361 if (XDND_DEST_VERSION(info) < XDND_VERSION)
362 version = XDND_DEST_VERSION(info);
363 else
364 version = XDND_VERSION;
365 } else {
366 version = 3;
369 return sendDnDClientMessage(info, scr->xdndEnterAtom, (version << 24) | 1, /* 1: support of type list */
370 XDND_3_TYPES(info)[0], XDND_3_TYPES(info)[1], XDND_3_TYPES(info)[2]);
373 static Bool sendPositionMessage(WMDraggingInfo * info, WMPoint * mousePos)
375 WMScreen *scr = sourceScreen(info);
376 WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
378 if (noPosZone->size.width != 0 || noPosZone->size.height != 0) {
379 if (mousePos->x < noPosZone->pos.x || mousePos->x > (noPosZone->pos.x + noPosZone->size.width)
380 || mousePos->y < noPosZone->pos.y || mousePos->y > (noPosZone->pos.y + noPosZone->size.height)) {
381 /* send position if out of zone defined by destination */
382 return sendDnDClientMessage(info, scr->xdndPositionAtom,
384 mousePos->x << 16 | mousePos->y,
385 XDND_TIMESTAMP(info), XDND_SOURCE_ACTION(info));
388 /* Nothing to send, always succeed */
389 return True;
393 /* send position on each move */
394 return sendDnDClientMessage(info, scr->xdndPositionAtom,
396 mousePos->x << 16 | mousePos->y,
397 XDND_TIMESTAMP(info), XDND_SOURCE_ACTION(info));
400 static Bool sendLeaveMessage(WMDraggingInfo * info)
402 WMScreen *scr = sourceScreen(info);
404 return sendDnDClientMessage(info, scr->xdndLeaveAtom, 0, 0, 0, 0);
407 static Bool sendDropMessage(WMDraggingInfo * info)
409 WMScreen *scr = sourceScreen(info);
411 return sendDnDClientMessage(info, scr->xdndDropAtom, 0, XDND_TIMESTAMP(info), 0, 0);
414 /* ----- end of messages ----- */
416 static Atom *getTypeAtomList(WMScreen * scr, WMView * view, int *count)
418 WMArray *types;
419 Atom *typeAtoms;
420 int i;
422 types = view->dragSourceProcs->dropDataTypes(view);
424 if (types != NULL) {
425 *count = WMGetArrayItemCount(types);
426 if (*count > 0) {
427 typeAtoms = wmalloc((*count) * sizeof(Atom));
428 for (i = 0; i < *count; i++) {
429 typeAtoms[i] = XInternAtom(scr->display, WMGetFromArray(types, i), False);
432 /* WMFreeArray(types); */
433 return typeAtoms;
436 /* WMFreeArray(types); */
439 *count = 1;
440 typeAtoms = wmalloc(sizeof(Atom));
441 *typeAtoms = None;
443 return typeAtoms;
446 static void registerDropTypes(WMScreen * scr, WMView * view, WMDraggingInfo * info)
448 Atom *typeList;
449 int i, count;
451 typeList = getTypeAtomList(scr, view, &count);
453 /* store the first 3 types */
454 for (i = 0; i < 3 && i < count; i++)
455 XDND_3_TYPES(info)[i] = typeList[i];
457 for (; i < 3; i++)
458 XDND_3_TYPES(info)[i] = None;
460 /* store the entire type list */
461 XChangeProperty(scr->display,
462 WMViewXID(view),
463 scr->xdndTypeListAtom,
464 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, (unsigned char *)typeList, count);
467 static void registerOperationList(WMScreen * scr, WMView * view, WMArray * operationArray)
469 Atom *actionList;
470 WMDragOperationType operation;
471 int count = WMGetArrayItemCount(operationArray);
472 int i;
474 actionList = wmalloc(sizeof(Atom) * count);
476 for (i = 0; i < count; i++) {
477 operation = WMGetDragOperationItemType(WMGetFromArray(operationArray, i));
478 actionList[i] = W_OperationToAction(scr, operation);
481 XChangeProperty(scr->display,
482 WMViewXID(view),
483 scr->xdndActionListAtom,
484 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, (unsigned char *)actionList, count);
487 static void registerDescriptionList(WMScreen * scr, WMView * view, WMArray * operationArray)
489 char *text, *textListItem, *textList;
490 int count = WMGetArrayItemCount(operationArray);
491 int i;
492 int size = 0;
494 /* size of XA_STRING info */
495 for (i = 0; i < count; i++) {
496 size += strlen(WMGetDragOperationItemText(WMGetFromArray(operationArray, i))) + 1 /* NULL */;
499 /* create text list */
500 textList = wmalloc(size);
501 textListItem = textList;
503 for (i = 0; i < count; i++) {
504 text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
505 wstrlcpy(textListItem, text, size);
507 /* to next text offset */
508 textListItem = &(textListItem[strlen(textListItem) + 1]);
511 XChangeProperty(scr->display,
512 WMViewXID(view),
513 scr->xdndActionDescriptionAtom,
514 XA_STRING,
515 XDND_ACTION_DESCRIPTION_FORMAT, PropModeReplace, (unsigned char *)textList, size);
518 /* called if wanted operation is WDOperationAsk */
519 static void registerSupportedOperations(WMView * view)
521 WMScreen *scr = W_VIEW_SCREEN(view);
522 WMArray *operationArray;
524 operationArray = view->dragSourceProcs->askedOperations(view);
526 registerOperationList(scr, view, operationArray);
527 registerDescriptionList(scr, view, operationArray);
529 /* WMFreeArray(operationArray); */
532 static void initSourceDragInfo(WMView * sourceView, WMDraggingInfo * info)
534 WMRect emptyZone;
536 XDND_SOURCE_INFO(info) = (W_DragSourceInfo *) wmalloc(sizeof(W_DragSourceInfo));
538 XDND_SOURCE_VIEW(info) = sourceView;
539 XDND_DEST_WIN(info) = None;
540 XDND_DRAG_ICON(info) = None;
542 XDND_SOURCE_ACTION(info) = W_OperationToAction(W_VIEW_SCREEN(sourceView),
543 sourceView->dragSourceProcs->
544 wantedDropOperation(sourceView));
546 XDND_DEST_ACTION(info) = None;
548 XDND_SOURCE_STATE(info) = idleState;
550 emptyZone.pos = wmkpoint(0, 0);
551 emptyZone.size = wmksize(0, 0);
552 XDND_NO_POS_ZONE(info) = emptyZone;
556 Returned array is destroyed after dropDataTypes call
558 static WMArray *defDropDataTypes(WMView * self)
560 /* Parameter not used, but tell the compiler that it is ok */
561 (void) self;
563 return NULL;
566 static WMDragOperationType defWantedDropOperation(WMView * self)
568 /* Parameter not used, but tell the compiler that it is ok */
569 (void) self;
571 return WDOperationNone;
575 Must be defined if wantedDropOperation return WDOperationAsk
576 (useless otherwise).
577 Return a WMDragOperationItem array (destroyed after call).
578 A WMDragOperationItem links a label to an operation.
579 static WMArray*
580 defAskedOperations(WMView *self); */
582 static Bool defAcceptDropOperation(WMView * self, WMDragOperationType allowedOperation)
584 /* Parameter not used, but tell the compiler that it is ok */
585 (void) self;
586 (void) allowedOperation;
588 return False;
591 static void defBeganDrag(WMView * self, WMPoint * point)
593 /* Parameter not used, but tell the compiler that it is ok */
594 (void) self;
595 (void) point;
598 static void defEndedDrag(WMView * self, WMPoint * point, Bool deposited)
600 /* Parameter not used, but tell the compiler that it is ok */
601 (void) self;
602 (void) point;
603 (void) deposited;
607 Returned data is not destroyed
609 static WMData *defFetchDragData(WMView * self, char *type)
611 /* Parameter not used, but tell the compiler that it is ok */
612 (void) self;
613 (void) type;
615 return NULL;
618 void WMSetViewDragSourceProcs(WMView * view, WMDragSourceProcs * procs)
620 if (view->dragSourceProcs)
621 wfree(view->dragSourceProcs);
622 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
624 *view->dragSourceProcs = *procs;
626 if (procs->dropDataTypes == NULL)
627 view->dragSourceProcs->dropDataTypes = defDropDataTypes;
629 if (procs->wantedDropOperation == NULL)
630 view->dragSourceProcs->wantedDropOperation = defWantedDropOperation;
633 Note: askedOperations can be NULL, if wantedDropOperation never returns
634 WDOperationAsk.
637 if (procs->acceptDropOperation == NULL)
638 view->dragSourceProcs->acceptDropOperation = defAcceptDropOperation;
640 if (procs->beganDrag == NULL)
641 view->dragSourceProcs->beganDrag = defBeganDrag;
643 if (procs->endedDrag == NULL)
644 view->dragSourceProcs->endedDrag = defEndedDrag;
646 if (procs->fetchDragData == NULL)
647 view->dragSourceProcs->fetchDragData = defFetchDragData;
650 static Bool isXdndAware(WMScreen * scr, Window win)
652 Atom type;
653 int format;
654 unsigned long count, remain;
655 unsigned char *winXdndVersion;
657 if (win == None)
658 return False;
660 XGetWindowProperty(scr->display, win, scr->xdndAwareAtom,
661 0, 1, False, XA_ATOM, &type, &format, &count, &remain, &winXdndVersion);
663 if (type != XA_ATOM || format != XDND_PROPERTY_FORMAT || count == 0 || !winXdndVersion) {
664 if (winXdndVersion)
665 XFree(winXdndVersion);
666 return False;
669 XFree(winXdndVersion);
670 return (count == 1); /* xdnd version is set */
673 static Window *windowChildren(Display * dpy, Window win, unsigned *nchildren)
675 Window *children;
676 Window foo, bar;
678 if (!XQueryTree(dpy, win, &foo, &bar, &children, nchildren)) {
679 *nchildren = 0;
680 return NULL;
681 } else
682 return children;
685 static Window lookForAwareWindow(WMScreen * scr, WMPoint * mousePos, Window win)
687 int tmpx, tmpy;
688 Window child;
690 /* Since xdnd v3, only the toplevel window should be aware */
691 if (isXdndAware(scr, win))
692 return win;
694 /* inspect child under pointer */
695 if (XTranslateCoordinates(scr->display, scr->rootWin, win, mousePos->x, mousePos->y, &tmpx, &tmpy, &child)) {
696 if (child == None)
697 return None;
698 else
699 return lookForAwareWindow(scr, mousePos, child);
702 return None;
705 static Window findDestination(WMDraggingInfo * info, WMPoint * mousePos)
707 WMScreen *scr = sourceScreen(info);
708 unsigned nchildren;
709 Window *children = windowChildren(scr->display, scr->rootWin, &nchildren);
710 int i;
711 XWindowAttributes attr;
713 if (isXdndAware(scr, scr->rootWin))
714 return scr->rootWin;
716 /* exclude drag icon (and upper) from search */
717 for (i = nchildren - 1; i >= 0; i--) {
718 if (children[i] == XDND_DRAG_ICON(info)) {
719 i--;
720 break;
724 if (i < 0) {
725 /* root window has no child under drag icon, and is not xdnd aware. */
726 return None;
729 /* inspecting children, from upper to lower */
730 for (; i >= 0; i--) {
731 if (XGetWindowAttributes(scr->display, children[i], &attr)
732 && attr.map_state == IsViewable
733 && mousePos->x >= attr.x
734 && mousePos->y >= attr.y
735 && mousePos->x < attr.x + attr.width && mousePos->y < attr.y + attr.height) {
736 return lookForAwareWindow(scr, mousePos, children[i]);
740 /* No child window under drag pointer */
741 return None;
744 static void storeDestinationProtocolVersion(WMDraggingInfo * info)
746 Atom type;
747 int format;
748 unsigned long count, remain;
749 unsigned char *winXdndVersion;
750 WMScreen *scr = W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
752 wassertr(XDND_DEST_WIN(info) != None);
754 if (XGetWindowProperty(scr->display, XDND_DEST_WIN(info),
755 scr->xdndAwareAtom,
756 0, 1, False, XA_ATOM, &type, &format,
757 &count, &remain, &winXdndVersion) == Success) {
758 XDND_DEST_VERSION(info) = *winXdndVersion;
759 XFree(winXdndVersion);
760 } else {
761 XDND_DEST_VERSION(info) = 0;
762 wwarning("failed to read XDND version of drop target");
766 static void initMotionProcess(WMView * view, WMDraggingInfo * info, XEvent * event, WMPoint * startLocation)
768 WMScreen *scr = W_VIEW_SCREEN(view);
770 /* take ownership of XdndSelection */
771 XDND_SELECTION_PROCS(info) = (WMSelectionProcs *) wmalloc(sizeof(WMSelectionProcs));
772 XDND_SELECTION_PROCS(info)->convertSelection = convertSelection;
773 XDND_SELECTION_PROCS(info)->selectionLost = selectionLost;
774 XDND_SELECTION_PROCS(info)->selectionDone = selectionDone;
775 XDND_TIMESTAMP(info) = event->xmotion.time;
777 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom, CurrentTime, XDND_SELECTION_PROCS(info), NULL)) {
778 wwarning("could not get ownership or DND selection");
779 return;
782 registerDropTypes(scr, view, info);
784 if (XDND_SOURCE_ACTION(info) == W_VIEW_SCREEN(view)->xdndActionAsk)
785 registerSupportedOperations(view);
787 if (view->dragSourceProcs->beganDrag != NULL) {
788 view->dragSourceProcs->beganDrag(view, startLocation);
792 static void processMotion(WMDraggingInfo * info, WMPoint * mousePos)
794 Window newDestination = findDestination(info, mousePos);
796 W_DragSourceStopTimer();
798 if (newDestination != XDND_DEST_WIN(info)) {
799 recolorCursor(info, False);
801 if (XDND_DEST_WIN(info) != None) {
802 /* leaving a xdnd window */
803 sendLeaveMessage(info);
806 XDND_DEST_WIN(info) = newDestination;
807 XDND_DEST_ACTION(info) = None;
808 XDND_NO_POS_ZONE(info).size.width = 0;
809 XDND_NO_POS_ZONE(info).size.height = 0;
811 if (newDestination != None) {
812 /* entering a xdnd window */
813 XDND_SOURCE_STATE(info) = idleState;
814 storeDestinationProtocolVersion(info);
816 if (!sendEnterMessage(info)) {
817 XDND_DEST_WIN(info) = None;
818 return;
821 W_DragSourceStartTimer(info);
822 } else {
823 XDND_SOURCE_STATE(info) = NULL;
825 } else {
826 if (XDND_DEST_WIN(info) != None) {
827 if (!sendPositionMessage(info, mousePos)) {
828 XDND_DEST_WIN(info) = None;
829 return;
832 W_DragSourceStartTimer(info);
837 static Bool processButtonRelease(WMDraggingInfo * info)
839 if (XDND_SOURCE_STATE(info) == dropAllowedState) {
840 /* begin drop */
841 W_DragSourceStopTimer();
843 if (!sendDropMessage(info))
844 return False;
846 W_DragSourceStartTimer(info);
847 return True;
848 } else {
849 if (XDND_DEST_WIN(info) != None)
850 sendLeaveMessage(info);
852 W_DragSourceStopTimer();
853 return False;
857 Bool WMIsDraggingFromView(WMView * view)
859 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
861 return (XDND_SOURCE_INFO(info) != NULL && XDND_SOURCE_STATE(info) != finishDropState);
862 /* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
865 void WMDragImageFromView(WMView * view, XEvent * event)
867 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
868 WMPoint mouseLocation;
870 switch (event->type) {
871 case ButtonPress:
872 if (event->xbutton.button == Button1) {
873 XEvent nextEvent;
875 XPeekEvent(event->xbutton.display, &nextEvent);
877 /* Initialize only if a drag really begins (avoid clicks) */
878 if (nextEvent.type == MotionNotify) {
879 initSourceDragInfo(view, info);
883 break;
885 case ButtonRelease:
886 if (WMIsDraggingFromView(view)) {
887 Bool dropBegan = processButtonRelease(info);
889 recolorCursor(info, False);
890 if (dropBegan) {
891 endDragImage(info, False);
892 XDND_SOURCE_STATE(info) = finishDropState;
893 } else {
894 /* drop failed */
895 endDragImage(info, True);
896 endDragProcess(info, False);
899 break;
901 case MotionNotify:
902 if (WMIsDraggingFromView(view)) {
903 mouseLocation = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
905 if (abs(XDND_DRAG_ICON_POS(info).x - mouseLocation.x) >=
906 MIN_X_MOVE_OFFSET
907 || abs(XDND_DRAG_ICON_POS(info).y - mouseLocation.y) >= MIN_Y_MOVE_OFFSET) {
908 if (XDND_DRAG_ICON(info) == None) {
909 initMotionProcess(view, info, event, &mouseLocation);
910 startDragImage(view, info, event);
911 } else {
912 XDND_DRAG_ICON_POS(info).x = mouseLocation.x - XDND_MOUSE_OFFSET(info).x;
913 XDND_DRAG_ICON_POS(info).y = mouseLocation.y - XDND_MOUSE_OFFSET(info).y;
915 refreshDragImage(view, info);
916 processMotion(info, &mouseLocation);
920 break;
924 /* Minimal mouse events handler: no right or double-click detection,
925 only drag is supported */
926 static void dragImageHandler(XEvent * event, void *cdata)
928 WMView *view = (WMView *) cdata;
930 WMDragImageFromView(view, event);
933 /* ----- source states ----- */
935 #ifdef XDND_DEBUG
936 static void traceStatusMsg(Display * dpy, XClientMessageEvent * statusEvent)
938 printf("Xdnd status message:\n");
940 if (statusEvent->data.l[1] & 0x2UL)
941 printf("\tsend position on every move\n");
942 else {
943 int x, y, w, h;
944 x = statusEvent->data.l[2] >> 16;
945 y = statusEvent->data.l[2] & 0xFFFFL;
946 w = statusEvent->data.l[3] >> 16;
947 h = statusEvent->data.l[3] & 0xFFFFL;
949 printf("\tsend position out of ((%d,%d) , (%d,%d))\n", x, y, x + w, y + h);
952 if (statusEvent->data.l[1] & 0x1L)
953 printf("\tallowed action: %s\n", XGetAtomName(dpy, statusEvent->data.l[4]));
954 else
955 printf("\tno action allowed\n");
957 #endif
959 static void storeDropAction(WMDraggingInfo * info, Atom destAction)
961 WMView *sourceView = XDND_SOURCE_VIEW(info);
962 WMScreen *scr = W_VIEW_SCREEN(sourceView);
964 if (sourceView->dragSourceProcs->acceptDropOperation != NULL) {
965 if (sourceView->dragSourceProcs->acceptDropOperation(sourceView,
966 W_ActionToOperation(scr, destAction)))
967 XDND_DEST_ACTION(info) = destAction;
968 else
969 XDND_DEST_ACTION(info) = None;
970 } else {
971 XDND_DEST_ACTION(info) = destAction;
975 static void storeStatusMessageInfos(WMDraggingInfo * info, XClientMessageEvent * statusEvent)
977 WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
979 #ifdef XDND_DEBUG
981 traceStatusMsg(sourceScreen(info)->display, statusEvent);
982 #endif
984 if (statusEvent->data.l[1] & 0x2UL) {
985 /* bit 1 set: destination wants position messages on every move */
986 noPosZone->size.width = 0;
987 noPosZone->size.height = 0;
988 } else {
989 /* don't send another position message while in given rectangle */
990 noPosZone->pos.x = statusEvent->data.l[2] >> 16;
991 noPosZone->pos.y = statusEvent->data.l[2] & 0xFFFFL;
992 noPosZone->size.width = statusEvent->data.l[3] >> 16;
993 noPosZone->size.height = statusEvent->data.l[3] & 0xFFFFL;
996 if ((statusEvent->data.l[1] & 0x1L) || statusEvent->data.l[4] != None) {
997 /* destination accept drop */
998 storeDropAction(info, statusEvent->data.l[4]);
999 } else {
1000 XDND_DEST_ACTION(info) = None;
1004 static void *idleState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
1006 WMScreen *scr;
1007 Atom destMsg = event->message_type;
1009 scr = W_VIEW_SCREEN(view);
1011 if (destMsg == scr->xdndStatusAtom) {
1012 storeStatusMessageInfos(info, event);
1014 if (XDND_DEST_ACTION(info) != None) {
1015 recolorCursor(info, True);
1016 W_DragSourceStartTimer(info);
1017 return dropAllowedState;
1018 } else {
1019 /* drop denied */
1020 recolorCursor(info, False);
1021 return idleState;
1025 if (destMsg == scr->xdndFinishedAtom) {
1026 wwarning("received xdndFinishedAtom before drop began");
1029 W_DragSourceStartTimer(info);
1030 return idleState;
1033 static void *dropAllowedState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
1035 WMScreen *scr = W_VIEW_SCREEN(view);
1036 Atom destMsg = event->message_type;
1038 if (destMsg == scr->xdndStatusAtom) {
1039 storeStatusMessageInfos(info, event);
1041 if (XDND_DEST_ACTION(info) == None) {
1042 /* drop denied */
1043 recolorCursor(info, False);
1044 return idleState;
1048 W_DragSourceStartTimer(info);
1049 return dropAllowedState;
1052 static void *finishDropState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
1054 WMScreen *scr = W_VIEW_SCREEN(view);
1055 Atom destMsg = event->message_type;
1057 if (destMsg == scr->xdndFinishedAtom) {
1058 endDragProcess(info, True);
1059 return NULL;
1062 W_DragSourceStartTimer(info);
1063 return finishDropState;
1066 /* ----- end of source states ----- */
1068 /* ----- source timer ----- */
1069 static void dragSourceResponseTimeOut(void *source)
1071 WMView *view = (WMView *) source;
1072 WMDraggingInfo *info = &(W_VIEW_SCREEN(view)->dragInfo);
1074 wwarning("delay for drag destination response expired");
1075 sendLeaveMessage(info);
1077 recolorCursor(info, False);
1078 if (XDND_SOURCE_STATE(info) == finishDropState) {
1079 /* drop failed */
1080 endDragImage(info, True);
1081 endDragProcess(info, False);
1082 } else {
1083 XDND_SOURCE_STATE(info) = idleState;
1087 void W_DragSourceStopTimer()
1089 if (dndSourceTimer != NULL) {
1090 WMDeleteTimerHandler(dndSourceTimer);
1091 dndSourceTimer = NULL;
1095 void W_DragSourceStartTimer(WMDraggingInfo * info)
1097 W_DragSourceStopTimer();
1099 dndSourceTimer = WMAddTimerHandler(XDND_DESTINATION_RESPONSE_MAX_DELAY,
1100 dragSourceResponseTimeOut, XDND_SOURCE_VIEW(info));
1103 /* ----- End of Destination timer ----- */
1105 void W_DragSourceStateHandler(WMDraggingInfo * info, XClientMessageEvent * event)
1107 WMView *view;
1108 W_DndState *newState;
1110 if (XDND_SOURCE_VIEW_STORED(info)) {
1111 if (XDND_SOURCE_STATE(info) != NULL) {
1112 view = XDND_SOURCE_VIEW(info);
1113 #ifdef XDND_DEBUG
1115 printf("current source state: %s\n", stateName(XDND_SOURCE_STATE(info)));
1116 #endif
1118 newState = (W_DndState *) XDND_SOURCE_STATE(info) (view, event, info);
1120 #ifdef XDND_DEBUG
1122 printf("new source state: %s\n", stateName(newState));
1123 #endif
1125 if (newState != NULL)
1126 XDND_SOURCE_STATE(info) = newState;
1127 /* else drop finished, and info has been flushed */
1130 } else {
1131 wwarning("received DnD message without having a target");
1135 void WMSetViewDragImage(WMView * view, WMPixmap * dragImage)
1137 if (view->dragImage != NULL)
1138 WMReleasePixmap(view->dragImage);
1140 view->dragImage = WMRetainPixmap(dragImage);
1143 void WMReleaseViewDragImage(WMView * view)
1145 if (view->dragImage != NULL)
1146 WMReleasePixmap(view->dragImage);
1149 /* Create a drag handler, associating drag event masks with dragEventProc */
1150 void WMCreateDragHandler(WMView * view, WMEventProc * dragEventProc, void *clientData)
1152 WMCreateEventHandler(view,
1153 ButtonPressMask | ButtonReleaseMask | Button1MotionMask, dragEventProc, clientData);
1156 void WMDeleteDragHandler(WMView * view, WMEventProc * dragEventProc, void *clientData)
1158 WMDeleteEventHandler(view,
1159 ButtonPressMask | ButtonReleaseMask | Button1MotionMask, dragEventProc, clientData);
1162 /* set default drag handler for view */
1163 void WMSetViewDraggable(WMView * view, WMDragSourceProcs * dragSourceProcs, WMPixmap * dragImage)
1165 wassertr(dragImage != NULL);
1166 view->dragImage = WMRetainPixmap(dragImage);
1168 WMSetViewDragSourceProcs(view, dragSourceProcs);
1170 WMCreateDragHandler(view, dragImageHandler, view);
1173 void WMUnsetViewDraggable(WMView * view)
1175 if (view->dragSourceProcs) {
1176 wfree(view->dragSourceProcs);
1177 view->dragSourceProcs = NULL;
1180 WMReleaseViewDragImage(view);
1182 WMDeleteDragHandler(view, dragImageHandler, view);