5 #define XDND_SOURCE_RESPONSE_MAX_DELAY 3000
7 #define XDND_PROPERTY_FORMAT 32
8 #define XDND_ACTION_DESCRIPTION_FORMAT 8
10 #define XDND_SOURCE_VERSION(dragInfo) dragInfo->protocolVersion
11 #define XDND_DEST_INFO(dragInfo) dragInfo->destInfo
12 #define XDND_AWARE_VIEW(dragInfo) dragInfo->destInfo->xdndAwareView
13 #define XDND_SOURCE_WIN(dragInfo) dragInfo->destInfo->sourceWindow
14 #define XDND_DEST_VIEW(dragInfo) dragInfo->destInfo->destView
15 #define XDND_DEST_STATE(dragInfo) dragInfo->destInfo->state
16 #define XDND_SOURCE_ACTION_CHANGED(dragInfo) dragInfo->destInfo->sourceActionChanged
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_IS_REGISTERED(dragInfo) ((dragInfo->destInfo) != NULL)\
26 && ((dragInfo->destInfo->destView->dragDestinationProcs) != NULL)
28 static unsigned char XDNDversion
= XDND_VERSION
;
29 static WMHandlerID dndDestinationTimer
= NULL
;
31 static void *idleState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
32 static void *waitEnterState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
33 static void *inspectDropDataState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
34 static void *dropAllowedState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
35 static void *dropNotAllowedState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
36 static void *waitForDropDataState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
);
38 /* ----- Types & datas list ----- */
39 static void freeSourceTypeArrayItem(void *type
)
44 static WMArray
*createSourceTypeArray(int initialSize
)
46 return WMCreateArrayWithDestructor(initialSize
, freeSourceTypeArrayItem
);
49 static void freeDropDataArrayItem(void *data
)
52 WMReleaseData((WMData
*) data
);
55 static WMArray
*createDropDataArray(WMArray
* requiredTypes
)
57 if (requiredTypes
!= NULL
)
58 return WMCreateArrayWithDestructor(WMGetArrayItemCount(requiredTypes
), freeDropDataArrayItem
);
61 return WMCreateArray(0);
64 static WMArray
*getTypesFromTypeList(WMScreen
* scr
, Window sourceWin
)
70 unsigned long count
, remaining
;
71 unsigned char *data
= NULL
;
73 XGetWindowProperty(scr
->display
, sourceWin
, scr
->xdndTypeListAtom
,
74 0, 0x8000000L
, False
, XA_ATOM
, &dataType
, &format
, &count
, &remaining
, &data
);
76 if (dataType
!= XA_ATOM
|| format
!= XDND_PROPERTY_FORMAT
|| count
== 0 || !data
) {
80 return createSourceTypeArray(0);
83 typeList
= createSourceTypeArray(count
);
84 typeAtomList
= (Atom
*) data
;
85 for (i
= 0; i
< count
; i
++) {
86 WMAddToArray(typeList
, XGetAtomName(scr
->display
, typeAtomList
[i
]));
94 static WMArray
*getTypesFromThreeTypes(WMScreen
* scr
, XClientMessageEvent
* event
)
100 typeList
= createSourceTypeArray(3);
101 for (i
= 2; i
< 5; i
++) {
102 if (event
->data
.l
[i
] != None
) {
103 atom
= (Atom
) event
->data
.l
[i
];
104 WMAddToArray(typeList
, XGetAtomName(scr
->display
, atom
));
111 void storeRequiredTypeList(WMDraggingInfo
* info
)
113 WMView
*destView
= XDND_DEST_VIEW(info
);
114 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
115 WMArray
*requiredTypes
;
117 /* First, see if the stored source types are enough for dest requirements */
118 requiredTypes
= destView
->dragDestinationProcs
->requiredDataTypes(destView
,
119 W_ActionToOperation(scr
,
122 XDND_SOURCE_TYPES(info
));
124 if (requiredTypes
== NULL
&& XDND_TYPE_LIST_AVAILABLE(info
)) {
125 /* None of the stored source types fits, but the whole type list
126 hasn't been retrieved yet. */
127 WMFreeArray(XDND_SOURCE_TYPES(info
));
128 XDND_SOURCE_TYPES(info
) = getTypesFromTypeList(scr
, XDND_SOURCE_WIN(info
));
129 /* Don't retrieve the type list again */
130 XDND_TYPE_LIST_AVAILABLE(info
) = False
;
133 destView
->dragDestinationProcs
->requiredDataTypes(destView
,
134 W_ActionToOperation(scr
,
137 XDND_SOURCE_TYPES(info
));
140 XDND_REQUIRED_TYPES(info
) = requiredTypes
;
143 char *getNextRequestedDataType(WMDraggingInfo
* info
)
145 /* get the type of the first data not yet retrieved from selection */
148 if (XDND_REQUIRED_TYPES(info
) != NULL
) {
149 nextTypeIndex
= WMGetArrayItemCount(XDND_DROP_DATAS(info
));
150 return WMGetFromArray(XDND_REQUIRED_TYPES(info
), nextTypeIndex
);
151 /* NULL if no more type */
156 /* ----- Action list ----- */
158 WMArray
*sourceOperationList(WMScreen
* scr
, Window sourceWin
)
160 Atom dataType
, *actionList
;
162 unsigned long count
, remaining
;
163 unsigned char *actionDatas
= NULL
;
164 unsigned char *descriptionList
= NULL
;
165 WMArray
*operationArray
;
166 WMDragOperationItem
*operationItem
;
170 XGetWindowProperty(scr
->display
, sourceWin
, scr
->xdndActionListAtom
,
171 0, 0x8000000L
, False
, XA_ATOM
, &dataType
, &size
, &count
, &remaining
, &actionDatas
);
173 if (dataType
!= XA_ATOM
|| size
!= XDND_PROPERTY_FORMAT
|| count
== 0 || !actionDatas
) {
174 wwarning("Cannot read action list");
181 actionList
= (Atom
*) actionDatas
;
183 XGetWindowProperty(scr
->display
, sourceWin
, scr
->xdndActionDescriptionAtom
,
184 0, 0x8000000L
, False
, XA_STRING
, &dataType
, &size
,
185 &count
, &remaining
, &descriptionList
);
187 if (dataType
!= XA_STRING
|| size
!= XDND_ACTION_DESCRIPTION_FORMAT
|| count
== 0 || !descriptionList
) {
188 wwarning("Cannot read action description list");
192 if (descriptionList
) {
193 XFree(descriptionList
);
198 operationArray
= WMCreateDragOperationArray(count
);
199 description
= (char *)descriptionList
;
201 for (i
= 0; count
> 0; i
++) {
202 size
= strlen(description
);
203 operationItem
= WMCreateDragOperationItem(W_ActionToOperation(scr
, actionList
[i
]),
204 wstrdup(description
));
206 WMAddToArray(operationArray
, operationItem
);
207 count
-= (size
+ 1); /* -1 : -NULL char */
209 /* next description */
210 description
= &(description
[size
+ 1]);
214 XFree(descriptionList
);
216 return operationArray
;
219 /* ----- Dragging Info ----- */
220 static void updateSourceWindow(WMDraggingInfo
* info
, XClientMessageEvent
* event
)
222 XDND_SOURCE_WIN(info
) = (Window
) event
->data
.l
[0];
225 static WMView
*findChildInView(WMView
* parent
, int x
, int y
)
227 if (parent
->childrenList
== NULL
)
230 WMView
*child
= parent
->childrenList
;
233 && (!child
->flags
.mapped
234 || x
< WMGetViewPosition(child
).x
235 || x
> WMGetViewPosition(child
).x
+ WMGetViewSize(child
).width
236 || y
< WMGetViewPosition(child
).y
237 || y
> WMGetViewPosition(child
).y
+ WMGetViewSize(child
).height
))
239 child
= child
->nextSister
;
244 return findChildInView(child
,
245 x
- WMGetViewPosition(child
).x
, y
- WMGetViewPosition(child
).y
);
249 static WMView
*findDestinationViewInToplevel(WMView
* toplevel
, int x
, int y
)
251 WMScreen
*scr
= W_VIEW_SCREEN(toplevel
);
252 Window toplevelWin
= WMViewXID(toplevel
);
253 int xInToplevel
, yInToplevel
;
256 XTranslateCoordinates(scr
->display
, scr
->rootWin
, toplevelWin
, x
, y
, &xInToplevel
, &yInToplevel
, &foo
);
257 return findChildInView(toplevel
, xInToplevel
, yInToplevel
);
260 /* Clear datas only used by current destination view */
261 static void freeDestinationViewInfos(WMDraggingInfo
* info
)
263 if (XDND_SOURCE_TYPES(info
) != NULL
) {
264 WMFreeArray(XDND_SOURCE_TYPES(info
));
265 XDND_SOURCE_TYPES(info
) = NULL
;
268 if (XDND_DROP_DATAS(info
) != NULL
) {
269 WMFreeArray(XDND_DROP_DATAS(info
));
270 XDND_DROP_DATAS(info
) = NULL
;
273 XDND_REQUIRED_TYPES(info
) = NULL
;
276 void W_DragDestinationInfoClear(WMDraggingInfo
* info
)
278 W_DragDestinationStopTimer();
280 if (XDND_DEST_INFO(info
) != NULL
) {
281 freeDestinationViewInfos(info
);
283 wfree(XDND_DEST_INFO(info
));
284 XDND_DEST_INFO(info
) = NULL
;
288 static void initDestinationDragInfo(WMDraggingInfo
* info
, WMView
* destView
)
290 wassertr(destView
!= NULL
);
292 XDND_DEST_INFO(info
) = (W_DragDestinationInfo
*) wmalloc(sizeof(W_DragDestinationInfo
));
294 XDND_DEST_STATE(info
) = idleState
;
295 XDND_DEST_VIEW(info
) = destView
;
297 XDND_SOURCE_ACTION_CHANGED(info
) = False
;
298 XDND_SOURCE_TYPES(info
) = NULL
;
299 XDND_REQUIRED_TYPES(info
) = NULL
;
300 XDND_DROP_DATAS(info
) = NULL
;
303 void W_DragDestinationStoreEnterMsgInfo(WMDraggingInfo
* info
, WMView
* toplevel
, XClientMessageEvent
* event
)
305 WMScreen
*scr
= W_VIEW_SCREEN(toplevel
);
307 if (XDND_DEST_INFO(info
) == NULL
)
308 initDestinationDragInfo(info
, toplevel
);
310 XDND_SOURCE_VERSION(info
) = (event
->data
.l
[1] >> 24);
311 XDND_AWARE_VIEW(info
) = toplevel
;
312 updateSourceWindow(info
, event
);
315 if (event->data.l[1] & 1)
316 /* XdndTypeList property is available */
317 /* XDND_SOURCE_TYPES(info) = getTypesFromTypeList(scr, XDND_SOURCE_WIN(info));
319 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
321 XDND_SOURCE_TYPES(info
) = getTypesFromThreeTypes(scr
, event
);
323 /* to use if the 3 types are not enough */
324 XDND_TYPE_LIST_AVAILABLE(info
) = (event
->data
.l
[1] & 1);
327 void W_DragDestinationStorePositionMsgInfo(WMDraggingInfo
* info
, WMView
* toplevel
, XClientMessageEvent
* event
)
329 int x
= event
->data
.l
[2] >> 16;
330 int y
= event
->data
.l
[2] & 0xffff;
333 newDestView
= findDestinationViewInToplevel(toplevel
, x
, y
);
335 if (XDND_DEST_INFO(info
) == NULL
) {
336 initDestinationDragInfo(info
, newDestView
);
337 XDND_AWARE_VIEW(info
) = toplevel
;
338 updateSourceWindow(info
, event
);
340 if (newDestView
!= XDND_DEST_VIEW(info
)) {
341 updateSourceWindow(info
, event
);
342 XDND_DEST_VIEW(info
) = newDestView
;
343 XDND_SOURCE_ACTION_CHANGED(info
) = False
;
345 if (XDND_DEST_STATE(info
) != waitEnterState
)
346 XDND_DEST_STATE(info
) = idleState
;
348 XDND_SOURCE_ACTION_CHANGED(info
) = (XDND_SOURCE_ACTION(info
) != event
->data
.l
[4]);
352 XDND_SOURCE_ACTION(info
) = event
->data
.l
[4];
354 /* note: source position is not stored */
357 /* ----- End of Dragging Info ----- */
359 /* ----- Messages ----- */
361 /* send a DnD message to the source window */
363 sendDnDClientMessage(WMDraggingInfo
* info
, Atom message
,
364 unsigned long data1
, unsigned long data2
, unsigned long data3
, unsigned long data4
)
366 if (!W_SendDnDClientMessage(W_VIEW_SCREEN(XDND_AWARE_VIEW(info
))->display
,
367 XDND_SOURCE_WIN(info
),
368 message
, WMViewXID(XDND_AWARE_VIEW(info
)), data1
, data2
, data3
, data4
)) {
370 W_DragDestinationInfoClear(info
);
374 /* send a xdndStatus message to the source, with position and size
375 of the destination if it has no subwidget (requesting a position message
376 on every move otherwise) */
377 static void sendStatusMessage(WMView
* destView
, WMDraggingInfo
* info
, Atom action
)
381 data1
= (action
== None
) ? 0 : 1;
383 if (destView
->childrenList
== NULL
) {
384 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
386 WMSize destSize
= WMGetViewSize(destView
);
389 XTranslateCoordinates(scr
->display
, WMViewXID(destView
), scr
->rootWin
, 0, 0, &destX
, &destY
, &foo
);
391 sendDnDClientMessage(info
,
392 W_VIEW_SCREEN(destView
)->xdndStatusAtom
,
394 (destX
<< 16) | destY
, (destSize
.width
<< 16) | destSize
.height
, action
);
396 /* set bit 1 to request explicitly position message on every move */
399 sendDnDClientMessage(info
, W_VIEW_SCREEN(destView
)->xdndStatusAtom
, data1
, 0, 0, action
);
404 storeDropData(WMView
* destView
, Atom selection
, Atom target
, Time timestamp
, void *cdata
, WMData
* data
)
406 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
407 WMDraggingInfo
*info
= &(scr
->dragInfo
);
408 WMData
*dataToStore
= NULL
;
411 dataToStore
= WMRetainData(data
);
413 if (XDND_DEST_INFO(info
) != NULL
&& XDND_DROP_DATAS(info
) != NULL
) {
414 WMAddToArray(XDND_DROP_DATAS(info
), dataToStore
);
415 W_SendDnDClientMessage(scr
->display
, WMViewXID(destView
),
416 scr
->xdndSelectionAtom
, WMViewXID(destView
), 0, 0, 0, 0);
420 Bool
requestDropDataInSelection(WMView
* destView
, char *type
)
422 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
425 if (!WMRequestSelection(destView
,
426 scr
->xdndSelectionAtom
,
427 XInternAtom(scr
->display
, type
, False
),
428 CurrentTime
, storeDropData
, NULL
)) {
429 wwarning("could not request data for dropped data");
439 Bool
requestDropData(WMDraggingInfo
* info
)
441 WMView
*destView
= XDND_DEST_VIEW(info
);
442 char *nextType
= getNextRequestedDataType(info
);
444 while ((nextType
!= NULL
)
445 && (!requestDropDataInSelection(destView
, nextType
))) {
446 /* store NULL if request failed, and try with next type */
447 WMAddToArray(XDND_DROP_DATAS(info
), NULL
);
448 nextType
= getNextRequestedDataType(info
);
451 /* remains types to retrieve ? */
452 return (nextType
!= NULL
);
455 static void concludeDrop(WMView
* destView
)
457 destView
->dragDestinationProcs
->concludeDragOperation(destView
);
460 /* send cancel message to the source */
461 static void cancelDrop(WMView
* destView
, WMDraggingInfo
* info
)
463 sendStatusMessage(destView
, info
, None
);
464 concludeDrop(destView
);
465 freeDestinationViewInfos(info
);
468 /* suspend drop, when dragged icon enter an unregistered view
469 or a register view that doesn't accept the drop */
470 static void suspendDropAuthorization(WMView
* destView
, WMDraggingInfo
* info
)
472 sendStatusMessage(destView
, info
, None
);
474 /* Free datas that depend on destination behaviour */
475 if (XDND_DROP_DATAS(info
) != NULL
) {
476 WMFreeArray(XDND_DROP_DATAS(info
));
477 XDND_DROP_DATAS(info
) = NULL
;
480 XDND_REQUIRED_TYPES(info
) = NULL
;
483 /* cancel drop on Enter message, if protocol version is nok */
484 void W_DragDestinationCancelDropOnEnter(WMView
* toplevel
, WMDraggingInfo
* info
)
486 if (XDND_DEST_VIEW_IS_REGISTERED(info
))
487 cancelDrop(XDND_DEST_VIEW(info
), info
);
489 sendStatusMessage(toplevel
, info
, None
);
491 W_DragDestinationInfoClear(info
);
494 static void finishDrop(WMView
* destView
, WMDraggingInfo
* info
)
496 sendDnDClientMessage(info
, W_VIEW_SCREEN(destView
)->xdndFinishedAtom
, 0, 0, 0, 0);
497 concludeDrop(destView
);
498 W_DragDestinationInfoClear(info
);
501 static Atom
getAllowedAction(WMView
* destView
, WMDraggingInfo
* info
)
503 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
505 return W_OperationToAction(scr
,
506 destView
->dragDestinationProcs
->allowedOperation(destView
,
507 W_ActionToOperation(scr
,
510 XDND_SOURCE_TYPES(info
)));
513 static void *checkActionAllowed(WMView
* destView
, WMDraggingInfo
* info
)
515 XDND_DEST_ACTION(info
) = getAllowedAction(destView
, info
);
517 if (XDND_DEST_ACTION(info
) == None
) {
518 suspendDropAuthorization(destView
, info
);
519 return dropNotAllowedState
;
522 sendStatusMessage(destView
, info
, XDND_DEST_ACTION(info
));
523 return dropAllowedState
;
526 static void *checkDropAllowed(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
528 storeRequiredTypeList(info
);
530 if (destView
->dragDestinationProcs
->inspectDropData
!= NULL
) {
531 XDND_DROP_DATAS(info
) = createDropDataArray(XDND_REQUIRED_TYPES(info
));
533 /* store first available data */
534 if (requestDropData(info
))
535 return inspectDropDataState
;
537 /* no data retrieved, but inspect can allow it */
538 if (destView
->dragDestinationProcs
->inspectDropData(destView
, XDND_DROP_DATAS(info
)))
539 return checkActionAllowed(destView
, info
);
541 suspendDropAuthorization(destView
, info
);
542 return dropNotAllowedState
;
545 return checkActionAllowed(destView
, info
);
548 static WMPoint
*getDropLocationInView(WMView
* view
)
550 Window rootWin
, childWin
;
555 location
= (WMPoint
*) wmalloc(sizeof(WMPoint
));
557 XQueryPointer(W_VIEW_SCREEN(view
)->display
,
558 WMViewXID(view
), &rootWin
, &childWin
, &rootX
, &rootY
, &(location
->x
), &(location
->y
), &mask
);
563 static void callPerformDragOperation(WMView
* destView
, WMDraggingInfo
* info
)
565 WMArray
*operationList
= NULL
;
566 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
567 WMPoint
*dropLocation
;
569 if (XDND_SOURCE_ACTION(info
) == scr
->xdndActionAsk
)
570 operationList
= sourceOperationList(scr
, XDND_SOURCE_WIN(info
));
572 dropLocation
= getDropLocationInView(destView
);
573 destView
->dragDestinationProcs
->performDragOperation(destView
,
574 XDND_DROP_DATAS(info
), operationList
, dropLocation
);
577 if (operationList
!= NULL
)
578 WMFreeArray(operationList
);
581 /* ----- Destination timer ----- */
582 static void dragSourceResponseTimeOut(void *destView
)
584 WMView
*view
= (WMView
*) destView
;
585 WMDraggingInfo
*info
;
587 wwarning("delay for drag source response expired");
588 info
= &(W_VIEW_SCREEN(view
)->dragInfo
);
589 if (XDND_DEST_VIEW_IS_REGISTERED(info
))
590 cancelDrop(view
, info
);
592 sendStatusMessage(view
, info
, None
);
595 W_DragDestinationInfoClear(info
);
598 void W_DragDestinationStopTimer()
600 if (dndDestinationTimer
!= NULL
) {
601 WMDeleteTimerHandler(dndDestinationTimer
);
602 dndDestinationTimer
= NULL
;
606 void W_DragDestinationStartTimer(WMDraggingInfo
* info
)
608 W_DragDestinationStopTimer();
610 if (XDND_DEST_STATE(info
) != idleState
)
611 dndDestinationTimer
= WMAddTimerHandler(XDND_SOURCE_RESPONSE_MAX_DELAY
,
612 dragSourceResponseTimeOut
, XDND_DEST_VIEW(info
));
615 /* ----- End of Destination timer ----- */
617 /* ----- Destination states ----- */
620 static const char *stateName(W_DndState
* state
)
623 return "no state defined";
625 if (state
== idleState
)
628 if (state
== waitEnterState
)
629 return "waitEnterState";
631 if (state
== inspectDropDataState
)
632 return "inspectDropDataState";
634 if (state
== dropAllowedState
)
635 return "dropAllowedState";
637 if (state
== dropNotAllowedState
)
638 return "dropNotAllowedState";
640 if (state
== waitForDropDataState
)
641 return "waitForDropDataState";
643 return "unknown state";
647 static void *idleState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
652 if (destView
->dragDestinationProcs
!= NULL
) {
653 scr
= W_VIEW_SCREEN(destView
);
654 sourceMsg
= event
->message_type
;
656 if (sourceMsg
== scr
->xdndPositionAtom
) {
657 destView
->dragDestinationProcs
->prepareForDragOperation(destView
);
659 if (XDND_SOURCE_TYPES(info
) != NULL
) {
660 /* enter message infos are available */
661 return checkDropAllowed(destView
, event
, info
);
664 /* waiting for enter message */
665 return waitEnterState
;
669 suspendDropAuthorization(destView
, info
);
673 /* Source position and action are stored,
674 waiting for xdnd protocol version and source type */
675 static void *waitEnterState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
677 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
678 Atom sourceMsg
= event
->message_type
;
680 if (sourceMsg
== scr
->xdndEnterAtom
) {
681 W_DragDestinationStoreEnterMsgInfo(info
, destView
, event
);
682 return checkDropAllowed(destView
, event
, info
);
685 return waitEnterState
;
688 /* We have requested a data, and have received it */
689 static void *inspectDropDataState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
694 scr
= W_VIEW_SCREEN(destView
);
695 sourceMsg
= event
->message_type
;
697 if (sourceMsg
== scr
->xdndSelectionAtom
) {
698 /* a data has been retrieved, store next available */
699 if (requestDropData(info
))
700 return inspectDropDataState
;
702 /* all required (and available) datas are stored */
703 if (destView
->dragDestinationProcs
->inspectDropData(destView
, XDND_DROP_DATAS(info
)))
704 return checkActionAllowed(destView
, info
);
706 suspendDropAuthorization(destView
, info
);
707 return dropNotAllowedState
;
710 return inspectDropDataState
;
713 static void *dropNotAllowedState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
715 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
716 Atom sourceMsg
= event
->message_type
;
718 if (sourceMsg
== scr
->xdndDropAtom
) {
719 finishDrop(destView
, info
);
723 if (sourceMsg
== scr
->xdndPositionAtom
) {
724 if (XDND_SOURCE_ACTION_CHANGED(info
)) {
725 return checkDropAllowed(destView
, event
, info
);
727 sendStatusMessage(destView
, info
, None
);
728 return dropNotAllowedState
;
732 return dropNotAllowedState
;
735 static void *dropAllowedState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
737 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
738 Atom sourceMsg
= event
->message_type
;
740 if (sourceMsg
== scr
->xdndDropAtom
) {
741 if (XDND_DROP_DATAS(info
) != NULL
) {
742 /* drop datas were cached with inspectDropData call */
743 callPerformDragOperation(destView
, info
);
745 XDND_DROP_DATAS(info
) = createDropDataArray(XDND_REQUIRED_TYPES(info
));
747 /* store first available data */
748 if (requestDropData(info
))
749 return waitForDropDataState
;
751 /* no data retrieved */
752 callPerformDragOperation(destView
, info
);
755 finishDrop(destView
, info
);
759 if (sourceMsg
== scr
->xdndPositionAtom
) {
760 if (XDND_SOURCE_ACTION_CHANGED(info
)) {
761 return checkDropAllowed(destView
, event
, info
);
763 sendStatusMessage(destView
, info
, XDND_DEST_ACTION(info
));
764 return dropAllowedState
;
768 return dropAllowedState
;
771 static void *waitForDropDataState(WMView
* destView
, XClientMessageEvent
* event
, WMDraggingInfo
* info
)
773 WMScreen
*scr
= W_VIEW_SCREEN(destView
);
774 Atom sourceMsg
= event
->message_type
;
776 if (sourceMsg
== scr
->xdndSelectionAtom
) {
777 /* store next data */
778 if (requestDropData(info
))
779 return waitForDropDataState
;
781 /* all required (and available) datas are stored */
782 callPerformDragOperation(destView
, info
);
784 finishDrop(destView
, info
);
788 return waitForDropDataState
;
791 /* ----- End of Destination states ----- */
793 void W_DragDestinationStateHandler(WMDraggingInfo
* info
, XClientMessageEvent
* event
)
796 W_DndState
*newState
;
798 wassertr(XDND_DEST_INFO(info
) != NULL
);
799 wassertr(XDND_DEST_VIEW(info
) != NULL
);
801 destView
= XDND_DEST_VIEW(info
);
802 if (XDND_DEST_STATE(info
) == NULL
)
803 XDND_DEST_STATE(info
) = idleState
;
807 printf("current dest state: %s\n", stateName(XDND_DEST_STATE(info
)));
810 newState
= (W_DndState
*) XDND_DEST_STATE(info
) (destView
, event
, info
);
814 printf("new dest state: %s\n", stateName(newState
));
817 if (XDND_DEST_INFO(info
) != NULL
) {
818 XDND_DEST_STATE(info
) = newState
;
819 if (XDND_DEST_STATE(info
) != idleState
)
820 W_DragDestinationStartTimer(info
);
824 static void realizedObserver(void *self
, WMNotification
* notif
)
826 WMView
*view
= (WMView
*) WMGetNotificationObject(notif
);
827 WMScreen
*scr
= W_VIEW_SCREEN(view
);
829 XChangeProperty(scr
->display
, W_VIEW_DRAWABLE(view
),
830 scr
->xdndAwareAtom
, XA_ATOM
, XDND_PROPERTY_FORMAT
, PropModeReplace
, &XDNDversion
, 1);
832 WMRemoveNotificationObserver(self
);
835 void W_SetXdndAwareProperty(WMScreen
* scr
, WMView
* view
, Atom
* types
, int typeCount
)
837 WMView
*toplevel
= W_TopLevelOfView(view
);
839 if (!toplevel
->flags
.xdndHintSet
) {
840 toplevel
->flags
.xdndHintSet
= 1;
842 if (toplevel
->flags
.realized
) {
843 XChangeProperty(scr
->display
, W_VIEW_DRAWABLE(toplevel
),
844 scr
->xdndAwareAtom
, XA_ATOM
, XDND_PROPERTY_FORMAT
,
845 PropModeReplace
, &XDNDversion
, 1);
847 WMAddNotificationObserver(realizedObserver
,
848 /* just use as an id */
849 &view
->dragDestinationProcs
,
850 WMViewRealizedNotification
, toplevel
);
855 void WMRegisterViewForDraggedTypes(WMView
* view
, WMArray
* acceptedTypes
)
861 typeCount
= WMGetArrayItemCount(acceptedTypes
);
862 types
= wmalloc(sizeof(Atom
) * (typeCount
+ 1));
864 for (i
= 0; i
< typeCount
; i
++) {
865 types
[i
] = XInternAtom(W_VIEW_SCREEN(view
)->display
, WMGetFromArray(acceptedTypes
, i
), False
);
869 view
->droppableTypes
= types
;
870 /* WMFreeArray(acceptedTypes); */
872 W_SetXdndAwareProperty(W_VIEW_SCREEN(view
), view
, types
, typeCount
);
875 void WMUnregisterViewDraggedTypes(WMView
* view
)
877 if (view
->droppableTypes
!= NULL
)
878 wfree(view
->droppableTypes
);
879 view
->droppableTypes
= NULL
;
883 requestedOperation: operation requested by the source
884 sourceDataTypes: data types (mime-types strings) supported by the source
885 (never NULL, destroyed when drop ends)
886 return operation allowed by destination (self)
888 static WMDragOperationType
889 defAllowedOperation(WMView
* self
, WMDragOperationType requestedOperation
, WMArray
* sourceDataTypes
)
891 /* no operation allowed */
892 return WDOperationNone
;
896 requestedOperation: operation requested by the source
897 sourceDataTypes: data types (mime-types strings) supported by the source
898 (never NULL, destroyed when drop ends)
899 return data types (mime-types strings) required by destination (self)
900 or NULL if no suitable data type is available (force
901 to 2nd pass with full source type list).
903 static WMArray
*defRequiredDataTypes(WMView
* self
,
904 WMDragOperationType requestedOperation
, WMArray
* sourceDataTypes
)
906 /* no data type allowed (NULL even at 2nd pass) */
911 Executed when the drag enters destination (self)
913 static void defPrepareForDragOperation(WMView
* self
)
918 Checks datas to be dropped (optional).
919 dropDatas: datas (WMData*) required by destination (self)
920 (given in same order as returned by requiredDataTypes).
921 A NULL data means it couldn't be retreived.
922 Destroyed when drop ends.
923 return true if data array is ok
925 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
929 dropDatas: datas (WMData*) required by destination (self)
930 (given in same order as returned by requiredDataTypes).
931 A NULL data means it couldn't be retrieved.
932 Destroyed when drop ends.
933 operationList: if source operation is WDOperationAsk, contains
934 operations (and associated texts) that can be asked
935 to source. (destroyed after performDragOperation call)
936 Otherwise this parameter is NULL.
939 defPerformDragOperation(WMView
* self
, WMArray
* dropDatas
, WMArray
* operationList
, WMPoint
* dropLocation
)
943 /* Executed after drop */
944 static void defConcludeDragOperation(WMView
* self
)
948 void WMSetViewDragDestinationProcs(WMView
* view
, WMDragDestinationProcs
* procs
)
950 if (view
->dragDestinationProcs
== NULL
) {
951 view
->dragDestinationProcs
= wmalloc(sizeof(WMDragDestinationProcs
));
953 free(view
->dragDestinationProcs
);
956 *view
->dragDestinationProcs
= *procs
;
958 /*XXX fill in non-implemented stuffs */
959 if (procs
->allowedOperation
== NULL
) {
960 view
->dragDestinationProcs
->allowedOperation
= defAllowedOperation
;
962 if (procs
->allowedOperation
== NULL
) {
963 view
->dragDestinationProcs
->requiredDataTypes
= defRequiredDataTypes
;
966 /* note: inspectDropData can be NULL, if data consultation is not needed
967 to give drop permission */
969 if (procs
->prepareForDragOperation
== NULL
) {
970 view
->dragDestinationProcs
->prepareForDragOperation
= defPrepareForDragOperation
;
972 if (procs
->performDragOperation
== NULL
) {
973 view
->dragDestinationProcs
->performDragOperation
= defPerformDragOperation
;
975 if (procs
->concludeDragOperation
== NULL
) {
976 view
->dragDestinationProcs
->concludeDragOperation
= defConcludeDragOperation
;