Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / dragsource.c
Commit [+]AuthorDateLineData
558615a8 dan2004-10-30 05:46:37 +00001
558615a8 dan2004-10-30 05:46:37 +00002#include "wconfig.h"
3#include "WINGsP.h"
6672180d kojima2000-03-28 02:30:13 +00004
5#include <X11/Xatom.h>
e7d0c5d9 dan2004-04-07 02:50:52 +00006#include <X11/cursorfont.h>
567e53c6 kojima2000-04-03 06:08:31 +00007#ifdef SHAPE
8#include <X11/extensions/shape.h>
9#endif
10
e7d0c5d9 dan2004-04-07 02:50:52 +000011#define XDND_DESTINATION_RESPONSE_MAX_DELAY 10000
12#define MIN_X_MOVE_OFFSET 5
13#define MIN_Y_MOVE_OFFSET 5
14#define MAX_SLIDEBACK_ITER 15
15
e7d0c5d9 dan2004-04-07 02:50:52 +000016#define XDND_PROPERTY_FORMAT 32
17#define XDND_ACTION_DESCRIPTION_FORMAT 8
18
085e9d62 dan2006-01-22 20:33:19 +000019#define XDND_DEST_VERSION(dragInfo) dragInfo->protocolVersion
e7d0c5d9 dan2004-04-07 02:50:52 +000020#define XDND_SOURCE_INFO(dragInfo) dragInfo->sourceInfo
21#define XDND_DEST_WIN(dragInfo) dragInfo->sourceInfo->destinationWindow
22#define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
23#define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
24#define XDND_SOURCE_VIEW(dragInfo) dragInfo->sourceInfo->sourceView
25#define XDND_SOURCE_STATE(dragInfo) dragInfo->sourceInfo->state
26#define XDND_SELECTION_PROCS(dragInfo) dragInfo->sourceInfo->selectionProcs
27#define XDND_DRAG_ICON(dragInfo) dragInfo->sourceInfo->icon
28#define XDND_MOUSE_OFFSET(dragInfo) dragInfo->sourceInfo->mouseOffset
29#define XDND_DRAG_CURSOR(dragInfo) dragInfo->sourceInfo->dragCursor
30#define XDND_DRAG_ICON_POS(dragInfo) dragInfo->sourceInfo->imageLocation
31#define XDND_NO_POS_ZONE(dragInfo) dragInfo->sourceInfo->noPositionMessageZone
32#define XDND_TIMESTAMP(dragInfo) dragInfo->timestamp
33#define XDND_3_TYPES(dragInfo) dragInfo->sourceInfo->firstThreeTypes
34#define XDND_SOURCE_VIEW_STORED(dragInfo) dragInfo->sourceInfo != NULL \
35 && dragInfo->sourceInfo->sourceView != NULL
36
e7d0c5d9 dan2004-04-07 02:50:52 +000037static WMHandlerID dndSourceTimer = NULL;
38
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020039static void *idleState(WMView * srcView, XClientMessageEvent * event, WMDraggingInfo * info);
40static void *dropAllowedState(WMView * srcView, XClientMessageEvent * event, WMDraggingInfo * info);
41static void *finishDropState(WMView * srcView, XClientMessageEvent * event, WMDraggingInfo * info);
e7d0c5d9 dan2004-04-07 02:50:52 +000042
43#ifdef XDND_DEBUG
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020044static const char *stateName(W_DndState * state)
e7d0c5d9 dan2004-04-07 02:50:52 +000045{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020046 if (state == NULL)
47 return "no state defined";
6672180d kojima2000-03-28 02:30:13 +000048
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020049 if (state == idleState)
50 return "idleState";
6672180d kojima2000-03-28 02:30:13 +000051
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020052 if (state == dropAllowedState)
53 return "dropAllowedState";
6672180d kojima2000-03-28 02:30:13 +000054
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020055 if (state == finishDropState)
56 return "finishDropState";
6672180d kojima2000-03-28 02:30:13 +000057
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020058 return "unknown state";
e7d0c5d9 dan2004-04-07 02:50:52 +000059}
60#endif
6672180d kojima2000-03-28 02:30:13 +000061
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020062static WMScreen *sourceScreen(WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +000063{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020064 return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
e7d0c5d9 dan2004-04-07 02:50:52 +000065}
60a247f2 kojima2000-04-03 03:10:20 +000066
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020067static void endDragProcess(WMDraggingInfo * info, Bool deposited)
e7d0c5d9 dan2004-04-07 02:50:52 +000068{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020069 WMView *view = XDND_SOURCE_VIEW(info);
70 WMScreen *scr = W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
71
72 /* free selection handler while view exists */
73 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom, CurrentTime);
74 wfree(XDND_SELECTION_PROCS(info));
75
76 if (XDND_DRAG_CURSOR(info) != None) {
77 XFreeCursor(scr->display, XDND_DRAG_CURSOR(info));
78 XDND_DRAG_CURSOR(info) = None;
79 }
80
81 if (view->dragSourceProcs->endedDrag != NULL) {
82 /* this can destroy source view (with a "move" action for example) */
83 view->dragSourceProcs->endedDrag(view, &XDND_DRAG_ICON_POS(info), deposited);
84 }
85
86 /* clear remaining draggging infos */
87 wfree(XDND_SOURCE_INFO(info));
88 XDND_SOURCE_INFO(info) = NULL;
567e53c6 kojima2000-04-03 06:08:31 +000089}
90
e7d0c5d9 dan2004-04-07 02:50:52 +000091/* ----- drag cursor ----- */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020092static void initDragCursor(WMDraggingInfo * info)
567e53c6 kojima2000-04-03 06:08:31 +000093{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020094 WMScreen *scr = sourceScreen(info);
95 XColor cursorFgColor, cursorBgColor;
96
97 /* green */
98 cursorFgColor.red = 0x4500;
99 cursorFgColor.green = 0xb000;
100 cursorFgColor.blue = 0x4500;
101
102 /* white */
103 cursorBgColor.red = 0xffff;
104 cursorBgColor.green = 0xffff;
105 cursorBgColor.blue = 0xffff;
106
107 XDND_DRAG_CURSOR(info) = XCreateFontCursor(scr->display, XC_left_ptr);
108 XRecolorCursor(scr->display, XDND_DRAG_CURSOR(info), &cursorFgColor, &cursorBgColor);
109
110 XFlush(scr->display);
567e53c6 kojima2000-04-03 06:08:31 +0000111}
112
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200113static void recolorCursor(WMDraggingInfo * info, Bool dropIsAllowed)
567e53c6 kojima2000-04-03 06:08:31 +0000114{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200115 WMScreen *scr = sourceScreen(info);
116
117 if (dropIsAllowed) {
118 XDefineCursor(scr->display, scr->rootWin, XDND_DRAG_CURSOR(info));
119 } else {
120 XDefineCursor(scr->display, scr->rootWin, scr->defaultCursor);
121 }
122
123 XFlush(scr->display);
567e53c6 kojima2000-04-03 06:08:31 +0000124}
125
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200126/* ----- end of drag cursor ----- */
9e615bcf dan2001-12-28 03:29:50 +0000127
e7d0c5d9 dan2004-04-07 02:50:52 +0000128/* ----- selection procs ----- */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200129static WMData *convertSelection(WMView * view, Atom selection, Atom target, void *cdata, Atom * type)
567e53c6 kojima2000-04-03 06:08:31 +0000130{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200131 WMScreen *scr;
132 WMData *data;
133 char *typeName;
567e53c6 kojima2000-04-03 06:08:31 +0000134
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200135 scr = W_VIEW_SCREEN(view);
136 typeName = XGetAtomName(scr->display, target);
6672180d kojima2000-03-28 02:30:13 +0000137
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200138 *type = target;
567e53c6 kojima2000-04-03 06:08:31 +0000139
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200140 if (view->dragSourceProcs->fetchDragData != NULL) {
141 data = view->dragSourceProcs->fetchDragData(view, typeName);
142 } else {
143 data = NULL;
144 }
6672180d kojima2000-03-28 02:30:13 +0000145
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200146 if (typeName != NULL)
147 XFree(typeName);
6672180d kojima2000-03-28 02:30:13 +0000148
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200149 return data;
e7d0c5d9 dan2004-04-07 02:50:52 +0000150}
6672180d kojima2000-03-28 02:30:13 +0000151
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200152static void selectionLost(WMView * view, Atom selection, void *cdata)
6672180d kojima2000-03-28 02:30:13 +0000153{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200154 wwarning("DND selection lost during drag operation...");
6672180d kojima2000-03-28 02:30:13 +0000155}
156
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200157static void selectionDone(WMView * view, Atom selection, Atom target, void *cdata)
6672180d kojima2000-03-28 02:30:13 +0000158{
e7d0c5d9 dan2004-04-07 02:50:52 +0000159#ifdef XDND_DEBUG
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200160 printf("selection done\n");
e7d0c5d9 dan2004-04-07 02:50:52 +0000161#endif
6672180d kojima2000-03-28 02:30:13 +0000162}
163
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200164/* ----- end of selection procs ----- */
6672180d kojima2000-03-28 02:30:13 +0000165
e7d0c5d9 dan2004-04-07 02:50:52 +0000166/* ----- visual part ----- */
6672180d kojima2000-03-28 02:30:13 +0000167
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200168static Window makeDragIcon(WMScreen * scr, WMPixmap * pixmap)
6672180d kojima2000-03-28 02:30:13 +0000169{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200170 Window window;
171 WMSize size;
172 unsigned long flags;
173 XSetWindowAttributes attribs;
174 Pixmap pix, mask;
175
176 if (!pixmap) {
177 pixmap = scr->defaultObjectIcon;
178 }
179
180 size = WMGetPixmapSize(pixmap);
181 pix = pixmap->pixmap;
182 mask = pixmap->mask;
183
184 flags = CWSaveUnder | CWBackPixmap | CWOverrideRedirect | CWColormap;
185 attribs.save_under = True;
186 attribs.background_pixmap = pix;
187 attribs.override_redirect = True;
188 attribs.colormap = scr->colormap;
189
190 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
191 size.height, 0, scr->depth, InputOutput, scr->visual, flags, &attribs);
6672180d kojima2000-03-28 02:30:13 +0000192
193#ifdef SHAPE
e7d0c5d9 dan2004-04-07 02:50:52 +0000194
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200195 if (mask) {
196 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask, ShapeSet);
197 }
6672180d kojima2000-03-28 02:30:13 +0000198#endif
199
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200200 return window;
6672180d kojima2000-03-28 02:30:13 +0000201}
202
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200203static void slideWindow(Display * dpy, Window win, int srcX, int srcY, int dstX, int dstY)
6672180d kojima2000-03-28 02:30:13 +0000204{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200205 double x, y, dx, dy;
206 int i;
207 int iterations;
6672180d kojima2000-03-28 02:30:13 +0000208
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200209 iterations = WMIN(MAX_SLIDEBACK_ITER, WMAX(abs(dstX - srcX), abs(dstY - srcY)));
e7d0c5d9 dan2004-04-07 02:50:52 +0000210
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200211 x = srcX;
212 y = srcY;
e7d0c5d9 dan2004-04-07 02:50:52 +0000213
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200214 dx = (double)(dstX - srcX) / iterations;
215 dy = (double)(dstY - srcY) / iterations;
6672180d kojima2000-03-28 02:30:13 +0000216
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200217 for (i = 0; i <= iterations; i++) {
218 XMoveWindow(dpy, win, x, y);
219 XFlush(dpy);
e7d0c5d9 dan2004-04-07 02:50:52 +0000220
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200221 wusleep(800);
e7d0c5d9 dan2004-04-07 02:50:52 +0000222
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200223 x += dx;
224 y += dy;
225 }
6672180d kojima2000-03-28 02:30:13 +0000226}
227
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200228static int getInitialDragImageCoord(int viewCoord, int mouseCoord, int viewSize, int iconSize)
6672180d kojima2000-03-28 02:30:13 +0000229{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200230 if (iconSize >= viewSize) {
231 /* center icon coord on view */
232 return viewCoord - iconSize / 2;
233 } else {
234 /* try to center icon on mouse pos */
235
236 if (mouseCoord - iconSize / 2 <= viewCoord)
237 /* if icon was centered on mouse, it would be off view
238 thus, put icon left (resp. top) side
239 at view (resp. top) side */
240 return viewCoord;
241
242 else if (mouseCoord + iconSize / 2 >= viewCoord + viewSize)
243 /* if icon was centered on mouse, it would be off view
244 thus, put icon right (resp. bottom) side
245 at view right (resp. bottom) side */
246 return viewCoord + viewSize - iconSize;
247
248 else
249 return mouseCoord - iconSize / 2;
250 }
6672180d kojima2000-03-28 02:30:13 +0000251
6672180d kojima2000-03-28 02:30:13 +0000252}
253
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200254static void initDragImagePos(WMView * view, WMDraggingInfo * info, XEvent * event)
6672180d kojima2000-03-28 02:30:13 +0000255{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200256 WMSize iconSize = WMGetPixmapSize(view->dragImage);
257 WMSize viewSize = WMGetViewSize(view);
258 WMPoint viewPos;
259 Window foo;
9e45e85d kojima2000-03-29 02:17:39 +0000260
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200261 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
262 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
263 0, 0, &(viewPos.x), &(viewPos.y), &foo);
567e53c6 kojima2000-04-03 06:08:31 +0000264
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200265 /* set icon pos */
266 XDND_DRAG_ICON_POS(info).x =
267 getInitialDragImageCoord(viewPos.x, event->xmotion.x_root, viewSize.width, iconSize.width);
6672180d kojima2000-03-28 02:30:13 +0000268
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200269 XDND_DRAG_ICON_POS(info).y =
270 getInitialDragImageCoord(viewPos.y, event->xmotion.y_root, viewSize.height, iconSize.height);
271
272 /* set mouse offset relative to icon */
273 XDND_MOUSE_OFFSET(info).x = event->xmotion.x_root - XDND_DRAG_ICON_POS(info).x;
274 XDND_MOUSE_OFFSET(info).y = event->xmotion.y_root - XDND_DRAG_ICON_POS(info).y;
e7d0c5d9 dan2004-04-07 02:50:52 +0000275}
6672180d kojima2000-03-28 02:30:13 +0000276
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200277static void refreshDragImage(WMView * view, WMDraggingInfo * info)
278{
279 WMScreen *scr = W_VIEW_SCREEN(view);
6672180d kojima2000-03-28 02:30:13 +0000280
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200281 XMoveWindow(scr->display, XDND_DRAG_ICON(info), XDND_DRAG_ICON_POS(info).x, XDND_DRAG_ICON_POS(info).y);
282}
283
284static void startDragImage(WMView * view, WMDraggingInfo * info, XEvent * event)
6672180d kojima2000-03-28 02:30:13 +0000285{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200286 WMScreen *scr = W_VIEW_SCREEN(view);
e7d0c5d9 dan2004-04-07 02:50:52 +0000287
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200288 XDND_DRAG_ICON(info) = makeDragIcon(scr, view->dragImage);
289 initDragImagePos(view, info, event);
290 refreshDragImage(view, info);
291 XMapRaised(scr->display, XDND_DRAG_ICON(info));
e7d0c5d9 dan2004-04-07 02:50:52 +0000292
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200293 initDragCursor(info);
6672180d kojima2000-03-28 02:30:13 +0000294}
295
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200296static void endDragImage(WMDraggingInfo * info, Bool slideBack)
6672180d kojima2000-03-28 02:30:13 +0000297{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200298 WMView *view = XDND_SOURCE_VIEW(info);
299 Display *dpy = W_VIEW_SCREEN(view)->display;
300
301 if (slideBack) {
302 WMPoint toLocation;
303 Window foo;
304
305 XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
306 WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
307 0, 0, &(toLocation.x), &(toLocation.y), &foo);
308
309 slideWindow(dpy, XDND_DRAG_ICON(info),
310 XDND_DRAG_ICON_POS(info).x, XDND_DRAG_ICON_POS(info).y, toLocation.x, toLocation.y);
311 }
312
313 XDestroyWindow(dpy, XDND_DRAG_ICON(info));
e7d0c5d9 dan2004-04-07 02:50:52 +0000314}
6672180d kojima2000-03-28 02:30:13 +0000315
e7d0c5d9 dan2004-04-07 02:50:52 +0000316/* ----- end of visual part ----- */
317
e7d0c5d9 dan2004-04-07 02:50:52 +0000318/* ----- messages ----- */
319
320/* send a DnD message to the destination window */
321static Bool
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200322sendDnDClientMessage(WMDraggingInfo * info, Atom message,
323 unsigned long data1, unsigned long data2, unsigned long data3, unsigned long data4)
e7d0c5d9 dan2004-04-07 02:50:52 +0000324{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200325 Display *dpy = sourceScreen(info)->display;
326 Window srcWin = WMViewXID(XDND_SOURCE_VIEW(info));
327 Window destWin = XDND_DEST_WIN(info);
328
329 if (!W_SendDnDClientMessage(dpy, destWin, message, srcWin, data1, data2, data3, data4)) {
330 /* drop failed */
331 recolorCursor(info, False);
332 endDragImage(info, True);
333 endDragProcess(info, False);
334 return False;
335 }
336
337 return True;
6672180d kojima2000-03-28 02:30:13 +0000338}
339
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200340static Bool sendEnterMessage(WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +0000341{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200342 WMScreen *scr = sourceScreen(info);
343 unsigned long version;
344
345 if (XDND_DEST_VERSION(info) > 2) {
346 if (XDND_DEST_VERSION(info) < XDND_VERSION)
347 version = XDND_DEST_VERSION(info);
348 else
349 version = XDND_VERSION;
350 } else {
351 version = 3;
352 }
353
354 return sendDnDClientMessage(info, scr->xdndEnterAtom, (version << 24) | 1, /* 1: support of type list */
355 XDND_3_TYPES(info)[0], XDND_3_TYPES(info)[1], XDND_3_TYPES(info)[2]);
e7d0c5d9 dan2004-04-07 02:50:52 +0000356}
6672180d kojima2000-03-28 02:30:13 +0000357
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200358static Bool sendPositionMessage(WMDraggingInfo * info, WMPoint * mousePos)
6672180d kojima2000-03-28 02:30:13 +0000359{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200360 WMScreen *scr = sourceScreen(info);
361 WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
362
363 if (noPosZone->size.width != 0 || noPosZone->size.height != 0) {
364 if (mousePos->x < noPosZone->pos.x || mousePos->x > (noPosZone->pos.x + noPosZone->size.width)
365 || mousePos->y < noPosZone->pos.y || mousePos->y > (noPosZone->pos.y + noPosZone->size.height)) {
366 /* send position if out of zone defined by destination */
367 return sendDnDClientMessage(info, scr->xdndPositionAtom,
368 0,
369 mousePos->x << 16 | mousePos->y,
370 XDND_TIMESTAMP(info), XDND_SOURCE_ACTION(info));
371 }
372
373 /* Nothing to send, always succeed */
374 return True;
375
376 }
377
378 /* send position on each move */
379 return sendDnDClientMessage(info, scr->xdndPositionAtom,
380 0,
381 mousePos->x << 16 | mousePos->y,
382 XDND_TIMESTAMP(info), XDND_SOURCE_ACTION(info));
6672180d kojima2000-03-28 02:30:13 +0000383}
384
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200385static Bool sendLeaveMessage(WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +0000386{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200387 WMScreen *scr = sourceScreen(info);
e7d0c5d9 dan2004-04-07 02:50:52 +0000388
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200389 return sendDnDClientMessage(info, scr->xdndLeaveAtom, 0, 0, 0, 0);
e7d0c5d9 dan2004-04-07 02:50:52 +0000390}
6672180d kojima2000-03-28 02:30:13 +0000391
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200392static Bool sendDropMessage(WMDraggingInfo * info)
6672180d kojima2000-03-28 02:30:13 +0000393{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200394 WMScreen *scr = sourceScreen(info);
e7d0c5d9 dan2004-04-07 02:50:52 +0000395
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200396 return sendDnDClientMessage(info, scr->xdndDropAtom, 0, XDND_TIMESTAMP(info), 0, 0);
6672180d kojima2000-03-28 02:30:13 +0000397}
398
e7d0c5d9 dan2004-04-07 02:50:52 +0000399/* ----- end of messages ----- */
6672180d kojima2000-03-28 02:30:13 +0000400
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200401static Atom *getTypeAtomList(WMScreen * scr, WMView * view, int *count)
6672180d kojima2000-03-28 02:30:13 +0000402{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200403 WMArray *types;
404 Atom *typeAtoms;
405 int i;
6672180d kojima2000-03-28 02:30:13 +0000406
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200407 types = view->dragSourceProcs->dropDataTypes(view);
e7d0c5d9 dan2004-04-07 02:50:52 +0000408
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200409 if (types != NULL) {
410 *count = WMGetArrayItemCount(types);
411 if (*count > 0) {
412 typeAtoms = wmalloc((*count) * sizeof(Atom));
413 for (i = 0; i < *count; i++) {
414 typeAtoms[i] = XInternAtom(scr->display, WMGetFromArray(types, i), False);
415 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000416
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200417 /* WMFreeArray(types); */
418 return typeAtoms;
419 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000420
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200421 /* WMFreeArray(types); */
422 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000423
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200424 *count = 1;
425 typeAtoms = wmalloc(sizeof(Atom));
426 *typeAtoms = None;
e7d0c5d9 dan2004-04-07 02:50:52 +0000427
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200428 return typeAtoms;
9e45e85d kojima2000-03-29 02:17:39 +0000429}
430
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200431static void registerDropTypes(WMScreen * scr, WMView * view, WMDraggingInfo * info)
432{
433 Atom *typeList;
434 int i, count;
435
436 typeList = getTypeAtomList(scr, view, &count);
6672180d kojima2000-03-28 02:30:13 +0000437
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200438 /* store the first 3 types */
439 for (i = 0; i < 3 && i < count; i++)
440 XDND_3_TYPES(info)[i] = typeList[i];
441
442 for (; i < 3; i++)
443 XDND_3_TYPES(info)[i] = None;
444
445 /* store the entire type list */
446 XChangeProperty(scr->display,
447 WMViewXID(view),
448 scr->xdndTypeListAtom,
449 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, (unsigned char *)typeList, count);
450}
451
452static void registerOperationList(WMScreen * scr, WMView * view, WMArray * operationArray)
6672180d kojima2000-03-28 02:30:13 +0000453{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200454 Atom *actionList;
455 WMDragOperationType operation;
456 int count = WMGetArrayItemCount(operationArray);
457 int i;
458
459 actionList = wmalloc(sizeof(Atom) * count);
460
461 for (i = 0; i < count; i++) {
462 operation = WMGetDragOperationItemType(WMGetFromArray(operationArray, i));
463 actionList[i] = W_OperationToAction(scr, operation);
464 }
465
466 XChangeProperty(scr->display,
467 WMViewXID(view),
468 scr->xdndActionListAtom,
469 XA_ATOM, XDND_PROPERTY_FORMAT, PropModeReplace, (unsigned char *)actionList, count);
6672180d kojima2000-03-28 02:30:13 +0000470}
471
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200472static void registerDescriptionList(WMScreen * scr, WMView * view, WMArray * operationArray)
e7d0c5d9 dan2004-04-07 02:50:52 +0000473{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200474 char *text, *textListItem, *textList;
475 int count = WMGetArrayItemCount(operationArray);
476 int i;
477 int size = 0;
478
479 /* size of XA_STRING info */
480 for (i = 0; i < count; i++) {
481 size += strlen(WMGetDragOperationItemText(WMGetFromArray(operationArray, i))) + 1; /* +1 = +NULL */
482 }
483
484 /* create text list */
485 textList = wmalloc(size);
486 textListItem = textList;
487
488 for (i = 0; i < count; i++) {
489 text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
490 strcpy(textListItem, text);
491
492 /* to next text offset */
493 textListItem = &(textListItem[strlen(textListItem) + 1]);
494 }
495
496 XChangeProperty(scr->display,
497 WMViewXID(view),
498 scr->xdndActionDescriptionAtom,
499 XA_STRING,
500 XDND_ACTION_DESCRIPTION_FORMAT, PropModeReplace, (unsigned char *)textList, size);
e7d0c5d9 dan2004-04-07 02:50:52 +0000501}
502
503/* called if wanted operation is WDOperationAsk */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200504static void registerSupportedOperations(WMView * view)
6672180d kojima2000-03-28 02:30:13 +0000505{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200506 WMScreen *scr = W_VIEW_SCREEN(view);
507 WMArray *operationArray;
6672180d kojima2000-03-28 02:30:13 +0000508
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200509 operationArray = view->dragSourceProcs->askedOperations(view);
6672180d kojima2000-03-28 02:30:13 +0000510
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200511 registerOperationList(scr, view, operationArray);
512 registerDescriptionList(scr, view, operationArray);
6672180d kojima2000-03-28 02:30:13 +0000513
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200514 /* WMFreeArray(operationArray); */
e7d0c5d9 dan2004-04-07 02:50:52 +0000515}
6672180d kojima2000-03-28 02:30:13 +0000516
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200517static void initSourceDragInfo(WMView * sourceView, WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +0000518{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200519 WMRect emptyZone;
60a247f2 kojima2000-04-03 03:10:20 +0000520
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200521 XDND_SOURCE_INFO(info) = (W_DragSourceInfo *) wmalloc(sizeof(W_DragSourceInfo));
60a247f2 kojima2000-04-03 03:10:20 +0000522
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200523 XDND_SOURCE_VIEW(info) = sourceView;
524 XDND_DEST_WIN(info) = None;
525 XDND_DRAG_ICON(info) = None;
60a247f2 kojima2000-04-03 03:10:20 +0000526
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200527 XDND_SOURCE_ACTION(info) = W_OperationToAction(W_VIEW_SCREEN(sourceView),
528 sourceView->dragSourceProcs->
529 wantedDropOperation(sourceView));
60a247f2 kojima2000-04-03 03:10:20 +0000530
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200531 XDND_DEST_ACTION(info) = None;
60a247f2 kojima2000-04-03 03:10:20 +0000532
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200533 XDND_SOURCE_STATE(info) = idleState;
e7d0c5d9 dan2004-04-07 02:50:52 +0000534
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200535 emptyZone.pos = wmkpoint(0, 0);
536 emptyZone.size = wmksize(0, 0);
537 XDND_NO_POS_ZONE(info) = emptyZone;
60a247f2 kojima2000-04-03 03:10:20 +0000538}
539
e7d0c5d9 dan2004-04-07 02:50:52 +0000540/*
541 Returned array is destroyed after dropDataTypes call
542 */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200543static WMArray *defDropDataTypes(WMView * self)
60a247f2 kojima2000-04-03 03:10:20 +0000544{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200545 return NULL;
60a247f2 kojima2000-04-03 03:10:20 +0000546}
547
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200548static WMDragOperationType defWantedDropOperation(WMView * self)
60a247f2 kojima2000-04-03 03:10:20 +0000549{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200550 return WDOperationNone;
6672180d kojima2000-03-28 02:30:13 +0000551}
552
e7d0c5d9 dan2004-04-07 02:50:52 +0000553/*
554 Must be defined if wantedDropOperation return WDOperationAsk
555 (useless otherwise).
556 Return a WMDragOperationItem array (destroyed after call).
557 A WMDragOperationItem links a label to an operation.
558 static WMArray*
559 defAskedOperations(WMView *self); */
6672180d kojima2000-03-28 02:30:13 +0000560
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200561static Bool defAcceptDropOperation(WMView * self, WMDragOperationType allowedOperation)
e7d0c5d9 dan2004-04-07 02:50:52 +0000562{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200563 return False;
e7d0c5d9 dan2004-04-07 02:50:52 +0000564}
565
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200566static void defBeganDrag(WMView * self, WMPoint * point)
2c0fdba0 kojima2000-04-03 15:23:32 +0000567{
e7d0c5d9 dan2004-04-07 02:50:52 +0000568}
569
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200570static void defEndedDrag(WMView * self, WMPoint * point, Bool deposited)
e7d0c5d9 dan2004-04-07 02:50:52 +0000571{
572}
573
e7d0c5d9 dan2004-04-07 02:50:52 +0000574/*
575 Returned data is not destroyed
576 */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200577static WMData *defFetchDragData(WMView * self, char *type)
e7d0c5d9 dan2004-04-07 02:50:52 +0000578{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200579 return NULL;
e7d0c5d9 dan2004-04-07 02:50:52 +0000580}
581
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200582void WMSetViewDragSourceProcs(WMView * view, WMDragSourceProcs * procs)
e7d0c5d9 dan2004-04-07 02:50:52 +0000583{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200584 if (view->dragSourceProcs)
585 wfree(view->dragSourceProcs);
586 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
e7d0c5d9 dan2004-04-07 02:50:52 +0000587
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200588 *view->dragSourceProcs = *procs;
e7d0c5d9 dan2004-04-07 02:50:52 +0000589
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200590 if (procs->dropDataTypes == NULL)
591 view->dragSourceProcs->dropDataTypes = defDropDataTypes;
e7d0c5d9 dan2004-04-07 02:50:52 +0000592
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200593 if (procs->wantedDropOperation == NULL)
594 view->dragSourceProcs->wantedDropOperation = defWantedDropOperation;
2c0fdba0 kojima2000-04-03 15:23:32 +0000595
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200596 /*
597 Note: askedOperations can be NULL, if wantedDropOperation never returns
598 WDOperationAsk.
599 */
e7d0c5d9 dan2004-04-07 02:50:52 +0000600
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200601 if (procs->acceptDropOperation == NULL)
602 view->dragSourceProcs->acceptDropOperation = defAcceptDropOperation;
e7d0c5d9 dan2004-04-07 02:50:52 +0000603
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200604 if (procs->beganDrag == NULL)
605 view->dragSourceProcs->beganDrag = defBeganDrag;
e7d0c5d9 dan2004-04-07 02:50:52 +0000606
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200607 if (procs->endedDrag == NULL)
608 view->dragSourceProcs->endedDrag = defEndedDrag;
e7d0c5d9 dan2004-04-07 02:50:52 +0000609
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200610 if (procs->fetchDragData == NULL)
611 view->dragSourceProcs->fetchDragData = defFetchDragData;
2c0fdba0 kojima2000-04-03 15:23:32 +0000612}
613
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200614static Bool isXdndAware(WMScreen * scr, Window win)
2c0fdba0 kojima2000-04-03 15:23:32 +0000615{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200616 Atom type;
617 int format;
618 unsigned long count, remain;
619 unsigned char *winXdndVersion;
60a247f2 kojima2000-04-03 03:10:20 +0000620
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200621 if (win == None)
622 return False;
60a247f2 kojima2000-04-03 03:10:20 +0000623
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200624 XGetWindowProperty(scr->display, win, scr->xdndAwareAtom,
625 0, 1, False, XA_ATOM, &type, &format, &count, &remain, &winXdndVersion);
626
627 if (type != XA_ATOM || format != XDND_PROPERTY_FORMAT || count == 0 || !winXdndVersion) {
628 if (winXdndVersion)
629 XFree(winXdndVersion);
630 return False;
631 }
632
633 XFree(winXdndVersion);
634 return (count == 1); /* xdnd version is set */
6672180d kojima2000-03-28 02:30:13 +0000635}
636
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200637static Window *windowChildren(Display * dpy, Window win, unsigned *nchildren)
6672180d kojima2000-03-28 02:30:13 +0000638{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200639 Window *children;
640 Window foo, bar;
641
642 if (!XQueryTree(dpy, win, &foo, &bar, &children, nchildren)) {
643 *nchildren = 0;
644 return NULL;
645 } else
646 return children;
e7d0c5d9 dan2004-04-07 02:50:52 +0000647}
648
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200649static Window lookForAwareWindow(WMScreen * scr, WMPoint * mousePos, Window win)
e7d0c5d9 dan2004-04-07 02:50:52 +0000650{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200651 int tmpx, tmpy;
652 Window child;
653
654 /* Since xdnd v3, only the toplevel window should be aware */
655 if (isXdndAware(scr, win))
656 return win;
657
658 /* inspect child under pointer */
659 if (XTranslateCoordinates(scr->display, scr->rootWin, win, mousePos->x, mousePos->y, &tmpx, &tmpy, &child)) {
660 if (child == None)
661 return None;
662 else
663 return lookForAwareWindow(scr, mousePos, child);
664 }
665
666 return None;
e7d0c5d9 dan2004-04-07 02:50:52 +0000667}
668
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200669static Window findDestination(WMDraggingInfo * info, WMPoint * mousePos)
085e9d62 dan2006-01-22 20:33:19 +0000670{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200671 WMScreen *scr = sourceScreen(info);
672 unsigned nchildren;
673 Window *children = windowChildren(scr->display, scr->rootWin, &nchildren);
674 int i;
675 XWindowAttributes attr;
676
677 if (isXdndAware(scr, scr->rootWin))
678 return scr->rootWin;
679
680 /* exclude drag icon (and upper) from search */
681 for (i = nchildren - 1; i >= 0; i--) {
682 if (children[i] == XDND_DRAG_ICON(info)) {
683 i--;
684 break;
685 }
686 }
687
688 if (i < 0) {
689 /* root window has no child under drag icon, and is not xdnd aware. */
690 return None;
691 }
692
693 /* inspecting children, from upper to lower */
694 for (; i >= 0; i--) {
695 if (XGetWindowAttributes(scr->display, children[i], &attr)
696 && attr.map_state == IsViewable
697 && mousePos->x >= attr.x
698 && mousePos->y >= attr.y
699 && mousePos->x < attr.x + attr.width && mousePos->y < attr.y + attr.height) {
700 return lookForAwareWindow(scr, mousePos, children[i]);
701 }
702 }
703
704 /* No child window under drag pointer */
705 return None;
085e9d62 dan2006-01-22 20:33:19 +0000706}
707
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200708static void storeDestinationProtocolVersion(WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +0000709{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200710 Atom type;
711 int format;
712 unsigned long count, remain;
713 unsigned char *winXdndVersion;
714 WMScreen *scr = W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
715
716 wassertr(XDND_DEST_WIN(info) != None);
717
718 if (XGetWindowProperty(scr->display, XDND_DEST_WIN(info),
719 scr->xdndAwareAtom,
720 0, 1, False, XA_ATOM, &type, &format,
721 &count, &remain, &winXdndVersion) == Success) {
722 XDND_DEST_VERSION(info) = *winXdndVersion;
723 XFree(winXdndVersion);
724 } else {
725 XDND_DEST_VERSION(info) = 0;
726 wwarning("failed to read XDND version of drop target");
727 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000728}
729
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200730static void initMotionProcess(WMView * view, WMDraggingInfo * info, XEvent * event, WMPoint * startLocation)
e7d0c5d9 dan2004-04-07 02:50:52 +0000731{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200732 WMScreen *scr = W_VIEW_SCREEN(view);
6672180d kojima2000-03-28 02:30:13 +0000733
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200734 /* take ownership of XdndSelection */
735 XDND_SELECTION_PROCS(info) = (WMSelectionProcs *) wmalloc(sizeof(WMSelectionProcs));
736 XDND_SELECTION_PROCS(info)->convertSelection = convertSelection;
737 XDND_SELECTION_PROCS(info)->selectionLost = selectionLost;
738 XDND_SELECTION_PROCS(info)->selectionDone = selectionDone;
739 XDND_TIMESTAMP(info) = event->xmotion.time;
6672180d kojima2000-03-28 02:30:13 +0000740
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200741 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom, CurrentTime, XDND_SELECTION_PROCS(info), NULL)) {
742 wwarning("could not get ownership or DND selection");
743 return;
744 }
745
746 registerDropTypes(scr, view, info);
747
748 if (XDND_SOURCE_ACTION(info) == W_VIEW_SCREEN(view)->xdndActionAsk)
749 registerSupportedOperations(view);
750
751 if (view->dragSourceProcs->beganDrag != NULL) {
752 view->dragSourceProcs->beganDrag(view, startLocation);
753 }
754}
755
756static void processMotion(WMDraggingInfo * info, WMPoint * mousePos)
e7d0c5d9 dan2004-04-07 02:50:52 +0000757{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200758 Window newDestination = findDestination(info, mousePos);
759
760 W_DragSourceStopTimer();
761
762 if (newDestination != XDND_DEST_WIN(info)) {
763 recolorCursor(info, False);
764
765 if (XDND_DEST_WIN(info) != None) {
766 /* leaving a xdnd window */
767 sendLeaveMessage(info);
768 }
769
770 XDND_DEST_WIN(info) = newDestination;
771 XDND_DEST_ACTION(info) = None;
772 XDND_NO_POS_ZONE(info).size.width = 0;
773 XDND_NO_POS_ZONE(info).size.height = 0;
774
775 if (newDestination != None) {
776 /* entering a xdnd window */
777 XDND_SOURCE_STATE(info) = idleState;
778 storeDestinationProtocolVersion(info);
779
780 if (!sendEnterMessage(info)) {
781 XDND_DEST_WIN(info) = None;
782 return;
783 }
784
785 W_DragSourceStartTimer(info);
786 } else {
787 XDND_SOURCE_STATE(info) = NULL;
788 }
789 } else {
790 if (XDND_DEST_WIN(info) != None) {
791 if (!sendPositionMessage(info, mousePos)) {
792 XDND_DEST_WIN(info) = None;
793 return;
794 }
795
796 W_DragSourceStartTimer(info);
797 }
798 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000799}
6672180d kojima2000-03-28 02:30:13 +0000800
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200801static Bool processButtonRelease(WMDraggingInfo * info)
802{
803 if (XDND_SOURCE_STATE(info) == dropAllowedState) {
804 /* begin drop */
805 W_DragSourceStopTimer();
806
807 if (!sendDropMessage(info))
808 return False;
809
810 W_DragSourceStartTimer(info);
811 return True;
812 } else {
813 if (XDND_DEST_WIN(info) != None)
814 sendLeaveMessage(info);
815
816 W_DragSourceStopTimer();
817 return False;
818 }
819}
6672180d kojima2000-03-28 02:30:13 +0000820
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200821Bool WMIsDraggingFromView(WMView * view)
e7d0c5d9 dan2004-04-07 02:50:52 +0000822{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200823 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
6672180d kojima2000-03-28 02:30:13 +0000824
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200825 return (XDND_SOURCE_INFO(info) != NULL && XDND_SOURCE_STATE(info) != finishDropState);
826 /* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
e7d0c5d9 dan2004-04-07 02:50:52 +0000827}
6672180d kojima2000-03-28 02:30:13 +0000828
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200829void WMDragImageFromView(WMView * view, XEvent * event)
e7d0c5d9 dan2004-04-07 02:50:52 +0000830{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200831 WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
832 WMPoint mouseLocation;
833
834 switch (event->type) {
835 case ButtonPress:
836 if (event->xbutton.button == Button1) {
837 XEvent nextEvent;
838
839 XPeekEvent(event->xbutton.display, &nextEvent);
840
841 /* Initialize only if a drag really begins (avoid clicks) */
842 if (nextEvent.type == MotionNotify) {
843 initSourceDragInfo(view, info);
844 }
845 }
846
847 break;
848
849 case ButtonRelease:
850 if (WMIsDraggingFromView(view)) {
851 Bool dropBegan = processButtonRelease(info);
852
853 recolorCursor(info, False);
854 if (dropBegan) {
855 endDragImage(info, False);
856 XDND_SOURCE_STATE(info) = finishDropState;
857 } else {
858 /* drop failed */
859 endDragImage(info, True);
860 endDragProcess(info, False);
861 }
862 }
863 break;
864
865 case MotionNotify:
866 if (WMIsDraggingFromView(view)) {
867 mouseLocation = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
868
869 if (abs(XDND_DRAG_ICON_POS(info).x - mouseLocation.x) >=
870 MIN_X_MOVE_OFFSET
871 || abs(XDND_DRAG_ICON_POS(info).y - mouseLocation.y) >= MIN_Y_MOVE_OFFSET) {
872 if (XDND_DRAG_ICON(info) == None) {
873 initMotionProcess(view, info, event, &mouseLocation);
874 startDragImage(view, info, event);
875 } else {
876 XDND_DRAG_ICON_POS(info).x = mouseLocation.x - XDND_MOUSE_OFFSET(info).x;
877 XDND_DRAG_ICON_POS(info).y = mouseLocation.y - XDND_MOUSE_OFFSET(info).y;
878
879 refreshDragImage(view, info);
880 processMotion(info, &mouseLocation);
881 }
882 }
883 }
884 break;
885 }
e7d0c5d9 dan2004-04-07 02:50:52 +0000886}
6672180d kojima2000-03-28 02:30:13 +0000887
e7d0c5d9 dan2004-04-07 02:50:52 +0000888/* Minimal mouse events handler: no right or double-click detection,
889 only drag is supported */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200890static void dragImageHandler(XEvent * event, void *cdata)
60a247f2 kojima2000-04-03 03:10:20 +0000891{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200892 WMView *view = (WMView *) cdata;
e7d0c5d9 dan2004-04-07 02:50:52 +0000893
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200894 WMDragImageFromView(view, event);
e7d0c5d9 dan2004-04-07 02:50:52 +0000895}
896
e7d0c5d9 dan2004-04-07 02:50:52 +0000897/* ----- source states ----- */
898
899#ifdef XDND_DEBUG
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200900static void traceStatusMsg(Display * dpy, XClientMessageEvent * statusEvent)
e7d0c5d9 dan2004-04-07 02:50:52 +0000901{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200902 printf("Xdnd status message:\n");
903
904 if (statusEvent->data.l[1] & 0x2UL)
905 printf("\tsend position on every move\n");
906 else {
907 int x, y, w, h;
908 x = statusEvent->data.l[2] >> 16;
909 y = statusEvent->data.l[2] & 0xFFFFL;
910 w = statusEvent->data.l[3] >> 16;
911 h = statusEvent->data.l[3] & 0xFFFFL;
912
913 printf("\tsend position out of ((%d,%d) , (%d,%d))\n", x, y, x + w, y + h);
914 }
915
916 if (statusEvent->data.l[1] & 0x1L)
917 printf("\tallowed action: %s\n", XGetAtomName(dpy, statusEvent->data.l[4]));
918 else
919 printf("\tno action allowed\n");
60a247f2 kojima2000-04-03 03:10:20 +0000920}
e7d0c5d9 dan2004-04-07 02:50:52 +0000921#endif
60a247f2 kojima2000-04-03 03:10:20 +0000922
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200923static void storeDropAction(WMDraggingInfo * info, Atom destAction)
60a247f2 kojima2000-04-03 03:10:20 +0000924{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200925 WMView *sourceView = XDND_SOURCE_VIEW(info);
926 WMScreen *scr = W_VIEW_SCREEN(sourceView);
927
928 if (sourceView->dragSourceProcs->acceptDropOperation != NULL) {
929 if (sourceView->dragSourceProcs->acceptDropOperation(sourceView,
930 W_ActionToOperation(scr, destAction)))
931 XDND_DEST_ACTION(info) = destAction;
932 else
933 XDND_DEST_ACTION(info) = None;
934 } else {
935 XDND_DEST_ACTION(info) = destAction;
936 }
60a247f2 kojima2000-04-03 03:10:20 +0000937}
6672180d kojima2000-03-28 02:30:13 +0000938
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200939static void storeStatusMessageInfos(WMDraggingInfo * info, XClientMessageEvent * statusEvent)
e7d0c5d9 dan2004-04-07 02:50:52 +0000940{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200941 WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
6672180d kojima2000-03-28 02:30:13 +0000942
e7d0c5d9 dan2004-04-07 02:50:52 +0000943#ifdef XDND_DEBUG
6672180d kojima2000-03-28 02:30:13 +0000944
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200945 traceStatusMsg(sourceScreen(info)->display, statusEvent);
e7d0c5d9 dan2004-04-07 02:50:52 +0000946#endif
6672180d kojima2000-03-28 02:30:13 +0000947
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200948 if (statusEvent->data.l[1] & 0x2UL) {
949 /* bit 1 set: destination wants position messages on every move */
950 noPosZone->size.width = 0;
951 noPosZone->size.height = 0;
952 } else {
953 /* don't send another position message while in given rectangle */
954 noPosZone->pos.x = statusEvent->data.l[2] >> 16;
955 noPosZone->pos.y = statusEvent->data.l[2] & 0xFFFFL;
956 noPosZone->size.width = statusEvent->data.l[3] >> 16;
957 noPosZone->size.height = statusEvent->data.l[3] & 0xFFFFL;
958 }
959
960 if ((statusEvent->data.l[1] & 0x1L) || statusEvent->data.l[4] != None) {
961 /* destination accept drop */
962 storeDropAction(info, statusEvent->data.l[4]);
963 } else {
964 XDND_DEST_ACTION(info) = None;
965 }
6672180d kojima2000-03-28 02:30:13 +0000966}
967
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200968static void *idleState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +0000969{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200970 WMScreen *scr;
971 Atom destMsg = event->message_type;
972
973 scr = W_VIEW_SCREEN(view);
974
975 if (destMsg == scr->xdndStatusAtom) {
976 storeStatusMessageInfos(info, event);
977
978 if (XDND_DEST_ACTION(info) != None) {
979 recolorCursor(info, True);
980 W_DragSourceStartTimer(info);
981 return dropAllowedState;
982 } else {
983 /* drop denied */
984 recolorCursor(info, False);
985 return idleState;
986 }
987 }
988
989 if (destMsg == scr->xdndFinishedAtom) {
990 wwarning("received xdndFinishedAtom before drop began");
991 }
992
993 W_DragSourceStartTimer(info);
994 return idleState;
e7d0c5d9 dan2004-04-07 02:50:52 +0000995}
60a247f2 kojima2000-04-03 03:10:20 +0000996
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200997static void *dropAllowedState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
60a247f2 kojima2000-04-03 03:10:20 +0000998{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200999 WMScreen *scr = W_VIEW_SCREEN(view);
1000 Atom destMsg = event->message_type;
60a247f2 kojima2000-04-03 03:10:20 +00001001
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001002 if (destMsg == scr->xdndStatusAtom) {
1003 storeStatusMessageInfos(info, event);
60a247f2 kojima2000-04-03 03:10:20 +00001004
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001005 if (XDND_DEST_ACTION(info) == None) {
1006 /* drop denied */
1007 recolorCursor(info, False);
1008 return idleState;
1009 }
1010 }
60a247f2 kojima2000-04-03 03:10:20 +00001011
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001012 W_DragSourceStartTimer(info);
1013 return dropAllowedState;
e7d0c5d9 dan2004-04-07 02:50:52 +00001014}
60a247f2 kojima2000-04-03 03:10:20 +00001015
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001016static void *finishDropState(WMView * view, XClientMessageEvent * event, WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +00001017{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001018 WMScreen *scr = W_VIEW_SCREEN(view);
1019 Atom destMsg = event->message_type;
e7d0c5d9 dan2004-04-07 02:50:52 +00001020
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001021 if (destMsg == scr->xdndFinishedAtom) {
1022 endDragProcess(info, True);
1023 return NULL;
1024 }
e7d0c5d9 dan2004-04-07 02:50:52 +00001025
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001026 W_DragSourceStartTimer(info);
1027 return finishDropState;
60a247f2 kojima2000-04-03 03:10:20 +00001028}
1029
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001030/* ----- end of source states ----- */
60a247f2 kojima2000-04-03 03:10:20 +00001031
e7d0c5d9 dan2004-04-07 02:50:52 +00001032/* ----- source timer ----- */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001033static void dragSourceResponseTimeOut(void *source)
e7d0c5d9 dan2004-04-07 02:50:52 +00001034{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001035 WMView *view = (WMView *) source;
1036 WMDraggingInfo *info = &(W_VIEW_SCREEN(view)->dragInfo);
1037
1038 wwarning("delay for drag destination response expired");
1039 sendLeaveMessage(info);
1040
1041 recolorCursor(info, False);
1042 if (XDND_SOURCE_STATE(info) == finishDropState) {
1043 /* drop failed */
1044 endDragImage(info, True);
1045 endDragProcess(info, False);
1046 } else {
1047 XDND_SOURCE_STATE(info) = idleState;
1048 }
e7d0c5d9 dan2004-04-07 02:50:52 +00001049}
60a247f2 kojima2000-04-03 03:10:20 +00001050
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001051void W_DragSourceStopTimer()
6672180d kojima2000-03-28 02:30:13 +00001052{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001053 if (dndSourceTimer != NULL) {
1054 WMDeleteTimerHandler(dndSourceTimer);
1055 dndSourceTimer = NULL;
1056 }
e7d0c5d9 dan2004-04-07 02:50:52 +00001057}
567e53c6 kojima2000-04-03 06:08:31 +00001058
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001059void W_DragSourceStartTimer(WMDraggingInfo * info)
e7d0c5d9 dan2004-04-07 02:50:52 +00001060{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001061 W_DragSourceStopTimer();
60a247f2 kojima2000-04-03 03:10:20 +00001062
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001063 dndSourceTimer = WMAddTimerHandler(XDND_DESTINATION_RESPONSE_MAX_DELAY,
1064 dragSourceResponseTimeOut, XDND_SOURCE_VIEW(info));
e7d0c5d9 dan2004-04-07 02:50:52 +00001065}
60a247f2 kojima2000-04-03 03:10:20 +00001066
e7d0c5d9 dan2004-04-07 02:50:52 +00001067/* ----- End of Destination timer ----- */
60a247f2 kojima2000-04-03 03:10:20 +00001068
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001069void W_DragSourceStateHandler(WMDraggingInfo * info, XClientMessageEvent * event)
e7d0c5d9 dan2004-04-07 02:50:52 +00001070{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001071 WMView *view;
1072 W_DndState *newState;
e7d0c5d9 dan2004-04-07 02:50:52 +00001073
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001074 if (XDND_SOURCE_VIEW_STORED(info)) {
1075 if (XDND_SOURCE_STATE(info) != NULL) {
1076 view = XDND_SOURCE_VIEW(info);
e7d0c5d9 dan2004-04-07 02:50:52 +00001077#ifdef XDND_DEBUG
1078
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001079 printf("current source state: %s\n", stateName(XDND_SOURCE_STATE(info)));
e7d0c5d9 dan2004-04-07 02:50:52 +00001080#endif
1081
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001082 newState = (W_DndState *) XDND_SOURCE_STATE(info) (view, event, info);
e7d0c5d9 dan2004-04-07 02:50:52 +00001083
1084#ifdef XDND_DEBUG
1085
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001086 printf("new source state: %s\n", stateName(newState));
e7d0c5d9 dan2004-04-07 02:50:52 +00001087#endif
1088
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001089 if (newState != NULL)
1090 XDND_SOURCE_STATE(info) = newState;
1091 /* else drop finished, and info has been flushed */
1092 }
085e9d62 dan2006-01-22 20:33:19 +00001093
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001094 } else {
1095 wwarning("received DnD message without having a target");
1096 }
6672180d kojima2000-03-28 02:30:13 +00001097}
1098
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001099void WMSetViewDragImage(WMView * view, WMPixmap * dragImage)
e7d0c5d9 dan2004-04-07 02:50:52 +00001100{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001101 if (view->dragImage != NULL)
1102 WMReleasePixmap(view->dragImage);
e7d0c5d9 dan2004-04-07 02:50:52 +00001103
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001104 view->dragImage = WMRetainPixmap(dragImage);
e7d0c5d9 dan2004-04-07 02:50:52 +00001105}
1106
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001107void WMReleaseViewDragImage(WMView * view)
e7d0c5d9 dan2004-04-07 02:50:52 +00001108{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001109 if (view->dragImage != NULL)
1110 WMReleasePixmap(view->dragImage);
e7d0c5d9 dan2004-04-07 02:50:52 +00001111}
1112
e7d0c5d9 dan2004-04-07 02:50:52 +00001113/* Create a drag handler, associating drag event masks with dragEventProc */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001114void WMCreateDragHandler(WMView * view, WMEventProc * dragEventProc, void *clientData)
e7d0c5d9 dan2004-04-07 02:50:52 +00001115{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001116 WMCreateEventHandler(view,
1117 ButtonPressMask | ButtonReleaseMask | Button1MotionMask, dragEventProc, clientData);
e7d0c5d9 dan2004-04-07 02:50:52 +00001118}
1119
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001120void WMDeleteDragHandler(WMView * view, WMEventProc * dragEventProc, void *clientData)
e7d0c5d9 dan2004-04-07 02:50:52 +00001121{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001122 WMDeleteEventHandler(view,
1123 ButtonPressMask | ButtonReleaseMask | Button1MotionMask, dragEventProc, clientData);
e7d0c5d9 dan2004-04-07 02:50:52 +00001124}
1125
e7d0c5d9 dan2004-04-07 02:50:52 +00001126/* set default drag handler for view */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001127void WMSetViewDraggable(WMView * view, WMDragSourceProcs * dragSourceProcs, WMPixmap * dragImage)
e7d0c5d9 dan2004-04-07 02:50:52 +00001128{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001129 wassertr(dragImage != NULL);
1130 view->dragImage = WMRetainPixmap(dragImage);
e7d0c5d9 dan2004-04-07 02:50:52 +00001131
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001132 WMSetViewDragSourceProcs(view, dragSourceProcs);
e7d0c5d9 dan2004-04-07 02:50:52 +00001133
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001134 WMCreateDragHandler(view, dragImageHandler, view);
e7d0c5d9 dan2004-04-07 02:50:52 +00001135}
1136
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001137void WMUnsetViewDraggable(WMView * view)
e7d0c5d9 dan2004-04-07 02:50:52 +00001138{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001139 if (view->dragSourceProcs) {
1140 wfree(view->dragSourceProcs);
1141 view->dragSourceProcs = NULL;
1142 }
e7d0c5d9 dan2004-04-07 02:50:52 +00001143
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001144 WMReleaseViewDragImage(view);
e7d0c5d9 dan2004-04-07 02:50:52 +00001145
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001146 WMDeleteDragHandler(view, dragImageHandler, view);
e7d0c5d9 dan2004-04-07 02:50:52 +00001147}