Fix periodic focus bug
[wmaker-crm.git] / WINGs / dragdestination.c
blob9206cb82cee5de8d316d0583182a99719aa6909b
3 #include "WINGsP.h"
4 #include <X11/Xatom.h>
6 #define XDND_SOURCE_RESPONSE_MAX_DELAY 3000
8 #define XDND_PROPERTY_FORMAT 32
9 #define XDND_ACTION_DESCRIPTION_FORMAT 8
11 #define XDND_SOURCE_VERSION(dragInfo) dragInfo->protocolVersion
12 #define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
13 #define XDND_AWARE_VIEW(dragInfo) dragInfo->destInfo->xdndAwareView
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_ACTION_CHANGED(dragInfo) dragInfo->destInfo->sourceActionChanged
18 #define XDND_SOURCE_TYPES(dragInfo) dragInfo->destInfo->sourceTypes
19 #define XDND_TYPE_LIST_AVAILABLE(dragInfo) dragInfo->destInfo->typeListAvailable
20 #define XDND_REQUIRED_TYPES(dragInfo) dragInfo->destInfo->requiredTypes
21 #define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
22 #define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
23 #define XDND_SOURCE_OPERATIONS(dragInfo) dragInfo->destInfo->sourceOperations
24 #define XDND_DROP_DATAS(dragInfo) dragInfo->destInfo->dropDatas
25 #define XDND_DROP_DATA_COUNT(dragInfo) dragInfo->destInfo->dropDataCount
26 #define XDND_DEST_VIEW_IS_REGISTERED(dragInfo) ((dragInfo->destInfo) != NULL)\
27 && ((dragInfo->destInfo->destView->dragDestinationProcs) != NULL)
30 static unsigned char XDNDversion = XDND_VERSION;
31 static WMHandlerID dndDestinationTimer = NULL;
34 static void* idleState(WMView *destView, XClientMessageEvent *event,
35 WMDraggingInfo *info);
36 static void* waitEnterState(WMView *destView, XClientMessageEvent *event,
37 WMDraggingInfo *info);
38 static void* inspectDropDataState(WMView *destView, XClientMessageEvent *event,
39 WMDraggingInfo *info);
40 static void* dropAllowedState(WMView *destView, XClientMessageEvent *event,
41 WMDraggingInfo *info);
42 static void* dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
43 WMDraggingInfo *info);
44 static void* waitForDropDataState(WMView *destView, XClientMessageEvent *event,
45 WMDraggingInfo *info);
47 /* ----- Types & datas list ----- */
48 static void
49 freeSourceTypeArrayItem(void *type)
51 XFree(type);
55 static WMArray*
56 createSourceTypeArray(int initialSize)
58 return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
62 static void
63 freeDropDataArrayItem(void* data)
65 if (data != NULL)
66 WMReleaseData((WMData*) data);
70 static WMArray*
71 createDropDataArray(WMArray *requiredTypes)
73 if (requiredTypes != NULL)
74 return WMCreateArrayWithDestructor(
75 WMGetArrayItemCount(requiredTypes),
76 freeDropDataArrayItem);
78 else
79 return WMCreateArray(0);
82 static WMArray*
83 getTypesFromTypeList(WMScreen *scr, Window sourceWin)
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 stored 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 stored source types fits, but the whole type list
149 hasn't been retrieved yet. */
150 WMFreeArray(XDND_SOURCE_TYPES(info));
151 XDND_SOURCE_TYPES(info) = getTypesFromTypeList(
152 scr,
153 XDND_SOURCE_WIN(info));
154 /* Don't retrieve the type list again */
155 XDND_TYPE_LIST_AVAILABLE(info) = False;
157 requiredTypes =
158 destView->dragDestinationProcs->requiredDataTypes(
159 destView,
160 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
161 XDND_SOURCE_TYPES(info));
164 XDND_REQUIRED_TYPES(info) = requiredTypes;
168 char*
169 getNextRequestedDataType(WMDraggingInfo *info)
171 /* get the type of the first data not yet retrieved from selection */
172 int nextTypeIndex;
174 if (XDND_REQUIRED_TYPES(info) != NULL) {
175 nextTypeIndex = WMGetArrayItemCount(XDND_DROP_DATAS(info));
176 return WMGetFromArray(XDND_REQUIRED_TYPES(info), nextTypeIndex);
177 /* NULL if no more type */
178 } else
179 return NULL;
183 /* ----- Action list ----- */
185 WMArray*
186 sourceOperationList(WMScreen *scr, Window sourceWin)
188 Atom dataType, *actionList;
189 int i, size;
190 unsigned long count, remaining;
191 unsigned char* actionDatas = NULL;
192 unsigned char* descriptionList = NULL;
193 WMArray* operationArray;
194 WMDragOperationItem* operationItem;
195 char* description;
197 remaining = 0;
198 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionListAtom,
199 0, 0x8000000L, False, XA_ATOM, &dataType, &size,
200 &count, &remaining, &actionDatas);
202 if (dataType!=XA_ATOM || size!=XDND_PROPERTY_FORMAT || count==0 || !actionDatas) {
203 wwarning("Cannot read action list");
204 if (actionDatas) {
205 XFree(actionDatas);
207 return NULL;
210 actionList = (Atom*)actionDatas;
212 XGetWindowProperty(scr->display, sourceWin, scr->xdndActionDescriptionAtom,
213 0, 0x8000000L, False, XA_STRING, &dataType, &size,
214 &count, &remaining, &descriptionList);
216 if (dataType!=XA_STRING || size!=XDND_ACTION_DESCRIPTION_FORMAT ||
217 count==0 || !descriptionList) {
218 wwarning("Cannot read action description list");
219 if (actionList) {
220 XFree(actionList);
222 if (descriptionList) {
223 XFree(descriptionList);
225 return NULL;
228 operationArray = WMCreateDragOperationArray(count);
229 description = (char*)descriptionList;
231 for (i=0; count > 0; i++) {
232 size = strlen(description);
233 operationItem = WMCreateDragOperationItem(
234 W_ActionToOperation(scr, actionList[i]),
235 wstrdup(description));
237 WMAddToArray(operationArray, operationItem);
238 count -= (size + 1); /* -1 : -NULL char */
240 /* next description */
241 description = &(description[size + 1]);
244 XFree(actionList);
245 XFree(descriptionList);
247 return operationArray;
251 /* ----- Dragging Info ----- */
252 static void
253 updateSourceWindow(WMDraggingInfo *info, XClientMessageEvent *event)
255 XDND_SOURCE_WIN(info) = (Window) event->data.l[0];
259 static WMView*
260 findChildInView(WMView* parent, int x, int y)
262 if (parent->childrenList == NULL)
263 return parent;
264 else {
265 WMView* child = parent->childrenList;
267 while (child != NULL
268 && (! child->flags.mapped
269 || x < WMGetViewPosition(child).x
270 || x > WMGetViewPosition(child).x + WMGetViewSize(child).width
271 || y < WMGetViewPosition(child).y
272 || y > WMGetViewPosition(child).y + WMGetViewSize(child).height))
274 child = child->nextSister;
276 if (child == NULL)
277 return parent;
278 else
279 return findChildInView(child,
280 x - WMGetViewPosition(child).x,
281 y - WMGetViewPosition(child).y);
286 static WMView*
287 findDestinationViewInToplevel(WMView* toplevel, int x, int y)
289 WMScreen *scr = W_VIEW_SCREEN(toplevel);
290 Window toplevelWin = WMViewXID(toplevel);
291 int xInToplevel, yInToplevel;
292 Window child, foo;
293 WMView *childView;
295 XTranslateCoordinates(scr->display, scr->rootWin, toplevelWin,
296 x, y, &xInToplevel, &yInToplevel,
297 &foo);
298 return findChildInView(toplevel, xInToplevel, yInToplevel);
302 /* Clear datas only used by current destination view */
303 static void
304 freeDestinationViewInfos(WMDraggingInfo *info)
306 if (XDND_SOURCE_TYPES(info) != NULL) {
307 WMFreeArray(XDND_SOURCE_TYPES(info));
308 XDND_SOURCE_TYPES(info) = NULL;
311 if (XDND_DROP_DATAS(info) != NULL) {
312 WMFreeArray(XDND_DROP_DATAS(info));
313 XDND_DROP_DATAS(info) = NULL;
316 XDND_REQUIRED_TYPES(info) = NULL;
320 void
321 W_DragDestinationInfoClear(WMDraggingInfo *info)
323 W_DragDestinationStopTimer();
325 if (XDND_DEST_INFO(info) != NULL) {
326 freeDestinationViewInfos(info);
328 wfree(XDND_DEST_INFO(info));
329 XDND_DEST_INFO(info) = NULL;
334 static void
335 initDestinationDragInfo(WMDraggingInfo *info, WMView *destView)
337 wassertr(destView != NULL);
339 XDND_DEST_INFO(info) =
340 (W_DragDestinationInfo*) wmalloc(sizeof(W_DragDestinationInfo));
342 XDND_DEST_STATE(info) = idleState;
343 XDND_DEST_VIEW(info) = destView;
345 XDND_SOURCE_ACTION_CHANGED(info) = False;
346 XDND_SOURCE_TYPES(info) = NULL;
347 XDND_REQUIRED_TYPES(info) = NULL;
348 XDND_DROP_DATAS(info) = NULL;
352 void
353 W_DragDestinationStoreEnterMsgInfo(WMDraggingInfo *info,
354 WMView *toplevel, XClientMessageEvent *event)
356 WMScreen *scr = W_VIEW_SCREEN(toplevel);
358 if (XDND_DEST_INFO(info) == NULL)
359 initDestinationDragInfo(info, toplevel);
361 XDND_SOURCE_VERSION(info) = (event->data.l[1] >> 24);
362 XDND_AWARE_VIEW(info) = toplevel;
363 updateSourceWindow(info, event);
366 if (event->data.l[1] & 1)
367 /* XdndTypeList property is available */
368 /* XDND_SOURCE_TYPES(info) = getTypesFromTypeList(scr, XDND_SOURCE_WIN(info));
369 else
370 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
372 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
374 /* to use if the 3 types are not enough */
375 XDND_TYPE_LIST_AVAILABLE(info) = (event->data.l[1] & 1);
379 void
380 W_DragDestinationStorePositionMsgInfo(WMDraggingInfo *info,
381 WMView *toplevel, XClientMessageEvent *event)
383 int x = event->data.l[2] >> 16;
384 int y = event->data.l[2] & 0xffff;
385 WMView *newDestView;
387 newDestView = findDestinationViewInToplevel(toplevel, x, y);
389 if (XDND_DEST_INFO(info) == NULL) {
390 initDestinationDragInfo(info, newDestView);
391 XDND_AWARE_VIEW(info) = toplevel;
392 updateSourceWindow(info, event);
393 } else {
394 if (newDestView != XDND_DEST_VIEW(info)) {
395 updateSourceWindow(info, event);
396 XDND_DEST_VIEW(info) = newDestView;
397 XDND_SOURCE_ACTION_CHANGED(info) = False;
399 if (XDND_DEST_STATE(info) != waitEnterState)
400 XDND_DEST_STATE(info) = idleState;
401 } else {
402 XDND_SOURCE_ACTION_CHANGED(info) = (XDND_SOURCE_ACTION(info) != event->data.l[4]);
406 XDND_SOURCE_ACTION(info) = event->data.l[4];
408 /* note: source position is not stored */
411 /* ----- End of Dragging Info ----- */
414 /* ----- Messages ----- */
416 /* send a DnD message to the source window */
417 static void
418 sendDnDClientMessage(WMDraggingInfo *info, Atom message,
419 unsigned long data1,
420 unsigned long data2,
421 unsigned long data3,
422 unsigned long data4)
424 if (! W_SendDnDClientMessage(W_VIEW_SCREEN(XDND_AWARE_VIEW(info))->display,
425 XDND_SOURCE_WIN(info),
426 message,
427 WMViewXID(XDND_AWARE_VIEW(info)),
428 data1,
429 data2,
430 data3,
431 data4)) {
432 /* drop failed */
433 W_DragDestinationInfoClear(info);
438 /* send a xdndStatus message to the source, with position and size
439 of the destination if it has no subwidget (requesting a position message
440 on every move otherwise) */
441 static void
442 sendStatusMessage(WMView *destView, WMDraggingInfo *info, Atom action)
444 unsigned long data1;
446 data1 = (action == None) ? 0 : 1;
448 if (destView->childrenList == NULL) {
449 WMScreen *scr = W_VIEW_SCREEN(destView);
450 int destX, destY;
451 WMSize destSize = WMGetViewSize(destView);
452 Window foo;
454 XTranslateCoordinates(scr->display, WMViewXID(destView), scr->rootWin,
455 0, 0, &destX, &destY,
456 &foo);
458 sendDnDClientMessage(info,
459 W_VIEW_SCREEN(destView)->xdndStatusAtom,
460 data1,
461 (destX << 16)|destY,
462 (destSize.width << 16)|destSize.height,
463 action);
464 } else {
465 /* set bit 1 to request explicitly position message on every move */
466 data1 = data1 | 2;
468 sendDnDClientMessage(info,
469 W_VIEW_SCREEN(destView)->xdndStatusAtom,
470 data1,
473 action);
478 static void
479 storeDropData(WMView *destView, Atom selection, Atom target,
480 Time timestamp, void *cdata, WMData *data)
482 WMScreen *scr = W_VIEW_SCREEN(destView);
483 WMDraggingInfo *info = &(scr->dragInfo);
484 WMData *dataToStore = NULL;
486 if (data != NULL)
487 dataToStore = WMRetainData(data);
489 if (XDND_DEST_INFO(info) != NULL && XDND_DROP_DATAS(info) != NULL) {
490 WMAddToArray(XDND_DROP_DATAS(info), dataToStore);
491 W_SendDnDClientMessage(scr->display, WMViewXID(destView),
492 scr->xdndSelectionAtom,
493 WMViewXID(destView),
494 0, 0, 0, 0);
499 Bool
500 requestDropDataInSelection(WMView *destView, char* type)
502 WMScreen *scr = W_VIEW_SCREEN(destView);
504 if (type != NULL) {
505 if (!WMRequestSelection(destView,
506 scr->xdndSelectionAtom,
507 XInternAtom(scr->display, type, False),
508 CurrentTime,
509 storeDropData, NULL)) {
510 wwarning("could not request data for dropped data");
511 return False;
514 return True;
517 return False;
521 Bool
522 requestDropData(WMDraggingInfo *info)
524 WMView *destView = XDND_DEST_VIEW(info);
525 char* nextType = getNextRequestedDataType(info);
527 while ((nextType != NULL)
528 && (!requestDropDataInSelection(destView, nextType)) ) {
529 /* store NULL if request failed, and try with next type */
530 WMAddToArray(XDND_DROP_DATAS(info), NULL);
531 nextType = getNextRequestedDataType(info);
534 /* remains types to retrieve ? */
535 return (nextType != NULL);
539 static void
540 concludeDrop(WMView *destView)
542 destView->dragDestinationProcs->concludeDragOperation(destView);
546 /* send cancel message to the source */
547 static void
548 cancelDrop(WMView *destView, WMDraggingInfo *info)
550 sendStatusMessage(destView, info, None);
551 concludeDrop(destView);
552 freeDestinationViewInfos(info);
556 /* suspend drop, when dragged icon enter an unregistered view
557 or a register view that doesn't accept the drop */
558 static void
559 suspendDropAuthorization(WMView *destView, WMDraggingInfo *info)
561 sendStatusMessage(destView, info, None);
563 /* Free datas that depend on destination behaviour */
564 if (XDND_DROP_DATAS(info) != NULL) {
565 WMFreeArray(XDND_DROP_DATAS(info));
566 XDND_DROP_DATAS(info) = NULL;
569 XDND_REQUIRED_TYPES(info) = NULL;
573 /* cancel drop on Enter message, if protocol version is nok */
574 void
575 W_DragDestinationCancelDropOnEnter(WMView *toplevel, WMDraggingInfo *info)
577 if (XDND_DEST_VIEW_IS_REGISTERED(info))
578 cancelDrop(XDND_DEST_VIEW(info), info);
579 else
580 sendStatusMessage(toplevel, info, None);
582 W_DragDestinationInfoClear(info);
586 static void
587 finishDrop(WMView *destView, WMDraggingInfo *info)
589 sendDnDClientMessage(info,
590 W_VIEW_SCREEN(destView)->xdndFinishedAtom,
591 0, 0, 0, 0);
592 concludeDrop(destView);
593 W_DragDestinationInfoClear(info);
597 static Atom
598 getAllowedAction(WMView *destView, WMDraggingInfo *info)
600 WMScreen *scr = W_VIEW_SCREEN(destView);
602 return W_OperationToAction(scr,
603 destView->dragDestinationProcs->allowedOperation(
604 destView,
605 W_ActionToOperation(scr, XDND_SOURCE_ACTION(info)),
606 XDND_SOURCE_TYPES(info)));
610 static void*
611 checkActionAllowed(WMView *destView, WMDraggingInfo* info)
613 XDND_DEST_ACTION(info) =
614 getAllowedAction(destView, info);
616 if (XDND_DEST_ACTION(info) == None) {
617 suspendDropAuthorization(destView, info);
618 return dropNotAllowedState;
621 sendStatusMessage(destView, info, XDND_DEST_ACTION(info));
622 return dropAllowedState;
626 static void*
627 checkDropAllowed(WMView *destView, XClientMessageEvent *event,
628 WMDraggingInfo* info)
630 storeRequiredTypeList(info);
632 if (destView->dragDestinationProcs->inspectDropData != NULL) {
633 XDND_DROP_DATAS(info) = createDropDataArray(
634 XDND_REQUIRED_TYPES(info));
636 /* store first available data */
637 if (requestDropData(info))
638 return inspectDropDataState;
640 /* no data retrieved, but inspect can allow it */
641 if (destView->dragDestinationProcs->inspectDropData(
642 destView,
643 XDND_DROP_DATAS(info)))
644 return checkActionAllowed(destView, info);
646 suspendDropAuthorization(destView, info);
647 return dropNotAllowedState;
650 return checkActionAllowed(destView, info);
654 static WMPoint*
655 getDropLocationInView(WMView *view)
657 Window rootWin, childWin;
658 int rootX, rootY;
659 unsigned int mask;
660 WMPoint* location;
662 location = (WMPoint*) wmalloc(sizeof(WMPoint));
664 XQueryPointer(
665 W_VIEW_SCREEN(view)->display,
666 WMViewXID(view), &rootWin, &childWin,
667 &rootX, &rootY,
668 &(location->x), &(location->y),
669 &mask);
671 return location;
675 static void
676 callPerformDragOperation(WMView *destView, WMDraggingInfo *info)
678 WMArray *operationList = NULL;
679 WMScreen *scr = W_VIEW_SCREEN(destView);
680 WMPoint* dropLocation;
682 if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
683 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
685 dropLocation = getDropLocationInView(destView);
686 destView->dragDestinationProcs->performDragOperation(
687 destView,
688 XDND_DROP_DATAS(info),
689 operationList,
690 dropLocation);
692 wfree(dropLocation);
693 if (operationList != NULL)
694 WMFreeArray(operationList);
698 /* ----- Destination timer ----- */
699 static void
700 dragSourceResponseTimeOut(void *destView)
702 WMView *view = (WMView*) destView;
703 WMDraggingInfo *info;
705 wwarning("delay for drag source response expired");
706 info = &(W_VIEW_SCREEN(view)->dragInfo);
707 if (XDND_DEST_VIEW_IS_REGISTERED(info))
708 cancelDrop(view, info);
709 else {
710 sendStatusMessage(view, info, None);
713 W_DragDestinationInfoClear(info);
717 void
718 W_DragDestinationStopTimer()
720 if (dndDestinationTimer != NULL) {
721 WMDeleteTimerHandler(dndDestinationTimer);
722 dndDestinationTimer = NULL;
727 void
728 W_DragDestinationStartTimer(WMDraggingInfo *info)
730 W_DragDestinationStopTimer();
732 if (XDND_DEST_STATE(info) != idleState)
733 dndDestinationTimer = WMAddTimerHandler(
734 XDND_SOURCE_RESPONSE_MAX_DELAY,
735 dragSourceResponseTimeOut,
736 XDND_DEST_VIEW(info));
738 /* ----- End of Destination timer ----- */
741 /* ----- Destination states ----- */
743 #ifdef XDND_DEBUG
744 static const char*
745 stateName(W_DndState *state)
747 if (state == NULL)
748 return "no state defined";
750 if (state == idleState)
751 return "idleState";
753 if (state == waitEnterState)
754 return "waitEnterState";
756 if (state == inspectDropDataState)
757 return "inspectDropDataState";
759 if (state == dropAllowedState)
760 return "dropAllowedState";
762 if (state == dropNotAllowedState)
763 return "dropNotAllowedState";
765 if (state == waitForDropDataState)
766 return "waitForDropDataState";
768 return "unknown state";
770 #endif
773 static void*
774 idleState(WMView *destView, XClientMessageEvent *event,
775 WMDraggingInfo *info)
777 WMScreen *scr;
778 Atom sourceMsg;
780 if (destView->dragDestinationProcs != NULL) {
781 scr = W_VIEW_SCREEN(destView);
782 sourceMsg = event->message_type;
784 if (sourceMsg == scr->xdndPositionAtom) {
785 destView->dragDestinationProcs->prepareForDragOperation(destView);
787 if (XDND_SOURCE_TYPES(info) != NULL) {
788 /* enter message infos are available */
789 return checkDropAllowed(destView, event, info);
792 /* waiting for enter message */
793 return waitEnterState;
797 suspendDropAuthorization(destView, info);
798 return idleState;
802 /* Source position and action are stored,
803 waiting for xdnd protocol version and source type */
804 static void*
805 waitEnterState(WMView *destView, XClientMessageEvent *event,
806 WMDraggingInfo *info)
808 WMScreen *scr = W_VIEW_SCREEN(destView);
809 Atom sourceMsg = event->message_type;
811 if (sourceMsg == scr->xdndEnterAtom) {
812 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
813 return checkDropAllowed(destView, event, info);
816 return waitEnterState;
820 /* We have requested a data, and have received it */
821 static void*
822 inspectDropDataState(WMView *destView, XClientMessageEvent *event,
823 WMDraggingInfo *info)
825 WMScreen *scr;
826 Atom sourceMsg;
828 scr = W_VIEW_SCREEN(destView);
829 sourceMsg = event->message_type;
831 if (sourceMsg == scr->xdndSelectionAtom) {
832 /* a data has been retrieved, store next available */
833 if (requestDropData(info))
834 return inspectDropDataState;
836 /* all required (and available) datas are stored */
837 if (destView->dragDestinationProcs->inspectDropData(
838 destView,
839 XDND_DROP_DATAS(info)))
840 return checkActionAllowed(destView, info);
842 suspendDropAuthorization(destView, info);
843 return dropNotAllowedState;
846 return inspectDropDataState;
850 static void*
851 dropNotAllowedState(WMView *destView, XClientMessageEvent *event,
852 WMDraggingInfo *info)
854 WMScreen *scr = W_VIEW_SCREEN(destView);
855 Atom sourceMsg = event->message_type;
857 if (sourceMsg == scr->xdndDropAtom) {
858 finishDrop(destView, info);
859 return idleState;
862 if (sourceMsg == scr->xdndPositionAtom) {
863 if (XDND_SOURCE_ACTION_CHANGED(info)) {
864 return checkDropAllowed(destView, event, info);
865 } else {
866 sendStatusMessage(destView, info, None);
867 return dropNotAllowedState;
871 return dropNotAllowedState;
875 static void*
876 dropAllowedState(WMView *destView, XClientMessageEvent *event,
877 WMDraggingInfo *info)
879 WMScreen *scr = W_VIEW_SCREEN(destView);
880 Atom sourceMsg = event->message_type;
882 if (sourceMsg == scr->xdndDropAtom) {
883 if (XDND_DROP_DATAS(info) != NULL) {
884 /* drop datas were cached with inspectDropData call */
885 callPerformDragOperation(destView, info);
886 } else {
887 XDND_DROP_DATAS(info) = createDropDataArray(
888 XDND_REQUIRED_TYPES(info));
890 /* store first available data */
891 if (requestDropData(info))
892 return waitForDropDataState;
894 /* no data retrieved */
895 callPerformDragOperation(destView, info);
898 finishDrop(destView, info);
899 return idleState;
902 if (sourceMsg == scr->xdndPositionAtom) {
903 if (XDND_SOURCE_ACTION_CHANGED(info)) {
904 return checkDropAllowed(destView, event, info);
905 } else {
906 sendStatusMessage(destView, info, XDND_DEST_ACTION(info));
907 return dropAllowedState;
911 return dropAllowedState;
915 static void*
916 waitForDropDataState(WMView *destView, XClientMessageEvent *event,
917 WMDraggingInfo *info)
919 WMScreen *scr = W_VIEW_SCREEN(destView);
920 Atom sourceMsg = event->message_type;
922 if (sourceMsg == scr->xdndSelectionAtom) {
923 /* store next data */
924 if (requestDropData(info))
925 return waitForDropDataState;
927 /* all required (and available) datas are stored */
928 callPerformDragOperation(destView, info);
930 finishDrop(destView, info);
931 return idleState;
934 return waitForDropDataState;
937 /* ----- End of Destination states ----- */
940 void
941 W_DragDestinationStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
943 WMView *destView;
944 W_DndState* newState;
946 wassertr(XDND_DEST_INFO(info) != NULL);
947 wassertr(XDND_DEST_VIEW(info) != NULL);
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);
974 static void
975 realizedObserver(void *self, WMNotification *notif)
977 WMView *view = (WMView*)WMGetNotificationObject(notif);
978 WMScreen *scr = W_VIEW_SCREEN(view);
980 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
981 scr->xdndAwareAtom,
982 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace,
983 &XDNDversion, 1);
985 WMRemoveNotificationObserver(self);
989 void
990 W_SetXdndAwareProperty(WMScreen *scr, WMView *view, Atom *types,
991 int typeCount)
993 WMView *toplevel = W_TopLevelOfView(view);
995 if (!toplevel->flags.xdndHintSet) {
996 toplevel->flags.xdndHintSet = 1;
998 if (toplevel->flags.realized) {
999 XChangeProperty(scr->display, W_VIEW_DRAWABLE(toplevel),
1000 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT,
1001 PropModeReplace, &XDNDversion, 1);
1002 } else {
1003 WMAddNotificationObserver(realizedObserver,
1004 /* just use as an id */
1005 &view->dragDestinationProcs,
1006 WMViewRealizedNotification,
1007 toplevel);
1013 void
1014 WMRegisterViewForDraggedTypes(WMView *view, WMArray *acceptedTypes)
1016 Atom *types;
1017 int typeCount;
1018 int i;
1020 typeCount = WMGetArrayItemCount(acceptedTypes);
1021 types = wmalloc(sizeof(Atom)*(typeCount+1));
1023 for (i = 0; i < typeCount; i++) {
1024 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display,
1025 WMGetFromArray(acceptedTypes, i),
1026 False);
1028 types[i] = 0;
1030 view->droppableTypes = types;
1031 /* WMFreeArray(acceptedTypes); */
1033 W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view, types, typeCount);
1037 void
1038 WMUnregisterViewDraggedTypes(WMView *view)
1040 if (view->droppableTypes != NULL)
1041 wfree(view->droppableTypes);
1042 view->droppableTypes = NULL;
1047 requestedOperation: operation requested by the source
1048 sourceDataTypes: data types (mime-types strings) supported by the source
1049 (never NULL, destroyed when drop ends)
1050 return operation allowed by destination (self)
1052 static WMDragOperationType
1053 defAllowedOperation(WMView *self,
1054 WMDragOperationType requestedOperation,
1055 WMArray* sourceDataTypes)
1057 /* no operation allowed */
1058 return WDOperationNone;
1063 requestedOperation: operation requested by the source
1064 sourceDataTypes: data types (mime-types strings) supported by the source
1065 (never NULL, destroyed when drop ends)
1066 return data types (mime-types strings) required by destination (self)
1067 or NULL if no suitable data type is available (force
1068 to 2nd pass with full source type list).
1070 static WMArray*
1071 defRequiredDataTypes (WMView *self,
1072 WMDragOperationType requestedOperation,
1073 WMArray* sourceDataTypes)
1075 /* no data type allowed (NULL even at 2nd pass) */
1076 return NULL;
1081 Executed when the drag enters destination (self)
1083 static void
1084 defPrepareForDragOperation(WMView *self)
1090 Checks datas to be dropped (optional).
1091 dropDatas: datas (WMData*) required by destination (self)
1092 (given in same order as returned by requiredDataTypes).
1093 A NULL data means it couldn't be retreived.
1094 Destroyed when drop ends.
1095 return true if data array is ok
1097 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
1101 Process drop
1102 dropDatas: datas (WMData*) required by destination (self)
1103 (given in same order as returned by requiredDataTypes).
1104 A NULL data means it couldn't be retrieved.
1105 Destroyed when drop ends.
1106 operationList: if source operation is WDOperationAsk, contains
1107 operations (and associated texts) that can be asked
1108 to source. (destroyed after performDragOperation call)
1109 Otherwise this parameter is NULL.
1111 static void
1112 defPerformDragOperation(WMView *self, WMArray *dropDatas,
1113 WMArray *operationList, WMPoint *dropLocation)
1118 /* Executed after drop */
1119 static void
1120 defConcludeDragOperation(WMView *self)
1125 void
1126 WMSetViewDragDestinationProcs(WMView *view, WMDragDestinationProcs *procs)
1128 if (view->dragDestinationProcs == NULL) {
1129 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
1130 } else {
1131 free(view->dragDestinationProcs);
1134 *view->dragDestinationProcs = *procs;
1136 /*XXX fill in non-implemented stuffs */
1137 if (procs->allowedOperation == NULL) {
1138 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
1140 if (procs->allowedOperation == NULL) {
1141 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
1144 /* note: inspectDropData can be NULL, if data consultation is not needed
1145 to give drop permission */
1147 if (procs->prepareForDragOperation == NULL) {
1148 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
1150 if (procs->performDragOperation == NULL) {
1151 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
1153 if (procs->concludeDragOperation == NULL) {
1154 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;