Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / dragdestination.c
1
2 #include "WINGsP.h"
3 #include <X11/Xatom.h>
4
5 #define XDND_SOURCE_RESPONSE_MAX_DELAY 3000
6
7 #define XDND_PROPERTY_FORMAT 32
8 #define XDND_ACTION_DESCRIPTION_FORMAT 8
9
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)
27
28 static unsigned char XDNDversion = XDND_VERSION;
29 static WMHandlerID dndDestinationTimer = NULL;
30
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);
37
38 /* ----- Types & datas list ----- */
39 static void freeSourceTypeArrayItem(void *type)
40 {
41         XFree(type);
42 }
43
44 static WMArray *createSourceTypeArray(int initialSize)
45 {
46         return WMCreateArrayWithDestructor(initialSize, freeSourceTypeArrayItem);
47 }
48
49 static void freeDropDataArrayItem(void *data)
50 {
51         if (data != NULL)
52                 WMReleaseData((WMData *) data);
53 }
54
55 static WMArray *createDropDataArray(WMArray * requiredTypes)
56 {
57         if (requiredTypes != NULL)
58                 return WMCreateArrayWithDestructor(WMGetArrayItemCount(requiredTypes), freeDropDataArrayItem);
59
60         else
61                 return WMCreateArray(0);
62 }
63
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;
72
73         XGetWindowProperty(scr->display, sourceWin, scr->xdndTypeListAtom,
74                            0, 0x8000000L, False, XA_ATOM, &dataType, &format, &count, &remaining, &data);
75
76         if (dataType != XA_ATOM || format != XDND_PROPERTY_FORMAT || count == 0 || !data) {
77                 if (data) {
78                         XFree(data);
79                 }
80                 return createSourceTypeArray(0);
81         }
82
83         typeList = createSourceTypeArray(count);
84         typeAtomList = (Atom *) data;
85         for (i = 0; i < count; i++) {
86                 WMAddToArray(typeList, XGetAtomName(scr->display, typeAtomList[i]));
87         }
88
89         XFree(data);
90
91         return typeList;
92 }
93
94 static WMArray *getTypesFromThreeTypes(WMScreen * scr, XClientMessageEvent * event)
95 {
96         WMArray *typeList;
97         Atom atom;
98         int i;
99
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));
105                 }
106         }
107
108         return typeList;
109 }
110
111 void storeRequiredTypeList(WMDraggingInfo * info)
112 {
113         WMView *destView = XDND_DEST_VIEW(info);
114         WMScreen *scr = W_VIEW_SCREEN(destView);
115         WMArray *requiredTypes;
116
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));
123
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;
131
132                 requiredTypes =
133                     destView->dragDestinationProcs->requiredDataTypes(destView,
134                                                                       W_ActionToOperation(scr,
135                                                                                           XDND_SOURCE_ACTION
136                                                                                           (info)),
137                                                                       XDND_SOURCE_TYPES(info));
138         }
139
140         XDND_REQUIRED_TYPES(info) = requiredTypes;
141 }
142
143 char *getNextRequestedDataType(WMDraggingInfo * info)
144 {
145         /* get the type of the first data not yet retrieved from selection */
146         int nextTypeIndex;
147
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;
154 }
155
156 /* ----- Action list ----- */
157
158 WMArray *sourceOperationList(WMScreen * scr, Window sourceWin)
159 {
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;
168
169         remaining = 0;
170         XGetWindowProperty(scr->display, sourceWin, scr->xdndActionListAtom,
171                            0, 0x8000000L, False, XA_ATOM, &dataType, &size, &count, &remaining, &actionDatas);
172
173         if (dataType != XA_ATOM || size != XDND_PROPERTY_FORMAT || count == 0 || !actionDatas) {
174                 wwarning("Cannot read action list");
175                 if (actionDatas) {
176                         XFree(actionDatas);
177                 }
178                 return NULL;
179         }
180
181         actionList = (Atom *) actionDatas;
182
183         XGetWindowProperty(scr->display, sourceWin, scr->xdndActionDescriptionAtom,
184                            0, 0x8000000L, False, XA_STRING, &dataType, &size,
185                            &count, &remaining, &descriptionList);
186
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);
191                 }
192                 if (descriptionList) {
193                         XFree(descriptionList);
194                 }
195                 return NULL;
196         }
197
198         operationArray = WMCreateDragOperationArray(count);
199         description = (char *)descriptionList;
200
201         for (i = 0; count > 0; i++) {
202                 size = strlen(description);
203                 operationItem = WMCreateDragOperationItem(W_ActionToOperation(scr, actionList[i]),
204                                                           wstrdup(description));
205
206                 WMAddToArray(operationArray, operationItem);
207                 count -= (size + 1);    /* -1 : -NULL char */
208
209                 /* next description */
210                 description = &(description[size + 1]);
211         }
212
213         XFree(actionList);
214         XFree(descriptionList);
215
216         return operationArray;
217 }
218
219 /* ----- Dragging Info ----- */
220 static void updateSourceWindow(WMDraggingInfo * info, XClientMessageEvent * event)
221 {
222         XDND_SOURCE_WIN(info) = (Window) event->data.l[0];
223 }
224
225 static WMView *findChildInView(WMView * parent, int x, int y)
226 {
227         if (parent->childrenList == NULL)
228                 return parent;
229         else {
230                 WMView *child = parent->childrenList;
231
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))
238
239                         child = child->nextSister;
240
241                 if (child == NULL)
242                         return parent;
243                 else
244                         return findChildInView(child,
245                                                x - WMGetViewPosition(child).x, y - WMGetViewPosition(child).y);
246         }
247 }
248
249 static WMView *findDestinationViewInToplevel(WMView * toplevel, int x, int y)
250 {
251         WMScreen *scr = W_VIEW_SCREEN(toplevel);
252         Window toplevelWin = WMViewXID(toplevel);
253         int xInToplevel, yInToplevel;
254         Window foo;
255
256         XTranslateCoordinates(scr->display, scr->rootWin, toplevelWin, x, y, &xInToplevel, &yInToplevel, &foo);
257         return findChildInView(toplevel, xInToplevel, yInToplevel);
258 }
259
260 /* Clear datas only used by current destination view */
261 static void freeDestinationViewInfos(WMDraggingInfo * info)
262 {
263         if (XDND_SOURCE_TYPES(info) != NULL) {
264                 WMFreeArray(XDND_SOURCE_TYPES(info));
265                 XDND_SOURCE_TYPES(info) = NULL;
266         }
267
268         if (XDND_DROP_DATAS(info) != NULL) {
269                 WMFreeArray(XDND_DROP_DATAS(info));
270                 XDND_DROP_DATAS(info) = NULL;
271         }
272
273         XDND_REQUIRED_TYPES(info) = NULL;
274 }
275
276 void W_DragDestinationInfoClear(WMDraggingInfo * info)
277 {
278         W_DragDestinationStopTimer();
279
280         if (XDND_DEST_INFO(info) != NULL) {
281                 freeDestinationViewInfos(info);
282
283                 wfree(XDND_DEST_INFO(info));
284                 XDND_DEST_INFO(info) = NULL;
285         }
286 }
287
288 static void initDestinationDragInfo(WMDraggingInfo * info, WMView * destView)
289 {
290         wassertr(destView != NULL);
291
292         XDND_DEST_INFO(info) = (W_DragDestinationInfo *) wmalloc(sizeof(W_DragDestinationInfo));
293
294         XDND_DEST_STATE(info) = idleState;
295         XDND_DEST_VIEW(info) = destView;
296
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;
301 }
302
303 void W_DragDestinationStoreEnterMsgInfo(WMDraggingInfo * info, WMView * toplevel, XClientMessageEvent * event)
304 {
305         WMScreen *scr = W_VIEW_SCREEN(toplevel);
306
307         if (XDND_DEST_INFO(info) == NULL)
308                 initDestinationDragInfo(info, toplevel);
309
310         XDND_SOURCE_VERSION(info) = (event->data.l[1] >> 24);
311         XDND_AWARE_VIEW(info) = toplevel;
312         updateSourceWindow(info, event);
313
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);
322
323         /* to use if the 3 types are not enough */
324         XDND_TYPE_LIST_AVAILABLE(info) = (event->data.l[1] & 1);
325 }
326
327 void W_DragDestinationStorePositionMsgInfo(WMDraggingInfo * info, WMView * toplevel, XClientMessageEvent * event)
328 {
329         int x = event->data.l[2] >> 16;
330         int y = event->data.l[2] & 0xffff;
331         WMView *newDestView;
332
333         newDestView = findDestinationViewInToplevel(toplevel, x, y);
334
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;
344
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]);
349                 }
350         }
351
352         XDND_SOURCE_ACTION(info) = event->data.l[4];
353
354         /* note: source position is not stored */
355 }
356
357 /* ----- End of Dragging Info ----- */
358
359 /* ----- Messages ----- */
360
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)
365 {
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);
371         }
372 }
373
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)
378 {
379         unsigned long data1;
380
381         data1 = (action == None) ? 0 : 1;
382
383         if (destView->childrenList == NULL) {
384                 WMScreen *scr = W_VIEW_SCREEN(destView);
385                 int destX, destY;
386                 WMSize destSize = WMGetViewSize(destView);
387                 Window foo;
388
389                 XTranslateCoordinates(scr->display, WMViewXID(destView), scr->rootWin, 0, 0, &destX, &destY, &foo);
390
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;
398
399                 sendDnDClientMessage(info, W_VIEW_SCREEN(destView)->xdndStatusAtom, data1, 0, 0, action);
400         }
401 }
402
403 static void
404 storeDropData(WMView * destView, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
405 {
406         WMScreen *scr = W_VIEW_SCREEN(destView);
407         WMDraggingInfo *info = &(scr->dragInfo);
408         WMData *dataToStore = NULL;
409
410         if (data != NULL)
411                 dataToStore = WMRetainData(data);
412
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);
417         }
418 }
419
420 Bool requestDropDataInSelection(WMView * destView, char *type)
421 {
422         WMScreen *scr = W_VIEW_SCREEN(destView);
423
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;
431                 }
432
433                 return True;
434         }
435
436         return False;
437 }
438
439 Bool requestDropData(WMDraggingInfo * info)
440 {
441         WMView *destView = XDND_DEST_VIEW(info);
442         char *nextType = getNextRequestedDataType(info);
443
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);
449         }
450
451         /* remains types to retrieve ? */
452         return (nextType != NULL);
453 }
454
455 static void concludeDrop(WMView * destView)
456 {
457         destView->dragDestinationProcs->concludeDragOperation(destView);
458 }
459
460 /* send cancel message to the source */
461 static void cancelDrop(WMView * destView, WMDraggingInfo * info)
462 {
463         sendStatusMessage(destView, info, None);
464         concludeDrop(destView);
465         freeDestinationViewInfos(info);
466 }
467
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)
471 {
472         sendStatusMessage(destView, info, None);
473
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;
478         }
479
480         XDND_REQUIRED_TYPES(info) = NULL;
481 }
482
483 /* cancel drop on Enter message, if protocol version is nok */
484 void W_DragDestinationCancelDropOnEnter(WMView * toplevel, WMDraggingInfo * info)
485 {
486         if (XDND_DEST_VIEW_IS_REGISTERED(info))
487                 cancelDrop(XDND_DEST_VIEW(info), info);
488         else
489                 sendStatusMessage(toplevel, info, None);
490
491         W_DragDestinationInfoClear(info);
492 }
493
494 static void finishDrop(WMView * destView, WMDraggingInfo * info)
495 {
496         sendDnDClientMessage(info, W_VIEW_SCREEN(destView)->xdndFinishedAtom, 0, 0, 0, 0);
497         concludeDrop(destView);
498         W_DragDestinationInfoClear(info);
499 }
500
501 static Atom getAllowedAction(WMView * destView, WMDraggingInfo * info)
502 {
503         WMScreen *scr = W_VIEW_SCREEN(destView);
504
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)));
511 }
512
513 static void *checkActionAllowed(WMView * destView, WMDraggingInfo * info)
514 {
515         XDND_DEST_ACTION(info) = getAllowedAction(destView, info);
516
517         if (XDND_DEST_ACTION(info) == None) {
518                 suspendDropAuthorization(destView, info);
519                 return dropNotAllowedState;
520         }
521
522         sendStatusMessage(destView, info, XDND_DEST_ACTION(info));
523         return dropAllowedState;
524 }
525
526 static void *checkDropAllowed(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
527 {
528         storeRequiredTypeList(info);
529
530         if (destView->dragDestinationProcs->inspectDropData != NULL) {
531                 XDND_DROP_DATAS(info) = createDropDataArray(XDND_REQUIRED_TYPES(info));
532
533                 /* store first available data */
534                 if (requestDropData(info))
535                         return inspectDropDataState;
536
537                 /* no data retrieved, but inspect can allow it */
538                 if (destView->dragDestinationProcs->inspectDropData(destView, XDND_DROP_DATAS(info)))
539                         return checkActionAllowed(destView, info);
540
541                 suspendDropAuthorization(destView, info);
542                 return dropNotAllowedState;
543         }
544
545         return checkActionAllowed(destView, info);
546 }
547
548 static WMPoint *getDropLocationInView(WMView * view)
549 {
550         Window rootWin, childWin;
551         int rootX, rootY;
552         unsigned int mask;
553         WMPoint *location;
554
555         location = (WMPoint *) wmalloc(sizeof(WMPoint));
556
557         XQueryPointer(W_VIEW_SCREEN(view)->display,
558                       WMViewXID(view), &rootWin, &childWin, &rootX, &rootY, &(location->x), &(location->y), &mask);
559
560         return location;
561 }
562
563 static void callPerformDragOperation(WMView * destView, WMDraggingInfo * info)
564 {
565         WMArray *operationList = NULL;
566         WMScreen *scr = W_VIEW_SCREEN(destView);
567         WMPoint *dropLocation;
568
569         if (XDND_SOURCE_ACTION(info) == scr->xdndActionAsk)
570                 operationList = sourceOperationList(scr, XDND_SOURCE_WIN(info));
571
572         dropLocation = getDropLocationInView(destView);
573         destView->dragDestinationProcs->performDragOperation(destView,
574                                                              XDND_DROP_DATAS(info), operationList, dropLocation);
575
576         wfree(dropLocation);
577         if (operationList != NULL)
578                 WMFreeArray(operationList);
579 }
580
581 /* ----- Destination timer ----- */
582 static void dragSourceResponseTimeOut(void *destView)
583 {
584         WMView *view = (WMView *) destView;
585         WMDraggingInfo *info;
586
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);
593         }
594
595         W_DragDestinationInfoClear(info);
596 }
597
598 void W_DragDestinationStopTimer()
599 {
600         if (dndDestinationTimer != NULL) {
601                 WMDeleteTimerHandler(dndDestinationTimer);
602                 dndDestinationTimer = NULL;
603         }
604 }
605
606 void W_DragDestinationStartTimer(WMDraggingInfo * info)
607 {
608         W_DragDestinationStopTimer();
609
610         if (XDND_DEST_STATE(info) != idleState)
611                 dndDestinationTimer = WMAddTimerHandler(XDND_SOURCE_RESPONSE_MAX_DELAY,
612                                                         dragSourceResponseTimeOut, XDND_DEST_VIEW(info));
613 }
614
615 /* ----- End of Destination timer ----- */
616
617 /* ----- Destination states ----- */
618
619 #ifdef XDND_DEBUG
620 static const char *stateName(W_DndState * state)
621 {
622         if (state == NULL)
623                 return "no state defined";
624
625         if (state == idleState)
626                 return "idleState";
627
628         if (state == waitEnterState)
629                 return "waitEnterState";
630
631         if (state == inspectDropDataState)
632                 return "inspectDropDataState";
633
634         if (state == dropAllowedState)
635                 return "dropAllowedState";
636
637         if (state == dropNotAllowedState)
638                 return "dropNotAllowedState";
639
640         if (state == waitForDropDataState)
641                 return "waitForDropDataState";
642
643         return "unknown state";
644 }
645 #endif
646
647 static void *idleState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
648 {
649         WMScreen *scr;
650         Atom sourceMsg;
651
652         if (destView->dragDestinationProcs != NULL) {
653                 scr = W_VIEW_SCREEN(destView);
654                 sourceMsg = event->message_type;
655
656                 if (sourceMsg == scr->xdndPositionAtom) {
657                         destView->dragDestinationProcs->prepareForDragOperation(destView);
658
659                         if (XDND_SOURCE_TYPES(info) != NULL) {
660                                 /* enter message infos are available */
661                                 return checkDropAllowed(destView, event, info);
662                         }
663
664                         /* waiting for enter message */
665                         return waitEnterState;
666                 }
667         }
668
669         suspendDropAuthorization(destView, info);
670         return idleState;
671 }
672
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)
676 {
677         WMScreen *scr = W_VIEW_SCREEN(destView);
678         Atom sourceMsg = event->message_type;
679
680         if (sourceMsg == scr->xdndEnterAtom) {
681                 W_DragDestinationStoreEnterMsgInfo(info, destView, event);
682                 return checkDropAllowed(destView, event, info);
683         }
684
685         return waitEnterState;
686 }
687
688 /* We have requested a data, and have received it */
689 static void *inspectDropDataState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
690 {
691         WMScreen *scr;
692         Atom sourceMsg;
693
694         scr = W_VIEW_SCREEN(destView);
695         sourceMsg = event->message_type;
696
697         if (sourceMsg == scr->xdndSelectionAtom) {
698                 /* a data has been retrieved, store next available */
699                 if (requestDropData(info))
700                         return inspectDropDataState;
701
702                 /* all required (and available) datas are stored */
703                 if (destView->dragDestinationProcs->inspectDropData(destView, XDND_DROP_DATAS(info)))
704                         return checkActionAllowed(destView, info);
705
706                 suspendDropAuthorization(destView, info);
707                 return dropNotAllowedState;
708         }
709
710         return inspectDropDataState;
711 }
712
713 static void *dropNotAllowedState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
714 {
715         WMScreen *scr = W_VIEW_SCREEN(destView);
716         Atom sourceMsg = event->message_type;
717
718         if (sourceMsg == scr->xdndDropAtom) {
719                 finishDrop(destView, info);
720                 return idleState;
721         }
722
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;
729                 }
730         }
731
732         return dropNotAllowedState;
733 }
734
735 static void *dropAllowedState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
736 {
737         WMScreen *scr = W_VIEW_SCREEN(destView);
738         Atom sourceMsg = event->message_type;
739
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));
746
747                         /* store first available data */
748                         if (requestDropData(info))
749                                 return waitForDropDataState;
750
751                         /* no data retrieved */
752                         callPerformDragOperation(destView, info);
753                 }
754
755                 finishDrop(destView, info);
756                 return idleState;
757         }
758
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;
765                 }
766         }
767
768         return dropAllowedState;
769 }
770
771 static void *waitForDropDataState(WMView * destView, XClientMessageEvent * event, WMDraggingInfo * info)
772 {
773         WMScreen *scr = W_VIEW_SCREEN(destView);
774         Atom sourceMsg = event->message_type;
775
776         if (sourceMsg == scr->xdndSelectionAtom) {
777                 /* store next data */
778                 if (requestDropData(info))
779                         return waitForDropDataState;
780
781                 /* all required (and available) datas are stored */
782                 callPerformDragOperation(destView, info);
783
784                 finishDrop(destView, info);
785                 return idleState;
786         }
787
788         return waitForDropDataState;
789 }
790
791 /* ----- End of Destination states ----- */
792
793 void W_DragDestinationStateHandler(WMDraggingInfo * info, XClientMessageEvent * event)
794 {
795         WMView *destView;
796         W_DndState *newState;
797
798         wassertr(XDND_DEST_INFO(info) != NULL);
799         wassertr(XDND_DEST_VIEW(info) != NULL);
800
801         destView = XDND_DEST_VIEW(info);
802         if (XDND_DEST_STATE(info) == NULL)
803                 XDND_DEST_STATE(info) = idleState;
804
805 #ifdef XDND_DEBUG
806
807         printf("current dest state: %s\n", stateName(XDND_DEST_STATE(info)));
808 #endif
809
810         newState = (W_DndState *) XDND_DEST_STATE(info) (destView, event, info);
811
812 #ifdef XDND_DEBUG
813
814         printf("new dest state: %s\n", stateName(newState));
815 #endif
816
817         if (XDND_DEST_INFO(info) != NULL) {
818                 XDND_DEST_STATE(info) = newState;
819                 if (XDND_DEST_STATE(info) != idleState)
820                         W_DragDestinationStartTimer(info);
821         }
822 }
823
824 static void realizedObserver(void *self, WMNotification * notif)
825 {
826         WMView *view = (WMView *) WMGetNotificationObject(notif);
827         WMScreen *scr = W_VIEW_SCREEN(view);
828
829         XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
830                         scr->xdndAwareAtom, XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, &XDNDversion, 1);
831
832         WMRemoveNotificationObserver(self);
833 }
834
835 void W_SetXdndAwareProperty(WMScreen * scr, WMView * view, Atom * types, int typeCount)
836 {
837         WMView *toplevel = W_TopLevelOfView(view);
838
839         if (!toplevel->flags.xdndHintSet) {
840                 toplevel->flags.xdndHintSet = 1;
841
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);
851                 }
852         }
853 }
854
855 void WMRegisterViewForDraggedTypes(WMView * view, WMArray * acceptedTypes)
856 {
857         Atom *types;
858         int typeCount;
859         int i;
860
861         typeCount = WMGetArrayItemCount(acceptedTypes);
862         types = wmalloc(sizeof(Atom) * (typeCount + 1));
863
864         for (i = 0; i < typeCount; i++) {
865                 types[i] = XInternAtom(W_VIEW_SCREEN(view)->display, WMGetFromArray(acceptedTypes, i), False);
866         }
867         types[i] = 0;
868
869         view->droppableTypes = types;
870         /* WMFreeArray(acceptedTypes); */
871
872         W_SetXdndAwareProperty(W_VIEW_SCREEN(view), view, types, typeCount);
873 }
874
875 void WMUnregisterViewDraggedTypes(WMView * view)
876 {
877         if (view->droppableTypes != NULL)
878                 wfree(view->droppableTypes);
879         view->droppableTypes = NULL;
880 }
881
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)
890 {
891         /* no operation allowed */
892         return WDOperationNone;
893 }
894
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)
905 {
906         /* no data type allowed (NULL even at 2nd pass) */
907         return NULL;
908 }
909
910 /*
911  Executed when the drag enters destination (self)
912  */
913 static void defPrepareForDragOperation(WMView * self)
914 {
915 }
916
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); */
926
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)
940 {
941 }
942
943 /* Executed after drop */
944 static void defConcludeDragOperation(WMView * self)
945 {
946 }
947
948 void WMSetViewDragDestinationProcs(WMView * view, WMDragDestinationProcs * procs)
949 {
950         if (view->dragDestinationProcs == NULL) {
951                 view->dragDestinationProcs = wmalloc(sizeof(WMDragDestinationProcs));
952         } else {
953                 free(view->dragDestinationProcs);
954         }
955
956         *view->dragDestinationProcs = *procs;
957
958         /*XXX fill in non-implemented stuffs */
959         if (procs->allowedOperation == NULL) {
960                 view->dragDestinationProcs->allowedOperation = defAllowedOperation;
961         }
962         if (procs->allowedOperation == NULL) {
963                 view->dragDestinationProcs->requiredDataTypes = defRequiredDataTypes;
964         }
965
966         /* note: inspectDropData can be NULL, if data consultation is not needed
967            to give drop permission */
968
969         if (procs->prepareForDragOperation == NULL) {
970                 view->dragDestinationProcs->prepareForDragOperation = defPrepareForDragOperation;
971         }
972         if (procs->performDragOperation == NULL) {
973                 view->dragDestinationProcs->performDragOperation = defPerformDragOperation;
974         }
975         if (procs->concludeDragOperation == NULL) {
976                 view->dragDestinationProcs->concludeDragOperation = defConcludeDragOperation;
977         }
978 }