24c3ac48e19183e6f702efc8b63b431514a37861
[wmaker-crm.git] / WINGs / dragdestination.c
blob24c3ac48e19183e6f702efc8b63b431514a37861
2 #include "WINGsP.h"
3 #include <X11/Xatom.h>
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)
41 XFree(type);
44 static WMArray *createSourceTypeArray(int initialSize)
46 return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
49 static void freeDropDataArrayItem(void *data)
51 if (data != NULL)
52 WMReleaseData((WMData *) data);
55 static WMArray *createDropDataArray(WMArray * requiredTypes)
57 if (requiredTypes != NULL)
58 return WMCreateArrayWithDestructor(WMGetArrayItemCount(requiredTypes), freeDropDataArrayItem);
60 else
61 return WMCreateArray(0);
64 static WMArray *getTypesFromTypeList(WMScreen * scr, Window sourceWin)
66 Atom dataType;
67 Atom *typeAtomList;
68 WMArray *typeList;
69 int i, format;
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) {
77 if (data) {
78 XFree(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]));
89 XFree(data);
91 return typeList;
94 static WMArray *getTypesFromThreeTypes(WMScreen * scr, XClientMessageEvent * event)
96 WMArray *typeList;
97 Atom atom;
98 int i;
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));
108 return typeList;
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,
120 XDND_SOURCE_ACTION
121 (info)),
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;
132 requiredTypes =
133 destView->dragDestinationProcs->requiredDataTypes(destView,
134 W_ActionToOperation(scr,
135 XDND_SOURCE_ACTION
136 (info)),
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 */
146 int nextTypeIndex;
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 */
152 } else
153 return NULL;
156 /* ----- Action list ----- */
158 WMArray *sourceOperationList(WMScreen * scr, Window sourceWin)
160 Atom dataType, *actionList;
161 int i, size;
162 unsigned long count, remaining;
163 unsigned char *actionDatas = NULL;
164 unsigned char *descriptionList = NULL;
165 WMArray *operationArray;
166 WMDragOperationItem *operationItem;
167 char *description;
169 remaining = 0;
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");
175 if (actionDatas) {
176 XFree(actionDatas);
178 return NULL;
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");
189 if (actionList) {
190 XFree(actionList);
192 if (descriptionList) {
193 XFree(descriptionList);
195 return NULL;
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]);
213 XFree(actionList);
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)
228 return parent;
229 else {
230 WMView *child = parent->childrenList;
232 while (child != NULL
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;
241 if (child == NULL)
242 return parent;
243 else
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;
254 Window foo;
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);
314 #if 0
315 if (event->data.l[1] & 1)
316 /* XdndTypeList property is available */
317 XDND_SOURCE_TYPES(info) = getTypesFromTypeList(scr, XDND_SOURCE_WIN(info));
318 else
319 XDND_SOURCE_TYPES(info) = getTypesFromThreeTypes(scr, event);
320 #endif
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;
331 WMView *newDestView;
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);
339 } else {
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;
347 } else {
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 */
362 static void
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)) {
369 /* drop failed */
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)
379 unsigned long data1;
381 data1 = (action == None) ? 0 : 1;
383 if (destView->childrenList == NULL) {
384 WMScreen *scr = W_VIEW_SCREEN(destView);
385 int destX, destY;
386 WMSize destSize = WMGetViewSize(destView);
387 Window foo;
389 XTranslateCoordinates(scr->display, WMViewXID(destView), scr->rootWin, 0, 0, &destX, &destY, &foo);
391 sendDnDClientMessage(info,
392 W_VIEW_SCREEN(destView)->xdndStatusAtom,
393 data1,
394 (destX << 16) | destY, (destSize.width << 16) | destSize.height, action);
395 } else {
396 /* set bit 1 to request explicitly position message on every move */
397 data1 = data1 | 2;
399 sendDnDClientMessage(info, W_VIEW_SCREEN(destView)->xdndStatusAtom, data1, 0, 0, action);
403 static void
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;
410 if (data != 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, const char *type)
422 WMScreen *scr = W_VIEW_SCREEN(destView);
424 if (type != NULL) {
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");
430 return False;
433 return True;
436 return False;
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);
488 else
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,
508 XDND_SOURCE_ACTION
509 (info)),
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;
551 int rootX, rootY;
552 unsigned int mask;
553 WMPoint *location;
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);
560 return location;
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);
576 wfree(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);
591 else {
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 ----- */
619 #ifdef XDND_DEBUG
620 static const char *stateName(W_DndState * state)
622 if (state == NULL)
623 return "no state defined";
625 if (state == idleState)
626 return "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";
645 #endif
647 static void *idleState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
649 WMScreen *scr;
650 Atom sourceMsg;
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);
670 return idleState;
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)
691 WMScreen *scr;
692 Atom sourceMsg;
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);
720 return idleState;
723 if (sourceMsg == scr->xdndPositionAtom) {
724 if (XDND_SOURCE_ACTION_CHANGED(info)) {
725 return checkDropAllowed(destView, event, info);
726 } else {
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);
744 } else {
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);
756 return idleState;
759 if (sourceMsg == scr->xdndPositionAtom) {
760 if (XDND_SOURCE_ACTION_CHANGED(info)) {
761 return checkDropAllowed(destView, event, info);
762 } else {
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);
785 return idleState;
788 return waitForDropDataState;
791 /* ----- End of Destination states ----- */
793 void W_DragDestinationStateHandler(WMDraggingInfo * info, XClientMessageEvent * event)
795 WMView *destView;
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;
805 #ifdef XDND_DEBUG
807 printf("current dest state: %s\n", stateName(XDND_DEST_STATE(info)));
808 #endif
810 newState = (W_DndState *) XDND_DEST_STATE(info) (destView, event, info);
812 #ifdef XDND_DEBUG
814 printf("new dest state: %s\n", stateName(newState));
815 #endif
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);
846 } else {
847 WMAddNotificationObserver(realizedObserver,
848 /* just use as an id */
849 &view->dragDestinationProcs,
850 WMViewRealizedNotification, toplevel);
855 void WMRegisterViewForDraggedTypes(WMView * view, WMArray * acceptedTypes)
857 Atom *types;
858 int typeCount;
859 int i;
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);
867 types[i] = 0;
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) */
907 return NULL;
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); */
928 Process drop
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.
938 static void
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));
952 } else {
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;