|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** 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
|