0d9434d3888084b86138e2edb2cf993d74ed4bda
[wmaker-crm.git] / WINGs / dragdestination.c
blob0d9434d3888084b86138e2edb2cf993d74ed4bda
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 static 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 static 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 static 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 /* Parameter not used, but tell the compiler that it is ok */
411 (void) selection;
412 (void) target;
413 (void) timestamp;
414 (void) cdata;
416 if (data != NULL)
417 dataToStore = WMRetainData(data);
419 if (XDND_DEST_INFO(info) != NULL && XDND_DROP_DATAS(info) != NULL) {
420 WMAddToArray(XDND_DROP_DATAS(info), dataToStore);
421 W_SendDnDClientMessage(scr->display, WMViewXID(destView),
422 scr->xdndSelectionAtom, WMViewXID(destView), 0, 0, 0, 0);
426 static Bool requestDropDataInSelection(WMView * destView, const char *type)
428 WMScreen *scr = W_VIEW_SCREEN(destView);
430 if (type != NULL) {
431 if (!WMRequestSelection(destView,
432 scr->xdndSelectionAtom,
433 XInternAtom(scr->display, type, False),
434 CurrentTime, storeDropData, NULL)) {
435 wwarning("could not request data for dropped data");
436 return False;
439 return True;
442 return False;
445 static Bool requestDropData(WMDraggingInfo * info)
447 WMView *destView = XDND_DEST_VIEW(info);
448 char *nextType = getNextRequestedDataType(info);
450 while ((nextType != NULL)
451 && (!requestDropDataInSelection(destView, nextType))) {
452 /* store NULL if request failed, and try with next type */
453 WMAddToArray(XDND_DROP_DATAS(info), NULL);
454 nextType = getNextRequestedDataType(info);
457 /* remains types to retrieve ? */
458 return (nextType != NULL);
461 static void concludeDrop(WMView * destView)
463 destView->dragDestinationProcs->concludeDragOperation(destView);
466 /* send cancel message to the source */
467 static void cancelDrop(WMView * destView, WMDraggingInfo * info)
469 sendStatusMessage(destView, info, None);
470 concludeDrop(destView);
471 freeDestinationViewInfos(info);
474 /* suspend drop, when dragged icon enter an unregistered view
475 or a register view that doesn't accept the drop */
476 static void suspendDropAuthorization(WMView * destView, WMDraggingInfo * info)
478 sendStatusMessage(destView, info, None);
480 /* Free datas that depend on destination behaviour */
481 if (XDND_DROP_DATAS(info) != NULL) {
482 WMFreeArray(XDND_DROP_DATAS(info));
483 XDND_DROP_DATAS(info) = NULL;
486 XDND_REQUIRED_TYPES(info) = NULL;
489 /* cancel drop on Enter message, if protocol version is nok */
490 void W_DragDestinationCancelDropOnEnter(WMView * toplevel, WMDraggingInfo * info)
492 if (XDND_DEST_VIEW_IS_REGISTERED(info))
493 cancelDrop(XDND_DEST_VIEW(info), info);
494 else
495 sendStatusMessage(toplevel, info, None);
497 W_DragDestinationInfoClear(info);
500 static void finishDrop(WMView * destView, WMDraggingInfo * info)
502 sendDnDClientMessage(info, W_VIEW_SCREEN(destView)->xdndFinishedAtom, 0, 0, 0, 0);
503 concludeDrop(destView);
504 W_DragDestinationInfoClear(info);
507 static Atom getAllowedAction(WMView * destView, WMDraggingInfo * info)
509 WMScreen *scr = W_VIEW_SCREEN(destView);
511 return W_OperationToAction(scr,
512 destView->dragDestinationProcs->allowedOperation(destView,
513 W_ActionToOperation(scr,
514 XDND_SOURCE_ACTION
515 (info)),
516 XDND_SOURCE_TYPES(info)));
519 static void *checkActionAllowed(WMView * destView, WMDraggingInfo * info)
521 XDND_DEST_ACTION(info) = getAllowedAction(destView, info);
523 if (XDND_DEST_ACTION(info) == None) {
524 suspendDropAuthorization(destView, info);
525 return dropNotAllowedState;
528 sendStatusMessage(destView, info, XDND_DEST_ACTION(info));
529 return dropAllowedState;
532 static void *checkDropAllowed(WMView *destView, WMDraggingInfo *info)
534 storeRequiredTypeList(info);
536 if (destView->dragDestinationProcs->inspectDropData != NULL) {
537 XDND_DROP_DATAS(info) = createDropDataArray(XDND_REQUIRED_TYPES(info));
539 /* store first available data */
540 if (requestDropData(info))
541 return inspectDropDataState;
543 /* no data retrieved, but inspect can allow it */
544 if (destView->dragDestinationProcs->inspectDropData(destView, XDND_DROP_DATAS(info)))
545 return checkActionAllowed(destView, info);
547 suspendDropAuthorization(destView, info);
548 return dropNotAllowedState;
551 return checkActionAllowed(destView, info);
554 static WMPoint *getDropLocationInView(WMView * view)
556 Window rootWin, childWin;
557 int rootX, rootY;
558 unsigned int mask;
559 WMPoint *location;
561 location = (WMPoint *) wmalloc(sizeof(WMPoint));
563 XQueryPointer(W_VIEW_SCREEN(view)->display,
564 WMViewXID(view), &rootWin, &childWin, &rootX, &rootY, &(location->x), &(location->y), &mask);
566 return location;
569 static void callPerformDragOperation(WMView * destView, WMDraggingInfo * info)
571 WMArray *operationList = NULL;
572 WMScreen *scr = W_VIEW_SCREEN(destView);
573 WMPoint *dropLocation;
575 if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
576 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
578 dropLocation = getDropLocationInView(destView);
579 destView->dragDestinationProcs->performDragOperation(destView,
580 XDND_DROP_DATAS(info), operationList, dropLocation);
582 wfree(dropLocation);
583 if (operationList != NULL)
584 WMFreeArray(operationList);
587 /* ----- Destination timer ----- */
588 static void dragSourceResponseTimeOut(void *destView)
590 WMView *view = (WMView *) destView;
591 WMDraggingInfo *info;
593 wwarning("delay for drag source response expired");
594 info = &(W_VIEW_SCREEN(view)->dragInfo);
595 if (XDND_DEST_VIEW_IS_REGISTERED(info))
596 cancelDrop(view, info);
597 else {
598 sendStatusMessage(view, info, None);
601 W_DragDestinationInfoClear(info);
604 void W_DragDestinationStopTimer()
606 if (dndDestinationTimer != NULL) {
607 WMDeleteTimerHandler(dndDestinationTimer);
608 dndDestinationTimer = NULL;
612 void W_DragDestinationStartTimer(WMDraggingInfo * info)
614 W_DragDestinationStopTimer();
616 if (XDND_DEST_STATE(info) != idleState)
617 dndDestinationTimer = WMAddTimerHandler(XDND_SOURCE_RESPONSE_MAX_DELAY,
618 dragSourceResponseTimeOut, XDND_DEST_VIEW(info));
621 /* ----- End of Destination timer ----- */
623 /* ----- Destination states ----- */
625 #ifdef XDND_DEBUG
626 static const char *stateName(W_DndState * state)
628 if (state == NULL)
629 return "no state defined";
631 if (state == idleState)
632 return "idleState";
634 if (state == waitEnterState)
635 return "waitEnterState";
637 if (state == inspectDropDataState)
638 return "inspectDropDataState";
640 if (state == dropAllowedState)
641 return "dropAllowedState";
643 if (state == dropNotAllowedState)
644 return "dropNotAllowedState";
646 if (state == waitForDropDataState)
647 return "waitForDropDataState";
649 return "unknown state";
651 #endif
653 static void *idleState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
655 WMScreen *scr;
656 Atom sourceMsg;
658 if (destView->dragDestinationProcs != NULL) {
659 scr = W_VIEW_SCREEN(destView);
660 sourceMsg = event->message_type;
662 if (sourceMsg == scr->xdndPositionAtom) {
663 destView->dragDestinationProcs->prepareForDragOperation(destView);
665 if (XDND_SOURCE_TYPES(info) != NULL) {
666 /* enter message infos are available */
667 return checkDropAllowed(destView, info);
670 /* waiting for enter message */
671 return waitEnterState;
675 suspendDropAuthorization(destView, info);
676 return idleState;
679 /* Source position and action are stored,
680 waiting for xdnd protocol version and source type */
681 static void *waitEnterState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
683 WMScreen *scr = W_VIEW_SCREEN(destView);
684 Atom sourceMsg = event->message_type;
686 if (sourceMsg == scr->xdndEnterAtom) {
687 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
688 return checkDropAllowed(destView, info);
691 return waitEnterState;
694 /* We have requested a data, and have received it */
695 static void *inspectDropDataState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
697 WMScreen *scr;
698 Atom sourceMsg;
700 scr = W_VIEW_SCREEN(destView);
701 sourceMsg = event->message_type;
703 if (sourceMsg == scr->xdndSelectionAtom) {
704 /* a data has been retrieved, store next available */
705 if (requestDropData(info))
706 return inspectDropDataState;
708 /* all required (and available) datas are stored */
709 if (destView->dragDestinationProcs->inspectDropData(destView, XDND_DROP_DATAS(info)))
710 return checkActionAllowed(destView, info);
712 suspendDropAuthorization(destView, info);
713 return dropNotAllowedState;
716 return inspectDropDataState;
719 static void *dropNotAllowedState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
721 WMScreen *scr = W_VIEW_SCREEN(destView);
722 Atom sourceMsg = event->message_type;
724 if (sourceMsg == scr->xdndDropAtom) {
725 finishDrop(destView, info);
726 return idleState;
729 if (sourceMsg == scr->xdndPositionAtom) {
730 if (XDND_SOURCE_ACTION_CHANGED(info)) {
731 return checkDropAllowed(destView, info);
732 } else {
733 sendStatusMessage(destView, info, None);
734 return dropNotAllowedState;
738 return dropNotAllowedState;
741 static void *dropAllowedState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
743 WMScreen *scr = W_VIEW_SCREEN(destView);
744 Atom sourceMsg = event->message_type;
746 if (sourceMsg == scr->xdndDropAtom) {
747 if (XDND_DROP_DATAS(info) != NULL) {
748 /* drop datas were cached with inspectDropData call */
749 callPerformDragOperation(destView, info);
750 } else {
751 XDND_DROP_DATAS(info) = createDropDataArray(XDND_REQUIRED_TYPES(info));
753 /* store first available data */
754 if (requestDropData(info))
755 return waitForDropDataState;
757 /* no data retrieved */
758 callPerformDragOperation(destView, info);
761 finishDrop(destView, info);
762 return idleState;
765 if (sourceMsg == scr->xdndPositionAtom) {
766 if (XDND_SOURCE_ACTION_CHANGED(info)) {
767 return checkDropAllowed(destView, info);
768 } else {
769 sendStatusMessage(destView, info, XDND_DEST_ACTION(info));
770 return dropAllowedState;
774 return dropAllowedState;
777 static void *waitForDropDataState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
779 WMScreen *scr = W_VIEW_SCREEN(destView);
780 Atom sourceMsg = event->message_type;
782 if (sourceMsg == scr->xdndSelectionAtom) {
783 /* store next data */
784 if (requestDropData(info))
785 return waitForDropDataState;
787 /* all required (and available) datas are stored */
788 callPerformDragOperation(destView, info);
790 finishDrop(destView, info);
791 return idleState;
794 return waitForDropDataState;
797 /* ----- End of Destination states ----- */
799 void W_DragDestinationStateHandler(WMDraggingInfo * info, XClientMessageEvent * event)
801 WMView *destView;
802 W_DndState *newState;
804 wassertr(XDND_DEST_INFO(info) != NULL);
805 wassertr(XDND_DEST_VIEW(info) != NULL);
807 destView = XDND_DEST_VIEW(info);
808 if (XDND_DEST_STATE(info) == NULL)
809 XDND_DEST_STATE(info) = idleState;
811 #ifdef XDND_DEBUG
813 printf("current dest state: %s\n", stateName(XDND_DEST_STATE(info)));
814 #endif
816 newState = (W_DndState *) XDND_DEST_STATE(info) (destView, event, info);
818 #ifdef XDND_DEBUG
820 printf("new dest state: %s\n", stateName(newState));
821 #endif
823 if (XDND_DEST_INFO(info) != NULL) {
824 XDND_DEST_STATE(info) = newState;
825 if (XDND_DEST_STATE(info) != idleState)
826 W_DragDestinationStartTimer(info);
830 static void realizedObserver(void *self, WMNotification * notif)
832 WMView *view = (WMView *) WMGetNotificationObject(notif);
833 WMScreen *scr = W_VIEW_SCREEN(view);
835 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
836 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, &XDNDversion, 1);
838 WMRemoveNotificationObserver(self);
841 static void W_SetXdndAwareProperty(WMScreen *scr, WMView *view)
843 WMView *toplevel = W_TopLevelOfView(view);
845 if (!toplevel->flags.xdndHintSet) {
846 toplevel->flags.xdndHintSet = 1;
848 if (toplevel->flags.realized) {
849 XChangeProperty(scr->display, W_VIEW_DRAWABLE(toplevel),
850 scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT,
851 PropModeReplace, &XDNDversion, 1);
852 } else {
853 WMAddNotificationObserver(realizedObserver,
854 /* just use as an id */
855 &view->dragDestinationProcs,
856 WMViewRealizedNotification, toplevel);
861 void WMRegisterViewForDraggedTypes(WMView * view, WMArray * acceptedTypes)
863 Atom *types;
864 int typeCount;
865 int i;
867 typeCount = WMGetArrayItemCount(acceptedTypes);
868 types = wmalloc(sizeof(Atom) * (typeCount + 1));
870 for (i = 0; i < typeCount; i++) {
871 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display, WMGetFromArray(acceptedTypes, i), False);
873 types[i] = 0;
875 view->droppableTypes = types;
876 /* WMFreeArray(acceptedTypes); */
878 W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view);
881 void WMUnregisterViewDraggedTypes(WMView * view)
883 if (view->droppableTypes != NULL)
884 wfree(view->droppableTypes);
885 view->droppableTypes = NULL;
889 requestedOperation: operation requested by the source
890 sourceDataTypes: data types (mime-types strings) supported by the source
891 (never NULL, destroyed when drop ends)
892 return operation allowed by destination (self)
894 static WMDragOperationType
895 defAllowedOperation(WMView * self, WMDragOperationType requestedOperation, WMArray * sourceDataTypes)
897 /* Parameter not used, but tell the compiler that it is ok */
898 (void) self;
899 (void) requestedOperation;
900 (void) sourceDataTypes;
902 /* no operation allowed */
903 return WDOperationNone;
907 requestedOperation: operation requested by the source
908 sourceDataTypes: data types (mime-types strings) supported by the source
909 (never NULL, destroyed when drop ends)
910 return data types (mime-types strings) required by destination (self)
911 or NULL if no suitable data type is available (force
912 to 2nd pass with full source type list).
914 static WMArray *defRequiredDataTypes(WMView * self,
915 WMDragOperationType requestedOperation, WMArray * sourceDataTypes)
917 /* Parameter not used, but tell the compiler that it is ok */
918 (void) self;
919 (void) requestedOperation;
920 (void) sourceDataTypes;
922 /* no data type allowed (NULL even at 2nd pass) */
923 return NULL;
927 Executed when the drag enters destination (self)
929 static void defPrepareForDragOperation(WMView * self)
931 /* Parameter not used, but tell the compiler that it is ok */
932 (void) self;
936 Checks datas to be dropped (optional).
937 dropDatas: datas (WMData*) required by destination (self)
938 (given in same order as returned by requiredDataTypes).
939 A NULL data means it couldn't be retreived.
940 Destroyed when drop ends.
941 return true if data array is ok
943 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
946 Process drop
947 dropDatas: datas (WMData*) required by destination (self)
948 (given in same order as returned by requiredDataTypes).
949 A NULL data means it couldn't be retrieved.
950 Destroyed when drop ends.
951 operationList: if source operation is WDOperationAsk, contains
952 operations (and associated texts) that can be asked
953 to source. (destroyed after performDragOperation call)
954 Otherwise this parameter is NULL.
956 static void
957 defPerformDragOperation(WMView * self, WMArray * dropDatas, WMArray * operationList, WMPoint * dropLocation)
959 /* Parameter not used, but tell the compiler that it is ok */
960 (void) self;
961 (void) dropDatas;
962 (void) operationList;
963 (void) dropLocation;
966 /* Executed after drop */
967 static void defConcludeDragOperation(WMView * self)
969 /* Parameter not used, but tell the compiler that it is ok */
970 (void) self;
973 void WMSetViewDragDestinationProcs(WMView * view, WMDragDestinationProcs * procs)
975 if (view->dragDestinationProcs == NULL) {
976 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
977 } else {
978 free(view->dragDestinationProcs);
981 *view->dragDestinationProcs = *procs;
983 /*XXX fill in non-implemented stuffs */
984 if (procs->allowedOperation == NULL) {
985 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
987 if (procs->allowedOperation == NULL) {
988 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
991 /* note: inspectDropData can be NULL, if data consultation is not needed
992 to give drop permission */
994 if (procs->prepareForDragOperation == NULL) {
995 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
997 if (procs->performDragOperation == NULL) {
998 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
1000 if (procs->concludeDragOperation == NULL) {
1001 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;