updated changelog
[wmaker-crm.git] / WINGs / dragdestination.c
blob34f29c4b692dc8c47ec5292564e8180eb2fd5d51
3 #include "WINGsP.h"
4 #include <X11/Xatom.h>
6 #define XDND_SOURCE_RESPONSE_MAX_DELAY 3000
8 #define VERSION_INFO(dragInfo) dragInfo->protocolVersion
10 #define XDND_PROPERTY_FORMAT 32
11 #define XDND_ACTION_DESCRIPTION_FORMAT 8
13 #define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
14 #define XDND_SOURCE_WIN(dragInfo) dragInfo->destInfo->sourceWindow
15 #define XDND_DEST_VIEW(dragInfo) dragInfo->destInfo->destView
16 #define XDND_DEST_STATE(dragInfo) dragInfo->destInfo->state
17 #define XDND_SOURCE_TYPES(dragInfo) dragInfo->destInfo->sourceTypes
18 #define XDND_TYPE_LIST_AVAILABLE(dragInfo) dragInfo->destInfo->typeListAvailable
19 #define XDND_REQUIRED_TYPES(dragInfo) dragInfo->destInfo->requiredTypes
20 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
21 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
22 #define XDND_SOURCE_OPERATIONS(dragInfo) dragInfo->destInfo->sourceOperations
23 #define XDND_DROP_DATAS(dragInfo) dragInfo->destInfo->dropDatas
24 #define XDND_DROP_DATA_COUNT(dragInfo) dragInfo->destInfo->dropDataCount
25 #define XDND_DEST_VIEW_STORED(dragInfo) ((dragInfo->destInfo) != NULL)\
26 && ((dragInfo->destInfo->destView) != NULL)
29 static unsigned char XDNDversion = XDND_VERSION;
30 static WMHandlerID dndDestinationTimer = NULL;
33 static void* idleState(WMView *destView, XClientMessageEvent *event,
34 WMDraggingInfo *info);
35 static void* waitEnterState(WMView *destView, XClientMessageEvent *event,
36 WMDraggingInfo *info);
37 static void* inspectDropDataState(WMView *destView, XClientMessageEvent *event,
38 WMDraggingInfo *info);
39 static void* dropAllowedState(WMView *destView, XClientMessageEvent *event,
40 WMDraggingInfo *info);
41 static void* dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
42 WMDraggingInfo *info);
43 static void* waitForDropDataState(WMView *destView, XClientMessageEvent *event,
44 WMDraggingInfo *info);
46 /* ----- Types & datas list ----- */
47 static void
48 freeSourceTypeArrayItem(void *type)
50 XFree(type);
54 static WMArray*
55 createSourceTypeArray(int initialSize)
57 return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
61 static void
62 freeDropDataArrayItem(void* data)
64 if (data != NULL)
65 WMReleaseData((WMData*) data);
69 static WMArray*
70 createDropDataArray(WMArray *requiredTypes)
72 if (requiredTypes != NULL)
73 return WMCreateArrayWithDestructor(
74 WMGetArrayItemCount(requiredTypes),
75 freeDropDataArrayItem);
77 else
78 return WMCreateArray(0);
81 static WMArray*
82 getTypesFromTypeList(WMScreen *scr, Window sourceWin)
84 /* // WMDraggingInfo *info = &scr->dragInfo;*/
85 Atom dataType;
86 Atom* typeAtomList;
87 WMArray* typeList;
88 int i, format;
89 unsigned long count, remaining;
90 unsigned char *data = NULL;
92 XGetWindowProperty(scr->display, sourceWin, scr->xdndTypeListAtom,
93 0, 0x8000000L, False, XA_ATOM, &dataType, &format,
94 &count, &remaining, &data);
96 if (dataType!=XA_ATOM || format!=XDND_PROPERTY_FORMAT || count==0 || !data) {
97 if (data) {
98 XFree(data);
100 return createSourceTypeArray(0);
103 typeList = createSourceTypeArray(count);
104 typeAtomList = (Atom*) data;
105 for (i=0; i < count; i++) {
106 WMAddToArray(typeList, XGetAtomName(scr->display, typeAtomList[i]));
109 XFree(data);
111 return typeList;
115 static WMArray*
116 getTypesFromThreeTypes(WMScreen *scr, XClientMessageEvent *event)
118 WMArray* typeList;
119 Atom atom;
120 int i;
122 typeList = createSourceTypeArray(3);
123 for (i = 2; i < 5; i++) {
124 if (event->data.l[i] != None) {
125 atom = (Atom)event->data.l[i];
126 WMAddToArray(typeList, XGetAtomName(scr->display, atom));
130 return typeList;
134 void
135 storeRequiredTypeList(WMDraggingInfo *info)
137 WMView *destView = XDND_DEST_VIEW(info);
138 WMScreen *scr = W_VIEW_SCREEN(destView);
139 WMArray *requiredTypes;
141 /* First, see if the 3 source types are enough for dest requirements */
142 requiredTypes = destView->dragDestinationProcs->requiredDataTypes(
143 destView,
144 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
145 XDND_SOURCE_TYPES(info));
147 if (requiredTypes == NULL && XDND_TYPE_LIST_AVAILABLE(info)) {
148 /* None of the 3 source types fits, get the whole type list */
149 requiredTypes =
150 destView->dragDestinationProcs->requiredDataTypes(
151 destView,
152 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
153 getTypesFromTypeList(scr, XDND_SOURCE_WIN(info)));
157 XDND_REQUIRED_TYPES(info) = requiredTypes;
161 char*
162 getNextRequestedDataType(WMDraggingInfo *info)
164 /* get the type of the first data not yet retrieved from selection */
165 int nextTypeIndex;
167 if (XDND_REQUIRED_TYPES(info) != NULL) {
168 nextTypeIndex = WMGetArrayItemCount(XDND_DROP_DATAS(info));
169 return WMGetFromArray(XDND_REQUIRED_TYPES(info), nextTypeIndex);
170 /* NULL if no more type */
171 } else
172 return NULL;
176 /* ----- Action list ----- */
178 WMArray*
179 sourceOperationList(WMScreen *scr, Window sourceWin)
181 Atom dataType, *actionList;
182 int i, size;
183 unsigned long count, remaining;
184 unsigned char* actionDatas = NULL;
185 unsigned char* descriptionList = NULL;
186 WMArray* operationArray;
187 WMDragOperationItem* operationItem;
188 char* description;
190 remaining = 0;
191 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionListAtom,
192 0, 0x8000000L, False, XA_ATOM, &dataType, &size,
193 &count, &remaining, &actionDatas);
195 if (dataType!=XA_ATOM || size!=XDND_PROPERTY_FORMAT || count==0 || !actionDatas) {
196 wwarning("Cannot read action list");
197 if (actionDatas) {
198 XFree(actionDatas);
200 return NULL;
203 actionList = (Atom*)actionDatas;
205 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionDescriptionAtom,
206 0, 0x8000000L, False, XA_STRING, &dataType, &size,
207 &count, &remaining, &descriptionList);
209 if (dataType!=XA_STRING || size!=XDND_ACTION_DESCRIPTION_FORMAT ||
210 count==0 || !descriptionList) {
211 wwarning("Cannot read action description list");
212 if (actionList) {
213 XFree(actionList);
215 if (descriptionList) {
216 XFree(descriptionList);
218 return NULL;
221 operationArray = WMCreateDragOperationArray(count);
222 description = descriptionList;
224 for (i=0; count > 0; i++) {
225 size = strlen(description);
226 operationItem = WMCreateDragOperationItem(
227 W_ActionToOperation(scr, actionList[i]),
228 wstrdup(description));
230 WMAddToArray(operationArray, operationItem);
231 count -= (size + 1); /* -1 : -NULL char */
233 /* next description */
234 description = &(description[size + 1]);
237 XFree(actionList);
238 XFree(descriptionList);
240 return operationArray;
244 /* ----- Dragging Info ----- */
245 static void
246 updateSourceWindow(WMDraggingInfo *info, XClientMessageEvent *event)
248 XDND_SOURCE_WIN(info) = (Window) event->data.l[0];
252 static Window
253 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
255 Window foo, bar;
256 Window *children;
257 unsigned nchildren;
258 int i;
260 if (!XQueryTree(dpy, toplevel, &foo, &bar,
261 &children, &nchildren) || children == NULL) {
262 return None;
265 /* first window that contains the point is the one */
266 for (i = nchildren-1; i >= 0; i--) {
267 XWindowAttributes attr;
269 if (XGetWindowAttributes(dpy, children[i], &attr)
270 && attr.map_state == IsViewable
271 && x >= attr.x && y >= attr.y
272 && x < attr.x + attr.width && y < attr.y + attr.height) {
273 Window child, tmp;
275 tmp = children[i];
276 child = findChildInWindow(dpy, tmp, x - attr.x, y - attr.y);
277 XFree(children);
279 if (child == None)
280 return tmp;
281 else
282 return child;
286 XFree(children);
287 return None;
291 static WMView*
292 findXdndViewInToplevel(WMView* toplevel, int x, int y)
294 WMScreen *scr = W_VIEW_SCREEN(toplevel);
295 Window toplevelWin = WMViewXID(toplevel);
296 int xInToplevel, yInToplevel;
297 Window child, foo;
298 WMView *childView;
300 XTranslateCoordinates(scr->display, scr->rootWin, toplevelWin,
301 x, y, &xInToplevel, &yInToplevel,
302 &foo);
304 child = findChildInWindow(scr->display, toplevelWin,
305 xInToplevel, yInToplevel);
307 if (child != None) {
308 childView = W_GetViewForXWindow(scr->display, child);
310 /* if childView supports Xdnd, return childView */
311 if (childView != NULL
312 && childView->dragDestinationProcs != NULL)
313 return childView;
316 return NULL;
320 /* Clear datas only used by current destination view */
321 static void
322 freeDestinationViewInfos(WMDraggingInfo *info)
324 if (XDND_SOURCE_TYPES(info) != NULL) {
325 WMFreeArray(XDND_SOURCE_TYPES(info));
326 XDND_SOURCE_TYPES(info) = NULL;
329 if (XDND_DROP_DATAS(info) != NULL) {
330 WMFreeArray(XDND_DROP_DATAS(info));
331 XDND_DROP_DATAS(info) = NULL;
334 XDND_REQUIRED_TYPES(info) = NULL;
337 void
338 W_DragDestinationInfoClear(WMDraggingInfo *info)
340 W_DragDestinationStopTimer();
342 if (XDND_DEST_INFO(info) != NULL) {
343 freeDestinationViewInfos(info);
345 wfree(XDND_DEST_INFO(info));
346 XDND_DEST_INFO(info) = NULL;
350 static void
351 initDestinationDragInfo(WMDraggingInfo *info)
353 XDND_DEST_INFO(info) =
354 (W_DragDestinationInfo*) wmalloc(sizeof(W_DragDestinationInfo));
356 XDND_DEST_STATE(info) = idleState;
357 XDND_DEST_VIEW(info) = NULL;
359 XDND_SOURCE_TYPES(info) = NULL;
360 XDND_REQUIRED_TYPES(info) = NULL;
361 XDND_DROP_DATAS(info) = NULL;
365 void
366 W_DragDestinationStoreEnterMsgInfo(WMDraggingInfo *info,
367 WMView *toplevel, XClientMessageEvent *event)
369 WMScreen *scr = W_VIEW_SCREEN(toplevel);
371 if (XDND_DEST_INFO(info) == NULL)
372 initDestinationDragInfo(info);
374 updateSourceWindow(info, event);
376 /* store xdnd version for source */
377 info->protocolVersion = (event->data.l[1] >> 24);
379 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
381 /* to use if the 3 types are not enough */
382 XDND_TYPE_LIST_AVAILABLE(info) = (event->data.l[1] & 1);
386 static void
387 cancelDrop(WMView *destView, WMDraggingInfo *info);
389 static void
390 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info);
393 void
394 W_DragDestinationStorePositionMsgInfo(WMDraggingInfo *info,
395 WMView *toplevel, XClientMessageEvent *event)
397 int x = event->data.l[2] >> 16;
398 int y = event->data.l[2] & 0xffff;
399 WMView *oldDestView;
400 WMView *newDestView;
402 newDestView = findXdndViewInToplevel(toplevel, x, y);
404 if (XDND_DEST_INFO(info) == NULL) {
405 initDestinationDragInfo(info);
406 updateSourceWindow(info, event);
407 XDND_DEST_VIEW(info) = newDestView;
409 else {
410 oldDestView = XDND_DEST_VIEW(info);
412 if (newDestView != oldDestView) {
413 if (oldDestView != NULL) {
414 suspendDropAuthorization(oldDestView, info);
415 XDND_DEST_STATE(info) = dropNotAllowedState;
418 updateSourceWindow(info, event);
419 XDND_DEST_VIEW(info) = newDestView;
421 if (newDestView != NULL) {
422 if (XDND_DEST_STATE(info) != waitEnterState)
423 XDND_DEST_STATE(info) = idleState;
428 XDND_SOURCE_ACTION(info) = event->data.l[4];
430 /* note: source position is not stored */
433 /* ----- End of Dragging Info ----- */
436 /* ----- Messages ----- */
438 /* send a DnD message to the source window */
439 static void
440 sendDnDClientMessage(WMView *destView, Atom message,
441 unsigned long data1,
442 unsigned long data2,
443 unsigned long data3,
444 unsigned long data4)
446 WMScreen *scr = W_VIEW_SCREEN(destView);
447 WMDraggingInfo *info = &scr->dragInfo;
449 if (XDND_DEST_INFO(info) != NULL) {
450 if (! W_SendDnDClientMessage(scr->display,
451 XDND_SOURCE_WIN(info),
452 message,
453 WMViewXID(destView),
454 data1,
455 data2,
456 data3,
457 data4)) {
458 /* drop failed */
459 W_DragDestinationInfoClear(info);
465 static void
466 storeDropData(WMView *destView, Atom selection, Atom target,
467 Time timestamp, void *cdata, WMData *data)
469 WMScreen *scr = W_VIEW_SCREEN(destView);
470 WMDraggingInfo *info = &(scr->dragInfo);
471 WMData *dataToStore = NULL;
473 if (data != NULL)
474 dataToStore = WMRetainData(data);
476 if (XDND_DEST_INFO(info) != NULL && XDND_DROP_DATAS(info) != NULL) {
477 WMAddToArray(XDND_DROP_DATAS(info), dataToStore);
478 W_SendDnDClientMessage(scr->display, WMViewXID(destView),
479 scr->xdndSelectionAtom,
480 WMViewXID(destView),
481 0, 0, 0, 0);
486 Bool
487 requestDropDataInSelection(WMView *destView, char* type)
489 WMScreen *scr = W_VIEW_SCREEN(destView);
491 if (type != NULL) {
492 if (!WMRequestSelection(destView,
493 scr->xdndSelectionAtom,
494 XInternAtom(scr->display, type, False),
495 CurrentTime,
496 storeDropData, NULL)) {
497 wwarning("could not request data for dropped data");
498 return False;
501 return True;
504 return False;
508 Bool
509 requestDropData(WMDraggingInfo *info)
511 WMView *destView = XDND_DEST_VIEW(info);
512 char* nextType = getNextRequestedDataType(info);
514 while ((nextType != NULL)
515 && (!requestDropDataInSelection(destView, nextType)) ) {
516 /* store NULL if request failed, and try with next type */
517 WMAddToArray(XDND_DROP_DATAS(info), NULL);
518 nextType = getNextRequestedDataType(info);
521 /* remains types to retrieve ? */
522 return (nextType != NULL);
526 static void
527 concludeDrop(WMView *destView)
529 destView->dragDestinationProcs->concludeDragOperation(destView);
533 /* send cancel message to the source */
534 static void
535 cancelDrop(WMView *destView, WMDraggingInfo *info)
537 /* send XdndStatus with action None */
538 sendDnDClientMessage(destView,
539 W_VIEW_SCREEN(destView)->xdndStatusAtom,
540 0, 0, 0, None);
541 concludeDrop(destView);
542 freeDestinationViewInfos(info);
546 /* suspend drop, when dragged icon enter an unaware subview of destView */
547 static void
548 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info)
550 /* free datas that depend on destination behaviour */
551 /* (in short: only keep source's types) */
552 if (XDND_DROP_DATAS(info) != NULL) {
553 WMFreeArray(XDND_DROP_DATAS(info));
554 XDND_DROP_DATAS(info) = NULL;
556 XDND_REQUIRED_TYPES(info) = NULL;
558 /* send XdndStatus with action None */
559 sendDnDClientMessage(destView,
560 W_VIEW_SCREEN(destView)->xdndStatusAtom,
561 0, 0, 0, None);
565 /* cancel drop on Enter message, if protocol version is nok */
566 void
567 W_DragDestinationCancelDropOnEnter(WMView *toplevel, WMDraggingInfo *info)
569 if (XDND_DEST_VIEW_STORED(info))
570 cancelDrop(XDND_DEST_VIEW(info), info);
571 else {
572 /* send XdndStatus with action None */
573 sendDnDClientMessage(toplevel,
574 W_VIEW_SCREEN(toplevel)->xdndStatusAtom,
575 0, 0, 0, None);
578 W_DragDestinationInfoClear(info);
582 static void
583 finishDrop(WMView *destView, WMDraggingInfo *info)
585 sendDnDClientMessage(destView,
586 W_VIEW_SCREEN(destView)->xdndFinishedAtom,
587 0, 0, 0, 0);
588 concludeDrop(destView);
589 W_DragDestinationInfoClear(info);
593 static Atom
594 getAllowedAction(WMView *destView, WMDraggingInfo *info)
596 WMScreen *scr = W_VIEW_SCREEN(destView);
598 return W_OperationToAction(scr,
599 destView->dragDestinationProcs->allowedOperation(
600 destView,
601 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
602 XDND_SOURCE_TYPES(info)));
606 /* send the action that can be performed,
607 and the limits outside wich the source must re-send
608 its position and action */
609 static void
610 sendAllowedAction(WMView *destView, Atom action)
612 WMScreen *scr = W_VIEW_SCREEN(destView);
613 /* // WMPoint destPos = WMGetViewScreenPosition(destView); */
614 WMSize destSize = WMGetViewSize(destView);
615 int destX, destY;
616 Window foo;
618 XTranslateCoordinates(scr->display, scr->rootWin, WMViewXID(destView),
619 0, 0, &destX, &destY,
620 &foo);
622 sendDnDClientMessage(destView,
623 scr->xdndStatusAtom,
625 (destX << 16)|destY,
626 (destSize.width << 16)|destSize.height,
627 action);
631 static void*
632 checkActionAllowed(WMView *destView, WMDraggingInfo* info)
634 XDND_DEST_ACTION(info) =
635 getAllowedAction(destView, info);
637 if (XDND_DEST_ACTION(info) == None) {
638 suspendDropAuthorization(destView, info);
639 return dropNotAllowedState;
642 sendAllowedAction(destView, XDND_DEST_ACTION(info));
643 return dropAllowedState;
646 static void*
647 checkDropAllowed(WMView *destView, XClientMessageEvent *event,
648 WMDraggingInfo* info)
650 storeRequiredTypeList(info);
652 if (destView->dragDestinationProcs->inspectDropData != NULL) {
653 XDND_DROP_DATAS(info) = createDropDataArray(
654 XDND_REQUIRED_TYPES(info));
656 /* store first available data */
657 if (requestDropData(info))
658 return inspectDropDataState;
660 /* no data retrieved, but inspect can allow it */
661 if (destView->dragDestinationProcs->inspectDropData(
662 destView,
663 XDND_DROP_DATAS(info)))
664 return checkActionAllowed(destView, info);
666 suspendDropAuthorization(destView, info);
667 return dropNotAllowedState;
670 return checkActionAllowed(destView, info);
673 static WMPoint*
674 getDropLocationInView(WMView *view)
676 Window rootWin, childWin;
677 int rootX, rootY;
678 unsigned int mask;
679 WMPoint* location;
681 location = (WMPoint*) wmalloc(sizeof(WMPoint));
683 XQueryPointer(
684 W_VIEW_SCREEN(view)->display,
685 WMViewXID(view), &rootWin, &childWin,
686 &rootX, &rootY,
687 &(location->x), &(location->y),
688 &mask);
690 return location;
693 static void
694 callPerformDragOperation(WMView *destView, WMDraggingInfo *info)
696 WMArray *operationList = NULL;
697 WMScreen *scr = W_VIEW_SCREEN(destView);
698 WMPoint* dropLocation;
700 if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
701 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
703 dropLocation = getDropLocationInView(destView);
704 destView->dragDestinationProcs->performDragOperation(
705 destView,
706 XDND_DROP_DATAS(info),
707 operationList,
708 dropLocation);
710 wfree(dropLocation);
711 if (operationList != NULL)
712 WMFreeArray(operationList);
716 /* ----- Destination timer ----- */
717 static void
718 dragSourceResponseTimeOut(void *destView)
720 WMView *view = (WMView*) destView;
721 WMDraggingInfo *info;
723 wwarning("delay for drag source response expired");
724 if (view != NULL) {
725 info = &(W_VIEW_SCREEN(view)->dragInfo);
726 if (XDND_DEST_VIEW_STORED(info))
727 cancelDrop(view, info);
728 else {
729 /* send XdndStatus with action None */
730 sendDnDClientMessage(view,
731 W_VIEW_SCREEN(view)->xdndStatusAtom,
732 0, 0, 0, None);
735 W_DragDestinationInfoClear(info);
739 void
740 W_DragDestinationStopTimer()
742 if (dndDestinationTimer != NULL) {
743 WMDeleteTimerHandler(dndDestinationTimer);
744 dndDestinationTimer = NULL;
748 void
749 W_DragDestinationStartTimer(WMDraggingInfo *info)
751 W_DragDestinationStopTimer();
753 if (XDND_DEST_STATE(info) != idleState
754 || XDND_DEST_VIEW(info) == NULL) {
755 /* note: info->destView == NULL means :
756 Enter message has been received, waiting for Position message */
758 dndDestinationTimer = WMAddTimerHandler(
759 XDND_SOURCE_RESPONSE_MAX_DELAY,
760 dragSourceResponseTimeOut,
761 XDND_DEST_VIEW(info));
764 /* ----- End of Destination timer ----- */
767 /* ----- Destination states ----- */
769 #ifdef XDND_DEBUG
770 static const char*
771 stateName(W_DndState *state)
773 if (state == NULL)
774 return "no state defined";
776 if (state == idleState)
777 return "idleState";
779 if (state == waitEnterState)
780 return "waitEnterState";
782 if (state == inspectDropDataState)
783 return "inspectDropDataState";
785 if (state == dropAllowedState)
786 return "dropAllowedState";
788 if (state == dropNotAllowedState)
789 return "dropNotAllowedState";
791 if (state == waitForDropDataState)
792 return "waitForDropDataState";
794 return "unknown state";
796 #endif
798 static void*
799 idleState(WMView *destView, XClientMessageEvent *event,
800 WMDraggingInfo *info)
802 WMScreen *scr;
803 Atom sourceMsg;
805 scr = W_VIEW_SCREEN(destView);
806 sourceMsg = event->message_type;
808 if (sourceMsg == scr->xdndPositionAtom) {
809 destView->dragDestinationProcs->prepareForDragOperation(destView);
811 if (XDND_SOURCE_TYPES(info) != NULL) {
812 /* enter message infos are available */
813 return checkDropAllowed(destView, event, info);
816 /* waiting for enter message */
817 return waitEnterState;
820 return idleState;
824 /* Source position and action are stored,
825 waiting for xdnd protocol version and source type */
826 static void*
827 waitEnterState(WMView *destView, XClientMessageEvent *event,
828 WMDraggingInfo *info)
830 WMScreen *scr = W_VIEW_SCREEN(destView);
831 Atom sourceMsg = event->message_type;
833 if (sourceMsg == scr->xdndEnterAtom) {
834 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
835 return checkDropAllowed(destView, event, info);
838 return waitEnterState;
842 /* We have requested a data, and have received it */
843 static void*
844 inspectDropDataState(WMView *destView, XClientMessageEvent *event,
845 WMDraggingInfo *info)
847 WMScreen *scr;
848 Atom sourceMsg;
850 scr = W_VIEW_SCREEN(destView);
851 sourceMsg = event->message_type;
853 if (sourceMsg == scr->xdndSelectionAtom) {
854 /* a data has been retrieved, store next available */
855 if (requestDropData(info))
856 return inspectDropDataState;
858 /* all required (and available) datas are stored */
859 if (destView->dragDestinationProcs->inspectDropData(
860 destView,
861 XDND_DROP_DATAS(info)))
862 return checkActionAllowed(destView, info);
864 suspendDropAuthorization(destView, info);
865 return dropNotAllowedState;
868 return inspectDropDataState;
872 static void*
873 dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
874 WMDraggingInfo *info)
876 WMScreen *scr = W_VIEW_SCREEN(destView);
877 Atom sourceMsg = event->message_type;
879 if (sourceMsg == scr->xdndDropAtom) {
880 finishDrop(destView, info);
881 return idleState;
884 return dropNotAllowedState;
888 static void*
889 dropAllowedState(WMView *destView, XClientMessageEvent *event,
890 WMDraggingInfo *info)
892 WMScreen *scr = W_VIEW_SCREEN(destView);
893 Atom sourceMsg = event->message_type;
895 if (sourceMsg == scr->xdndDropAtom) {
896 if (XDND_DROP_DATAS(info) != NULL) {
897 /* drop datas were cached with inspectDropData call */
898 callPerformDragOperation(destView, info);
899 } else {
900 XDND_DROP_DATAS(info) = createDropDataArray(
901 XDND_REQUIRED_TYPES(info));
903 /* store first available data */
904 if (requestDropData(info))
905 return waitForDropDataState;
907 /* no data retrieved */
908 callPerformDragOperation(destView, info);
911 finishDrop(destView, info);
912 return idleState;
915 return dropAllowedState;
919 static void*
920 waitForDropDataState(WMView *destView, XClientMessageEvent *event,
921 WMDraggingInfo *info)
923 WMScreen *scr = W_VIEW_SCREEN(destView);
924 Atom sourceMsg = event->message_type;
926 if (sourceMsg == scr->xdndSelectionAtom) {
927 /* store next data */
928 if (requestDropData(info))
929 return waitForDropDataState;
931 /* all required (and available) datas are stored */
932 callPerformDragOperation(destView, info);
934 finishDrop(destView, info);
935 return idleState;
938 return waitForDropDataState;
941 /* ----- End of Destination states ----- */
944 void
945 W_DragDestinationStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
947 WMView *destView;
948 W_DndState* newState;
950 if (XDND_DEST_VIEW_STORED(info)) {
951 destView = XDND_DEST_VIEW(info);
952 if (XDND_DEST_STATE(info) == NULL)
953 XDND_DEST_STATE(info) = idleState;
955 #ifdef XDND_DEBUG
957 printf("current dest state: %s\n",
958 stateName(XDND_DEST_STATE(info)));
959 #endif
961 newState = (W_DndState*) XDND_DEST_STATE(info)(destView, event, info);
963 #ifdef XDND_DEBUG
965 printf("new dest state: %s\n", stateName(newState));
966 #endif
968 if (XDND_DEST_INFO(info) != NULL) {
969 XDND_DEST_STATE(info) = newState;
970 if (XDND_DEST_STATE(info) != idleState)
971 W_DragDestinationStartTimer(info);
977 static void
978 realizedObserver(void *self, WMNotification *notif)
980 WMView *view = (WMView*)WMGetNotificationObject(notif);
981 WMScreen *scr = W_VIEW_SCREEN(view);
983 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
984 scr->xdndAwareAtom,
985 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace,
986 &XDNDversion, 1);
988 WMRemoveNotificationObserver(self);
992 void
993 W_SetXdndAwareProperty(WMScreen *scr, WMView *view, Atom *types,
994 int typeCount)
996 WMView *toplevel = W_TopLevelOfView(view);
998 if (!toplevel->flags.xdndHintSet) {
999 toplevel->flags.xdndHintSet = 1;
1001 if (toplevel->flags.realized) {
1002 XChangeProperty(scr->display, W_VIEW_DRAWABLE(toplevel),
1003 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT,
1004 PropModeReplace, &XDNDversion, 1);
1005 } else {
1006 WMAddNotificationObserver(realizedObserver,
1007 /* just use as an id */
1008 &view->dragDestinationProcs,
1009 WMViewRealizedNotification,
1010 toplevel);
1016 void
1017 WMRegisterViewForDraggedTypes(WMView *view, WMArray *acceptedTypes)
1019 Atom *types;
1020 int typeCount;
1021 int i;
1023 typeCount = WMGetArrayItemCount(acceptedTypes);
1024 types = wmalloc(sizeof(Atom)*(typeCount+1));
1026 for (i = 0; i < typeCount; i++) {
1027 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display,
1028 WMGetFromArray(acceptedTypes, i),
1029 False);
1031 types[i] = 0;
1033 view->droppableTypes = types;
1034 /* WMFreeArray(acceptedTypes); */
1036 W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view, types, typeCount);
1040 void
1041 WMUnregisterViewDraggedTypes(WMView *view)
1043 if (view->droppableTypes != NULL)
1044 wfree(view->droppableTypes);
1045 view->droppableTypes = NULL;
1050 requestedOperation: operation requested by the source
1051 sourceDataTypes: data types (mime-types strings) supported by the source
1052 (never NULL, destroyed when drop ends)
1053 return operation allowed by destination (self)
1055 static WMDragOperationType
1056 defAllowedOperation(WMView *self,
1057 WMDragOperationType requestedOperation,
1058 WMArray* sourceDataTypes)
1060 /* no operation allowed */
1061 return WDOperationNone;
1066 requestedOperation: operation requested by the source
1067 sourceDataTypes: data types (mime-types strings) supported by the source
1068 (never NULL, destroyed when drop ends)
1069 return data types (mime-types strings) required by destination (self)
1070 or NULL if no suitable data type is available (force
1071 to 2nd pass with full source type list).
1073 static WMArray*
1074 defRequiredDataTypes (WMView *self,
1075 WMDragOperationType requestedOperation,
1076 WMArray* sourceDataTypes)
1078 /* no data type allowed (NULL even at 2nd pass) */
1079 return NULL;
1084 Executed when the drag enters destination (self)
1086 static void
1087 defPrepareForDragOperation(WMView *self)
1093 Checks datas to be dropped (optional).
1094 dropDatas: datas (WMData*) required by destination (self)
1095 (given in same order as returned by requiredDataTypes).
1096 A NULL data means it couldn't be retreived.
1097 Destroyed when drop ends.
1098 return true if data array is ok
1100 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
1104 Process drop
1105 dropDatas: datas (WMData*) required by destination (self)
1106 (given in same order as returned by requiredDataTypes).
1107 A NULL data means it couldn't be retrivied.
1108 Destroyed when drop ends.
1109 operationList: if source operation is WDOperationAsk, contains
1110 operations (and associated texts) that can be asked
1111 to source. (destroyed after performDragOperation call)
1112 Otherwise this parameter is NULL.
1114 static void
1115 defPerformDragOperation(WMView *self, WMArray *dropDatas,
1116 WMArray *operationList, WMPoint *dropLocation)
1121 /* Executed after drop */
1122 static void
1123 defConcludeDragOperation(WMView *self)
1128 void
1129 WMSetViewDragDestinationProcs(WMView *view, WMDragDestinationProcs *procs)
1131 if (view->dragDestinationProcs == NULL) {
1132 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
1133 } else {
1134 free(view->dragDestinationProcs);
1137 *view->dragDestinationProcs = *procs;
1139 /*XXX fill in non-implemented stuffs */
1140 if (procs->allowedOperation == NULL) {
1141 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
1143 if (procs->allowedOperation == NULL) {
1144 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
1147 /* note: inspectDropData can be NULL, if data consultation is not needed
1148 to give drop permission */
1150 if (procs->prepareForDragOperation == NULL) {
1151 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
1153 if (procs->performDragOperation == NULL) {
1154 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
1156 if (procs->concludeDragOperation == NULL) {
1157 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;