1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-19 12:28:22 +01:00
Files
wmaker/WINGs/dragsource.c
dan 9aca0d5f6e - Check whether libXft is at least version 2.1.2 else refuse to compile.
- Fixed bug in icon chooser dialog that could cause a segmentation fault
  in some cases (Pascal Hofstee <caelian@gmail.com>)
- Fixed crash in asm code in wrlib, with new versions of gcc.
- Fixed bug in the x86_PseudoColor_32_to_8() function which incorrectly
  used the r, g, b fields in the conversion.
- Fixed x86 ASM code in wrlib to work on 64 bit architectures.
- Fixed the focus flicker seen with some apps (notably gtk2)
  (Alexey Spiridonov <snarkmaster@gmail.com>)
- Fixed all crashing bugs that were generated by wmaker starting with the
  WMState file missing.
- Added NetWM support (a modified version of the patch originaly written
  by Peter Zijlstra <a.p.zijlstra@chello.nl>)
- Applied patch to enhance the Virtual Desktop behaviour, and to integrate
  it with the NetWM code (Peter Zijlstra <a.p.zijlstra@chello.nl>)
- Applied a few xinerama and placement fixes (Peter Zijlstra
    <a.p.zijlstra@chello.nl>)
- Fixed memory leak in dock code.
- Fixed and enhanced the text wrapping in WINGs.
- Fixed the layout of some elements in WPrefs.app
- Added workaround for aplications that don't set the required hints on the
  client leader window, but they set them on normal windows (observer with
  KDE 3.3.0 mainly). This will allow these apps to get an appicon again.
  (they should be fixed still)
- Added workaround for applications that do not set a command with
  XSetCommand(), but instead they set the _NET_WM_PID property. This works
  with operating systems that offer a /proc interface similar to what linux
  has. (This also is to fix problems with KDE 3.3.0 apps, but not only them).
- Fixed bug with autostart and exit scripts not being executed if user
  GNUstep path was different from ~/GNUstep (when setting GNUSTEP_USER_ROOT)
- Added utf8 support in WINGs (removed old X core font code)
- Added utility to convert old font names to new font names in style files
2004-10-12 01:34:32 +00:00

1315 lines
34 KiB
C

