Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / dragdestination.c
blob0f1849fc6d4fc5906547ed13de97bf13cbb00ecb
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)
40 {
41 XFree(type);
42 }
44 static WMArray *createSourceTypeArray(int initialSize)
45 {
46 return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
47 }
49 static void freeDropDataArrayItem(void *data)
50 {
51 if (data != NULL)
52 WMReleaseData((WMData *) data);
53 }
55 static WMArray *createDropDataArray(WMArray * requiredTypes)
56 {
57 if (requiredTypes != NULL)
58 return WMCreateArrayWithDestructor(WMGetArrayItemCount(requiredTypes), freeDropDataArrayItem);
60 else
61 return WMCreateArray(0);
62 }
64 static WMArray *getTypesFromTypeList(WMScreen * scr, Window sourceWin)
65 {
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);
79 }
80 return createSourceTypeArray(0);
81 }
83 typeList = createSourceTypeArray(count);
84 typeAtomList = (Atom *) data;
85 for (i = 0; i < count; i++) {
86 WMAddToArray(typeList, XGetAtomName(scr->display, typeAtomList[i]));
87 }
89 XFree(data);
91 return typeList;
92 }
94 static WMArray *getTypesFromThreeTypes(WMScreen * scr, XClientMessageEvent * event)
95 {
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 /*
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 */
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, 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;
882 /*
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)
887 */
888 static WMDragOperationType
889 defAllowedOperation(WMView * self, WMDragOperationType requestedOperation, WMArray * sourceDataTypes)
891 /* no operation allowed */
892 return WDOperationNone;
895 /*
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).
902 */
903 static WMArray *defRequiredDataTypes(WMView * self,
904 WMDragOperationType requestedOperation, WMArray * sourceDataTypes)
906 /* no data type allowed (NULL even at 2nd pass) */
907 return NULL;
910 /*
911 Executed when the drag enters destination (self)
912 */
913 static void defPrepareForDragOperation(WMView * self)
917 /*
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
924 */
925 /* Bool (*inspectDropData)(WMView *self, WMArray *dropDatas); */
927 /*
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.
937 */
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;