mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 12:28:22 +01:00
- 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
1315 lines
34 KiB
C
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);
|
|
}
|
|
|
|
|