#include "../src/config.h"
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif
#include "WINGsP.h"
#define XDND_DESTINATION_RESPONSE_MAX_DELAY 10000
#define MIN_X_MOVE_OFFSET 5
#define MIN_Y_MOVE_OFFSET 5
#define MAX_SLIDEBACK_ITER 15
#define VERSION_INFO(dragInfo) dragInfo->protocolVersion
#define XDND_PROPERTY_FORMAT 32
#define XDND_ACTION_DESCRIPTION_FORMAT 8
#define XDND_SOURCE_INFO(dragInfo) dragInfo->sourceInfo
#define XDND_DEST_WIN(dragInfo) dragInfo->sourceInfo->destinationWindow
#define XDND_SOURCE_ACTION(dragInfo) dragInfo->sourceAction
#define XDND_DEST_ACTION(dragInfo) dragInfo->destinationAction
#define XDND_SOURCE_VIEW(dragInfo) dragInfo->sourceInfo->sourceView
#define XDND_SOURCE_STATE(dragInfo) dragInfo->sourceInfo->state
#define XDND_SELECTION_PROCS(dragInfo) dragInfo->sourceInfo->selectionProcs
#define XDND_DRAG_ICON(dragInfo) dragInfo->sourceInfo->icon
#define XDND_MOUSE_OFFSET(dragInfo) dragInfo->sourceInfo->mouseOffset
#define XDND_DRAG_CURSOR(dragInfo) dragInfo->sourceInfo->dragCursor
#define XDND_DRAG_ICON_POS(dragInfo) dragInfo->sourceInfo->imageLocation
#define XDND_NO_POS_ZONE(dragInfo) dragInfo->sourceInfo->noPositionMessageZone
#define XDND_TIMESTAMP(dragInfo) dragInfo->timestamp
#define XDND_3_TYPES(dragInfo) dragInfo->sourceInfo->firstThreeTypes
#define XDND_SOURCE_VIEW_STORED(dragInfo) dragInfo->sourceInfo != NULL \
&& dragInfo->sourceInfo->sourceView != NULL
static WMHandlerID dndSourceTimer = NULL;
static void* idleState(WMView *srcView, XClientMessageEvent *event,
WMDraggingInfo *info);
static void* dropAllowedState(WMView *srcView, XClientMessageEvent *event,
WMDraggingInfo *info);
static void* finishDropState(WMView *srcView, XClientMessageEvent *event,
WMDraggingInfo *info);
#ifdef XDND_DEBUG
static const char*
stateName(W_DndState *state)
{
if (state == NULL)
return "no state defined";
if (state == idleState)
return "idleState";
if (state == dropAllowedState)
return "dropAllowedState";
if (state == finishDropState)
return "finishDropState";
return "unknown state";
}
#endif
static WMScreen*
sourceScreen(WMDraggingInfo *info)
{
return W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
}
static void
endDragProcess(WMDraggingInfo *info, Bool deposited)
{
WMView *view = XDND_SOURCE_VIEW(info);
WMScreen *scr = W_VIEW_SCREEN(XDND_SOURCE_VIEW(info));
/* free selection handler while view exists */
WMDeleteSelectionHandler(view,
scr->xdndSelectionAtom,
CurrentTime);
wfree(XDND_SELECTION_PROCS(info));
if (XDND_DRAG_CURSOR(info) != None) {
XFreeCursor(scr->display,
XDND_DRAG_CURSOR(info));
XDND_DRAG_CURSOR(info) = None;
}
if (view->dragSourceProcs->endedDrag != NULL) {
/* this can destroy source view (with a "move" action for example) */
view->dragSourceProcs->endedDrag(view, &XDND_DRAG_ICON_POS(info),
deposited);
}
/* clear remaining draggging infos */
wfree(XDND_SOURCE_INFO(info));
XDND_SOURCE_INFO(info) = NULL;
}
/* ----- drag cursor ----- */
static void
initDragCursor(WMDraggingInfo *info)
{
WMScreen *scr = sourceScreen(info);
XColor cursorFgColor, cursorBgColor;
/* green */
cursorFgColor.red = 0x4500;
cursorFgColor.green = 0xb000;
cursorFgColor.blue = 0x4500;
/* white */
cursorBgColor.red = 0xffff;
cursorBgColor.green = 0xffff;
cursorBgColor.blue = 0xffff;
XDND_DRAG_CURSOR(info) = XCreateFontCursor(scr->display, XC_left_ptr);
XRecolorCursor(scr->display,
XDND_DRAG_CURSOR(info),
&cursorFgColor,
&cursorBgColor);
XFlush(scr->display);
}
static void
recolorCursor(WMDraggingInfo *info, Bool dropIsAllowed)
{
WMScreen *scr = sourceScreen(info);
if (dropIsAllowed) {
XDefineCursor(scr->display,
scr->rootWin,
XDND_DRAG_CURSOR(info));
} else {
XDefineCursor(scr->display,
scr->rootWin,
scr->defaultCursor);
}
XFlush(scr->display);
}
/* ----- end of drag cursor ----- */
/* ----- selection procs ----- */
static WMData*
convertSelection(WMView *view, Atom selection, Atom target,
void *cdata, Atom *type)
{
WMScreen *scr;
WMData *data;
char *typeName;
scr = W_VIEW_SCREEN(view);
typeName = XGetAtomName(scr->display, target);
*type = target;
if (view->dragSourceProcs->fetchDragData != NULL) {
data = view->dragSourceProcs->fetchDragData(
view,
typeName);
} else {
data = NULL;
}
if (typeName != NULL)
XFree(typeName);
return data;
}
static void
selectionLost(WMView *view, Atom selection, void *cdata)
{
wwarning("DND selection lost during drag operation...");
}
static void
selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
{
#ifdef XDND_DEBUG
printf("selection done\n");
#endif
}
/* ----- end of selection procs ----- */
/* ----- visual part ----- */
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(scr->display, 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(MAX_SLIDEBACK_ITER,
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 int
getInitialDragImageCoord(int viewCoord, int mouseCoord,
int viewSize, int iconSize)
{
if (iconSize >= viewSize) {
/* center icon coord on view */
return viewCoord - iconSize/2;
} else {
/* try to center icon on mouse pos */
if (mouseCoord - iconSize/2 <= viewCoord)
/* if icon was centered on mouse, it would be off view
thus, put icon left (resp. top) side
at view (resp. top) side */
return viewCoord;
else if (mouseCoord + iconSize/2 >= viewCoord + viewSize)
/* if icon was centered on mouse, it would be off view
thus, put icon right (resp. bottom) side
at view right (resp. bottom) side */
return viewCoord + viewSize - iconSize;
else
return mouseCoord - iconSize/2;
}
}
static void
initDragImagePos(WMView *view, WMDraggingInfo *info, XEvent *event)
{
WMSize iconSize = WMGetPixmapSize(view->dragImage);
WMSize viewSize = WMGetViewSize(view);
WMPoint viewPos;
Window foo;
XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
0, 0, &(viewPos.x), &(viewPos.y),
&foo);
/* set icon pos */
XDND_DRAG_ICON_POS(info).x =
getInitialDragImageCoord(viewPos.x, event->xmotion.x_root,
viewSize.width, iconSize.width);
XDND_DRAG_ICON_POS(info).y =
getInitialDragImageCoord(viewPos.y, event->xmotion.y_root,
viewSize.height, iconSize.height);
/* set mouse offset relative to icon */
XDND_MOUSE_OFFSET(info).x =
event->xmotion.x_root - XDND_DRAG_ICON_POS(info).x;
XDND_MOUSE_OFFSET(info).y =
event->xmotion.y_root - XDND_DRAG_ICON_POS(info).y;
}
static void
refreshDragImage(WMView *view, WMDraggingInfo *info)
{
WMScreen *scr = W_VIEW_SCREEN(view);
XMoveWindow(scr->display, XDND_DRAG_ICON(info),
XDND_DRAG_ICON_POS(info).x,
XDND_DRAG_ICON_POS(info).y);
}
static void
startDragImage(WMView *view, WMDraggingInfo *info, XEvent* event)
{
WMScreen *scr = W_VIEW_SCREEN(view);
XDND_DRAG_ICON(info) = makeDragIcon(scr, view->dragImage);
initDragImagePos(view, info, event);
refreshDragImage(view, info);
XMapRaised(scr->display, XDND_DRAG_ICON(info));
initDragCursor(info);
}
static void
endDragImage(WMDraggingInfo *info, Bool slideBack)
{
WMView *view = XDND_SOURCE_VIEW(info);
Display *dpy = W_VIEW_SCREEN(view)->display;
if (slideBack) {
WMPoint toLocation;
Window foo;
XTranslateCoordinates(W_VIEW_SCREEN(view)->display,
WMViewXID(view), W_VIEW_SCREEN(view)->rootWin,
0, 0, &(toLocation.x), &(toLocation.y),
&foo);
slideWindow(dpy, XDND_DRAG_ICON(info),
XDND_DRAG_ICON_POS(info).x,
XDND_DRAG_ICON_POS(info).y,
toLocation.x,
toLocation.y);
}
XDestroyWindow(dpy, XDND_DRAG_ICON(info));
}
/* ----- end of visual part ----- */
/* ----- messages ----- */
/* send a DnD message to the destination window */
static Bool
sendDnDClientMessage(WMDraggingInfo *info, Atom message,
unsigned long data1,
unsigned long data2,
unsigned long data3,
unsigned long data4)
{
Display *dpy = sourceScreen(info)->display;
Window srcWin = WMViewXID(XDND_SOURCE_VIEW(info));
Window destWin = XDND_DEST_WIN(info);
if (! W_SendDnDClientMessage(dpy,
destWin,
message,
srcWin,
data1,
data2,
data3,
data4)) {
/* drop failed */
recolorCursor(info, False);
endDragImage(info, True);
endDragProcess(info, False);
return False;
}
return True;
}
static Bool
sendEnterMessage(WMDraggingInfo *info)
{
WMScreen *scr = sourceScreen(info);
unsigned long data1;
data1 = (VERSION_INFO(info) << 24)|1; /* 1: support of type list */
return sendDnDClientMessage(info, scr->xdndEnterAtom,
data1,
XDND_3_TYPES(info)[0],
XDND_3_TYPES(info)[1],
XDND_3_TYPES(info)[2]);
}
// this functon doesn't return something in all cases.
// control reaches end of non-void function. fix this -Dan
static Bool
sendPositionMessage(WMDraggingInfo *info, WMPoint *mousePos)
{
WMScreen *scr = sourceScreen(info);
WMRect *noPosZone = &(XDND_NO_POS_ZONE(info));
if (noPosZone->size.width != 0 || noPosZone->size.height != 0) {
if (mousePos->x < noPosZone->pos.x
|| mousePos->x > (noPosZone->pos.x + noPosZone->size.width)
|| mousePos->y < noPosZone->pos.y
|| mousePos->y > (noPosZone->pos.y + noPosZone->size.width)) {
/* send position if out of zone defined by destination */
return sendDnDClientMessage(info, scr->xdndPositionAtom,
0,
mousePos->x<<16|mousePos->y,
XDND_TIMESTAMP(info),
XDND_SOURCE_ACTION(info));
}
} else {
/* send position on each move */
return sendDnDClientMessage(info, scr->xdndPositionAtom,
0,
mousePos->x<<16|mousePos->y,
XDND_TIMESTAMP(info),
XDND_SOURCE_ACTION(info));
}
}
static Bool
sendLeaveMessage(WMDraggingInfo *info)
{
WMScreen *scr = sourceScreen(info);
return sendDnDClientMessage(info, scr->xdndLeaveAtom,
0, 0, 0, 0);
}
static Bool
sendDropMessage(WMDraggingInfo *info)
{
WMScreen *scr = sourceScreen(info);
return sendDnDClientMessage(info,
scr->xdndDropAtom,
0,
XDND_TIMESTAMP(info),
0, 0);
}
/* ----- end of messages ----- */
static Atom*
getTypeAtomList(WMScreen *scr, WMView *view, int* count)
{
WMArray* types;
Atom* typeAtoms;
int i;
types = view->dragSourceProcs->dropDataTypes(view);
if (types != NULL) {
*count = WMGetArrayItemCount(types);
if (*count > 0) {
typeAtoms = wmalloc((*count)*sizeof(Atom));
for (i=0; i < *count; i++) {
typeAtoms[i] = XInternAtom(scr->display,
WMGetFromArray(types, i),
False);
}
/* WMFreeArray(types); */
return typeAtoms;
}
/* WMFreeArray(types); */
}
*count = 1;
typeAtoms = wmalloc(sizeof(Atom));
*typeAtoms = None;
return typeAtoms;
}
static void
registerDropTypes(WMScreen *scr, WMView *view, WMDraggingInfo *info)
{
Atom* typeList;
int i, count;
typeList = getTypeAtomList(scr, view, &count);
/* store the first 3 types */
for(i=0; i < 3 && i < count; i++)
XDND_3_TYPES(info)[i] = typeList[i];
for(; i < 3; i++)
XDND_3_TYPES(info)[i] = None;
/* store the entire type list */
XChangeProperty(scr->display,
WMViewXID(view),
scr->xdndTypeListAtom,
XA_ATOM,
XDND_PROPERTY_FORMAT,
PropModeReplace,
(unsigned char*) typeList,
count);
}
static void
registerOperationList(WMScreen *scr, WMView *view, WMArray* operationArray)
{
Atom* actionList;
WMDragOperationType operation;
int count = WMGetArrayItemCount(operationArray);
int i;
actionList = wmalloc(sizeof(Atom)*count);
for(i=0; i < count; i++) {
operation = WMGetDragOperationItemType(
WMGetFromArray(operationArray, i));
actionList[i] = W_OperationToAction(scr, operation);
}
XChangeProperty(scr->display,
WMViewXID(view),
scr->xdndActionListAtom,
XA_ATOM,
XDND_PROPERTY_FORMAT,
PropModeReplace,
(unsigned char*) actionList,
count);
}
static void
registerDescriptionList(WMScreen *scr, WMView *view, WMArray* operationArray)
{
char *text, *textListItem, *textList;
int count = WMGetArrayItemCount(operationArray);
int i;
int size = 0;
/* size of XA_STRING info */
for(i=0; i < count; i++) {
size += strlen(WMGetDragOperationItemText(
WMGetFromArray(operationArray, i))) + 1; /* +1 = +NULL */
}
/* create text list */
textList = wmalloc(size);
textListItem = textList;
for(i=0; i < count; i++) {
text = WMGetDragOperationItemText(WMGetFromArray(operationArray, i));
strcpy(textListItem, text);
/* to next text offset */
textListItem = &(textListItem[strlen(textListItem) + 1]);
}
XChangeProperty(scr->display,
WMViewXID(view),
scr->xdndActionDescriptionAtom,
XA_STRING,
XDND_ACTION_DESCRIPTION_FORMAT,
PropModeReplace,
(unsigned char*) textList,
size);
}
/* called if wanted operation is WDOperationAsk */
static void
registerSupportedOperations(WMView *view)
{
WMScreen *scr = W_VIEW_SCREEN(view);
WMArray* operationArray;
operationArray = view->dragSourceProcs->askedOperations(view);
registerOperationList(scr, view, operationArray);
registerDescriptionList(scr, view, operationArray);
/* WMFreeArray(operationArray); */
}
static void
initSourceDragInfo(WMView *sourceView, WMDraggingInfo *info)
{
WMRect emptyZone;
XDND_SOURCE_INFO(info) = (W_DragSourceInfo*) wmalloc(sizeof(W_DragSourceInfo));
XDND_SOURCE_VIEW(info) = sourceView;
XDND_DEST_WIN(info) = None;
XDND_DRAG_ICON(info) = None;
XDND_SOURCE_ACTION(info) = W_OperationToAction(
W_VIEW_SCREEN(sourceView),
sourceView->dragSourceProcs->wantedDropOperation(sourceView));
XDND_DEST_ACTION(info) = None;
XDND_SOURCE_STATE(info) = idleState;
emptyZone.pos = wmkpoint(0, 0);
emptyZone.size = wmksize(0, 0);
XDND_NO_POS_ZONE(info) = emptyZone;
}
/*
Returned array is destroyed after dropDataTypes call
*/
static WMArray*
defDropDataTypes(WMView *self)
{
return NULL;
}
static WMDragOperationType
defWantedDropOperation(WMView *self)
{
return WDOperationNone;
}
/*
Must be defined if wantedDropOperation return WDOperationAsk
(useless otherwise).
Return a WMDragOperationItem array (destroyed after call).
A WMDragOperationItem links a label to an operation.
static WMArray*
defAskedOperations(WMView *self); */
static Bool
defAcceptDropOperation(WMView *self, WMDragOperationType allowedOperation)
{
return False;
}
static void
defBeganDrag(WMView *self, WMPoint *point)
{
}
static void
defEndedDrag(WMView *self, WMPoint *point, Bool deposited)
{
}
/*
Returned data is not destroyed
*/
static WMData*
defFetchDragData(WMView *self, char *type)
{
return NULL;
}
void
WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
{
if (view->dragSourceProcs)
wfree(view->dragSourceProcs);
view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
*view->dragSourceProcs = *procs;
if (procs->dropDataTypes == NULL)
view->dragSourceProcs->dropDataTypes = defDropDataTypes;
if (procs->wantedDropOperation == NULL)
view->dragSourceProcs->wantedDropOperation = defWantedDropOperation;
/*
Note: askedOperations can be NULL, if wantedDropOperation never returns
WDOperationAsk.
*/
if (procs->acceptDropOperation == NULL)
view->dragSourceProcs->acceptDropOperation = defAcceptDropOperation;
if (procs->beganDrag == NULL)
view->dragSourceProcs->beganDrag = defBeganDrag;
if (procs->endedDrag == NULL)
view->dragSourceProcs->endedDrag = defEndedDrag;
if (procs->fetchDragData == NULL)
view->dragSourceProcs->fetchDragData = defFetchDragData;
}
static Bool
isXdndAware(WMScreen *scr, Window win)
{
Atom type;
int format;
unsigned long count, remain;
unsigned char *winXdndVersion;
if (win == None)
return False;
XGetWindowProperty(scr->display, win, scr->xdndAwareAtom,
0, 1, False, XA_ATOM, &type, &format,
&count, &remain, &winXdndVersion);
if (type != XA_ATOM
|| format != XDND_PROPERTY_FORMAT
|| count == 0 || !winXdndVersion) {
if (winXdndVersion)
XFree(winXdndVersion);
return False;
}
XFree(winXdndVersion);
return (count == 1); /* xdnd version is set */
}
static Window*
windowChildren(Display *dpy, Window win, unsigned *nchildren)
{
Window *children;
Window foo, bar;
if (!XQueryTree(dpy, win, &foo, &bar, &children, nchildren)) {
*nchildren = 0;
return NULL;
} else
return children;
}
static Window
lookForAwareWindow(WMScreen *scr, WMPoint *mousePos, Window win)
{
int tmpx, tmpy;
Window child;
/* Since xdnd v3, only the toplevel window should be aware */
if (isXdndAware(scr, win))
return win;
/* inspect child under pointer */
if (XTranslateCoordinates(scr->display, scr->rootWin, win,
mousePos->x, mousePos->y, &tmpx, &tmpy,
&child)) {
if (child == None)
return None;
else
return lookForAwareWindow(scr, mousePos, child);
}
return None;
}
static Window
findDestination(WMDraggingInfo *info, WMPoint *mousePos)
{
WMScreen *scr = sourceScreen(info);
unsigned nchildren;
Window *children = windowChildren(scr->display, scr->rootWin, &nchildren);
int i;
XWindowAttributes attr;
if (isXdndAware(scr, scr->rootWin))
return scr->rootWin;
/* exclude drag icon (and upper) from search */
for (i = nchildren-1; i >= 0; i--) {
if (children[i] == XDND_DRAG_ICON(info)) {
i--;
break;
}
}
if (i < 0) {
/* root window has no child under drag icon, and is not xdnd aware. */
return None;
}
/* inspecting children, from upper to lower */
for (; i >= 0; i--) {
if (XGetWindowAttributes(scr->display, children[i], &attr)
&& attr.map_state == IsViewable
&& mousePos->x >= attr.x
&& mousePos->y >= attr.y
&& mousePos->x < attr.x + attr.width
&& mousePos->y < attr.y + attr.height) {
return lookForAwareWindow(scr, mousePos, children[i]);
}
}
/* No child window under drag pointer */
return None;
}
static void
initMotionProcess(WMView *view, WMDraggingInfo *info,
XEvent *event, WMPoint *startLocation)
{
WMScreen *scr = W_VIEW_SCREEN(view);
/* take ownership of XdndSelection */
XDND_SELECTION_PROCS(info) =
(WMSelectionProcs*) wmalloc(sizeof(WMSelectionProcs));
XDND_SELECTION_PROCS(info)->convertSelection = convertSelection;
XDND_SELECTION_PROCS(info)->selectionLost = selectionLost;
XDND_SELECTION_PROCS(info)->selectionDone = selectionDone;
XDND_TIMESTAMP(info) = event->xmotion.time;
if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
CurrentTime,
XDND_SELECTION_PROCS(info), NULL)) {
wwarning("could not get ownership or DND selection");
return;
}
registerDropTypes(scr, view, info);
if (XDND_SOURCE_ACTION(info) == W_VIEW_SCREEN(view)->xdndActionAsk)
registerSupportedOperations(view);
if (view->dragSourceProcs->beganDrag != NULL) {
view->dragSourceProcs->beganDrag(view, startLocation);
}
}
static void
processMotion(WMDraggingInfo *info, Window windowUnderDrag, WMPoint *mousePos)
{
/* // WMScreen *scr = sourceScreen(info); */
Window newDestination = findDestination(info, mousePos);
W_DragSourceStopTimer();
if (newDestination != XDND_DEST_WIN(info)) {
recolorCursor(info, False);
if (XDND_DEST_WIN(info) != None) {
/* leaving a xdnd window */
sendLeaveMessage(info);
}
XDND_DEST_WIN(info) = newDestination;
XDND_SOURCE_STATE(info) = idleState;
XDND_DEST_ACTION(info) = None;
XDND_NO_POS_ZONE(info).size.width = 0;
XDND_NO_POS_ZONE(info).size.height = 0;
if (newDestination != None) {
/* entering a xdnd window */
if (! sendEnterMessage(info)) {
XDND_DEST_WIN(info) = None;
return;
}
W_DragSourceStartTimer(info);
}
} else {
if (XDND_DEST_WIN(info) != None) {
if (! sendPositionMessage(info, mousePos)) {
XDND_DEST_WIN(info) = None;
return;
}
W_DragSourceStartTimer(info);
}
}
}
static Bool
processButtonRelease(WMDraggingInfo *info)
{
if (XDND_SOURCE_STATE(info) == dropAllowedState) {
/* begin drop */
W_DragSourceStopTimer();
if (! sendDropMessage(info))
return False;
W_DragSourceStartTimer(info);
return True;
} else {
if (XDND_DEST_WIN(info) != None)
sendLeaveMessage(info);
W_DragSourceStopTimer();
return False;
}
}
Bool
WMIsDraggingFromView(WMView *view)
{
WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
return ( XDND_SOURCE_INFO(info) != NULL
&& XDND_SOURCE_STATE(info) != finishDropState);
/* return W_VIEW_SCREEN(view)->dragInfo.sourceInfo != NULL; */
}
void
WMDragImageFromView(WMView *view, XEvent *event)
{
WMDraggingInfo *info = &W_VIEW_SCREEN(view)->dragInfo;
WMPoint mouseLocation;
switch(event->type) {
case ButtonPress:
if (event->xbutton.button == Button1) {
XEvent nextEvent;
XPeekEvent(event->xbutton.display, &nextEvent);
/* Initialize only if a drag really begins (avoid clicks) */
if (nextEvent.type == MotionNotify) {
initSourceDragInfo(view, info);
}
}
break;
case ButtonRelease:
if (WMIsDraggingFromView(view)) {
Bool dropBegan = processButtonRelease(info);
recolorCursor(info, False);
if (dropBegan) {
endDragImage(info, False);
XDND_SOURCE_STATE(info) = finishDropState;
} else {
/* drop failed */
endDragImage(info, True);
endDragProcess(info,False);
}
}
break;
case MotionNotify:
if (WMIsDraggingFromView(view)) {
mouseLocation = wmkpoint(event->xmotion.x_root,
event->xmotion.y_root);
if (abs(XDND_DRAG_ICON_POS(info).x - mouseLocation.x) >=
MIN_X_MOVE_OFFSET
|| abs(XDND_DRAG_ICON_POS(info).y - mouseLocation.y) >=
MIN_Y_MOVE_OFFSET) {
if (XDND_DRAG_ICON(info) == None) {
initMotionProcess(view, info, event, &mouseLocation);
startDragImage(view, info, event);
} else {
XDND_DRAG_ICON_POS(info).x =
mouseLocation.x - XDND_MOUSE_OFFSET(info).x;
XDND_DRAG_ICON_POS(info).y =
mouseLocation.y - XDND_MOUSE_OFFSET(info).y;
refreshDragImage(view, info);
processMotion(info,
event->xmotion.window,
&mouseLocation);
}
}
}
break;
}
}
/* Minimal mouse events handler: no right or double-click detection,
only drag is supported */
static void
dragImageHandler(XEvent *event, void *cdata)
{
WMView *view = (WMView*)cdata;
WMDragImageFromView(view, event);
}
/* ----- source states ----- */
#ifdef XDND_DEBUG
static void
traceStatusMsg(Display *dpy, XClientMessageEvent *statusEvent)
{
printf("Xdnd status message:\n");
if (statusEvent->data.l[1] & 0x2UL)
printf("send position on every move\n");
else {
int x, y, w, h;
x = statusEvent->data.l[2] >> 16;
y = statusEvent->data.l[2] & 0xFFFFL;
w = statusEvent->data.l[3] >> 16;
h = statusEvent->data.l[3] & 0xFFFFL;
printf("send position out of ((%d,%d) , (%d,%d))\n",
x, y, x+w, y+h);
}
if (statusEvent->data.l[1] & 0x1L)
printf("allowed action: %s\n",
XGetAtomName(dpy, statusEvent->data.l[4]));
else
printf("no action allowed\n");
}
#endif
static void
storeDropAction(WMDraggingInfo *info, Atom destAction)
{
WMView* sourceView = XDND_SOURCE_VIEW(info);
WMScreen *scr = W_VIEW_SCREEN(sourceView);
if (sourceView->dragSourceProcs->acceptDropOperation != NULL) {
if (sourceView->dragSourceProcs->acceptDropOperation(
sourceView,
W_ActionToOperation(scr, destAction)))
XDND_DEST_ACTION(info) = destAction;
else
XDND_DEST_ACTION(info) = None;
} else {
XDND_DEST_ACTION(info) = destAction;
}
}
static void
storeStatusMessageInfos(WMDraggingInfo *info, XClientMessageEvent *statusEvent)
{
WMRect* noPosZone = &(XDND_NO_POS_ZONE(info));
#ifdef XDND_DEBUG
traceStatusMsg(sourceScreen(info)->display, statusEvent);
#endif
if (statusEvent->data.l[1] & 0x2UL) {
/* bit 1 set: destination wants position messages on every move */
noPosZone->size.width = 0;
noPosZone->size.height = 0;
} else {
/* don't send another position message while in given rectangle */
noPosZone->pos.x = statusEvent->data.l[2] >> 16;
noPosZone->pos.y = statusEvent->data.l[2] & 0xFFFFL;
noPosZone->size.width = statusEvent->data.l[3] >> 16;
noPosZone->size.height = statusEvent->data.l[3] & 0xFFFFL;
}
if ((statusEvent->data.l[1] & 0x1L) || statusEvent->data.l[4] != None) {
/* destination accept drop */
storeDropAction(info, statusEvent->data.l[4]);
} else {
XDND_DEST_ACTION(info) = None;
}
}
static void*
idleState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
{
WMScreen *scr;
Atom destMsg = event->message_type;
scr = W_VIEW_SCREEN(view);
if (destMsg == scr->xdndStatusAtom) {
storeStatusMessageInfos(info, event);
if (XDND_DEST_ACTION(info) != None) {
recolorCursor(info, True);
W_DragSourceStartTimer(info);
return dropAllowedState;
} else {
/* drop denied */
recolorCursor(info, False);
return idleState;
}
}
if (destMsg == scr->xdndFinishedAtom) {
wwarning("received xdndFinishedAtom before drop began");
}
W_DragSourceStartTimer(info);
return idleState;
}
static void*
dropAllowedState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
{
WMScreen *scr = W_VIEW_SCREEN(view);
Atom destMsg = event->message_type;
if (destMsg == scr->xdndStatusAtom) {
storeStatusMessageInfos(info, event);
if (XDND_DEST_ACTION(info) == None) {
/* drop denied */
recolorCursor(info, False);
return idleState;
}
}
W_DragSourceStartTimer(info);
return dropAllowedState;
}
static void*
finishDropState(WMView *view, XClientMessageEvent *event, WMDraggingInfo *info)
{
WMScreen *scr = W_VIEW_SCREEN(view);
Atom destMsg = event->message_type;
if (destMsg == scr->xdndFinishedAtom) {
endDragProcess(info, True);
return NULL;
}
W_DragSourceStartTimer(info);
return finishDropState;
}
/* ----- end of source states ----- */
/* ----- source timer ----- */
static void
dragSourceResponseTimeOut(void *source)
{
WMView *view = (WMView*)source;
WMDraggingInfo *info = &(W_VIEW_SCREEN(view)->dragInfo);
wwarning("delay for drag destination response expired");
sendLeaveMessage(info);
recolorCursor(info, False);
if (XDND_SOURCE_STATE(info) == finishDropState) {
/* drop failed */
endDragImage(info, True);
endDragProcess(info, False);
} else {
XDND_SOURCE_STATE(info) = idleState;
}
}
void
W_DragSourceStopTimer()
{
if (dndSourceTimer != NULL) {
WMDeleteTimerHandler(dndSourceTimer);
dndSourceTimer = NULL;
}
}
void
W_DragSourceStartTimer(WMDraggingInfo *info)
{
W_DragSourceStopTimer();
dndSourceTimer = WMAddTimerHandler(
XDND_DESTINATION_RESPONSE_MAX_DELAY,
dragSourceResponseTimeOut,
XDND_SOURCE_VIEW(info));
}
/* ----- End of Destination timer ----- */
void
W_DragSourceStateHandler(WMDraggingInfo *info, XClientMessageEvent *event)
{
WMView *view;
W_DndState* newState;
if (XDND_SOURCE_VIEW_STORED(info)) {
view = XDND_SOURCE_VIEW(info);
#ifdef XDND_DEBUG
printf("current source state: %s\n",
stateName(XDND_SOURCE_STATE(info)));
#endif
newState = (W_DndState*) XDND_SOURCE_STATE(info)(view, event, info);
#ifdef XDND_DEBUG
printf("new source state: %s\n", stateName(newState));
#endif
if (newState != NULL)
XDND_SOURCE_STATE(info) = newState;
/* else drop finished, and info has been flushed */
}
}
void WMSetViewDragImage(WMView* view, WMPixmap *dragImage)
{
if (view->dragImage != NULL)
WMReleasePixmap(view->dragImage);
view->dragImage = WMRetainPixmap(dragImage);
}
void WMReleaseViewDragImage(WMView* view)
{
if (view->dragImage != NULL)
WMReleasePixmap(view->dragImage);
}
/* Create a drag handler, associating drag event masks with dragEventProc */
void
WMCreateDragHandler(WMView *view, WMEventProc *dragEventProc, void *clientData)
{
WMCreateEventHandler(view,
ButtonPressMask|ButtonReleaseMask|Button1MotionMask,
dragEventProc, clientData);
}
void
WMDeleteDragHandler(WMView *view, WMEventProc *dragEventProc, void *clientData)
{
WMDeleteEventHandler(view,
ButtonPressMask|ButtonReleaseMask|Button1MotionMask,
dragEventProc, clientData);
}
/* set default drag handler for view */
void
WMSetViewDraggable(WMView *view, WMDragSourceProcs *dragSourceProcs,
WMPixmap *dragImage)
{
wassertr(dragImage != NULL);
view->dragImage = WMRetainPixmap(dragImage);
WMSetViewDragSourceProcs(view, dragSourceProcs);
WMCreateDragHandler(view, dragImageHandler, view);
}
void
WMUnsetViewDraggable(WMView *view)
{
if (view->dragSourceProcs) {
wfree(view->dragSourceProcs);
view->dragSourceProcs = NULL;
}
WMReleaseViewDragImage(view);
WMDeleteDragHandler(view, dragImageHandler, view);
}