summaryrefslogtreecommitdiffstats
path: root/src/kernel/qdnd_x11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/qdnd_x11.cpp')
-rw-r--r--src/kernel/qdnd_x11.cpp1859
1 files changed, 1859 insertions, 0 deletions
diff --git a/src/kernel/qdnd_x11.cpp b/src/kernel/qdnd_x11.cpp
new file mode 100644
index 0000000..607a358
--- /dev/null
+++ b/src/kernel/qdnd_x11.cpp
@@ -0,0 +1,1859 @@
+/****************************************************************************
+**
+** XDND implementation for Qt. See http://www.cco.caltech.edu/~jafl/xdnd/
+**
+** Created : 980320
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qapplication.h"
+
+#ifndef QT_NO_DRAGANDDROP
+
+#include "qwidget.h"
+#include "qintdict.h"
+#include "qdatetime.h"
+#include "qdict.h"
+#include "qguardedptr.h"
+#include "qdragobject.h"
+#include "qobjectlist.h"
+#include "qcursor.h"
+#include "qbitmap.h"
+#include "qpainter.h"
+
+#include "qt_x11_p.h"
+
+// conflict resolution
+
+const int XKeyPress = KeyPress;
+const int XKeyRelease = KeyRelease;
+#undef KeyPress
+#undef KeyRelease
+
+// this stuff is copied from qapp_x11.cpp
+
+extern void qt_x11_intern_atom( const char *, Atom * );
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+extern void qt_ignore_badwindow();
+extern bool qt_badwindow();
+extern void qt_enter_modal( QWidget *widget );
+extern void qt_leave_modal( QWidget *widget );
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+extern Window qt_x11_findClientWindow( Window, Atom, bool );
+extern Atom qt_wm_state;
+extern Time qt_x_time;
+extern Time qt_x_user_time;
+
+// this stuff is copied from qclb_x11.cpp
+
+extern bool qt_xclb_wait_for_event( Display *dpy, Window win, int type,
+ XEvent *event, int timeout );
+extern bool qt_xclb_read_property( Display *dpy, Window win, Atom property,
+ bool deleteProperty,
+ QByteArray *buffer, int *size, Atom *type,
+ int *format, bool nullterm );
+extern QByteArray qt_xclb_read_incremental_property( Display *dpy, Window win,
+ Atom property,
+ int nbytes, bool nullterm );
+// and all this stuff is copied -into- qapp_x11.cpp
+
+void qt_xdnd_setup();
+void qt_handle_xdnd_enter( QWidget *, const XEvent *, bool );
+void qt_handle_xdnd_position( QWidget *, const XEvent *, bool );
+void qt_handle_xdnd_status( QWidget *, const XEvent *, bool );
+void qt_handle_xdnd_leave( QWidget *, const XEvent *, bool );
+void qt_handle_xdnd_drop( QWidget *, const XEvent *, bool );
+void qt_handle_xdnd_finished( QWidget *, const XEvent *, bool );
+void qt_xdnd_handle_selection_request( const XSelectionRequestEvent * );
+bool qt_xdnd_handle_badwindow();
+// client messages
+Atom qt_xdnd_enter;
+Atom qt_xdnd_position;
+Atom qt_xdnd_status;
+Atom qt_xdnd_leave;
+Atom qt_xdnd_drop;
+Atom qt_xdnd_finished;
+Atom qt_xdnd_type_list;
+const int qt_xdnd_version = 4;
+
+extern int qt_x11_translateButtonState( int s );
+
+// Actions
+//
+// The Xdnd spec allows for user-defined actions. This could be implemented
+// with a registration process in Qt. WE SHOULD do that later.
+//
+Atom qt_xdnd_action_copy;
+Atom qt_xdnd_action_link;
+Atom qt_xdnd_action_move;
+Atom qt_xdnd_action_private;
+static
+QDropEvent::Action xdndaction_to_qtaction(Atom atom)
+{
+ if ( atom == qt_xdnd_action_copy || atom == 0 )
+ return QDropEvent::Copy;
+ if ( atom == qt_xdnd_action_link )
+ return QDropEvent::Link;
+ if ( atom == qt_xdnd_action_move )
+ return QDropEvent::Move;
+ return QDropEvent::Private;
+}
+static
+int qtaction_to_xdndaction(QDropEvent::Action a)
+{
+ switch ( a ) {
+ case QDropEvent::Copy:
+ return qt_xdnd_action_copy;
+ case QDropEvent::Link:
+ return qt_xdnd_action_link;
+ case QDropEvent::Move:
+ return qt_xdnd_action_move;
+ case QDropEvent::Private:
+ return qt_xdnd_action_private;
+ default:
+ return qt_xdnd_action_copy;
+ }
+}
+
+// clean up the stuff used.
+static void qt_xdnd_cleanup();
+
+static void qt_xdnd_send_leave();
+
+// XDND selection
+Atom qt_xdnd_selection;
+// other selection
+static Atom qt_selection_property;
+// INCR
+static Atom qt_incr_atom;
+
+// properties for XDND drop sites
+Atom qt_xdnd_aware;
+Atom qt_xdnd_proxy;
+
+// real variables:
+// xid of current drag source
+static Atom qt_xdnd_dragsource_xid = 0;
+
+// the types in this drop. 100 is no good, but at least it's big.
+const int qt_xdnd_max_type = 100;
+static Atom qt_xdnd_types[qt_xdnd_max_type];
+
+static QIntDict<QCString> * qt_xdnd_drag_types = 0;
+static QDict<Atom> * qt_xdnd_atom_numbers = 0;
+
+// timer used when target wants "continuous" move messages (eg. scroll)
+static int heartbeat = -1;
+// rectangle in which the answer will be the same
+static QRect qt_xdnd_source_sameanswer;
+//static QRect qt_xdnd_target_sameanswer;
+static bool qt_xdnd_target_answerwas;
+// top-level window we sent position to last.
+static Window qt_xdnd_current_target;
+// window to send events to (always valid if qt_xdnd_current_target)
+static Window qt_xdnd_current_proxy_target;
+// widget we forwarded position to last, and local position
+static QGuardedPtr<QWidget> qt_xdnd_current_widget;
+static QPoint qt_xdnd_current_position;
+// time of this drop, as type Atom to save on casts
+static Atom qt_xdnd_source_current_time;
+// timestamp from the XdndPosition and XdndDrop
+static Time qt_xdnd_target_current_time;
+// screen number containing the pointer... -1 means default
+static int qt_xdnd_current_screen = -1;
+// state of dragging... true if dragging, false if not
+bool qt_xdnd_dragging = FALSE;
+// need to check state of keyboard modifiers
+static bool need_modifiers_check = FALSE;
+
+// dict of payload data, sorted by type atom
+static QIntDict<QByteArray> * qt_xdnd_target_data = 0;
+
+// first drag object, or 0
+static QDragObject * qt_xdnd_source_object = 0;
+
+// Motif dnd
+extern void qt_motifdnd_enable( QWidget *, bool );
+extern QByteArray qt_motifdnd_obtain_data( const char *format );
+extern const char *qt_motifdnd_format( int n );
+
+bool qt_motifdnd_active = FALSE;
+static bool dndCancelled = FALSE;
+
+// Shift/Ctrl handling, and final drop status
+static QDragObject::DragMode drag_mode;
+static QDropEvent::Action global_requested_action = QDropEvent::Copy;
+static QDropEvent::Action global_accepted_action = QDropEvent::Copy;
+
+// for embedding only
+static QWidget* current_embedding_widget = 0;
+static XEvent last_enter_event;
+
+// cursors
+static QCursor *noDropCursor = 0;
+static QCursor *moveCursor = 0;
+static QCursor *copyCursor = 0;
+static QCursor *linkCursor = 0;
+
+static QPixmap *defaultPm = 0;
+
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+static const char* const default_pm[] = {
+"13 9 3 1",
+". c None",
+" c #000000",
+"X c #FFFFFF",
+"X X X X X X X",
+" X X X X X X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X.........X ",
+"X ......... X",
+" X X X X X X ",
+"X X X X X X X"
+};
+
+class QShapedPixmapWidget : public QWidget {
+
+public:
+ QShapedPixmapWidget(int screen = -1) :
+ QWidget(QApplication::desktop()->screen( screen ),
+ 0, WStyle_Customize | WStyle_Tool | WStyle_NoBorder | WX11BypassWM ), oldpmser( 0 ), oldbmser( 0 )
+ {
+ x11SetWindowType( X11WindowTypeDND );
+ }
+
+ void setPixmap(QPixmap pm, QPoint hot)
+ {
+ int bmser = pm.mask() ? pm.mask()->serialNumber() : 0;
+ if( oldpmser == pm.serialNumber() && oldbmser == bmser
+ && oldhot == hot )
+ return;
+ oldpmser = pm.serialNumber();
+ oldbmser = bmser;
+ oldhot = hot;
+ bool hotspot_in = !(hot.x() < 0 || hot.y() < 0 || hot.x() >= pm.width() || hot.y() >= pm.height());
+// if the pixmap has hotspot in its area, make a "hole" in it at that position
+// this will allow XTranslateCoordinates() to find directly the window below the cursor instead
+// of finding this pixmap, and therefore there won't be needed any (slow) search for the window
+// using findRealWindow()
+ if( hotspot_in ) {
+ QBitmap mask = pm.mask() ? *pm.mask() : QBitmap( pm.width(), pm.height());
+ if( !pm.mask())
+ mask.fill( Qt::color1 );
+ QPainter p( &mask );
+ p.setPen( Qt::color0 );
+ p.drawPoint( hot.x(), hot.y());
+ p.end();
+ pm.setMask( mask );
+ setMask( mask );
+ } else if ( pm.mask() ) {
+ setMask( *pm.mask() );
+ } else {
+ clearMask();
+ }
+ resize(pm.width(),pm.height());
+ setErasePixmap(pm);
+ erase();
+ }
+private:
+ int oldpmser;
+ int oldbmser;
+ QPoint oldhot;
+};
+
+static QShapedPixmapWidget * qt_xdnd_deco = 0;
+
+static QWidget* desktop_proxy = 0;
+
+class QExtraWidget : public QWidget
+{
+public:
+ QWExtra* extraData() { return QWidget::extraData(); }
+ QTLWExtra* topData() { return QWidget::topData(); }
+};
+
+
+static bool qt_xdnd_enable( QWidget* w, bool on )
+{
+ if ( on ) {
+ QWidget * xdnd_widget = 0;
+ if ( w->isDesktop() ) {
+ if ( desktop_proxy ) // *WE* already have one.
+ return FALSE;
+
+ // As per Xdnd4, use XdndProxy
+ XGrabServer( w->x11Display() );
+ Atom type = None;
+ int f;
+ unsigned long n, a;
+ WId *proxy_id_ptr;
+ XGetWindowProperty( w->x11Display(), w->winId(),
+ qt_xdnd_proxy, 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id_ptr );
+ WId proxy_id = 0;
+ if ( type == XA_WINDOW && proxy_id_ptr ) {
+ proxy_id = *proxy_id_ptr;
+ XFree(proxy_id_ptr);
+ proxy_id_ptr = 0;
+ // Already exists. Real?
+ qt_ignore_badwindow();
+ XGetWindowProperty( w->x11Display(), proxy_id,
+ qt_xdnd_proxy, 0, 1, False,
+ XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id_ptr );
+ if ( qt_badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id ) {
+ // Bogus - we will overwrite.
+ proxy_id = 0;
+ }
+ }
+ if ( proxy_id_ptr )
+ XFree(proxy_id_ptr);
+
+ if ( !proxy_id ) {
+ xdnd_widget = desktop_proxy = new QWidget;
+ proxy_id = desktop_proxy->winId();
+ XChangeProperty ( w->x11Display(),
+ w->winId(), qt_xdnd_proxy,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&proxy_id, 1 );
+ XChangeProperty ( w->x11Display(),
+ proxy_id, qt_xdnd_proxy,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)&proxy_id, 1 );
+ }
+
+ XUngrabServer( w->x11Display() );
+ } else {
+ xdnd_widget = w->topLevelWidget();
+ }
+ if ( xdnd_widget ) {
+ Atom atm = (Atom)qt_xdnd_version;
+ XChangeProperty ( xdnd_widget->x11Display(), xdnd_widget->winId(),
+ qt_xdnd_aware, XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)&atm, 1 );
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ } else {
+ if ( w->isDesktop() ) {
+ XDeleteProperty( w->x11Display(), w->winId(),
+ qt_xdnd_proxy );
+ delete desktop_proxy;
+ desktop_proxy = 0;
+ }
+ return TRUE;
+ }
+}
+
+const char* qt_xdnd_atom_to_str( Atom a )
+{
+ if ( !a ) return 0;
+
+ if ( a == XA_STRING )
+ return "text/plain"; // some Xdnd clients are dumb
+
+ if ( !qt_xdnd_drag_types ) {
+ qt_xdnd_drag_types = new QIntDict<QCString>( 17 );
+ qt_xdnd_drag_types->setAutoDelete( TRUE );
+ }
+ QCString* result;
+ if ( !(result=qt_xdnd_drag_types->find( a )) ) {
+ const char* mimeType = XGetAtomName( QPaintDevice::x11AppDisplay(), a );
+ if ( !mimeType )
+ return 0; // only happens on protocol error
+ result = new QCString( mimeType );
+ qt_xdnd_drag_types->insert( (long)a, result );
+ XFree((void*)mimeType);
+ }
+ return *result;
+}
+
+Atom* qt_xdnd_str_to_atom( const char *mimeType )
+{
+ if ( !mimeType || !*mimeType )
+ return 0;
+ if ( !qt_xdnd_atom_numbers ) {
+ qt_xdnd_atom_numbers = new QDict<Atom>( 17 );
+ qt_xdnd_atom_numbers->setAutoDelete( TRUE );
+ }
+
+ Atom * result;
+ if ( (result = qt_xdnd_atom_numbers->find( mimeType )) )
+ return result;
+
+ result = new Atom;
+ *result = 0;
+ qt_x11_intern_atom( mimeType, result );
+ qt_xdnd_atom_numbers->insert( mimeType, result );
+ qt_xdnd_atom_to_str( *result );
+
+ return result;
+}
+
+
+void qt_xdnd_setup() {
+ // set up protocol atoms
+ qt_x11_intern_atom( "XdndEnter", &qt_xdnd_enter );
+ qt_x11_intern_atom( "XdndPosition", &qt_xdnd_position );
+ qt_x11_intern_atom( "XdndStatus", &qt_xdnd_status );
+ qt_x11_intern_atom( "XdndLeave", &qt_xdnd_leave );
+ qt_x11_intern_atom( "XdndDrop", &qt_xdnd_drop );
+ qt_x11_intern_atom( "XdndFinished", &qt_xdnd_finished );
+ qt_x11_intern_atom( "XdndTypeList", &qt_xdnd_type_list );
+
+ qt_x11_intern_atom( "XdndSelection", &qt_xdnd_selection );
+
+ qt_x11_intern_atom( "XdndAware", &qt_xdnd_aware );
+ qt_x11_intern_atom( "XdndProxy", &qt_xdnd_proxy );
+
+
+ qt_x11_intern_atom( "XdndActionCopy", &qt_xdnd_action_copy );
+ qt_x11_intern_atom( "XdndActionLink", &qt_xdnd_action_link );
+ qt_x11_intern_atom( "XdndActionMove", &qt_xdnd_action_move );
+ qt_x11_intern_atom( "XdndActionPrivate", &qt_xdnd_action_private );
+
+ qt_x11_intern_atom( "QT_SELECTION", &qt_selection_property );
+ qt_x11_intern_atom( "INCR", &qt_incr_atom );
+
+ qAddPostRoutine( qt_xdnd_cleanup );
+}
+
+
+void qt_xdnd_cleanup()
+{
+ delete qt_xdnd_drag_types;
+ qt_xdnd_drag_types = 0;
+ delete qt_xdnd_atom_numbers;
+ qt_xdnd_atom_numbers = 0;
+ delete qt_xdnd_target_data;
+ qt_xdnd_target_data = 0;
+ delete noDropCursor;
+ noDropCursor = 0;
+ delete copyCursor;
+ copyCursor = 0;
+ delete moveCursor;
+ moveCursor = 0;
+ delete linkCursor;
+ linkCursor = 0;
+ delete defaultPm;
+ defaultPm = 0;
+ delete desktop_proxy;
+ desktop_proxy = 0;
+}
+
+
+static QWidget * find_child( QWidget * tlw, QPoint & p )
+{
+ QWidget * w = tlw;
+
+ p = w->mapFromGlobal( p );
+ bool done = FALSE;
+ while ( !done ) {
+ done = TRUE;
+ if ( ((QExtraWidget*)w)->extraData() &&
+ ((QExtraWidget*)w)->extraData()->xDndProxy != 0 )
+ break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
+ if ( w->children() ) {
+ QObjectListIt it( *w->children() );
+ it.toLast();
+ QObject * o;
+ while( (o=it.current()) ) {
+ --it;
+ if ( o->isWidgetType() &&
+ ((QWidget*)o)->isVisible() &&
+ ((QWidget*)o)->geometry().contains( p ) &&
+ !((QWidget*)o)->isTopLevel()) {
+ w = (QWidget *)o;
+ done = FALSE;
+ p = w->mapFromParent( p );
+ break;
+ }
+ }
+ }
+ }
+ return w;
+}
+
+
+static bool checkEmbedded(QWidget* w, const XEvent* xe)
+{
+ if (!w)
+ return FALSE;
+
+ if (current_embedding_widget != 0 && current_embedding_widget != w) {
+ qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
+ qt_xdnd_current_proxy_target = qt_xdnd_current_target;
+ qt_xdnd_send_leave();
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ current_embedding_widget = 0;
+ }
+
+ QWExtra* extra = ((QExtraWidget*)w)->extraData();
+ if ( extra && extra->xDndProxy != 0 ) {
+
+ if (current_embedding_widget != w) {
+
+ last_enter_event.xany.window = extra->xDndProxy;
+ XSendEvent( QPaintDevice::x11AppDisplay(), extra->xDndProxy, False, NoEventMask,
+ &last_enter_event );
+ current_embedding_widget = w;
+ }
+
+ ((XEvent*)xe)->xany.window = extra->xDndProxy;
+ XSendEvent( QPaintDevice::x11AppDisplay(), extra->xDndProxy, False, NoEventMask,
+ (XEvent*)xe );
+ qt_xdnd_current_widget = w;
+ return TRUE;
+ }
+ current_embedding_widget = 0;
+ return FALSE;
+}
+
+void qt_handle_xdnd_enter( QWidget *, const XEvent * xe, bool /*passive*/ )
+{
+ //if ( !w->neveHadAChildWithDropEventsOn() )
+ //return; // haven't been set up for dnd
+
+ qt_motifdnd_active = FALSE;
+
+ last_enter_event.xclient = xe->xclient;
+
+ qt_xdnd_target_answerwas = FALSE;
+
+ const long *l = xe->xclient.data.l;
+ int version = (int)(((unsigned long)(l[1])) >> 24);
+
+ if ( version > qt_xdnd_version )
+ return;
+
+ qt_xdnd_dragsource_xid = l[0];
+
+ int j = 0;
+ if ( l[1] & 1 ) {
+ // get the types from XdndTypeList
+ Atom type = None;
+ int f;
+ unsigned long n, a;
+ Atom *data;
+ XGetWindowProperty( QPaintDevice::x11AppDisplay(), qt_xdnd_dragsource_xid,
+ qt_xdnd_type_list, 0,
+ qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,(uchar**)&data );
+ for ( ; j<qt_xdnd_max_type && j < (int)n; j++ ) {
+ qt_xdnd_types[j] = data[j];
+ }
+ if ( data )
+ XFree( (uchar*)data );
+ } else {
+ // get the types from the message
+ int i;
+ for( i=2; i < 5; i++ ) {
+ qt_xdnd_types[j++] = l[i];
+ }
+ }
+ qt_xdnd_types[j] = 0;
+}
+
+
+
+void qt_handle_xdnd_position( QWidget *w, const XEvent * xe, bool passive )
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QPoint p( (l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff );
+ QWidget * c = find_child( w, p ); // changes p to to c-local coordinates
+
+ if (!passive && checkEmbedded(c, xe))
+ return;
+
+ if ( !c || !c->acceptDrops() && c->isDesktop() ) {
+ return;
+ }
+
+ if ( l[0] != qt_xdnd_dragsource_xid ) {
+ //qDebug( "xdnd drag position from unexpected source (%08lx not %08lx)",
+ // l[0], qt_xdnd_dragsource_xid );
+ return;
+ }
+
+ if (l[3] != 0) {
+ // timestamp from the source
+ qt_xdnd_target_current_time = qt_x_user_time = l[3];
+ }
+
+ XClientMessageEvent response;
+ response.type = ClientMessage;
+ response.window = qt_xdnd_dragsource_xid;
+ response.format = 32;
+ response.message_type = qt_xdnd_status;
+ response.data.l[0] = w->winId();
+ response.data.l[1] = 0; // flags
+ response.data.l[2] = 0; // x, y
+ response.data.l[3] = 0; // w, h
+ response.data.l[4] = 0; // action
+
+ if ( !passive ) { // otherwise just reject
+ while ( c && !c->acceptDrops() && !c->isTopLevel() ) {
+ p = c->mapToParent( p );
+ c = c->parentWidget();
+ }
+
+ QRect answerRect( c->mapToGlobal( p ), QSize( 1,1 ) );
+
+ QDragMoveEvent me( p );
+ QDropEvent::Action accepted_action = xdndaction_to_qtaction(l[4]);
+ me.setAction(accepted_action);
+
+ if ( c != qt_xdnd_current_widget ) {
+ qt_xdnd_target_answerwas = FALSE;
+ if ( qt_xdnd_current_widget ) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent( qt_xdnd_current_widget, &e );
+ }
+ if ( c->acceptDrops() ) {
+ qt_xdnd_current_widget = c;
+ qt_xdnd_current_position = p;
+
+ QDragEnterEvent de( p );
+ de.setAction(accepted_action);
+ QApplication::sendEvent( c, &de );
+ if ( de.isAccepted() ) {
+ me.accept( de.answerRect() );
+ if ( !de.isActionAccepted() ) // only as a copy (move if we del)
+ accepted_action = QDropEvent::Copy;
+ else
+ me.acceptAction(TRUE);
+ } else {
+ me.ignore( de.answerRect() );
+ }
+ }
+ } else {
+ if ( qt_xdnd_target_answerwas ) {
+ me.accept();
+ me.acceptAction(global_requested_action == global_accepted_action);
+ }
+ }
+
+ if ( !c->acceptDrops() ) {
+ qt_xdnd_current_widget = 0;
+ answerRect = QRect( p, QSize( 1, 1 ) );
+ } else if ( xdndaction_to_qtaction(l[4]) < QDropEvent::Private ) {
+ qt_xdnd_current_widget = c;
+ qt_xdnd_current_position = p;
+
+ QApplication::sendEvent( c, &me );
+ qt_xdnd_target_answerwas = me.isAccepted();
+ if ( me.isAccepted() ) {
+ response.data.l[1] = 1; // yes
+ if ( !me.isActionAccepted() ) // only as a copy (move if we del)
+ accepted_action = QDropEvent::Copy;
+ } else {
+ response.data.l[0] = 0;
+ }
+ answerRect = me.answerRect().intersect( c->rect() );
+ } else {
+ response.data.l[0] = 0;
+ answerRect = QRect( p, QSize( 1, 1 ) );
+ }
+ answerRect = QRect( c->mapToGlobal( answerRect.topLeft() ),
+ answerRect.size() );
+
+ if ( answerRect.left() < 0 )
+ answerRect.setLeft( 0 );
+ if ( answerRect.right() > 4096 )
+ answerRect.setRight( 4096 );
+ if ( answerRect.top() < 0 )
+ answerRect.setTop( 0 );
+ if ( answerRect.bottom() > 4096 )
+ answerRect.setBottom( 4096 );
+ if ( answerRect.width() < 0 )
+ answerRect.setWidth( 0 );
+ if ( answerRect.height() < 0 )
+ answerRect.setHeight( 0 );
+
+ response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
+ response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
+ response.data.l[4] = qtaction_to_xdndaction(accepted_action);
+ global_accepted_action = accepted_action;
+ }
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+
+ QWidget * source = QWidget::find( qt_xdnd_dragsource_xid );
+
+ if ( source && source->isDesktop() && !source->acceptDrops() )
+ source = 0;
+
+ if ( source )
+ qt_handle_xdnd_status( source, (const XEvent *)&response, passive );
+ else
+ XSendEvent( QPaintDevice::x11AppDisplay(), qt_xdnd_dragsource_xid, False,
+ NoEventMask, (XEvent*)&response );
+}
+
+
+void qt_handle_xdnd_status( QWidget * w, const XEvent * xe, bool /*passive*/ )
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+ // Messy: QDragResponseEvent is just a call to QDragManager function
+ global_accepted_action = xdndaction_to_qtaction(l[4]);
+ QDragResponseEvent e( (int)(l[1] & 1) );
+ QApplication::sendEvent( w, &e );
+
+ if ( (int)(l[1] & 2) == 0 ) {
+ QPoint p( (l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff );
+ QSize s( (l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff );
+ qt_xdnd_source_sameanswer = QRect( p, s );
+ if ( qt_xdnd_source_sameanswer.isNull() ) {
+ // Application wants "coninutous" move events
+ }
+ } else {
+ qt_xdnd_source_sameanswer = QRect();
+ }
+}
+
+
+void qt_handle_xdnd_leave( QWidget *w, const XEvent * xe, bool /*passive*/ )
+{
+ //qDebug( "xdnd leave" );
+ if ( !qt_xdnd_current_widget ||
+ w->topLevelWidget() != qt_xdnd_current_widget->topLevelWidget() ) {
+ return; // sanity
+ }
+
+ if (checkEmbedded(current_embedding_widget, xe)) {
+ current_embedding_widget = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ QDragLeaveEvent e;
+ QApplication::sendEvent( qt_xdnd_current_widget, &e );
+
+ if ( l[0] != qt_xdnd_dragsource_xid ) {
+ // This often happens - leave other-process window quickly
+ //qDebug( "xdnd drag leave from unexpected source (%08lx not %08lx",
+ //l[0], qt_xdnd_dragsource_xid );
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_types[0] = 0;
+ qt_xdnd_current_widget = 0;
+}
+
+
+void qt_xdnd_send_leave()
+{
+ if ( !qt_xdnd_current_target )
+ return;
+
+ XClientMessageEvent leave;
+ leave.type = ClientMessage;
+ leave.window = qt_xdnd_current_target;
+ leave.format = 32;
+ leave.message_type = qt_xdnd_leave;
+ leave.data.l[0] = qt_xdnd_dragsource_xid;
+ leave.data.l[1] = 0; // flags
+ leave.data.l[2] = 0; // x, y
+ leave.data.l[3] = 0; // w, h
+ leave.data.l[4] = 0; // just null
+
+ QWidget * w = QWidget::find( qt_xdnd_current_proxy_target );
+
+ if ( w && w->isDesktop() && !w->acceptDrops() )
+ w = 0;
+
+ if ( w )
+ qt_handle_xdnd_leave( w, (const XEvent *)&leave, FALSE );
+ else
+ XSendEvent( QPaintDevice::x11AppDisplay(), qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&leave );
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+}
+
+
+
+void qt_handle_xdnd_drop( QWidget *, const XEvent * xe, bool passive )
+{
+ if ( !qt_xdnd_current_widget ) {
+ qt_xdnd_dragsource_xid = 0;
+ return; // sanity
+ }
+
+ if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
+ current_embedding_widget = 0;
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+ return;
+ }
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ //qDebug( "xdnd drop" );
+
+ if ( l[0] != qt_xdnd_dragsource_xid ) {
+ //qDebug( "xdnd drop from unexpected source (%08lx not %08lx",
+ // l[0], qt_xdnd_dragsource_xid );
+ return;
+ }
+
+ if (l[2] != 0) {
+ // update the "user time" from the timestamp in the event.
+ qt_xdnd_target_current_time = qt_x_user_time = l[2];
+ }
+
+ if ( qt_xdnd_source_object )
+ qt_xdnd_source_object->setTarget( qt_xdnd_current_widget );
+
+ if ( !passive ) {
+ QDropEvent de( qt_xdnd_current_position );
+ de.setAction( global_accepted_action );
+ QApplication::sendEvent( qt_xdnd_current_widget, &de );
+ if ( !de.isAccepted() ) {
+ // Ignore a failed drag
+ global_accepted_action = QDropEvent::Copy;
+ dndCancelled = TRUE;
+ }
+ XClientMessageEvent finished;
+ finished.type = ClientMessage;
+ finished.window = qt_xdnd_dragsource_xid;
+ finished.format = 32;
+ finished.message_type = qt_xdnd_finished;
+ finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->topLevelWidget()->winId():0;
+ finished.data.l[1] = 0; // flags
+ XSendEvent( QPaintDevice::x11AppDisplay(), qt_xdnd_dragsource_xid, False,
+ NoEventMask, (XEvent*)&finished );
+ } else {
+ QDragLeaveEvent e;
+ QApplication::sendEvent( qt_xdnd_current_widget, &e );
+ }
+ qt_xdnd_dragsource_xid = 0;
+ qt_xdnd_current_widget = 0;
+
+ // reset
+ qt_xdnd_target_current_time = CurrentTime;
+}
+
+
+void qt_handle_xdnd_finished( QWidget *, const XEvent * xe, bool passive )
+{
+ const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
+
+ if ( l[0] && (l[0] == qt_xdnd_current_target
+ || l[0] == qt_xdnd_current_proxy_target) ) {
+ //
+ if ( !passive )
+ (void ) checkEmbedded( qt_xdnd_current_widget, xe);
+ current_embedding_widget = 0;
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ delete qt_xdnd_source_object;
+ qt_xdnd_source_object = 0;
+ }
+}
+
+
+void QDragManager::timerEvent( QTimerEvent* e )
+{
+ if ( e->timerId() == heartbeat ) {
+ if( need_modifiers_check ) {
+ Window root, child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ XQueryPointer( qt_xdisplay(), qt_xrootwin( qt_xdnd_current_screen ),
+ &root, &child, &root_x, &root_y, &win_x, &win_y, &mask );
+ if( updateMode( (ButtonState)qt_x11_translateButtonState( mask )))
+ qt_xdnd_source_sameanswer = QRect(); // force move
+ }
+ need_modifiers_check = TRUE;
+ if( qt_xdnd_source_sameanswer.isNull() )
+ move( QCursor::pos() );
+ }
+}
+
+static bool qt_xdnd_was_move = false;
+static bool qt_xdnd_found = false;
+// check whole incoming X queue for move events
+// checking whole queue is done by always returning False in the predicate
+// if there's another move event in the queue, and there's not a mouse button
+// or keyboard or ClientMessage event before it, the current move event
+// may be safely discarded
+// this helps avoiding being overloaded by being flooded from many events
+// from the XServer
+static
+Bool qt_xdnd_predicate( Display*, XEvent* ev, XPointer )
+{
+ if( qt_xdnd_found )
+ return False;
+ if( ev->type == MotionNotify )
+ {
+ qt_xdnd_was_move = true;
+ qt_xdnd_found = true;
+ }
+ if( ev->type == ButtonPress || ev->type == ButtonRelease
+ || ev->type == XKeyPress || ev->type == XKeyRelease
+ || ev->type == ClientMessage )
+ {
+ qt_xdnd_was_move = false;
+ qt_xdnd_found = true;
+ }
+ return False;
+}
+
+static
+bool qt_xdnd_another_movement()
+{
+ qt_xdnd_was_move = false;
+ qt_xdnd_found = false;
+ XEvent dummy;
+ XCheckIfEvent( qt_xdisplay(), &dummy, qt_xdnd_predicate, NULL );
+ return qt_xdnd_was_move;
+}
+
+bool QDragManager::eventFilter( QObject * o, QEvent * e)
+{
+ if ( beingCancelled ) {
+ if ( e->type() == QEvent::KeyRelease &&
+ ((QKeyEvent*)e)->key() == Key_Escape ) {
+ qApp->removeEventFilter( this );
+ object = 0;
+ dragSource = 0;
+ beingCancelled = FALSE;
+ qApp->exit_loop();
+ return TRUE; // block the key release
+ }
+ return FALSE;
+ }
+
+ Q_ASSERT( object != 0 );
+
+ if ( !o->isWidgetType() )
+ return FALSE;
+
+ if ( e->type() == QEvent::MouseMove ) {
+ QMouseEvent* me = (QMouseEvent *)e;
+ if( !qt_xdnd_another_movement()) {
+ updateMode(me->stateAfter());
+ move( me->globalPos() );
+ }
+ need_modifiers_check = FALSE;
+ return TRUE;
+ } else if ( e->type() == QEvent::MouseButtonRelease ) {
+ qApp->removeEventFilter( this );
+ if ( willDrop )
+ drop();
+ else
+ cancel();
+ object = 0;
+ dragSource = 0;
+ beingCancelled = FALSE;
+ qApp->exit_loop();
+ return TRUE;
+ } else if ( e->type() == QEvent::DragResponse ) {
+ if ( ((QDragResponseEvent *)e)->dragAccepted() ) {
+ if ( !willDrop ) {
+ willDrop = TRUE;
+ }
+ } else {
+ if ( willDrop ) {
+ willDrop = FALSE;
+ }
+ }
+ updateCursor();
+ return TRUE;
+ }
+
+ if ( e->type() == QEvent::KeyPress
+ || e->type() == QEvent::KeyRelease )
+ {
+ QKeyEvent *ke = ((QKeyEvent*)e);
+ if ( ke->key() == Key_Escape && e->type() == QEvent::KeyPress ) {
+ cancel();
+ qApp->removeEventFilter( this );
+ object = 0;
+ dragSource = 0;
+ beingCancelled = FALSE;
+ qApp->exit_loop();
+ } else {
+ if( updateMode(ke->stateAfter())) {
+ qt_xdnd_source_sameanswer = QRect(); // force move
+ move( QCursor::pos() );
+ }
+ need_modifiers_check = FALSE;
+ }
+ return TRUE; // Eat all key events
+ }
+
+ // ### We bind modality to widgets, so we have to do this
+ // ### "manually".
+ // DnD is modal - eat all other interactive events
+ switch ( e->type() ) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::Wheel:
+ case QEvent::Accel:
+ case QEvent::AccelAvailable:
+ case QEvent::AccelOverride:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+static Qt::ButtonState oldstate;
+bool QDragManager::updateMode( ButtonState newstate )
+{
+ if ( newstate == oldstate )
+ return false;
+ const int both = ShiftButton|ControlButton;
+ if ( (newstate & both) == both ) {
+ global_requested_action = QDropEvent::Link;
+ } else {
+ bool local = qt_xdnd_source_object != 0;
+ if ( drag_mode == QDragObject::DragMove )
+ global_requested_action = QDropEvent::Move;
+ else if ( drag_mode == QDragObject::DragCopy )
+ global_requested_action = QDropEvent::Copy;
+ else if ( drag_mode == QDragObject::DragLink )
+ global_requested_action = QDropEvent::Link;
+ else {
+ if ( drag_mode == QDragObject::DragDefault && local )
+ global_requested_action = QDropEvent::Move;
+ else
+ global_requested_action = QDropEvent::Copy;
+ if ( newstate & ShiftButton )
+ global_requested_action = QDropEvent::Move;
+ else if ( newstate & ControlButton )
+ global_requested_action = QDropEvent::Copy;
+ }
+ }
+ oldstate = newstate;
+ return true;
+}
+
+
+void QDragManager::createCursors()
+{
+ if ( !noDropCursor ) {
+ noDropCursor = new QCursor( ForbiddenCursor );
+ if ( !pm_cursor[0].isNull() )
+ moveCursor = new QCursor(pm_cursor[0], 0,0);
+ if ( !pm_cursor[1].isNull() )
+ copyCursor = new QCursor(pm_cursor[1], 0,0);
+ if ( !pm_cursor[2].isNull() )
+ linkCursor = new QCursor(pm_cursor[2], 0,0);
+ }
+}
+
+void QDragManager::updateCursor()
+{
+ QCursor *c;
+ if ( willDrop ) {
+ if ( global_accepted_action == QDropEvent::Copy ) {
+ if ( global_requested_action == QDropEvent::Move )
+ c = moveCursor; // (source can delete)
+ else
+ c = copyCursor;
+ } else if ( global_accepted_action == QDropEvent::Link ) {
+ c = linkCursor;
+ } else {
+ c = moveCursor;
+ }
+ if ( qt_xdnd_deco ) {
+ qt_xdnd_deco->show();
+ qt_xdnd_deco->raise();
+ }
+ } else {
+ c = noDropCursor;
+ //if ( qt_xdnd_deco )
+ // qt_xdnd_deco->hide();
+ }
+#ifndef QT_NO_CURSOR
+ if ( c )
+ qApp->setOverrideCursor( *c, TRUE );
+#endif
+}
+
+
+void QDragManager::cancel( bool deleteSource )
+{
+ killTimer( heartbeat );
+ heartbeat = -1;
+ if ( object ) {
+ beingCancelled = TRUE;
+ object = 0;
+ }
+
+ if ( qt_xdnd_current_target ) {
+ qt_xdnd_send_leave();
+ }
+
+#ifndef QT_NO_CURSOR
+ if ( restoreCursor ) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = FALSE;
+ }
+#endif
+
+ if ( deleteSource )
+ delete qt_xdnd_source_object;
+ qt_xdnd_source_object = 0;
+ delete qt_xdnd_deco;
+ qt_xdnd_deco = 0;
+
+ dndCancelled = TRUE;
+}
+
+static
+Window findRealWindow( const QPoint & pos, Window w, int md )
+{
+ if ( qt_xdnd_deco && w == qt_xdnd_deco->winId() )
+ return 0;
+
+ if ( md ) {
+ qt_ignore_badwindow();
+ XWindowAttributes attr;
+ XGetWindowAttributes( QPaintDevice::x11AppDisplay(), w, &attr );
+ if (qt_badwindow())
+ return 0;
+
+ if ( attr.map_state == IsViewable
+ && QRect(attr.x,attr.y,attr.width,attr.height)
+ .contains(pos) )
+ {
+ {
+ Atom type = None;
+ int f;
+ unsigned long n, a;
+ unsigned char *data;
+
+ XGetWindowProperty( QPaintDevice::x11AppDisplay(), w, qt_xdnd_aware, 0,
+ 0, False, AnyPropertyType, &type, &f,&n,&a,&data );
+ if ( data ) XFree(data);
+ if ( type ) return w;
+ }
+
+ Window r, p;
+ Window* c;
+ uint nc;
+ if ( XQueryTree( QPaintDevice::x11AppDisplay(), w, &r, &p, &c, &nc ) ) {
+ r=0;
+ for (uint i=nc; !r && i--; ) {
+ r = findRealWindow( pos-QPoint(attr.x,attr.y),
+ c[i], md-1 );
+ }
+ XFree(c);
+ if ( r )
+ return r;
+
+ // We didn't find a client window! Just use the
+ // innermost window.
+ }
+
+ // No children!
+ return w;
+ }
+ }
+ return 0;
+}
+
+void QDragManager::move( const QPoint & globalPos )
+{
+ if (!object) {
+ // perhaps the target crashed?
+ return;
+ }
+
+ int screen = QCursor::x11Screen();
+ if ( ( qt_xdnd_current_screen == -1 && screen != QPaintDevice::x11AppScreen() ) ||
+ ( screen != qt_xdnd_current_screen ) ) {
+ // recreate the pixmap on the new screen...
+ delete qt_xdnd_deco;
+ qt_xdnd_deco = new QShapedPixmapWidget( screen );
+ qt_xdnd_deco->x11SetWindowTransient( dragSource->topLevelWidget());
+ if (!QWidget::mouseGrabber()) {
+ updatePixmap();
+ qt_xdnd_deco->grabMouse();
+ }
+ }
+ updatePixmap( globalPos );
+
+ if ( qt_xdnd_source_sameanswer.contains( globalPos ) &&
+ qt_xdnd_source_sameanswer.isValid() ) {
+ return;
+ }
+
+ qt_xdnd_current_screen = screen;
+ Window rootwin = QPaintDevice::x11AppRootWindow( qt_xdnd_current_screen );
+ Window target = 0;
+ int lx = 0, ly = 0;
+ if ( !XTranslateCoordinates( QPaintDevice::x11AppDisplay(), rootwin, rootwin,
+ globalPos.x(), globalPos.y(),
+ &lx, &ly, &target) )
+ // some wierd error...
+ return;
+
+ if ( target == rootwin ) {
+ // Ok.
+ } else if ( target ) {
+ //me
+ Window src = rootwin;
+ while (target != 0) {
+ int lx2, ly2;
+ Window t;
+ // translate coordinates
+ if (!XTranslateCoordinates(QPaintDevice::x11AppDisplay(), src, target,
+ lx, ly, &lx2, &ly2, &t)) {
+ target = 0;
+ break;
+ }
+ lx = lx2;
+ ly = ly2;
+ src = target;
+
+ // check if it has XdndAware
+ Atom type = None;
+ int f;
+ unsigned long n, a;
+ unsigned char *data = 0;
+ XGetWindowProperty(QPaintDevice::x11AppDisplay(), target, qt_xdnd_aware, 0, 0, False,
+ AnyPropertyType, &type, &f,&n,&a,&data);
+ if (data)
+ XFree(data);
+ if (type)
+ break;
+
+ // find child at the coordinates
+ if (!XTranslateCoordinates( QPaintDevice::x11AppDisplay(), src, src,
+ lx, ly, &lx2, &ly2, &target)) {
+ target = 0;
+ break;
+ }
+ }
+ if ( qt_xdnd_deco && (!target || target == qt_xdnd_deco->winId()) ) {
+ target = findRealWindow(globalPos,rootwin,6);
+ }
+ }
+
+ QWidget* w;
+ if ( target ) {
+ w = QWidget::find( (WId)target );
+ if ( w && w->isDesktop() && !w->acceptDrops() )
+ w = 0;
+ } else {
+ w = 0;
+ target = rootwin;
+ }
+
+ WId proxy_target = target;
+ int target_version = 1;
+
+ {
+ Atom type = None;
+ int r, f;
+ unsigned long n, a;
+ WId *proxy_id;
+ qt_ignore_badwindow();
+ r = XGetWindowProperty( qt_xdisplay(), target, qt_xdnd_proxy, 0,
+ 1, False, XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id );
+ if ( ( r != Success ) || qt_badwindow() ) {
+ proxy_target = target = 0;
+ } else if ( type == XA_WINDOW && proxy_id ) {
+ proxy_target = *proxy_id;
+ XFree(proxy_id);
+ proxy_id = 0;
+ r = XGetWindowProperty( qt_xdisplay(), proxy_target, qt_xdnd_proxy, 0,
+ 1, False, XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id );
+ if ( ( r != Success ) || qt_badwindow() || !type || !proxy_id || *proxy_id != proxy_target ) {
+ // Bogus
+ proxy_target = 0;
+ target = 0;
+ }
+ if ( proxy_id )
+ XFree(proxy_id);
+ }
+ if ( proxy_target ) {
+ int *tv;
+ qt_ignore_badwindow();
+ r = XGetWindowProperty( qt_xdisplay(), proxy_target, qt_xdnd_aware, 0,
+ 1, False, AnyPropertyType, &type, &f,&n,&a,(uchar**)&tv );
+ if ( r != Success ) {
+ target = 0;
+ } else {
+ target_version = QMIN(qt_xdnd_version,tv ? *tv : 1);
+ if ( tv )
+ XFree( tv );
+ if (!(!qt_badwindow() && type))
+ target = 0;
+ }
+ }
+ }
+
+ if ( target != qt_xdnd_current_target ) {
+ if ( qt_xdnd_current_target )
+ qt_xdnd_send_leave();
+
+ qt_xdnd_current_target = target;
+ qt_xdnd_current_proxy_target = proxy_target;
+ if ( target ) {
+ QMemArray<Atom> type;
+ int flags = target_version << 24;
+ const char* fmt;
+ int nfmt=0;
+ for (nfmt=0; (fmt=object->format(nfmt)); nfmt++) {
+ type.resize(nfmt+1);
+ type[nfmt] = *qt_xdnd_str_to_atom( fmt );
+ }
+ if ( nfmt >= 3 ) {
+ XChangeProperty( QPaintDevice::x11AppDisplay(),
+ object->source()->winId(), qt_xdnd_type_list,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *)type.data(),
+ type.size() );
+ flags |= 0x0001;
+ }
+ XClientMessageEvent enter;
+ enter.type = ClientMessage;
+ enter.window = target;
+ enter.format = 32;
+ enter.message_type = qt_xdnd_enter;
+ enter.data.l[0] = object->source()->winId();
+ enter.data.l[1] = flags;
+ enter.data.l[2] = type.size()>0 ? type[0] : 0;
+ enter.data.l[3] = type.size()>1 ? type[1] : 0;
+ enter.data.l[4] = type.size()>2 ? type[2] : 0;
+ // provisionally set the rectangle to 5x5 pixels...
+ qt_xdnd_source_sameanswer = QRect( globalPos.x() - 2,
+ globalPos.y() -2 , 5, 5 );
+
+ if ( w ) {
+ qt_handle_xdnd_enter( w, (const XEvent *)&enter, FALSE );
+ } else if ( target ) {
+ XSendEvent( QPaintDevice::x11AppDisplay(), proxy_target, False, NoEventMask,
+ (XEvent*)&enter );
+ }
+ }
+ }
+
+ if ( target ) {
+ XClientMessageEvent move;
+ move.type = ClientMessage;
+ move.window = target;
+ move.format = 32;
+ move.message_type = qt_xdnd_position;
+ move.window = target;
+ move.data.l[0] = object->source()->winId();
+ move.data.l[1] = 0; // flags
+ move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
+ move.data.l[3] = qt_x_time;
+ move.data.l[4] = qtaction_to_xdndaction( global_requested_action );
+
+ if ( w )
+ qt_handle_xdnd_position( w, (const XEvent *)&move, FALSE );
+ else
+ XSendEvent( QPaintDevice::x11AppDisplay(), proxy_target, False, NoEventMask,
+ (XEvent*)&move );
+ } else {
+ if ( willDrop ) {
+ willDrop = FALSE;
+ updateCursor();
+ }
+ }
+}
+
+
+void QDragManager::drop()
+{
+ killTimer( heartbeat );
+ heartbeat = -1;
+ if ( !qt_xdnd_current_target )
+ return;
+
+ delete qt_xdnd_deco;
+ qt_xdnd_deco = 0;
+
+ XClientMessageEvent drop;
+ drop.type = ClientMessage;
+ drop.window = qt_xdnd_current_target;
+ drop.format = 32;
+ drop.message_type = qt_xdnd_drop;
+ drop.data.l[0] = object->source()->winId();
+ drop.data.l[1] = 0; // flags
+ drop.data.l[2] = qt_x_time;
+ drop.data.l[3] = 0;
+ drop.data.l[4] = 0;
+
+ QWidget * w = QWidget::find( qt_xdnd_current_proxy_target );
+
+ if ( w && w->isDesktop() && !w->acceptDrops() )
+ w = 0;
+
+ if ( w )
+ qt_handle_xdnd_drop( w, (const XEvent *)&drop, FALSE );
+ else
+ XSendEvent( QPaintDevice::x11AppDisplay(), qt_xdnd_current_proxy_target, False,
+ NoEventMask, (XEvent*)&drop );
+
+#ifndef QT_NO_CURSOR
+ if ( restoreCursor ) {
+ QApplication::restoreOverrideCursor();
+ restoreCursor = FALSE;
+ }
+#endif
+}
+
+
+
+bool qt_xdnd_handle_badwindow()
+{
+ if ( qt_xdnd_source_object && qt_xdnd_current_target ) {
+ qt_xdnd_current_target = 0;
+ qt_xdnd_current_proxy_target = 0;
+ delete qt_xdnd_source_object;
+ qt_xdnd_source_object = 0;
+ delete qt_xdnd_deco;
+ qt_xdnd_deco = 0;
+ return TRUE;
+ }
+ if ( qt_xdnd_dragsource_xid ) {
+ qt_xdnd_dragsource_xid = 0;
+ if ( qt_xdnd_current_widget ) {
+ QDragLeaveEvent e;
+ QApplication::sendEvent( qt_xdnd_current_widget, &e );
+ qt_xdnd_current_widget = 0;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*!
+ \class QDragMoveEvent qevent.h
+ \ingroup events
+ \ingroup draganddrop
+ \brief The QDragMoveEvent class provides an event which is sent while a drag and drop is in progress.
+
+ When a widget \link QWidget::setAcceptDrops() accepts drop
+ events\endlink, it will receive this event repeatedly while the
+ drag is within the widget's boundaries. The widget should examine
+ the event to see what data it \link QDragMoveEvent::provides()
+ provides\endlink, and accept() the drop if appropriate.
+
+ Note that this class inherits most of its functionality from
+ QDropEvent.
+*/
+
+
+/*!
+ Returns TRUE if this event provides format \a mimeType; otherwise
+ returns FALSE.
+
+ \sa data()
+*/
+
+bool QDropEvent::provides( const char *mimeType ) const
+{
+ if ( qt_motifdnd_active && qstrnicmp( mimeType, "text/", 5 ) == 0 )
+ return TRUE;
+
+ int n=0;
+ const char* f;
+ do {
+ f = format( n );
+ if ( !f )
+ return FALSE;
+ n++;
+ } while( qstricmp( mimeType, f ) );
+ return TRUE;
+}
+
+void qt_xdnd_handle_selection_request( const XSelectionRequestEvent * req )
+{
+ if ( !req )
+ return;
+ XEvent evt;
+ evt.xselection.type = SelectionNotify;
+ evt.xselection.display = req->display;
+ evt.xselection.requestor = req->requestor;
+ evt.xselection.selection = req->selection;
+ evt.xselection.target = req->target;
+ evt.xselection.property = None;
+ evt.xselection.time = req->time;
+ const char* format = qt_xdnd_atom_to_str( req->target );
+ if ( format && qt_xdnd_source_object &&
+ qt_xdnd_source_object->provides( format ) ) {
+ QByteArray a = qt_xdnd_source_object->encodedData(format);
+ XChangeProperty ( QPaintDevice::x11AppDisplay(), req->requestor, req->property,
+ req->target, 8, PropModeReplace,
+ (unsigned char *)a.data(), a.size() );
+ evt.xselection.property = req->property;
+ }
+ // ### this can die if req->requestor crashes at the wrong
+ // ### moment
+ XSendEvent( QPaintDevice::x11AppDisplay(), req->requestor, False, 0, &evt );
+}
+
+/*
+ XChangeProperty ( QPaintDevice::x11AppDisplay(), req->requestor, req->property,
+ XA_STRING, 8,
+ PropModeReplace,
+ (uchar *)d->text(), strlen(d->text()) );
+ evt.xselection.property = req->property;
+*/
+
+static QByteArray qt_xdnd_obtain_data( const char *format )
+{
+ QByteArray result;
+
+ QWidget* w;
+ if ( qt_xdnd_dragsource_xid && qt_xdnd_source_object &&
+ (w=QWidget::find( qt_xdnd_dragsource_xid ))
+ && (!w->isDesktop() || w->acceptDrops()) )
+ {
+ QDragObject * o = qt_xdnd_source_object;
+ if ( o->provides( format ) )
+ result = o->encodedData(format);
+ return result;
+ }
+
+ Atom * a = qt_xdnd_str_to_atom( format );
+ if ( !a || !*a )
+ return result;
+
+ if ( !qt_xdnd_target_data )
+ qt_xdnd_target_data = new QIntDict<QByteArray>( 17 );
+
+ if ( qt_xdnd_target_data->find( (int)*a ) ) {
+ result = *(qt_xdnd_target_data->find( (int)*a ));
+ } else {
+ if ( XGetSelectionOwner( QPaintDevice::x11AppDisplay(),
+ qt_xdnd_selection ) == None )
+ return result; // should never happen?
+
+ QWidget* tw = qt_xdnd_current_widget;
+ if ( !qt_xdnd_current_widget ||
+ qt_xdnd_current_widget->isDesktop() ) {
+ tw = new QWidget;
+ }
+ XConvertSelection( QPaintDevice::x11AppDisplay(),
+ qt_xdnd_selection,
+ *a,
+ qt_xdnd_selection,
+ tw->winId(),
+ qt_xdnd_target_current_time );
+ XFlush( QPaintDevice::x11AppDisplay() );
+
+ XEvent xevent;
+ bool got=qt_xclb_wait_for_event( QPaintDevice::x11AppDisplay(),
+ tw->winId(),
+ SelectionNotify, &xevent, 5000);
+ if ( got ) {
+ Atom type;
+
+ if ( qt_xclb_read_property( QPaintDevice::x11AppDisplay(),
+ tw->winId(),
+ qt_xdnd_selection, TRUE,
+ &result, 0, &type, 0, FALSE ) ) {
+ if ( type == qt_incr_atom ) {
+ int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
+ result = qt_xclb_read_incremental_property( QPaintDevice::x11AppDisplay(),
+ tw->winId(),
+ qt_xdnd_selection,
+ nbytes, FALSE );
+ } else if ( type != *a ) {
+ // (includes None) qDebug( "Qt clipboard: unknown atom %ld", type);
+ }
+#if 0
+ // this needs to be matched by a qt_xdnd_target_data->clear()
+ // when each drag is finished. for 2.0, we do the safe thing
+ // and disable the entire caching.
+ if ( type != None )
+ qt_xdnd_target_data->insert( (int)((long)a), new QByteArray(result) );
+#endif
+ }
+ }
+ if ( !qt_xdnd_current_widget ||
+ qt_xdnd_current_widget->isDesktop() ) {
+ delete tw;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ Enable drag and drop for widget w by installing the proper
+ properties on w's toplevel widget.
+*/
+bool qt_dnd_enable( QWidget* w, bool on )
+{
+ w = w->topLevelWidget();
+
+ if ( on ) {
+ if ( ( (QExtraWidget*)w)->topData()->dnd )
+ return TRUE; // been there, done that
+ ((QExtraWidget*)w)->topData()->dnd = 1;
+ }
+
+ qt_motifdnd_enable( w, on );
+ return qt_xdnd_enable( w, on );
+}
+
+
+/*!
+ \class QDropEvent qevent.h
+ \ingroup events
+ \ingroup draganddrop
+
+ \brief The QDropEvent class provides an event which is sent when a drag and drop is completed.
+
+ When a widget \link QWidget::setAcceptDrops() accepts drop
+ events\endlink, it will receive this event if it has accepted the
+ most recent QDragEnterEvent or QDragMoveEvent sent to it.
+
+ The widget should use data() to extract the data in an appropriate
+ format.
+*/
+
+
+/*!
+ \fn QDropEvent::QDropEvent (const QPoint & pos, Type typ)
+
+ Constructs a drop event that drops a drop of type \a typ on point
+ \a pos.
+*/ // ### pos is in which coordinate system?
+
+
+/*!
+ Returns a byte array containing the drag's data, in \a format.
+
+ data() normally needs to get the data from the drag source, which
+ is potentially very slow, so it's advisable to call this function
+ only if you're sure that you will need the data in \a format.
+
+ The resulting data will have a size of 0 if the format was not
+ available.
+
+ \sa format() QByteArray::size()
+*/
+
+QByteArray QDropEvent::encodedData( const char *format ) const
+{
+ if ( qt_motifdnd_active )
+ return qt_motifdnd_obtain_data( format );
+ return qt_xdnd_obtain_data( format );
+}
+
+/*!
+ Returns a string describing one of the available data types for
+ this drag. Common examples are "text/plain" and "image/gif". If \a
+ n is less than zero or greater than the number of available data
+ types, format() returns 0.
+
+ This function is provided mainly for debugging. Most drop targets
+ will use provides().
+
+ \sa data() provides()
+*/
+
+const char* QDropEvent::format( int n ) const
+{
+ if ( qt_motifdnd_active )
+ return qt_motifdnd_format( n );
+
+ int i = 0;
+ while( i<n && qt_xdnd_types[i] )
+ i++;
+ if ( i < n )
+ return 0;
+
+ const char* name = qt_xdnd_atom_to_str( qt_xdnd_types[i] );
+ if ( !name )
+ return 0; // should never happen
+
+ return name;
+}
+
+bool QDragManager::drag( QDragObject * o, QDragObject::DragMode mode )
+{
+ if ( object == o || !o || !o->parent() )
+ return FALSE;
+
+ if ( object ) {
+ cancel();
+ qApp->removeEventFilter( this );
+ beingCancelled = FALSE;
+ }
+
+ if ( qt_xdnd_source_object ) {
+ // the last drag and drop operation hasn't finished, so we are going to wait
+ // for one second to see if it does... if the finish message comes after this,
+ // then we could still have problems, but this is highly unlikely
+ QApplication::flushX();
+
+ QTime started = QTime::currentTime();
+ QTime now = started;
+ do {
+ XEvent event;
+ if ( XCheckTypedEvent( QPaintDevice::x11AppDisplay(),
+ ClientMessage, &event ) )
+ qApp->x11ProcessEvent( &event );
+
+ now = QTime::currentTime();
+ if ( started > now ) // crossed midnight
+ started = now;
+
+ // sleep 50ms, so we don't use up CPU cycles all the time.
+ struct timeval usleep_tv;
+ usleep_tv.tv_sec = 0;
+ usleep_tv.tv_usec = 50000;
+ select(0, 0, 0, 0, &usleep_tv);
+ } while ( qt_xdnd_source_object && started.msecsTo(now) < 1000 );
+ }
+
+ qt_xdnd_source_object = o;
+ qt_xdnd_source_object->setTarget( 0 );
+ qt_xdnd_deco = new QShapedPixmapWidget();
+
+ willDrop = FALSE;
+
+ object = o;
+ updatePixmap();
+
+ dragSource = (QWidget *)(object->parent());
+
+ qt_xdnd_deco->x11SetWindowTransient( dragSource->topLevelWidget());
+ qApp->installEventFilter( this );
+ qt_xdnd_source_current_time = qt_x_time;
+ XSetSelectionOwner( QPaintDevice::x11AppDisplay(), qt_xdnd_selection,
+ dragSource->topLevelWidget()->winId(),
+ qt_xdnd_source_current_time );
+ oldstate = ButtonState(-1); // #### Should use state that caused the drag
+ drag_mode = mode;
+ global_accepted_action = QDropEvent::Copy;
+ updateMode(ButtonState(0));
+ qt_xdnd_source_sameanswer = QRect();
+ move(QCursor::pos());
+ heartbeat = startTimer(200);
+ need_modifiers_check = FALSE;
+
+#ifndef QT_NO_CURSOR
+ qApp->setOverrideCursor( arrowCursor );
+ restoreCursor = TRUE;
+ updateCursor();
+#endif
+
+ dndCancelled = FALSE;
+ qt_xdnd_dragging = TRUE;
+
+ if (!QWidget::mouseGrabber())
+ qt_xdnd_deco->grabMouse();
+
+ qApp->enter_loop(); // Do the DND.
+
+#ifndef QT_NO_CURSOR
+ qApp->restoreOverrideCursor();
+#endif
+
+ delete qt_xdnd_deco;
+ qt_xdnd_deco = 0;
+ killTimer( heartbeat );
+ heartbeat = -1;
+ qt_xdnd_current_screen = -1;
+ qt_xdnd_dragging = FALSE;
+
+ return ((! dndCancelled) && // source del?
+ (global_accepted_action == QDropEvent::Copy &&
+ global_requested_action == QDropEvent::Move));
+
+ // qt_xdnd_source_object persists until we get an xdnd_finish message
+}
+
+void QDragManager::updatePixmap( const QPoint& cursorPos )
+{
+ if ( qt_xdnd_deco ) {
+ QPixmap pm;
+ QPoint pm_hot(default_pm_hotx,default_pm_hoty);
+ if ( object ) {
+ pm = object->pixmap();
+ if ( !pm.isNull() )
+ pm_hot = object->pixmapHotSpot();
+ }
+ if ( pm.isNull() ) {
+ if ( !defaultPm )
+ defaultPm = new QPixmap(default_pm);
+ pm = *defaultPm;
+ }
+ qt_xdnd_deco->setPixmap(pm, pm_hot);
+ qt_xdnd_deco->move(cursorPos-pm_hot);
+ //if ( willDrop ) {
+ qt_xdnd_deco->show();
+ //} else {
+ // qt_xdnd_deco->hide();
+ //}
+ }
+}
+
+void QDragManager::updatePixmap()
+{
+ updatePixmap( QCursor::pos());
+}
+
+#endif // QT_NO_DRAGANDDROP