- Check whether libXft is at least version 2.1.2 else refuse to compile.
[wmaker-crm.git] / WINGs / dragdestination.c
blob03dcd9bd11a7fa19d69ff8fa1dc20772799592f1
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);
369 if (XDND_DEST_INFO(info) == NULL)
370 initDestinationDragInfo(info);
372 updateSourceWindow(info, event);
374 /* store xdnd version for source */
375 info->protocolVersion = (event->data.l[1] >> 24);
377 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
379 /* to use if the 3 types are not enough */
380 XDND_TYPE_LIST_AVAILABLE(info) = (event->data.l[1] & 1);
384 static void
385 cancelDrop(WMView *destView, WMDraggingInfo *info);
387 static void
388 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info);
391 void
392 W_DragDestinationStorePositionMsgInfo(WMDraggingInfo *info,
393 WMView *toplevel, XClientMessageEvent *event)
395 int x = event->data.l[2] >> 16;
396 int y = event->data.l[2] & 0xffff;
397 WMView *oldDestView;
398 WMView *newDestView;
400 newDestView = findXdndViewInToplevel(toplevel, x, y);
402 if (XDND_DEST_INFO(info) == NULL) {
403 initDestinationDragInfo(info);
404 updateSourceWindow(info, event);
405 XDND_DEST_VIEW(info) = newDestView;
407 else {
408 oldDestView = XDND_DEST_VIEW(info);
410 if (newDestView != oldDestView) {
411 if (oldDestView != NULL) {
412 suspendDropAuthorization(oldDestView, info);
413 XDND_DEST_STATE(info) = dropNotAllowedState;
416 updateSourceWindow(info, event);
417 XDND_DEST_VIEW(info) = newDestView;
419 if (newDestView != NULL) {
420 if (XDND_DEST_STATE(info) != waitEnterState)
421 XDND_DEST_STATE(info) = idleState;
426 XDND_SOURCE_ACTION(info) = event->data.l[4];
428 /* note: source position is not stored */
431 /* ----- End of Dragging Info ----- */
434 /* ----- Messages ----- */
436 /* send a DnD message to the source window */
437 static void
438 sendDnDClientMessage(WMView *destView, Atom message,
439 unsigned long data1,
440 unsigned long data2,
441 unsigned long data3,
442 unsigned long data4)
444 WMScreen *scr = W_VIEW_SCREEN(destView);
445 WMDraggingInfo *info = &scr->dragInfo;
447 if (XDND_DEST_INFO(info) != NULL) {
448 if (! W_SendDnDClientMessage(scr->display,
449 XDND_SOURCE_WIN(info),
450 message,
451 WMViewXID(destView),
452 data1,
453 data2,
454 data3,
455 data4)) {
456 /* drop failed */
457 W_DragDestinationInfoClear(info);
463 static void
464 storeDropData(WMView *destView, Atom selection, Atom target,
465 Time timestamp, void *cdata, WMData *data)
467 WMScreen *scr = W_VIEW_SCREEN(destView);
468 WMDraggingInfo *info = &(scr->dragInfo);
469 WMData *dataToStore = NULL;
471 if (data != NULL)
472 dataToStore = WMRetainData(data);
474 if (XDND_DEST_INFO(info) != NULL && XDND_DROP_DATAS(info) != NULL) {
475 WMAddToArray(XDND_DROP_DATAS(info), dataToStore);
476 W_SendDnDClientMessage(scr->display, WMViewXID(destView),
477 scr->xdndSelectionAtom,
478 WMViewXID(destView),
479 0, 0, 0, 0);
484 Bool
485 requestDropDataInSelection(WMView *destView, char* type)
487 WMScreen *scr = W_VIEW_SCREEN(destView);
489 if (type != NULL) {
490 if (!WMRequestSelection(destView,
491 scr->xdndSelectionAtom,
492 XInternAtom(scr->display, type, False),
493 CurrentTime,
494 storeDropData, NULL)) {
495 wwarning("could not request data for dropped data");
496 return False;
499 return True;
502 return False;
506 Bool
507 requestDropData(WMDraggingInfo *info)
509 WMView *destView = XDND_DEST_VIEW(info);
510 char* nextType = getNextRequestedDataType(info);
512 while ((nextType != NULL)
513 && (!requestDropDataInSelection(destView, nextType)) ) {
514 /* store NULL if request failed, and try with next type */
515 WMAddToArray(XDND_DROP_DATAS(info), NULL);
516 nextType = getNextRequestedDataType(info);
519 /* remains types to retrieve ? */
520 return (nextType != NULL);
524 static void
525 concludeDrop(WMView *destView)
527 destView->dragDestinationProcs->concludeDragOperation(destView);
531 /* send cancel message to the source */
532 static void
533 cancelDrop(WMView *destView, WMDraggingInfo *info)
535 /* send XdndStatus with action None */
536 sendDnDClientMessage(destView,
537 W_VIEW_SCREEN(destView)->xdndStatusAtom,
538 0, 0, 0, None);
539 concludeDrop(destView);
540 freeDestinationViewInfos(info);
544 /* suspend drop, when dragged icon enter an unaware subview of destView */
545 static void
546 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info)
548 /* free datas that depend on destination behaviour */
549 /* (in short: only keep source's types) */
550 if (XDND_DROP_DATAS(info) != NULL) {
551 WMFreeArray(XDND_DROP_DATAS(info));
552 XDND_DROP_DATAS(info) = NULL;
554 XDND_REQUIRED_TYPES(info) = NULL;
556 /* send XdndStatus with action None */
557 sendDnDClientMessage(destView,
558 W_VIEW_SCREEN(destView)->xdndStatusAtom,
559 0, 0, 0, None);
563 /* cancel drop on Enter message, if protocol version is nok */
564 void
565 W_DragDestinationCancelDropOnEnter(WMView *toplevel, WMDraggingInfo *info)
567 if (XDND_DEST_VIEW_STORED(info))
568 cancelDrop(XDND_DEST_VIEW(info), info);
569 else {
570 /* send XdndStatus with action None */
571 sendDnDClientMessage(toplevel,
572 W_VIEW_SCREEN(toplevel)->xdndStatusAtom,
573 0, 0, 0, None);
576 W_DragDestinationInfoClear(info);
580 static void
581 finishDrop(WMView *destView, WMDraggingInfo *info)
583 sendDnDClientMessage(destView,
584 W_VIEW_SCREEN(destView)->xdndFinishedAtom,
585 0, 0, 0, 0);
586 concludeDrop(destView);
587 W_DragDestinationInfoClear(info);
591 static Atom
592 getAllowedAction(WMView *destView, WMDraggingInfo *info)
594 WMScreen *scr = W_VIEW_SCREEN(destView);
596 return W_OperationToAction(scr,
597 destView->dragDestinationProcs->allowedOperation(
598 destView,
599 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
600 XDND_SOURCE_TYPES(info)));
604 /* send the action that can be performed,
605 and the limits outside wich the source must re-send
606 its position and action */
607 static void
608 sendAllowedAction(WMView *destView, Atom action)
610 WMScreen *scr = W_VIEW_SCREEN(destView);
611 /* // WMPoint destPos = WMGetViewScreenPosition(destView); */
612 WMSize destSize = WMGetViewSize(destView);
613 int destX, destY;
614 Window foo;
616 XTranslateCoordinates(scr->display, scr->rootWin, WMViewXID(destView),
617 0, 0, &destX, &destY,
618 &foo);
620 sendDnDClientMessage(destView,
621 scr->xdndStatusAtom,
623 (destX << 16)|destY,
624 (destSize.width << 16)|destSize.height,
625 action);
629 static void*
630 checkActionAllowed(WMView *destView, WMDraggingInfo* info)
632 XDND_DEST_ACTION(info) =
633 getAllowedAction(destView, info);
635 if (XDND_DEST_ACTION(info) == None) {
636 suspendDropAuthorization(destView, info);
637 return dropNotAllowedState;
640 sendAllowedAction(destView, XDND_DEST_ACTION(info));
641 return dropAllowedState;
644 static void*
645 checkDropAllowed(WMView *destView, XClientMessageEvent *event,
646 WMDraggingInfo* info)
648 storeRequiredTypeList(info);
650 if (destView->dragDestinationProcs->inspectDropData != NULL) {
651 XDND_DROP_DATAS(info) = createDropDataArray(
652 XDND_REQUIRED_TYPES(info));
654 /* store first available data */
655 if (requestDropData(info))
656 return inspectDropDataState;
658 /* no data retrieved, but inspect can allow it */
659 if (destView->dragDestinationProcs->inspectDropData(
660 destView,
661 XDND_DROP_DATAS(info)))
662 return checkActionAllowed(destView, info);
664 suspendDropAuthorization(destView, info);
665 return dropNotAllowedState;
668 return checkActionAllowed(destView, info);
671 static WMPoint*
672 getDropLocationInView(WMView *view)
674 Window rootWin, childWin;
675 int rootX, rootY;
676 unsigned int mask;
677 WMPoint* location;
679 location = (WMPoint*) wmalloc(sizeof(WMPoint));
681 XQueryPointer(
682 W_VIEW_SCREEN(view)->display,
683 WMViewXID(view), &rootWin, &childWin,
684 &rootX, &rootY,
685 &(location->x), &(location->y),
686 &mask);
688 return location;
691 static void
692 callPerformDragOperation(WMView *destView, WMDraggingInfo *info)
694 WMArray *operationList = NULL;
695 WMScreen *scr = W_VIEW_SCREEN(destView);
696 WMPoint* dropLocation;
698 if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
699 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
701 dropLocation = getDropLocationInView(destView);
702 destView->dragDestinationProcs->performDragOperation(
703 destView,
704 XDND_DROP_DATAS(info),
705 operationList,
706 dropLocation);
708 wfree(dropLocation);
709 if (operationList != NULL)
710 WMFreeArray(operationList);
714 /* ----- Destination timer ----- */
715 static void
716 dragSourceResponseTimeOut(void *destView)
718 WMView *view = (WMView*) destView;
719 WMDraggingInfo *info;
721 wwarning("delay for drag source response expired");
722 if (view != NULL) {
723 info = &(W_VIEW_SCREEN(view)->dragInfo);
724 if (XDND_DEST_VIEW_STORED(info))
725 cancelDrop(view, info);
726 else {
727 /* send XdndStatus with action None */
728 sendDnDClientMessage(view,
729 W_VIEW_SCREEN(view)->xdndStatusAtom,
730 0, 0, 0, None);
733 W_DragDestinationInfoClear(info);
737 void
738 W_DragDestinationStopTimer()
740 if (dndDestinationTimer != NULL) {
741 WMDeleteTimerHandler(dndDestinationTimer);
742 dndDestinationTimer = NULL;
746 void
747 W_DragDestinationStartTimer(WMDraggingInfo *info)
749 W_DragDestinationStopTimer();
751 if (XDND_DEST_STATE(info) != idleState
752 || XDND_DEST_VIEW(info) == NULL) {
753 /* note: info->destView == NULL means :
754 Enter message has been received, waiting for Position message */
756 dndDestinationTimer = WMAddTimerHandler(
757 XDND_SOURCE_RESPONSE_MAX_DELAY,
758 dragSourceResponseTimeOut,
759 XDND_DEST_VIEW(info));
762 /* ----- End of Destination timer ----- */
765 /* ----- Destination states ----- */
767 #ifdef XDND_DEBUG
768 static const char*
769 stateName(W_DndState *state)
771 if (state == NULL)
772 return "no state defined";
774 if (state == idleState)
775 return "idleState";
777 if (state == waitEnterState)
778 return "waitEnterState";
780 if (state == inspectDropDataState)
781 return "inspectDropDataState";
783 if (state == dropAllowedState)
784 return "dropAllowedState";
786 if (state == dropNotAllowedState)
787 return "dropNotAllowedState";
789 if (state == waitForDropDataState)
790 return "waitForDropDataState";
792 return "unknown state";
794 #endif
796 static void*
797 idleState(WMView *destView, XClientMessageEvent *event,
798 WMDraggingInfo *info)
800 WMScreen *scr;
801 Atom sourceMsg;
803 scr = W_VIEW_SCREEN(destView);
804 sourceMsg = event->message_type;
806 if (sourceMsg == scr->xdndPositionAtom) {
807 destView->dragDestinationProcs->prepareForDragOperation(destView);
809 if (XDND_SOURCE_TYPES(info) != NULL) {
810 /* enter message infos are available */
811 return checkDropAllowed(destView, event, info);
814 /* waiting for enter message */
815 return waitEnterState;
818 return idleState;
822 /* Source position and action are stored,
823 waiting for xdnd protocol version and source type */
824 static void*
825 waitEnterState(WMView *destView, XClientMessageEvent *event,
826 WMDraggingInfo *info)
828 WMScreen *scr = W_VIEW_SCREEN(destView);
829 Atom sourceMsg = event->message_type;
831 if (sourceMsg == scr->xdndEnterAtom) {
832 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
833 return checkDropAllowed(destView, event, info);
836 return waitEnterState;
840 /* We have requested a data, and have received it */
841 static void*
842 inspectDropDataState(WMView *destView, XClientMessageEvent *event,
843 WMDraggingInfo *info)
845 WMScreen *scr;
846 Atom sourceMsg;
848 scr = W_VIEW_SCREEN(destView);
849 sourceMsg = event->message_type;
851 if (sourceMsg == scr->xdndSelectionAtom) {
852 /* a data has been retrieved, store next available */
853 if (requestDropData(info))
854 return inspectDropDataState;
856 /* all required (and available) datas are stored */
857 if (destView->dragDestinationProcs->inspectDropData(
858 destView,
859 XDND_DROP_DATAS(info)))
860 return checkActionAllowed(destView, info);
862 suspendDropAuthorization(destView, info);
863 return dropNotAllowedState;
866 return inspectDropDataState;
870 static void*
871 dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
872 WMDraggingInfo *info)
874 WMScreen *scr = W_VIEW_SCREEN(destView);
875 Atom sourceMsg = event->message_type;
877 if (sourceMsg == scr->xdndDropAtom) {
878 finishDrop(destView, info);
879 return idleState;
882 return dropNotAllowedState;
886 static void*
887 dropAllowedState(WMView *destView, XClientMessageEvent *event,
888 WMDraggingInfo *info)
890 WMScreen *scr = W_VIEW_SCREEN(destView);
891 Atom sourceMsg = event->message_type;
893 if (sourceMsg == scr->xdndDropAtom) {
894 if (XDND_DROP_DATAS(info) != NULL) {
895 /* drop datas were cached with inspectDropData call */
896 callPerformDragOperation(destView, info);
897 } else {
898 XDND_DROP_DATAS(info) = createDropDataArray(
899 XDND_REQUIRED_TYPES(info));
901 /* store first available data */
902 if (requestDropData(info))
903 return waitForDropDataState;
905 /* no data retrieved */
906 callPerformDragOperation(destView, info);
909 finishDrop(destView, info);
910 return idleState;
913 return dropAllowedState;
917 static void*
918 waitForDropDataState(WMView *destView, XClientMessageEvent *event,
919 WMDraggingInfo *info)
921 WMScreen *scr = W_VIEW_SCREEN(destView);
922 Atom sourceMsg = event->message_type;
924 if (sourceMsg == scr->xdndSelectionAtom) {
925 /* store next data */
926 if (requestDropData(info))
927 return waitForDropDataState;
929 /* all required (and available) datas are stored */
930 callPerformDragOperation(destView, info);
932 finishDrop(destView, info);
933 return idleState;
936 return waitForDropDataState;
939 /* ----- End of Destination states ----- */
942 void
943 W_DragDestinationStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
945 WMView *destView;
946 W_DndState* newState;
948 if (XDND_DEST_VIEW_STORED(info)) {
949 destView = XDND_DEST_VIEW(info);
950 if (XDND_DEST_STATE(info) == NULL)
951 XDND_DEST_STATE(info) = idleState;
953 #ifdef XDND_DEBUG
955 printf("current dest state: %s\n",
956 stateName(XDND_DEST_STATE(info)));
957 #endif
959 newState = (W_DndState*) XDND_DEST_STATE(info)(destView, event, info);
961 #ifdef XDND_DEBUG
963 printf("new dest state: %s\n", stateName(newState));
964 #endif
966 if (XDND_DEST_INFO(info) != NULL) {
967 XDND_DEST_STATE(info) = newState;
968 if (XDND_DEST_STATE(info) != idleState)
969 W_DragDestinationStartTimer(info);
975 static void
976 realizedObserver(void *self, WMNotification *notif)
978 WMView *view = (WMView*)WMGetNotificationObject(notif);
979 WMScreen *scr = W_VIEW_SCREEN(view);
981 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
982 scr->xdndAwareAtom,
983 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace,
984 &XDNDversion, 1);
986 WMRemoveNotificationObserver(self);
990 void
991 W_SetXdndAwareProperty(WMScreen *scr, WMView *view, Atom *types,
992 int typeCount)
994 WMView *toplevel = W_TopLevelOfView(view);
996 if (!toplevel->flags.xdndHintSet) {
997 toplevel->flags.xdndHintSet = 1;
999 if (toplevel->flags.realized) {
1000 XChangeProperty(scr->display, W_VIEW_DRAWABLE(toplevel),
1001 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT,
1002 PropModeReplace, &XDNDversion, 1);
1003 } else {
1004 WMAddNotificationObserver(realizedObserver,
1005 /* just use as an id */
1006 &view->dragDestinationProcs,
1007 WMViewRealizedNotification,
1008 toplevel);
1014 void
1015 WMRegisterViewForDraggedTypes(WMView *view, WMArray *acceptedTypes)
1017 Atom *types;
1018 int typeCount;
1019 int i;
1021 typeCount = WMGetArrayItemCount(acceptedTypes);
1022 types = wmalloc(sizeof(Atom)*(typeCount+1));
1024 for (i = 0; i < typeCount; i++) {
1025 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display,
1026 WMGetFromArray(acceptedTypes, i),
1027 False);
1029 types[i] = 0;
1031 view->droppableTypes = types;
1032 /* WMFreeArray(acceptedTypes); */
1034 W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view, types, typeCount);
1038 void
1039 WMUnregisterViewDraggedTypes(WMView *view)
1041 if (view->droppableTypes != NULL)
1042 wfree(view->droppableTypes);
1043 view->droppableTypes = NULL;
1048 requestedOperation: operation requested by the source
1049 sourceDataTypes: data types (mime-types strings) supported by the source
1050 (never NULL, destroyed when drop ends)
1051 return operation allowed by destination (self)
1053 static WMDragOperationType
1054 defAllowedOperation(WMView *self,
1055 WMDragOperationType requestedOperation,
1056 WMArray* sourceDataTypes)
1058 /* no operation allowed */
1059 return WDOperationNone;
1064 requestedOperation: operation requested by the source
1065 sourceDataTypes: data types (mime-types strings) supported by the source
1066 (never NULL, destroyed when drop ends)
1067 return data types (mime-types strings) required by destination (self)
1068 or NULL if no suitable data type is available (force
1069 to 2nd pass with full source type list).
1071 static WMArray*
1072 defRequiredDataTypes (WMView *self,
1073 WMDragOperationType requestedOperation,
1074 WMArray* sourceDataTypes)
1076 /* no data type allowed (NULL even at 2nd pass) */
1077 return NULL;
1082 Executed when the drag enters destination (self)
1084 static void
1085 defPrepareForDragOperation(WMView *self)
1091 Checks datas to be dropped (optional).
1092 dropDatas: datas (WMData*) required by destination (self)
1093 (given in same order as returned by requiredDataTypes).
1094 A NULL data means it couldn't be retreived.
1095 Destroyed when drop ends.
1096 return true if data array is ok
1098 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
1102 Process drop
1103 dropDatas: datas (WMData*) required by destination (self)
1104 (given in same order as returned by requiredDataTypes).
1105 A NULL data means it couldn't be retrivied.
1106 Destroyed when drop ends.
1107 operationList: if source operation is WDOperationAsk, contains
1108 operations (and associated texts) that can be asked
1109 to source. (destroyed after performDragOperation call)
1110 Otherwise this parameter is NULL.
1112 static void
1113 defPerformDragOperation(WMView *self, WMArray *dropDatas,
1114 WMArray *operationList, WMPoint *dropLocation)
1119 /* Executed after drop */
1120 static void
1121 defConcludeDragOperation(WMView *self)
1126 void
1127 WMSetViewDragDestinationProcs(WMView *view, WMDragDestinationProcs *procs)
1129 if (view->dragDestinationProcs == NULL) {
1130 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
1131 } else {
1132 free(view->dragDestinationProcs);
1135 *view->dragDestinationProcs = *procs;
1137 /*XXX fill in non-implemented stuffs */
1138 if (procs->allowedOperation == NULL) {
1139 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
1141 if (procs->allowedOperation == NULL) {
1142 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
1145 /* note: inspectDropData can be NULL, if data consultation is not needed
1146 to give drop permission */
1148 if (procs->prepareForDragOperation == NULL) {
1149 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
1151 if (procs->performDragOperation == NULL) {
1152 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
1154 if (procs->concludeDragOperation == NULL) {
1155 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;