- Added xdnd v3 support in WINGs (Sylvain Reynal <sreynal@nerim.net>)
[wmaker-crm.git] / WINGs / dragdestination.c
blobe982dacfd21ad8924179c4a3d306c5845493e21b
1 #include <X11/Xatom.h>
2 #include "WINGsP.h"
4 #define XDND_SOURCE_RESPONSE_MAX_DELAY 3000
6 #define VERSION_INFO(dragInfo) dragInfo->protocolVersion
8 #define XDND_PROPERTY_FORMAT 32
9 #define XDND_ACTION_DESCRIPTION_FORMAT 8
11 #define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
12 #define XDND_SOURCE_WIN(dragInfo) dragInfo->destInfo->sourceWindow
13 #define XDND_DEST_VIEW(dragInfo) dragInfo->destInfo->destView
14 #define XDND_DEST_STATE(dragInfo) dragInfo->destInfo->state
15 #define XDND_SOURCE_TYPES(dragInfo) dragInfo->destInfo->sourceTypes
16 #define XDND_TYPE_LIST_AVAILABLE(dragInfo) dragInfo->destInfo->typeListAvailable
17 #define XDND_REQUIRED_TYPES(dragInfo) dragInfo->destInfo->requiredTypes
18 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
19 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
20 #define XDND_SOURCE_OPERATIONS(dragInfo) dragInfo->destInfo->sourceOperations
21 #define XDND_DROP_DATAS(dragInfo) dragInfo->destInfo->dropDatas
22 #define XDND_DROP_DATA_COUNT(dragInfo) dragInfo->destInfo->dropDataCount
23 #define XDND_DEST_VIEW_STORED(dragInfo) ((dragInfo->destInfo) != NULL)\
24 && ((dragInfo->destInfo->destView) != NULL)
27 static unsigned char XDNDversion = XDND_VERSION;
28 static WMHandlerID dndDestinationTimer = NULL;
31 static void* idleState(WMView *destView, XClientMessageEvent *event,
32 WMDraggingInfo *info);
33 static void* waitEnterState(WMView *destView, XClientMessageEvent *event,
34 WMDraggingInfo *info);
35 static void* inspectDropDataState(WMView *destView, XClientMessageEvent *event,
36 WMDraggingInfo *info);
37 static void* dropAllowedState(WMView *destView, XClientMessageEvent *event,
38 WMDraggingInfo *info);
39 static void* dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
40 WMDraggingInfo *info);
41 static void* waitForDropDataState(WMView *destView, XClientMessageEvent *event,
42 WMDraggingInfo *info);
44 /* ----- Types & datas list ----- */
45 static void
46 freeSourceTypeArrayItem(void *type)
48 XFree(type);
52 static WMArray*
53 createSourceTypeArray(int initialSize)
55 return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
59 static void
60 freeDropDataArrayItem(void* data)
62 if (data != NULL)
63 WMReleaseData((WMData*) data);
67 static WMArray*
68 createDropDataArray(WMArray *requiredTypes)
70 if (requiredTypes != NULL)
71 return WMCreateArrayWithDestructor(
72 WMGetArrayItemCount(requiredTypes),
73 freeDropDataArrayItem);
75 else
76 return WMCreateArray(0);
79 static WMArray*
80 getTypesFromTypeList(WMScreen *scr, Window sourceWin)
82 WMDraggingInfo *info = &scr->dragInfo;
83 Atom dataType;
84 Atom* typeAtomList;
85 WMArray* typeList;
86 int i, format;
87 unsigned long count, remaining;
88 unsigned char *data = NULL;
90 XGetWindowProperty(scr->display, sourceWin, scr->xdndTypeListAtom,
91 0, 0x8000000L, False, XA_ATOM, &dataType, &format,
92 &count, &remaining, &data);
94 if (dataType!=XA_ATOM || format!=XDND_PROPERTY_FORMAT || count==0 || !data) {
95 if (data) {
96 XFree(data);
98 return createSourceTypeArray(0);
101 typeList = createSourceTypeArray(count);
102 typeAtomList = (Atom*) data;
103 for (i=0; i < count; i++) {
104 WMAddToArray(typeList, XGetAtomName(scr->display, typeAtomList[i]));
107 XFree(data);
109 return typeList;
113 static WMArray*
114 getTypesFromThreeTypes(WMScreen *scr, XClientMessageEvent *event)
116 WMArray* typeList;
117 Atom atom;
118 int i;
120 typeList = createSourceTypeArray(3);
121 for (i = 2; i < 5; i++) {
122 if (event->data.l[i] != None) {
123 atom = (Atom)event->data.l[i];
124 WMAddToArray(typeList, XGetAtomName(scr->display, atom));
128 return typeList;
132 void
133 storeRequiredTypeList(WMDraggingInfo *info)
135 WMView *destView = XDND_DEST_VIEW(info);
136 WMScreen *scr = W_VIEW_SCREEN(destView);
137 WMArray *requiredTypes;
139 /* First, see if the 3 source types are enough for dest requirements */
140 requiredTypes = destView->dragDestinationProcs->requiredDataTypes(
141 destView,
142 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
143 XDND_SOURCE_TYPES(info));
145 if (requiredTypes == NULL && XDND_TYPE_LIST_AVAILABLE(info)) {
146 /* None of the 3 source types fits, get the whole type list */
147 requiredTypes =
148 destView->dragDestinationProcs->requiredDataTypes(
149 destView,
150 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
151 getTypesFromTypeList(scr, XDND_SOURCE_WIN(info)));
155 XDND_REQUIRED_TYPES(info) = requiredTypes;
159 char*
160 getNextRequestedDataType(WMDraggingInfo *info)
162 /* get the type of the first data not yet retrieved from selection */
163 int nextTypeIndex;
165 if (XDND_REQUIRED_TYPES(info) != NULL) {
166 nextTypeIndex = WMGetArrayItemCount(XDND_DROP_DATAS(info));
167 return WMGetFromArray(XDND_REQUIRED_TYPES(info), nextTypeIndex);
168 /* NULL if no more type */
169 } else
170 return NULL;
174 /* ----- Action list ----- */
176 WMArray*
177 sourceOperationList(WMScreen *scr, Window sourceWin)
179 Atom dataType, *actionList;
180 int i, size;
181 unsigned long count, remaining;
182 unsigned char* actionDatas = NULL;
183 unsigned char* descriptionList = NULL;
184 WMArray* operationArray;
185 WMDragOperationItem* operationItem;
186 char* description;
188 remaining = 0;
189 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionListAtom,
190 0, 0x8000000L, False, XA_ATOM, &dataType, &size,
191 &count, &remaining, &actionDatas);
193 if (dataType!=XA_ATOM || size!=XDND_PROPERTY_FORMAT || count==0 || !actionDatas) {
194 wwarning("Cannot read action list");
195 if (actionDatas) {
196 XFree(actionDatas);
198 return NULL;
201 actionList = (Atom*)actionDatas;
203 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionDescriptionAtom,
204 0, 0x8000000L, False, XA_STRING, &dataType, &size,
205 &count, &remaining, &descriptionList);
207 if (dataType!=XA_STRING || size!=XDND_ACTION_DESCRIPTION_FORMAT ||
208 count==0 || !descriptionList) {
209 wwarning("Cannot read action description list");
210 if (actionList) {
211 XFree(actionList);
213 if (descriptionList) {
214 XFree(descriptionList);
216 return NULL;
219 operationArray = WMCreateDragOperationArray(count);
220 description = descriptionList;
222 for (i=0; count > 0; i++) {
223 size = strlen(description);
224 operationItem = WMCreateDragOperationItem(
225 W_ActionToOperation(scr, actionList[i]),
226 wstrdup(description));
228 WMAddToArray(operationArray, operationItem);
229 count -= (size + 1); /* -1 : -NULL char */
231 /* next description */
232 description = &(description[size + 1]);
235 XFree(actionList);
236 XFree(descriptionList);
238 return operationArray;
242 /* ----- Dragging Info ----- */
243 static void
244 updateSourceWindow(WMDraggingInfo *info, XClientMessageEvent *event)
246 XDND_SOURCE_WIN(info) = (Window) event->data.l[0];
250 static Window
251 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
253 Window foo, bar;
254 Window *children;
255 unsigned nchildren;
256 int i;
258 if (!XQueryTree(dpy, toplevel, &foo, &bar,
259 &children, &nchildren) || children == NULL) {
260 return None;
263 /* first window that contains the point is the one */
264 for (i = nchildren-1; i >= 0; i--) {
265 XWindowAttributes attr;
267 if (XGetWindowAttributes(dpy, children[i], &attr)
268 && attr.map_state == IsViewable
269 && x >= attr.x && y >= attr.y
270 && x < attr.x + attr.width && y < attr.y + attr.height) {
271 Window child, tmp;
273 tmp = children[i];
274 child = findChildInWindow(dpy, tmp, x - attr.x, y - attr.y);
275 XFree(children);
277 if (child == None)
278 return tmp;
279 else
280 return child;
284 XFree(children);
285 return None;
289 static WMView*
290 findXdndViewInToplevel(WMView* toplevel, int x, int y)
292 WMScreen *scr = W_VIEW_SCREEN(toplevel);
293 Window toplevelWin = WMViewXID(toplevel);
294 int xInToplevel, yInToplevel;
295 Window child, foo;
296 WMView *childView;
298 XTranslateCoordinates(scr->display, scr->rootWin, toplevelWin,
299 x, y, &xInToplevel, &yInToplevel,
300 &foo);
302 child = findChildInWindow(scr->display, toplevelWin,
303 xInToplevel, yInToplevel);
305 if (child != None) {
306 childView = W_GetViewForXWindow(scr->display, child);
308 /* if childView supports Xdnd, return childView */
309 if (childView != NULL
310 && childView->dragDestinationProcs != NULL)
311 return childView;
314 return NULL;
318 /* Clear datas only used by current destination view */
319 static void
320 freeDestinationViewInfos(WMDraggingInfo *info)
322 if (XDND_SOURCE_TYPES(info) != NULL) {
323 WMFreeArray(XDND_SOURCE_TYPES(info));
324 XDND_SOURCE_TYPES(info) = NULL;
327 if (XDND_DROP_DATAS(info) != NULL) {
328 WMFreeArray(XDND_DROP_DATAS(info));
329 XDND_DROP_DATAS(info) = NULL;
332 XDND_REQUIRED_TYPES(info) = NULL;
335 void
336 W_DragDestinationInfoClear(WMDraggingInfo *info)
338 W_DragDestinationStopTimer();
340 if (XDND_DEST_INFO(info) != NULL) {
341 freeDestinationViewInfos(info);
343 wfree(XDND_DEST_INFO(info));
344 XDND_DEST_INFO(info) = NULL;
348 static void
349 initDestinationDragInfo(WMDraggingInfo *info)
351 XDND_DEST_INFO(info) =
352 (W_DragDestinationInfo*) wmalloc(sizeof(W_DragDestinationInfo));
354 XDND_DEST_STATE(info) = idleState;
355 XDND_DEST_VIEW(info) = NULL;
357 XDND_SOURCE_TYPES(info) = NULL;
358 XDND_REQUIRED_TYPES(info) = NULL;
359 XDND_DROP_DATAS(info) = NULL;
363 void
364 W_DragDestinationStoreEnterMsgInfo(WMDraggingInfo *info,
365 WMView *toplevel, XClientMessageEvent *event)
367 WMScreen *scr = W_VIEW_SCREEN(toplevel);
368 int i,j, typesCount;
370 if (XDND_DEST_INFO(info) == NULL)
371 initDestinationDragInfo(info);
373 updateSourceWindow(info, event);
375 /* store xdnd version for source */
376 info->protocolVersion = (event->data.l[1] >> 24);
378 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
380 /* to use if the 3 types are not enough */
381 XDND_TYPE_LIST_AVAILABLE(info) = (event->data.l[1] & 1);
385 static void
386 cancelDrop(WMView *destView, WMDraggingInfo *info);
388 static void
389 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info);
392 void
393 W_DragDestinationStorePositionMsgInfo(WMDraggingInfo *info,
394 WMView *toplevel, XClientMessageEvent *event)
396 int x = event->data.l[2] >> 16;
397 int y = event->data.l[2] & 0xffff;
398 WMView *oldDestView;
399 WMView *newDestView;
401 newDestView = findXdndViewInToplevel(toplevel, x, y);
403 if (XDND_DEST_INFO(info) == NULL) {
404 initDestinationDragInfo(info);
405 updateSourceWindow(info, event);
406 XDND_DEST_VIEW(info) = newDestView;
408 else {
409 oldDestView = XDND_DEST_VIEW(info);
411 if (newDestView != oldDestView) {
412 if (oldDestView != NULL) {
413 suspendDropAuthorization(oldDestView, info);
414 XDND_DEST_STATE(info) = dropNotAllowedState;
417 updateSourceWindow(info, event);
418 XDND_DEST_VIEW(info) = newDestView;
420 if (newDestView != NULL) {
421 if (XDND_DEST_STATE(info) != waitEnterState)
422 XDND_DEST_STATE(info) = idleState;
427 XDND_SOURCE_ACTION(info) = event->data.l[4];
429 /* note: source position is not stored */
432 /* ----- End of Dragging Info ----- */
435 /* ----- Messages ----- */
437 /* send a DnD message to the source window */
438 static void
439 sendDnDClientMessage(WMView *destView, Atom message,
440 unsigned long data1,
441 unsigned long data2,
442 unsigned long data3,
443 unsigned long data4)
445 WMScreen *scr = W_VIEW_SCREEN(destView);
446 WMDraggingInfo *info = &scr->dragInfo;
448 if (XDND_DEST_INFO(info) != NULL) {
449 if (! W_SendDnDClientMessage(scr->display,
450 XDND_SOURCE_WIN(info),
451 message,
452 WMViewXID(destView),
453 data1,
454 data2,
455 data3,
456 data4)) {
457 /* drop failed */
458 W_DragDestinationInfoClear(info);
464 static void
465 storeDropData(WMView *destView, Atom selection, Atom target,
466 Time timestamp, void *cdata, WMData *data)
468 WMScreen *scr = W_VIEW_SCREEN(destView);
469 WMDraggingInfo *info = &(scr->dragInfo);
470 WMData *dataToStore = NULL;
472 if (data != NULL)
473 dataToStore = WMRetainData(data);
475 if (XDND_DEST_INFO(info) != NULL && XDND_DROP_DATAS(info) != NULL) {
476 WMAddToArray(XDND_DROP_DATAS(info), dataToStore);
477 W_SendDnDClientMessage(scr->display, WMViewXID(destView),
478 scr->xdndSelectionAtom,
479 WMViewXID(destView),
480 0, 0, 0, 0);
485 Bool
486 requestDropDataInSelection(WMView *destView, char* type)
488 WMScreen *scr = W_VIEW_SCREEN(destView);
490 if (type != NULL) {
491 if (!WMRequestSelection(destView,
492 scr->xdndSelectionAtom,
493 XInternAtom(scr->display, type, False),
494 CurrentTime,
495 storeDropData, NULL)) {
496 wwarning("could not request data for dropped data");
497 return False;
500 return True;
503 return False;
507 Bool
508 requestDropData(WMDraggingInfo *info)
510 WMView *destView = XDND_DEST_VIEW(info);
511 char* nextType = getNextRequestedDataType(info);
513 while ((nextType != NULL)
514 && (!requestDropDataInSelection(destView, nextType)) ) {
515 /* store NULL if request failed, and try with next type */
516 WMAddToArray(XDND_DROP_DATAS(info), NULL);
517 nextType = getNextRequestedDataType(info);
520 /* remains types to retrieve ? */
521 return (nextType != NULL);
525 static void
526 concludeDrop(WMView *destView)
528 destView->dragDestinationProcs->concludeDragOperation(destView);
532 /* send cancel message to the source */
533 static void
534 cancelDrop(WMView *destView, WMDraggingInfo *info)
536 /* send XdndStatus with action None */
537 sendDnDClientMessage(destView,
538 W_VIEW_SCREEN(destView)->xdndStatusAtom,
539 0, 0, 0, None);
540 concludeDrop(destView);
541 freeDestinationViewInfos(info);
545 /* suspend drop, when dragged icon enter an unaware subview of destView */
546 static void
547 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info)
549 /* free datas that depend on destination behaviour */
550 /* (in short: only keep source's types) */
551 if (XDND_DROP_DATAS(info) != NULL) {
552 WMFreeArray(XDND_DROP_DATAS(info));
553 XDND_DROP_DATAS(info) = NULL;
555 XDND_REQUIRED_TYPES(info) = NULL;
557 /* send XdndStatus with action None */
558 sendDnDClientMessage(destView,
559 W_VIEW_SCREEN(destView)->xdndStatusAtom,
560 0, 0, 0, None);
564 /* cancel drop on Enter message, if protocol version is nok */
565 void
566 W_DragDestinationCancelDropOnEnter(WMView *toplevel, WMDraggingInfo *info)
568 if (XDND_DEST_VIEW_STORED(info))
569 cancelDrop(XDND_DEST_VIEW(info), info);
570 else {
571 /* send XdndStatus with action None */
572 sendDnDClientMessage(toplevel,
573 W_VIEW_SCREEN(toplevel)->xdndStatusAtom,
574 0, 0, 0, None);
577 W_DragDestinationInfoClear(info);
581 static void
582 finishDrop(WMView *destView, WMDraggingInfo *info)
584 sendDnDClientMessage(destView,
585 W_VIEW_SCREEN(destView)->xdndFinishedAtom,
586 0, 0, 0, 0);
587 concludeDrop(destView);
588 W_DragDestinationInfoClear(info);
592 static Atom
593 getAllowedAction(WMView *destView, WMDraggingInfo *info)
595 WMScreen *scr = W_VIEW_SCREEN(destView);
597 return W_OperationToAction(scr,
598 destView->dragDestinationProcs->allowedOperation(
599 destView,
600 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
601 XDND_SOURCE_TYPES(info)));
605 /* send the action that can be performed,
606 and the limits outside wich the source must re-send
607 its position and action */
608 static void
609 sendAllowedAction(WMView *destView, Atom action)
611 WMScreen *scr = W_VIEW_SCREEN(destView);
612 WMPoint destPos = WMGetViewScreenPosition(destView);
613 WMSize destSize = WMGetViewSize(destView);
614 int destX, destY;
615 Window foo;
617 XTranslateCoordinates(scr->display, scr->rootWin, WMViewXID(destView),
618 0, 0, &destX, &destY,
619 &foo);
621 sendDnDClientMessage(destView,
622 scr->xdndStatusAtom,
624 (destX << 16)|destY,
625 (destSize.width << 16)|destSize.height,
626 action);
630 static void*
631 checkActionAllowed(WMView *destView, WMDraggingInfo* info)
633 XDND_DEST_ACTION(info) =
634 getAllowedAction(destView, info);
636 if (XDND_DEST_ACTION(info) == None) {
637 suspendDropAuthorization(destView, info);
638 return dropNotAllowedState;
641 sendAllowedAction(destView, XDND_DEST_ACTION(info));
642 return dropAllowedState;
645 static void*
646 checkDropAllowed(WMView *destView, XClientMessageEvent *event,
647 WMDraggingInfo* info)
649 storeRequiredTypeList(info);
651 if (destView->dragDestinationProcs->inspectDropData != NULL) {
652 XDND_DROP_DATAS(info) = createDropDataArray(
653 XDND_REQUIRED_TYPES(info));
655 /* store first available data */
656 if (requestDropData(info))
657 return inspectDropDataState;
659 /* no data retrieved, but inspect can allow it */
660 if (destView->dragDestinationProcs->inspectDropData(
661 destView,
662 XDND_DROP_DATAS(info)))
663 return checkActionAllowed(destView, info);
665 suspendDropAuthorization(destView, info);
666 return dropNotAllowedState;
669 return checkActionAllowed(destView, info);
672 static WMPoint*
673 getDropLocationInView(WMView *view)
675 Window rootWin, childWin;
676 int rootX, rootY;
677 unsigned int mask;
678 WMPoint* location;
680 location = (WMPoint*) wmalloc(sizeof(WMPoint));
682 XQueryPointer(
683 W_VIEW_SCREEN(view)->display,
684 WMViewXID(view), &rootWin, &childWin,
685 &rootX, &rootY,
686 &(location->x), &(location->y),
687 &mask);
689 return location;
692 static void
693 callPerformDragOperation(WMView *destView, WMDraggingInfo *info)
695 WMArray *operationList = NULL;
696 WMScreen *scr = W_VIEW_SCREEN(destView);
697 WMPoint* dropLocation;
699 if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
700 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
702 dropLocation = getDropLocationInView(destView);
703 destView->dragDestinationProcs->performDragOperation(
704 destView,
705 XDND_DROP_DATAS(info),
706 operationList,
707 dropLocation);
709 wfree(dropLocation);
710 if (operationList != NULL)
711 WMFreeArray(operationList);
715 /* ----- Destination timer ----- */
716 static void
717 dragSourceResponseTimeOut(void *destView)
719 WMView *view = (WMView*) destView;
720 WMDraggingInfo *info;
722 wwarning("delay for drag source response expired");
723 if (view != NULL) {
724 info = &(W_VIEW_SCREEN(view)->dragInfo);
725 if (XDND_DEST_VIEW_STORED(info))
726 cancelDrop(view, info);
727 else {
728 /* send XdndStatus with action None */
729 sendDnDClientMessage(view,
730 W_VIEW_SCREEN(view)->xdndStatusAtom,
731 0, 0, 0, None);
734 W_DragDestinationInfoClear(info);
738 void
739 W_DragDestinationStopTimer()
741 if (dndDestinationTimer != NULL) {
742 WMDeleteTimerHandler(dndDestinationTimer);
743 dndDestinationTimer = NULL;
747 void
748 W_DragDestinationStartTimer(WMDraggingInfo *info)
750 W_DragDestinationStopTimer();
752 if (XDND_DEST_STATE(info) != idleState
753 || XDND_DEST_VIEW(info) == NULL) {
754 /* note: info->destView == NULL means :
755 Enter message has been received, waiting for Position message */
757 dndDestinationTimer = WMAddTimerHandler(
758 XDND_SOURCE_RESPONSE_MAX_DELAY,
759 dragSourceResponseTimeOut,
760 XDND_DEST_VIEW(info));
763 /* ----- End of Destination timer ----- */
766 /* ----- Destination states ----- */
768 #ifdef XDND_DEBUG
769 static const char*
770 stateName(W_DndState *state)
772 if (state == NULL)
773 return "no state defined";
775 if (state == idleState)
776 return "idleState";
778 if (state == waitEnterState)
779 return "waitEnterState";
781 if (state == inspectDropDataState)
782 return "inspectDropDataState";
784 if (state == dropAllowedState)
785 return "dropAllowedState";
787 if (state == dropNotAllowedState)
788 return "dropNotAllowedState";
790 if (state == waitForDropDataState)
791 return "waitForDropDataState";
793 return "unknown state";
795 #endif
797 static void*
798 idleState(WMView *destView, XClientMessageEvent *event,
799 WMDraggingInfo *info)
801 WMScreen *scr;
802 Atom sourceMsg;
804 scr = W_VIEW_SCREEN(destView);
805 sourceMsg = event->message_type;
807 if (sourceMsg == scr->xdndPositionAtom) {
808 destView->dragDestinationProcs->prepareForDragOperation(destView);
810 if (XDND_SOURCE_TYPES(info) != NULL) {
811 /* enter message infos are available */
812 return checkDropAllowed(destView, event, info);
815 /* waiting for enter message */
816 return waitEnterState;
819 return idleState;
823 /* Source position and action are stored,
824 waiting for xdnd protocol version and source type */
825 static void*
826 waitEnterState(WMView *destView, XClientMessageEvent *event,
827 WMDraggingInfo *info)
829 WMScreen *scr = W_VIEW_SCREEN(destView);
830 Atom sourceMsg = event->message_type;
832 if (sourceMsg == scr->xdndEnterAtom) {
833 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
834 return checkDropAllowed(destView, event, info);
837 return waitEnterState;
841 /* We have requested a data, and have received it */
842 static void*
843 inspectDropDataState(WMView *destView, XClientMessageEvent *event,
844 WMDraggingInfo *info)
846 WMScreen *scr;
847 Atom sourceMsg;
849 scr = W_VIEW_SCREEN(destView);
850 sourceMsg = event->message_type;
852 if (sourceMsg == scr->xdndSelectionAtom) {
853 /* a data has been retrieved, store next available */
854 if (requestDropData(info))
855 return inspectDropDataState;
857 /* all required (and available) datas are stored */
858 if (destView->dragDestinationProcs->inspectDropData(
859 destView,
860 XDND_DROP_DATAS(info)))
861 return checkActionAllowed(destView, info);
863 suspendDropAuthorization(destView, info);
864 return dropNotAllowedState;
867 return inspectDropDataState;
871 static void*
872 dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
873 WMDraggingInfo *info)
875 WMScreen *scr = W_VIEW_SCREEN(destView);
876 Atom sourceMsg = event->message_type;
878 if (sourceMsg == scr->xdndDropAtom) {
879 finishDrop(destView, info);
880 return idleState;
883 return dropNotAllowedState;
887 static void*
888 dropAllowedState(WMView *destView, XClientMessageEvent *event,
889 WMDraggingInfo *info)
891 WMScreen *scr = W_VIEW_SCREEN(destView);
892 Atom sourceMsg = event->message_type;
894 if (sourceMsg == scr->xdndDropAtom) {
895 if (XDND_DROP_DATAS(info) != NULL) {
896 /* drop datas were cached with inspectDropData call */
897 callPerformDragOperation(destView, info);
898 } else {
899 XDND_DROP_DATAS(info) = createDropDataArray(
900 XDND_REQUIRED_TYPES(info));
902 /* store first available data */
903 if (requestDropData(info))
904 return waitForDropDataState;
906 /* no data retrieved */
907 callPerformDragOperation(destView, info);
910 finishDrop(destView, info);
911 return idleState;
914 return dropAllowedState;
918 static void*
919 waitForDropDataState(WMView *destView, XClientMessageEvent *event,
920 WMDraggingInfo *info)
922 WMScreen *scr = W_VIEW_SCREEN(destView);
923 Atom sourceMsg = event->message_type;
925 if (sourceMsg == scr->xdndSelectionAtom) {
926 /* store next data */
927 if (requestDropData(info))
928 return waitForDropDataState;
930 /* all required (and available) datas are stored */
931 callPerformDragOperation(destView, info);
933 finishDrop(destView, info);
934 return idleState;
937 return waitForDropDataState;
940 /* ----- End of Destination states ----- */
943 void
944 W_DragDestinationStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
946 WMView *destView;
947 W_DndState* newState;
949 if (XDND_DEST_VIEW_STORED(info)) {
950 destView = XDND_DEST_VIEW(info);
951 if (XDND_DEST_STATE(info) == NULL)
952 XDND_DEST_STATE(info) = idleState;
954 #ifdef XDND_DEBUG
956 printf("current dest state: %s\n",
957 stateName(XDND_DEST_STATE(info)));
958 #endif
960 newState = (W_DndState*) XDND_DEST_STATE(info)(destView, event, info);
962 #ifdef XDND_DEBUG
964 printf("new dest state: %s\n", stateName(newState));
965 #endif
967 if (XDND_DEST_INFO(info) != NULL) {
968 XDND_DEST_STATE(info) = newState;
969 if (XDND_DEST_STATE(info) != idleState)
970 W_DragDestinationStartTimer(info);
976 static void
977 realizedObserver(void *self, WMNotification *notif)
979 WMView *view = (WMView*)WMGetNotificationObject(notif);
980 WMScreen *scr = W_VIEW_SCREEN(view);
982 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
983 scr->xdndAwareAtom,
984 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace,
985 &XDNDversion, 1);
987 WMRemoveNotificationObserver(self);
991 void
992 W_SetXdndAwareProperty(WMScreen *scr, WMView *view, Atom *types,
993 int typeCount)
995 WMView *toplevel = W_TopLevelOfView(view);
997 if (!toplevel->flags.xdndHintSet) {
998 toplevel->flags.xdndHintSet = 1;
1000 if (toplevel->flags.realized) {
1001 XChangeProperty(scr->display, W_VIEW_DRAWABLE(toplevel),
1002 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT,
1003 PropModeReplace, &XDNDversion, 1);
1004 } else {
1005 WMAddNotificationObserver(realizedObserver,
1006 /* just use as an id */
1007 &view->dragDestinationProcs,
1008 WMViewRealizedNotification,
1009 toplevel);
1015 void
1016 WMRegisterViewForDraggedTypes(WMView *view, WMArray *acceptedTypes)
1018 Atom *types;
1019 int typeCount;
1020 int i;
1022 typeCount = WMGetArrayItemCount(acceptedTypes);
1023 types = wmalloc(sizeof(Atom)*(typeCount+1));
1025 for (i = 0; i < typeCount; i++) {
1026 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display,
1027 WMGetFromArray(acceptedTypes, i),
1028 False);
1030 types[i] = 0;
1032 view->droppableTypes = types;
1033 /* WMFreeArray(acceptedTypes); */
1035 W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view, types, typeCount);
1039 void
1040 WMUnregisterViewDraggedTypes(WMView *view)
1042 if (view->droppableTypes != NULL)
1043 wfree(view->droppableTypes);
1044 view->droppableTypes = NULL;
1049 requestedOperation: operation requested by the source
1050 sourceDataTypes: data types (mime-types strings) supported by the source
1051 (never NULL, destroyed when drop ends)
1052 return operation allowed by destination (self)
1054 static WMDragOperationType
1055 defAllowedOperation(WMView *self,
1056 WMDragOperationType requestedOperation,
1057 WMArray* sourceDataTypes)
1059 /* no operation allowed */
1060 return WDOperationNone;
1065 requestedOperation: operation requested by the source
1066 sourceDataTypes: data types (mime-types strings) supported by the source
1067 (never NULL, destroyed when drop ends)
1068 return data types (mime-types strings) required by destination (self)
1069 or NULL if no suitable data type is available (force
1070 to 2nd pass with full source type list).
1072 static WMArray*
1073 defRequiredDataTypes (WMView *self,
1074 WMDragOperationType requestedOperation,
1075 WMArray* sourceDataTypes)
1077 /* no data type allowed (NULL even at 2nd pass) */
1078 return NULL;
1083 Executed when the drag enters destination (self)
1085 static void
1086 defPrepareForDragOperation(WMView *self)
1092 Checks datas to be dropped (optional).
1093 dropDatas: datas (WMData*) required by destination (self)
1094 (given in same order as returned by requiredDataTypes).
1095 A NULL data means it couldn't be retreived.
1096 Destroyed when drop ends.
1097 return true if data array is ok
1099 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
1103 Process drop
1104 dropDatas: datas (WMData*) required by destination (self)
1105 (given in same order as returned by requiredDataTypes).
1106 A NULL data means it couldn't be retrivied.
1107 Destroyed when drop ends.
1108 operationList: if source operation is WDOperationAsk, contains
1109 operations (and associated texts) that can be asked
1110 to source. (destroyed after performDragOperation call)
1111 Otherwise this parameter is NULL.
1113 static void
1114 defPerformDragOperation(WMView *self, WMArray *dropDatas,
1115 WMArray *operationList, WMPoint *dropLocation)
1120 /* Executed after drop */
1121 static void
1122 defConcludeDragOperation(WMView *self)
1127 void
1128 WMSetViewDragDestinationProcs(WMView *view, WMDragDestinationProcs *procs)
1130 if (view->dragDestinationProcs == NULL) {
1131 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
1132 } else {
1133 free(view->dragDestinationProcs);
1136 *view->dragDestinationProcs = *procs;
1138 /*XXX fill in non-implemented stuffs */
1139 if (procs->allowedOperation == NULL) {
1140 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
1142 if (procs->allowedOperation == NULL) {
1143 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
1146 /* note: inspectDropData can be NULL, if data consultation is not needed
1147 to give drop permission */
1149 if (procs->prepareForDragOperation == NULL) {
1150 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
1152 if (procs->performDragOperation == NULL) {
1153 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
1155 if (procs->concludeDragOperation == NULL) {
1156 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;