From 7b185d5ae48a883ab67ee8cc3ed6b488ec7aae08 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Mon, 27 Nov 2006 06:50:19 +0000 Subject: [PATCH] X11 drag-and-drop support (files only) svn: r4957 --- src/wxxt/src/Misc/Clipboard.cc | 10 +- src/wxxt/src/Misc/Clipboard.h | 2 +- src/wxxt/src/Windows/Window.cc | 159 ++++++++++++- src/wxxt/src/Windows/Window.h | 7 +- src/wxxt/src/Windows/xdnd.c | 402 +++++++++++++++++++++++++++++++++ src/wxxt/src/Windows/xdnd.h | 135 +++++++++++ 6 files changed, 706 insertions(+), 9 deletions(-) create mode 100644 src/wxxt/src/Windows/xdnd.c create mode 100644 src/wxxt/src/Windows/xdnd.h diff --git a/src/wxxt/src/Misc/Clipboard.cc b/src/wxxt/src/Misc/Clipboard.cc index e8eb57ad06..f5619c07bc 100644 --- a/src/wxxt/src/Misc/Clipboard.cc +++ b/src/wxxt/src/Misc/Clipboard.cc @@ -455,14 +455,14 @@ static void abandoned_clip(void *_cb) cb->in_progress = -1; } -char *wxClipboard::GetClipboardData(char *format, long *length, long time) +char *wxClipboard::GetClipboardData(char *format, long *length, long time, int alt_sel) { - if (clipOwner) { + if (clipOwner && !alt_sel) { if (clipOwner->formats->Member(format)) return wxsGetDataInEventspace(clipOwner, format, length); else return NULL; - } else if (cbString) { + } else if (cbString && !alt_sel) { if (!strcmp(format, "TEXT")) return copystring(cbString); else @@ -482,7 +482,7 @@ char *wxClipboard::GetClipboardData(char *format, long *length, long time) receivedString = NULL; receivedTargets = NULL; - XtGetSelectionValue(getClipWindow, is_sel ? XA_PRIMARY : xa_clipboard, + XtGetSelectionValue(getClipWindow, alt_sel ? alt_sel : (is_sel ? XA_PRIMARY : xa_clipboard), xa_targets, wxGetTargets, (XtPointer)saferef, time); start_time = scheme_get_inexact_milliseconds(); @@ -517,7 +517,7 @@ char *wxClipboard::GetClipboardData(char *format, long *length, long time) return NULL; } - XtGetSelectionValue(getClipWindow, is_sel ? XA_PRIMARY : xa_clipboard, + XtGetSelectionValue(getClipWindow, alt_sel ? alt_sel : (is_sel ? XA_PRIMARY : xa_clipboard), xa, wxGetSelection, (XtPointer)saferef, 0); start_time = scheme_get_inexact_milliseconds(); diff --git a/src/wxxt/src/Misc/Clipboard.h b/src/wxxt/src/Misc/Clipboard.h index c0cd787ac2..e7ce6371ab 100644 --- a/src/wxxt/src/Misc/Clipboard.h +++ b/src/wxxt/src/Misc/Clipboard.h @@ -63,7 +63,7 @@ class wxClipboard : public wxObject wxBitmap *GetClipboardBitmap(long time); /* Get data from the clipboard */ - char *GetClipboardData(char *format, long *length, long time); + char *GetClipboardData(char *format, long *length, long time, int alt_sel = 0); /* Get the clipboard client directly. Will be NULL if clipboard data is a string, or if some other application owns the clipboard. diff --git a/src/wxxt/src/Windows/Window.cc b/src/wxxt/src/Windows/Window.cc index a43ebc6421..b383dc92af 100644 --- a/src/wxxt/src/Windows/Window.cc +++ b/src/wxxt/src/Windows/Window.cc @@ -38,6 +38,7 @@ #define Uses_wxItem #define Uses_wxCanvas #define Uses_wxApp +#define Uses_wxClipboard #include "wx.h" #define Uses_ScrollWinWidget #define Uses_Scrollbar @@ -49,6 +50,8 @@ #define Uses_ScrollbarWidget #include "widgets.h" +#include "xdnd.h" + #include #include // needed for IsFunctionKey, etc. #ifdef WX_USE_XFT @@ -60,11 +63,17 @@ static Atom utf8_atom = 0, net_wm_name_atom, net_wm_icon_name_atom; extern void wxSetSensitive(Widget, Bool enabled); extern int wxLocaleStringToChar(char *str, int slen); extern int wxUTF8StringToChar(char *str, int slen); +extern wxWindow *wxLocationToWindow(int x, int y); static wxWindow *grabbing_panel; static Time grabbing_panel_time; static Bool grabbing_panel_regsitered; +#include "xdnd.c" + +static int dnd_inited = 0; +static DndClass dnd; + #ifndef NO_XMB_LOOKUP_STRING static XIM the_im; #endif @@ -146,7 +155,9 @@ wxWindow::~wxWindow(void) // destroy widgets wxSetSensitive(X->frame, TRUE); - *saferef = NULL; /* MATTHEW */ + *saferef = NULL; + + dndTarget = NULL; /* just in case */ if (X->frame) XtDestroyWidget(X->frame); X->frame = X->handle = X->scroll = NULL; DELETE_OBJ constraints; constraints = NULL; @@ -1397,6 +1408,91 @@ void wxWindow::FrameEventHandler(Widget w, if (win->OnClose()) win->Show(FALSE); } + if (dnd_inited) { + if (xev->xclient.message_type == dnd.XdndEnter) { + /* Ok... */ + } else if (xev->xclient.message_type == dnd.XdndPosition) { + wxWindow *target = NULL; + + /* Find immediate target window: */ + { + Display *dpy = XtDisplay(w); + Screen *scn = XtScreen(w); + Window root = RootWindowOfScreen(scn); + Window xwin = XtWindow(w); + Window child = 0; + int cx, cy; + cx = XDND_POSITION_ROOT_X(xev); + cy = XDND_POSITION_ROOT_Y(xev); + while (1) { + if (XTranslateCoordinates(dpy, root, xwin, cx, cy, + &cx, &cy, &child)) { + if (!child) + break; + else { + root = xwin; + xwin = child; + } + } else + break; + } + if (xwin) { + Widget cw; + cw = XtWindowToWidget(dpy, xwin); + if (cw) { + target = win->FindChildByWidget(cw); + } + } + } + + /* Does this window (if found) accept drops? Or maybe a parent? */ + while (target && !target->drag_accept) { + if (wxSubType(target->__type, wxTYPE_FRAME) + || wxSubType(target->__type, wxTYPE_DIALOG_BOX)) { + target = NULL; + break; + } else { + target = target->GetParent(); + } + } + + xdnd_send_status(&dnd, XDND_ENTER_SOURCE_WIN(xev), xev->xclient.window, + !!target, + 0, 0, 0, 10, 10, dnd.XdndActionPrivate); + + win->dndTarget = target; + } else if (xev->xclient.message_type == dnd.XdndDrop) { + if (win->dndTarget) { + wxWindow *target = win->dndTarget; + win->dndTarget = NULL; + if (!xdnd_convert_selection(&dnd, XDND_DROP_SOURCE_WIN(xev), xev->xclient.window, dnd.text_uri_list)) { + long len; + char *data; + data = wxTheClipboard->GetClipboardData("text/uri-list", &len, XDND_DROP_TIME(xev), dnd.XdndSelection); + if (data) { + /* If file://... prefix (then drop it) */ + if (!strncmp(data, "file://", 7)) { + int i = 7; + char *data2; + while ((i < len) && (data[i] != '/')) { + i++; + } + if (i < len) { + data2 = new WXGC_ATOMIC char[len - i + 1]; + memcpy(data2, data + i, len - i); + data2[len - i] = 0; + target->OnDropFile(data2); + } + } + } + } + } + xdnd_send_finished(&dnd, XDND_DROP_SOURCE_WIN(xev), XtWindow(w), 0); + } else if (xev->xclient.message_type == dnd.XdndLeave) { + win->dndTarget = NULL; + xdnd_send_finished(&dnd, XDND_LEAVE_SOURCE_WIN(xev), XtWindow(w), 0); + } + } break; case CreateNotify: break; @@ -2256,3 +2352,64 @@ long wxWindow::GetWindowHandle() { return (long)X->handle; } + +//----------------------------------------------------------------------------- +// drag & drop +//----------------------------------------------------------------------------- + +void wxWindow::DragAcceptFiles(Bool accept) +{ + wxWindow *p; + + if (!drag_accept == !accept) + return; + + drag_accept = accept; + + if (!dnd_inited) { + xdnd_init(&dnd, wxAPP_DISPLAY); + dnd_inited = 1; + } + + /* Declare drag-and-drop possible at this + window's top-level frame: */ + + p = this; + while (p) { + if (wxSubType(p->__type, wxTYPE_FRAME) + || wxSubType(p->__type, wxTYPE_DIALOG_BOX)) + break; + p = p->GetParent(); + } + + { + Atom l[2]; + l[0] = dnd.text_uri_list; + l[1] = 0; + xdnd_set_dnd_aware(&dnd, XtWindow(p->X->frame), l); + } +} + +wxWindow *wxWindow::FindChildByWidget(Widget w) +{ + wxChildNode *node, *next; + wxWindow *r; + + if ((w == X->frame) + || (w == X->handle)) + return this; + + + for (node = children->First(); node; node = next) { + wxWindow *child; + next = node->Next(); + child = (wxWindow*)(node->Data()); + if (child) { + r = child->FindChildByWidget(w); + if (r) + return r; + } + } + + return NULL; +} diff --git a/src/wxxt/src/Windows/Window.h b/src/wxxt/src/Windows/Window.h index 6b23cbdfe8..d8fae1ec96 100644 --- a/src/wxxt/src/Windows/Window.h +++ b/src/wxxt/src/Windows/Window.h @@ -133,7 +133,7 @@ public: // miscellaneous virtual void AllowDoubleClick(Bool allow) { allow_dclicks = allow; } virtual void CaptureMouse(void); - virtual void DragAcceptFiles(Bool accept) { drag_accept = accept; } + virtual void DragAcceptFiles(Bool accept); virtual void Enable(Bool enable); virtual void EnablePainting(Bool enable) { painting_enabled = enable; } virtual void Fit(void) {} @@ -176,7 +176,7 @@ public: wxFont *GetFont() { return font; } long GetWindowHandle(); - + protected: // create and destroy associated device context void CreateDC(void); @@ -196,6 +196,7 @@ protected: static Status LookupKey(int unshifted, int unalted, Widget w, wxWindow *win, XEvent *xev, KeySym *_keysym, char *s, int *_len); void RegisterAll(Widget ww); + wxWindow *FindChildByWidget(Widget w); # endif protected: friend void wxXSetBusyCursor(wxWindow *, wxCursor *); @@ -224,6 +225,8 @@ protected: wxWindow **saferef; /* indirection for safety in callbacks */ + wxWindow *dndTarget; /* set between XdndPosition and XdndDrop/XdndLeave */ + long misc_flags; unsigned long current_state; diff --git a/src/wxxt/src/Windows/xdnd.c b/src/wxxt/src/Windows/xdnd.c new file mode 100644 index 0000000000..37dbe13b44 --- /dev/null +++ b/src/wxxt/src/Windows/xdnd.c @@ -0,0 +1,402 @@ +/* + xdnd.c, xdnd.h - C program library for handling the Xdnd protocol + + Copyright (C) 1998 Paul Sheer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + http://www.cco.caltech.edu/~jafl/xdnd/ + + Further info can also be obtained by emailing the author at, + psheer@obsidian.co.za + + Released 1998-08-07 +*/ + +#include +#include +#include +#include +#include +#include +#include "xdnd.h" + + +// #define DND_DEBUG +#define dnd_version_at_least(a,b) ((a) <= (b)) + +#ifdef DND_DEBUG +#define dnd_debug(a,b...) printf("%s: %d: " a "\n", __FILE__, __LINE__ , ## b) +#else + +#ifdef NeXT_RUNTIME +#define dnd_debug // +#else /* !NeXT_RUNTIME */ +#define dnd_debug(a,b...) +#endif /* NeXT_RUNTIME */ + +#endif + + + +void +xdnd_reset(DndClass * dnd) +{ + dnd->stage = XDND_DROP_STAGE_IDLE; + dnd->dragging_version = 0; + dnd->internal_drag = 0; + dnd->want_position = 0; + dnd->ready_to_drop = 0; + dnd->will_accept = 0; + dnd->rectangle.x = dnd->rectangle.y = 0; + dnd->rectangle.width = dnd->rectangle.height = 0; + dnd->dropper_window = 0; + dnd->dragger_window = 0; + dnd->dragger_typelist = 0; + dnd->desired_type = 0; + dnd->time = 0; +} + +void +xdnd_init(DndClass * dnd, Display * display) +{ + memset (dnd, 0, sizeof (*dnd)); + + dnd->display = display; + dnd->root_window = DefaultRootWindow (display); + dnd->version = XDND_VERSION; + dnd->XdndAware = XInternAtom (dnd->display, "XdndAware", False); + dnd->XdndSelection = XInternAtom (dnd->display, "XdndSelection", False); + dnd->XdndEnter = XInternAtom (dnd->display, "XdndEnter", False); + dnd->XdndLeave = XInternAtom (dnd->display, "XdndLeave", False); + dnd->XdndPosition = XInternAtom (dnd->display, "XdndPosition", False); + dnd->XdndDrop = XInternAtom (dnd->display, "XdndDrop", False); + dnd->XdndFinished = XInternAtom (dnd->display, "XdndFinished", False); + dnd->XdndStatus = XInternAtom (dnd->display, "XdndStatus", False); + dnd->XdndActionCopy = XInternAtom (dnd->display, "XdndActionCopy", False); + dnd->XdndActionMove = XInternAtom (dnd->display, "XdndActionMove", False); + dnd->XdndActionLink = XInternAtom (dnd->display, "XdndActionLink", False); + dnd->XdndActionAsk = XInternAtom (dnd->display, "XdndActionAsk", False); + dnd->XdndActionPrivate=XInternAtom(dnd->display,"XdndActionPrivate",False); + dnd->XdndTypeList = XInternAtom (dnd->display, "XdndTypeList", False); + dnd->XdndActionList = XInternAtom (dnd->display, "XdndActionList", False); + dnd->XdndActionDescription = XInternAtom(dnd->display, + "XdndActionDescription", False); + dnd->text_uri_list = XInternAtom(dnd->display, "text/uri-list", False); + xdnd_reset(dnd); +} + +static int +array_length(Atom * a) +{ // typelist is a null terminated array + int n = 0; + + while (a[n]) + n++; + return n; +} + +void +xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist) +{ + XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, + PropModeReplace, (unsigned char *) &dnd->version, 1); + if (typelist) + { + int n = array_length (typelist); + if (n) + XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, + PropModeAppend, (unsigned char *) typelist, n); + } +} + +void +xdnd_set_dnd_unaware (DndClass * dnd, Window window) +{ + XDeleteProperty (dnd->display, window, dnd->XdndAware); +} + +int +xdnd_is_dnd_aware(DndClass *dnd, Window window, int *version, Atom *typelist) +{ + Atom actual; + int format; + unsigned long count, remaining; + unsigned char *data = 0; + Atom *types, *t; + int result = 1; + + *version = 0; + XGetWindowProperty (dnd->display, window, dnd->XdndAware, + 0, 0x8000000L, False, XA_ATOM, &actual, &format, + &count, &remaining, &data); + + if (actual != XA_ATOM || format != 32 || count == 0 || !data) + { + dnd_debug("XGetWindowProperty failed in xdnd_is_dnd_aware - XdndAware = %ld", dnd->XdndAware); + if (data) + XFree(data); + return 0; + } + + types = (Atom *) data; + *version = dnd->version < types[0] ? dnd->version : types[0]; // minimum + dnd_debug ("Using XDND version %d", *version); + if (count > 1) + { + result = 0; + for (t = typelist; *t; t++) + { + unsigned long j; + for (j = 1; j < count; j++) + { + if (types[j] == *t) + { + result = 1; + break; + } + } + if (result) + break; + } + } + XFree(data); + return result; +} + +void +xdnd_send_enter(DndClass *dnd, Window window, Window from, Atom *typelist) +{ + XEvent xevent; + int n, i; + + n = array_length (typelist); + + memset(&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndEnter; + xevent.xclient.format = 32; + + XDND_ENTER_SOURCE_WIN (&xevent) = from; + XDND_ENTER_THREE_TYPES_SET (&xevent, n > XDND_THREE); + XDND_ENTER_VERSION_SET (&xevent, dnd->version); + for (i = 0; i < n && i < XDND_THREE; i++) + { + XDND_ENTER_TYPE (&xevent, i) = typelist[i]; + } + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +void +xdnd_send_position(DndClass *dnd, Window window, Window from, Atom action, + int x, int y, unsigned long time) +{ + XEvent xevent; + + memset (&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndPosition; + xevent.xclient.format = 32; + + XDND_POSITION_SOURCE_WIN (&xevent) = from; + XDND_POSITION_ROOT_SET (&xevent, x, y); + if (dnd_version_at_least (dnd->dragging_version, 1)) + XDND_POSITION_TIME (&xevent) = time; + if (dnd_version_at_least (dnd->dragging_version, 2)) + XDND_POSITION_ACTION (&xevent) = action; + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +void +xdnd_send_status(DndClass *dnd, Window window, Window from, int will_accept, + int want_position, int x, int y, int w, int h, Atom action) +{ + XEvent xevent; + + memset (&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndStatus; + xevent.xclient.format = 32; + + XDND_STATUS_TARGET_WIN (&xevent) = from; + XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept); + if (will_accept) + XDND_STATUS_WANT_POSITION_SET (&xevent, want_position); + if (want_position) + XDND_STATUS_RECT_SET (&xevent, x, y, w, h); + if (dnd_version_at_least (dnd->dragging_version, 2)) + if (will_accept) + XDND_STATUS_ACTION (&xevent) = action; + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +void +xdnd_send_leave(DndClass *dnd, Window window, Window from) +{ + XEvent xevent; + + memset(&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndLeave; + xevent.xclient.format = 32; + + XDND_LEAVE_SOURCE_WIN (&xevent) = from; + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +void +xdnd_send_drop(DndClass *dnd, Window window, Window from, unsigned long time) +{ + XEvent xevent; + + memset (&xevent, 0, sizeof (xevent)); + + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndDrop; + xevent.xclient.format = 32; + + XDND_DROP_SOURCE_WIN (&xevent) = from; + if (dnd_version_at_least (dnd->dragging_version, 1)) + XDND_DROP_TIME (&xevent) = time; + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +void +xdnd_send_finished(DndClass * dnd, Window window, Window from, int error) +{ + XEvent xevent; + memset (&xevent, 0, sizeof (xevent)); + xevent.xany.type = ClientMessage; + xevent.xany.display = dnd->display; + xevent.xclient.window = window; + xevent.xclient.message_type = dnd->XdndFinished; + xevent.xclient.format = 32; + + XDND_FINISHED_TARGET_WIN (&xevent) = from; + + XSendEvent (dnd->display, window, 0, 0, &xevent); +} + +int +xdnd_convert_selection(DndClass *dnd, Window window, Window requester, Atom type) +{ + if (window != XGetSelectionOwner (dnd->display, dnd->XdndSelection)) + { + dnd_debug ("xdnd_convert_selection(): XGetSelectionOwner failed"); + return 1; + } + + XConvertSelection (dnd->display, dnd->XdndSelection, type, + dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime); + return 0; +} + +int +xdnd_set_selection_owner(DndClass * dnd, Window window, Atom type) +{ + if (!XSetSelectionOwner(dnd->display,dnd->XdndSelection,window,CurrentTime)) + { + dnd_debug ("xdnd_set_selection_owner(): XSetSelectionOwner failed"); + return 1; + } + + return 0; +} + +void +xdnd_selection_send(DndClass * dnd, XSelectionRequestEvent * request, + unsigned char *data, int length) +{ + XEvent xevent; + + dnd_debug (" requestor = %ld", request->requestor); + dnd_debug (" property = %ld", request->property); + dnd_debug (" length = %d", length); + + XChangeProperty (dnd->display, request->requestor, request->property, + request->target, 8, PropModeReplace, data, length); + + xevent.xselection.type = SelectionNotify; + xevent.xselection.property = request->property; + xevent.xselection.display = request->display; + xevent.xselection.requestor = request->requestor; + xevent.xselection.selection = request->selection; + xevent.xselection.target = request->target; + xevent.xselection.time = request->time; + + XSendEvent (dnd->display, request->requestor, 0, 0, &xevent); +} + +// +// Unused +// + +void +xdnd_set_type_list(DndClass * dnd, Window window, Atom * typelist) +{ + int n = array_length (typelist); + + XChangeProperty (dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32, + PropModeReplace, (unsigned char *) typelist, n); +} + +void +xdnd_get_type_list(DndClass * dnd, Window window, Atom ** typelist) +{ + Atom type, *a, *tl; + int format; + unsigned long i, count, remaining; + unsigned char *data = NULL; + + *typelist = 0; + + XGetWindowProperty (dnd->display, window, dnd->XdndTypeList, + 0, 0x8000000L, False, XA_ATOM, &type, &format, &count, &remaining, &data); + + if (type != XA_ATOM || format != 32 || count == 0 || !data) + { + if (data) + XFree (data); + dnd_debug ("XGetWindowProperty failed in xdnd_get_type_list - dnd->XdndTypeList = %ld", dnd->XdndTypeList); + return; + } + tl = (Atom *)(new WXGC_ATOMIC char[(count + 1) * sizeof (Atom)]); + *typelist = tl; + a = (Atom *) data; + for (i = 0; i < count; i++) + (*typelist)[i] = a[i]; + (*typelist)[count] = 0; + + XFree (data); +} diff --git a/src/wxxt/src/Windows/xdnd.h b/src/wxxt/src/Windows/xdnd.h new file mode 100644 index 0000000000..7bf10e5ae4 --- /dev/null +++ b/src/wxxt/src/Windows/xdnd.h @@ -0,0 +1,135 @@ +/* + xdnd.c, xdnd.h - C program library for handling the Xdnd protocol + + Copyright (C) 1998 Paul Sheer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + Further info can also be obtained by emailing the author at, + psheer@obsidian.co.za +*/ + +#ifndef _X_DND_H +#define _X_DND_H + +#define XDND_VERSION 3 + +/* XdndEnter */ +#define XDND_THREE 3 +#define XDND_ENTER_SOURCE_WIN(e) ((e)->xclient.data.l[0]) +#define XDND_ENTER_THREE_TYPES(e) (((e)->xclient.data.l[1] & 0x1UL) == 0) +#define XDND_ENTER_THREE_TYPES_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL) +#define XDND_ENTER_VERSION(e) ((e)->xclient.data.l[1] >> 24) +#define XDND_ENTER_VERSION_SET(e,v) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24) +#define XDND_ENTER_TYPE(e,i) ((e)->xclient.data.l[2 + i]) /* i => (0, 1, 2) */ + +/* XdndPosition */ +#define XDND_POSITION_SOURCE_WIN(e) ((e)->xclient.data.l[0]) +#define XDND_POSITION_ROOT_X(e) ((e)->xclient.data.l[2] >> 16) +#define XDND_POSITION_ROOT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFUL) +#define XDND_POSITION_ROOT_SET(e,x,y) (e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL) +#define XDND_POSITION_TIME(e) ((e)->xclient.data.l[3]) +#define XDND_POSITION_ACTION(e) ((e)->xclient.data.l[4]) + +/* XdndStatus */ +#define XDND_STATUS_TARGET_WIN(e) ((e)->xclient.data.l[0]) +#define XDND_STATUS_WILL_ACCEPT(e) ((e)->xclient.data.l[1] & 0x1L) +#define XDND_STATUS_WILL_ACCEPT_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL) +#define XDND_STATUS_WANT_POSITION(e) ((e)->xclient.data.l[1] & 0x2UL) +#define XDND_STATUS_WANT_POSITION_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x2UL) | (((b) == 0) ? 0 : 0x2UL) +#define XDND_STATUS_RECT_X(e) ((e)->xclient.data.l[2] >> 16) +#define XDND_STATUS_RECT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFL) +#define XDND_STATUS_RECT_WIDTH(e) ((e)->xclient.data.l[3] >> 16) +#define XDND_STATUS_RECT_HEIGHT(e) ((e)->xclient.data.l[3] & 0xFFFFL) +#define XDND_STATUS_RECT_SET(e,x,y,w,h) {(e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL); (e)->xclient.data.l[3] = ((w) << 16) | ((h) & 0xFFFFUL); } +#define XDND_STATUS_ACTION(e) ((e)->xclient.data.l[4]) + +/* XdndLeave */ +#define XDND_LEAVE_SOURCE_WIN(e) ((e)->xclient.data.l[0]) + +/* XdndDrop */ +#define XDND_DROP_SOURCE_WIN(e) ((e)->xclient.data.l[0]) +#define XDND_DROP_TIME(e) ((e)->xclient.data.l[2]) + +/* XdndFinished */ +#define XDND_FINISHED_TARGET_WIN(e) ((e)->xclient.data.l[0]) + +typedef struct _DndClass DndClass; + +struct _DndClass { + + Display *display; + + Atom XdndAware; + Atom XdndSelection; + Atom XdndEnter; + Atom XdndLeave; + Atom XdndPosition; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndActionMove; + Atom XdndActionLink; + Atom XdndActionAsk; + Atom XdndActionPrivate; + Atom XdndTypeList; + Atom XdndActionList; + Atom XdndActionDescription; + Atom Xdnd_NON_PROTOCOL_ATOM; + Atom version; + Atom text_uri_list; + Window root_window; + +#define XDND_DROP_STAGE_IDLE 0 +#define XDND_DRAG_STAGE_DRAGGING 1 +#define XDND_DRAG_STAGE_ENTERED 2 +#define XDND_DROP_STAGE_CONVERTING 3 +#define XDND_DROP_STAGE_ENTERED 4 + int stage; + int dragging_version; + int internal_drag; + int want_position; + int ready_to_drop; + int will_accept; + XRectangle rectangle; + Window dropper_window, dragger_window; + Atom *dragger_typelist; + Atom desired_type; + Atom supported_action; + Time time; +/* drop position from last XdndPosition */ + int x, y; + +/* block for only this many seconds on not receiving a XdndFinished from target, default : 10 */ + int time_out; +}; + +void xdnd_init (DndClass * dnd, Display * display); +void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist); +void xdnd_set_dnd_unaware (DndClass * dnd, Window window); +int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist); +void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist); +void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist); +void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y, unsigned long etime); +void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, + int want_position, int x, int y, int w, int h, Atom action); +void xdnd_send_leave (DndClass * dnd, Window window, Window from); +void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long etime); +void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error); +int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type); +void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data, int length); + +#endif /* !_X_DND_H */