#include #include #include "WINGsP.h" #define SPIT(a) #define IS_DROPPABLE(view) (view!=NULL && view->droppableTypes!=NULL && \ view->dragDestinationProcs!=NULL) static Atom operationToAction(WMScreen *scr, WMDragOperationType operation); static WMDragOperationType actionToOperation(WMScreen *scr, Atom action); static Bool _XErrorOccured = False; void WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs) { if (view->dragSourceProcs) free(view->dragSourceProcs); view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs)); *view->dragSourceProcs = *procs; /* XXX fill in non-implemented stuffs */ } /***********************************************************************/ static int handleXError(Display *dpy, XErrorEvent *ev) { _XErrorOccured = True; return 1; } static void protectBlock(Bool begin) { static void *oldHandler = NULL; if (begin) { oldHandler = XSetErrorHandler(handleXError); } else { XSetErrorHandler(oldHandler); } } static Window makeDragIcon(WMScreen *scr, WMPixmap *pixmap) { Window window; WMSize size; unsigned long flags; XSetWindowAttributes attribs; Pixmap pix, mask; if (!pixmap) { pixmap = scr->defaultObjectIcon; } size = WMGetPixmapSize(pixmap); pix = pixmap->pixmap; mask = pixmap->mask; flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap; attribs.save_under = True; attribs.background_pixmap = pix; attribs.override_redirect = True; attribs.colormap = scr->colormap; window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width, size.height, 0, scr->depth, InputOutput, scr->visual, flags, &attribs); #ifdef SHAPE if (mask) { XShapeCombineMask(dpy, scr->balloon->window, ShapeBounding, 0, 0, mask, ShapeSet); } #endif return window; } static void slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY) { double x, y, dx, dy; int i; int iterations; iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY))); x = srcX; y = srcY; dx = (double)(dstX-srcX)/iterations; dy = (double)(dstY-srcY)/iterations; for (i = 0; i <= iterations; i++) { XMoveWindow(dpy, win, x, y); XFlush(dpy); wusleep(800); x += dx; y += dy; } } static Window findChildInWindow(Display *dpy, Window toplevel, int x, int y) { Window foo, bar; Window *children; unsigned nchildren; int i; if (!XQueryTree(dpy, toplevel, &foo, &bar, &children, &nchildren) || children == NULL) { return None; } /* first window that contains the point is the one */ for (i = nchildren-1; i >= 0; i--) { XWindowAttributes attr; if (XGetWindowAttributes(dpy, children[i], &attr) && attr.map_state == IsViewable && x >= attr.x && y >= attr.y && x < attr.x + attr.width && y < attr.y + attr.height) { Window child; child = findChildInWindow(dpy, children[i], x - attr.x, y - attr.y); XFree(children); if (child == None) return toplevel; else return child; } } XFree(children); return None; } static WMView* findViewInToplevel(Display *dpy, Window toplevel, int x, int y) { Window child; child = findChildInWindow(dpy, toplevel, x, y); if (child != None) return W_GetViewForXWindow(dpy, child); else return NULL; } static Window lookForToplevel(WMScreen *scr, Window window, Bool *isAware) { Window toplevel = None; Atom *atoms; int j, count; *isAware = False; atoms = XListProperties(scr->display, window, &count); for (j = 0; j < count; j++) { if (atoms[j] == scr->wmStateAtom) { toplevel = window; } else if (atoms[j] == scr->xdndAwareAtom) { *isAware = True; } } if (atoms) XFree(atoms); if (toplevel == None) { Window *children; Window foo, bar; unsigned nchildren; if (!XQueryTree(scr->display, window, &foo, &bar, &children, &nchildren) || children == NULL) { return None; } for (j = 0; j < nchildren; j++) { toplevel = lookForToplevel(scr, children[j], isAware); if (toplevel != None) break; } XFree(children); } return toplevel; } static Window findToplevelUnderDragPointer(WMScreen *scr, int x, int y, Window iconWindow) { Window foo, bar; Window *children; unsigned nchildren; Bool overSomething = False; int i; if (!XQueryTree(scr->display, scr->rootWin, &foo, &bar, &children, &nchildren) || children == NULL) { SPIT("couldnt query tree!"); return None; } /* try to find the window below the iconWindow by traversing * the whole window list */ /* first find the position of the iconWindow */ for (i = nchildren-1; i >= 0; i--) { if (children[i] == iconWindow) { i--; break; } } if (i <= 0) { XFree(children); return scr->rootWin; } /* first window that contains the point is the one */ for (; i >= 0; i--) { XWindowAttributes attr; if (XGetWindowAttributes(scr->display, children[i], &attr) && attr.map_state == IsViewable && x >= attr.x && y >= attr.y && x < attr.x + attr.width && y < attr.y + attr.height) { Window toplevel; Bool isaware; overSomething = True; toplevel = lookForToplevel(scr, children[i], &isaware); XFree(children); if (isaware) return toplevel; else return None; } } XFree(children); if (!overSomething) return scr->rootWin; else return None; } static void sendClientMessage(Display *dpy, Window win, Atom message, unsigned data1, unsigned data2, unsigned data3, unsigned data4, unsigned data5) { XEvent ev; ev.type = ClientMessage; ev.xclient.message_type = message; ev.xclient.format = 32; ev.xclient.window = win; ev.xclient.data.l[0] = data1; ev.xclient.data.l[1] = data2; ev.xclient.data.l[2] = data3; ev.xclient.data.l[3] = data4; ev.xclient.data.l[4] = data5; XSendEvent(dpy, win, False, 0, &ev); XFlush(dpy); } static unsigned notifyPosition(WMScreen *scr, WMDraggingInfo *info) { Atom action = operationToAction(scr, info->sourceOperation); sendClientMessage(scr->display, info->destinationWindow, scr->xdndPositionAtom, info->sourceWindow, 0, /* reserved */ info->location.x<<16|info->location.y, info->timestamp, action/* operation */); return 0; } static void notifyDrop(WMScreen *scr, WMDraggingInfo *info) { sendClientMessage(scr->display, info->destinationWindow, scr->xdndDropAtom, info->sourceWindow, 0, /* reserved */ info->timestamp, 0, 0); } static void notifyDragLeave(WMScreen *scr, WMDraggingInfo *info) { sendClientMessage(scr->display, info->destinationWindow, scr->xdndLeaveAtom, info->sourceWindow, 0, 0, 0, 0); } static unsigned notifyDragEnter(WMScreen *scr, WMDraggingInfo *info) { unsigned d; d = XDND_VERSION << 24; sendClientMessage(scr->display, info->destinationWindow, scr->xdndEnterAtom, info->sourceWindow, d, 0, 0, 0); return 0; } static void translateCoordinates(WMScreen *scr, Window target, int fromX, int fromY, int *x, int *y) { Window child; XTranslateCoordinates(scr->display, scr->rootWin, target, fromX, fromY, x, y, &child); } static void updateDraggingInfo(WMScreen *scr, WMDraggingInfo *info, XEvent *event, Window iconWindow) { Window toplevel; WMSize size; size = WMGetPixmapSize(info->image); if (event->type == MotionNotify) { info->imageLocation.x = event->xmotion.x_root-(int)size.width/2; info->imageLocation.y = event->xmotion.y_root-(int)size.height/2; info->location.x = event->xmotion.x_root; info->location.y = event->xmotion.y_root; info->timestamp = event->xmotion.time; } else if (event->type == ButtonRelease) { info->imageLocation.x = event->xbutton.x_root-(int)size.width/2; info->imageLocation.y = event->xbutton.y_root-(int)size.height/2; info->location.x = event->xbutton.x_root; info->location.y = event->xbutton.y_root; info->timestamp = event->xbutton.time; } toplevel = findToplevelUnderDragPointer(scr, info->location.x, info->location.y, iconWindow); info->destinationWindow = toplevel; } static void processMotion(WMScreen *scr, WMDraggingInfo *info, WMDraggingInfo *oldInfo, WMRect *rect, unsigned currentAction) { unsigned action; if (info->destinationWindow == None) { /* entered an unsupporeted window */ if (oldInfo->destinationWindow != None && oldInfo->destinationWindow != scr->rootWin) { SPIT("left window"); notifyDragLeave(scr, oldInfo); } } else if (info->destinationWindow == scr->rootWin) { if (oldInfo->destinationWindow != None && oldInfo->destinationWindow != scr->rootWin) { SPIT("left window to root"); notifyDragLeave(scr, oldInfo); } else { /* nothing */ } } else if (oldInfo->destinationWindow != info->destinationWindow) { if (oldInfo->destinationWindow != None && oldInfo->destinationWindow != scr->rootWin) { notifyDragLeave(scr, oldInfo); SPIT("crossed"); } else { SPIT("entered window"); } action = notifyDragEnter(scr, info); } else { #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \ || X >= r->pos.x + r->size.width \ || Y >= r->pos.y + r->size.height) if (rect->size.width == 0 || (LEFT_RECT(rect, info->location.x, info->location.y))) { action = notifyPosition(scr, info); rect->size.width = 0; } } } static WMData* convertSelection(WMView *view, Atom selection, Atom target, void *cdata, Atom *type) { WMScreen *scr = W_VIEW_SCREEN(view); WMData *data; char *typeName = XGetAtomName(scr->display, target); *type = target; data = view->dragSourceProcs->fetchDragData(view, typeName); if (typeName != NULL) XFree(typeName); return data; } static void selectionLost(WMView *view, Atom selection, void *cdata) { if (W_VIEW_SCREEN(view)->dragSourceView == view) { wwarning("DND selection lost during drag operation..."); } } static void selectionDone(WMView *view, Atom selection, Atom target, void *cdata) { } static void timeoutCallback(void *data) { wwarning("drag & drop timed out while waiting for response from 0x%x\n", (unsigned)data); _XErrorOccured = 2; } /* * State Machine For Drag Source: * ------------------------------ * Events * State Call Mtn Ent Lea Crs BUp StA StR Fin TO * 0) idle 1bu - - - - - - - - - * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w - * 2) drag over nothing - 2 1bu - - 0 - - 2w - * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z - * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z - * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w * 6) dropped - - - - - - - - 0 0w * * Events: * Call - called WMDragImageFromView() * Mtn - Motion * Ent - Enter droppable window * Lea - Leave droppable window (or rectangle) * Crs - Leave droppable window (or rectangle) and enter another * BUp - Button Released * StA - XdndStatus client msg with Accept drop * StR - XdndStatus client msg with Reject drop * Fin - XdndFinish client msg * TO - timeout * * Actions: * a - send update message * b - send enter message * c - send leave message * d - release drag section info * e - send drop message * f - setup timeout * u - update dragInfo * * X - ignore * w - warn about unexpected reply * z - abort operation.. unexpected reply * - shouldnt happen */ void WMDragImageFromView(WMView *view, WMPixmap *image, char *dataTypes[], WMPoint atLocation, WMSize mouseOffset, XEvent *event, Bool slideBack) { WMScreen *scr = view->screen; Display *dpy = scr->display; Window icon; XEvent ev; WMSize size; WMRect rect = {{0,0},{0,0}}; int ostate = -1; int state; int action = -1; XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue}; XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue}; XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue}; WMDraggingInfo dragInfo; WMDraggingInfo oldDragInfo; WMHandlerID timer = NULL; static WMSelectionProcs handler = { convertSelection, selectionLost, selectionDone }; wassertr(scr->dragSourceView == NULL); wassertr(view->dragSourceProcs != NULL); /* prepare icon to be dragged */ if (image == NULL) image = scr->defaultObjectIcon; size = WMGetPixmapSize(image); icon = makeDragIcon(scr, image); XMoveWindow(dpy, icon, event->xmotion.x_root-(int)size.width/2, event->xmotion.y_root-(int)size.height/2); XMapRaised(dpy, icon); /* init dragging info */ scr->dragSourceView = view; memset(&dragInfo, 0, sizeof(WMDraggingInfo)); memset(&oldDragInfo, 0, sizeof(WMDraggingInfo)); dragInfo.image = image; dragInfo.sourceView = view; dragInfo.sourceWindow = W_VIEW_DRAWABLE(W_TopLevelOfView(view)); dragInfo.destinationWindow = dragInfo.sourceWindow; dragInfo.location.x = event->xmotion.x_root; dragInfo.location.y = event->xmotion.y_root; dragInfo.imageLocation = atLocation; /* start pointer grab */ XGrabPointer(dpy, scr->rootWin, False, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask, GrabModeSync, GrabModeAsync, None, scr->defaultCursor, CurrentTime); XFlush(dpy); _XErrorOccured = False; /* take ownership of XdndSelection */ if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom, event->xmotion.time, &handler, NULL)) { wwarning("could not get ownership or DND selection"); return; } if (view->dragSourceProcs->beganDragImage != NULL) { view->dragSourceProcs->beganDragImage(view, image, atLocation); } processMotion(scr, &dragInfo, &oldDragInfo, &rect, action); state = 1; while (state != 6 && state != 0 && !_XErrorOccured) { XAllowEvents(dpy, SyncPointer, CurrentTime); WMNextEvent(dpy, &ev); switch (ev.type) { case MotionNotify: if (state >= 1 && state <= 4) { while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ; protectBlock(True); oldDragInfo = dragInfo; updateDraggingInfo(scr, &dragInfo, &ev, icon); XMoveWindow(dpy, icon, dragInfo.imageLocation.x, dragInfo.imageLocation.y); processMotion(scr, &dragInfo, &oldDragInfo, &rect, action); protectBlock(False); /* XXXif entered a different destination, check the operation */ switch (state) { case 1: if (oldDragInfo.destinationWindow != None && oldDragInfo.destinationWindow != scr->rootWin && (dragInfo.destinationWindow == None || dragInfo.destinationWindow == scr->rootWin)) { /* left the droppable window */ state = 2; action = -1; } break; case 2: if (dragInfo.destinationWindow != None && dragInfo.destinationWindow != scr->rootWin) { state = 1; action = -1; } break; case 3: case 4: if (oldDragInfo.destinationWindow != None && oldDragInfo.destinationWindow != scr->rootWin && (dragInfo.destinationWindow == None || dragInfo.destinationWindow == scr->rootWin)) { /* left the droppable window */ state = 2; action = -1; } break; } } break; case ButtonRelease: /* if (state >= 1 && state <= 4) */ { protectBlock(True); oldDragInfo = dragInfo; updateDraggingInfo(scr, &dragInfo, &ev, icon); XMoveWindow(dpy, icon, dragInfo.imageLocation.x, dragInfo.imageLocation.y); processMotion(scr, &dragInfo, &oldDragInfo, &rect, action); dragInfo.timestamp = ev.xbutton.time; protectBlock(False); switch (state) { case 1: state = 5; timer = WMAddTimerHandler(3000, timeoutCallback, (void*)dragInfo.destinationWindow); break; case 2: state = 0; break; case 3: state = 6; break; case 4: state = 0; break; } } break; case ClientMessage: if ((state == 1 || state == 3 || state == 4 || state == 5) && ev.xclient.message_type == scr->xdndStatusAtom && ev.xclient.data.l[0] == dragInfo.destinationWindow) { if (ev.xclient.data.l[1] & 1) { SPIT("got accept msg"); /* will accept drop */ switch (state) { case 1: case 3: case 4: state = 3; break; case 5: if (timer) { WMDeleteTimerHandler(timer); timer = NULL; } state = 6; break; } action = actionToOperation(scr, ev.xclient.data.l[4]); } else { SPIT("got reject msg"); switch (state) { case 1: case 3: case 4: state = 4; break; case 5: state = 0; if (timer) { WMDeleteTimerHandler(timer); timer = NULL; } break; } action = 0; } if (ev.xclient.data.l[1] & (1<<1)) { rect.pos.x = ev.xclient.data.l[2] >> 16; rect.pos.y = ev.xclient.data.l[2] & 0xffff; rect.size.width = ev.xclient.data.l[3] >> 16; rect.size.height = ev.xclient.data.l[3] & 0xffff; } else { rect.size.width = 0; } } else if ((state >= 1 && state <= 5) && ev.xclient.message_type == scr->xdndFinishedAtom && ev.xclient.window == dragInfo.destinationWindow) { wwarning("drag source received unexpected XdndFinished message from %x", (unsigned)dragInfo.destinationWindow); if (state == 3 || state == 4 || state == 5) { state = 0; if (timer) { WMDeleteTimerHandler(timer); timer = NULL; } } } default: WMHandleEvent(&ev); break; } if (ostate != state) { if (state == 3) { XRecolorCursor(dpy, scr->defaultCursor, &green, &back); } else if (ostate == 3) { XRecolorCursor(dpy, scr->defaultCursor, &black, &back); } ostate = state; } } if (timer) { WMDeleteTimerHandler(timer); timer = NULL; } else if (_XErrorOccured) { /* got a timeout, send leave */ notifyDragLeave(scr, &dragInfo); } XUngrabPointer(dpy, CurrentTime); SPIT("exited main loop"); if (_XErrorOccured || state != 6) { goto cancelled; } assert(dragInfo.destinationWindow != None); protectBlock(True); notifyDrop(scr, &dragInfo); protectBlock(False); if (_XErrorOccured) goto cancelled; SPIT("dropped"); XDestroyWindow(dpy, icon); return; cancelled: scr->dragSourceView = NULL; WMDeleteSelectionHandler(view, scr->xdndSelectionAtom, event->xmotion.time); if (slideBack) { slideWindow(dpy, icon, dragInfo.imageLocation.x, dragInfo.imageLocation.y, atLocation.x, atLocation.y); } XDestroyWindow(dpy, icon); if (view->dragSourceProcs->endedDragImage != NULL) { view->dragSourceProcs->endedDragImage(view, image, dragInfo.imageLocation, False); } } static Atom operationToAction(WMScreen *scr, WMDragOperationType operation) { switch (operation) { case WDOperationNone: return None; case WDOperationCopy: return scr->xdndActionCopy; case WDOperationMove: return scr->xdndActionMove; case WDOperationLink: return scr->xdndActionLink; case WDOperationAsk: return scr->xdndActionAsk; case WDOperationPrivate: return scr->xdndActionPrivate; default: return None; } } static WMDragOperationType actionToOperation(WMScreen *scr, Atom action) { if (action == scr->xdndActionCopy) { return WDOperationCopy; } else if (action == scr->xdndActionMove) { return WDOperationMove; } else if (action == scr->xdndActionLink) { return WDOperationLink; } else if (action == scr->xdndActionAsk) { return WDOperationAsk; } else if (action == scr->xdndActionPrivate) { return WDOperationPrivate; } else if (action == None) { return WDOperationCopy; } else { char *tmp = XGetAtomName(scr->display, action); wwarning("unknown XDND action %s from 0x%x", tmp, (unsigned)scr->dragInfo.sourceWindow); XFree(tmp); return WDOperationCopy; } } static Atom* getTypeList(Window window, XClientMessageEvent *event) { int i = 0; Atom *types = NULL; if (event->data.l[1] & 1) { /* > 3 types */ } else { types = wmalloc(4 * sizeof(Atom)); if (event->data.l[2] != None) types[i++] = event->data.l[2]; if (event->data.l[3] != None) types[i++] = event->data.l[3]; if (event->data.l[4] != None) types[i++] = event->data.l[4]; types[i] = 0; } if (types[0] == 0) { wwarning("received invalid drag & drop type list"); /*XXX return;*/ } return types; } #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info) static void receivedData(WMView *view, Atom selection, Atom target, Time timestamp, void *cdata, WMData *data) { WMScreen *scr = W_VIEW_SCREEN(view); WMDraggingInfo *info = (WMDraggingInfo*)cdata; Bool res; res = view->dragDestinationProcs->performDragOperation(view, info, data); if (res) { DISPATCH(view, concludeDragOperation, info); } /* send finished message */ sendClientMessage(scr->display, info->sourceWindow, scr->xdndFinishedAtom, info->destinationWindow, 0, 0, 0, 0); memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo)); } void W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event) { WMScreen *scr = W_VIEW_SCREEN(toplevel); WMView *oldView = NULL; WMView *newView = NULL; unsigned operation = 0; int x, y; enum { WNothing, WEnter, WLeave, WCross, /* leave one and enter another */ WUpdate, WDrop }; Window source; int what = WNothing; Bool sendStatus = False; source = scr->dragInfo.sourceWindow; oldView = scr->dragInfo.destView; if (event->message_type == scr->xdndFinishedAtom) { WMView *view = scr->dragSourceView; if (view->dragSourceProcs->endedDragImage != NULL) { view->dragSourceProcs->endedDragImage(view, scr->dragInfo.image, scr->dragInfo.imageLocation, True); } scr->dragSourceView = NULL; WMDeleteSelectionHandler(view, scr->xdndSelectionAtom, scr->dragInfo.timestamp); return; } if (event->message_type == scr->xdndEnterAtom) { Window foo, bar; int bla; unsigned ble; if (scr->dragInfo.sourceWindow != None) { puts("received Enter event in bad order"); } memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo)); if ((event->data.l[1] >> 24) > XDND_VERSION) { wwarning("received drag & drop request with unsupported version %i", (event->data.l[1] >> 24)); return; } scr->dragInfo.protocolVersion = event->data.l[1] >> 24; scr->dragInfo.sourceWindow = source = event->data.l[0]; scr->dragInfo.destinationWindow = event->window; /* XXX */ scr->dragInfo.image = NULL; XQueryPointer(scr->display, scr->rootWin, &foo, &bar, &scr->dragInfo.location.x, &scr->dragInfo.location.y, &bla, &bla, &ble); translateCoordinates(scr, scr->dragInfo.destinationWindow, scr->dragInfo.location.x, scr->dragInfo.location.y, &x, &y); newView = findViewInToplevel(scr->display, scr->dragInfo.destinationWindow, x, y); } else if (event->message_type == scr->xdndPositionAtom && scr->dragInfo.sourceWindow == event->data.l[0]) { scr->dragInfo.location.x = event->data.l[2] >> 16; scr->dragInfo.location.y = event->data.l[2] & 0xffff; if (scr->dragInfo.protocolVersion >= 1) { scr->dragInfo.timestamp = event->data.l[3]; scr->dragInfo.sourceOperation = actionToOperation(scr, event->data.l[4]); } else { scr->dragInfo.timestamp = CurrentTime; scr->dragInfo.sourceOperation = WDOperationCopy; } translateCoordinates(scr, scr->dragInfo.destinationWindow, scr->dragInfo.location.x, scr->dragInfo.location.y, &x, &y); newView = findViewInToplevel(scr->display, scr->dragInfo.destinationWindow, x, y); } else if (event->message_type == scr->xdndLeaveAtom && scr->dragInfo.sourceWindow == event->data.l[0]) { memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo)); } else if (event->message_type == scr->xdndDropAtom && scr->dragInfo.sourceWindow == event->data.l[0]) { /* drop */ what = WDrop; } else { return; } /* * Now map the XDND events to WINGs events. */ if (what == WNothing) { if (IS_DROPPABLE(newView)) { if (oldView == NULL) { /* entered */ what = WEnter; } else if (oldView == newView) { /* updated */ what = WUpdate; } else { what = WCross; } } else { if (oldView != NULL) { what = WLeave; } else { /* just send rejection msg */ sendStatus = True; } } } switch (what) { case WEnter: scr->dragInfo.destView = newView; operation = DISPATCH(newView, draggingEntered, &scr->dragInfo); sendStatus = True; break; case WLeave: scr->dragInfo.destView = NULL; DISPATCH(oldView, draggingExited, &scr->dragInfo); sendStatus = True; operation = WDOperationNone; break; case WCross: DISPATCH(oldView, draggingExited, &scr->dragInfo); scr->dragInfo.destView = newView; operation = DISPATCH(newView, draggingEntered, &scr->dragInfo); sendStatus = True; break; case WUpdate: operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo); sendStatus = True; break; case WDrop: { char *type; type = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo); if (type != NULL) { if (!WMRequestSelection(scr->dragInfo.destView, scr->xdndSelectionAtom, XInternAtom(scr->display, type, False), scr->dragInfo.timestamp, receivedData, &scr->dragInfo)) { wwarning("could not request data for dropped data"); /* send finished message */ sendClientMessage(scr->display, source, scr->xdndFinishedAtom, scr->dragInfo.destinationWindow, 0, 0, 0, 0); } } else { /* send finished message */ sendClientMessage(scr->display, source, scr->xdndFinishedAtom, scr->dragInfo.destinationWindow, 0, 0, 0, 0); } } break; default: break; } if (sendStatus) { Atom action; action = operationToAction(scr, operation); sendClientMessage(scr->display, source, scr->xdndStatusAtom, scr->dragInfo.destinationWindow, action != None ? 1 : 0, 0, 0, action); } }