Refactor the QPA dnd interface.

- Give QPlatformDrag a synchronous drag() function returning
  the Qt::DropAction
- Move the base functionality for asynchronous event handling
  to the platformsupport library as QBasicDrag (extendable base class
  handling drag icon and providing new virtuals)  and QSimpleDrag
  (sample implementation for drag within the Qt application).
- Change the Windows implementation accordingly.
- Change XCB to be based on QBasicDrag.
- Clean up QDragManager.

Change-Id: I654f76f0e55a385ba189bd74f3ceaded6a8fe318
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
This commit is contained in:
Friedemann Kleint 2012-02-22 10:58:28 +01:00 committed by Qt by Nokia
parent 70784b9069
commit 3a72a1c7ed
25 changed files with 1170 additions and 1086 deletions

View File

@ -66,6 +66,7 @@ SOURCES += \
kernel/qwindowsysteminterface_qpa.cpp \
kernel/qplatforminputcontext_qpa.cpp \
kernel/qplatformintegration_qpa.cpp \
kernel/qplatformdrag_qpa.cpp \
kernel/qplatformscreen_qpa.cpp \
kernel/qplatformintegrationfactory_qpa.cpp \
kernel/qplatformintegrationplugin_qpa.cpp \

View File

@ -125,417 +125,79 @@ QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
#endif
// the universe's only drag manager
QDragManager *QDragManager::instance = 0;
QDragManager *QDragManager::m_instance = 0;
QDragManager::QDragManager()
: QObject(qApp)
: QObject(qApp), m_platformDropData(0), m_currentDropTarget(0),
m_platformDrag(QGuiApplicationPrivate::platformIntegration()->drag()),
m_object(0)
{
Q_ASSERT(!instance);
Q_ASSERT(!m_instance);
object = 0;
beingCancelled = false;
restoreCursor = false;
willDrop = false;
eventLoop = 0;
currentDropTarget = 0;
shapedPixmapWindow = 0;
possible_actions = Qt::IgnoreAction;
QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
platformDrag = pi->drag();
platformDropData = 0;
if (platformDrag)
platformDropData = platformDrag->platformDropData();
if (m_platformDrag)
m_platformDropData = m_platformDrag->platformDropData();
}
QDragManager::~QDragManager()
{
#ifndef QT_NO_CURSOR
if (restoreCursor)
QGuiApplication::restoreOverrideCursor();
#endif
instance = 0;
m_instance = 0;
}
QDragManager *QDragManager::self()
{
if (!instance && !QGuiApplication::closingDown())
instance = new QDragManager;
return instance;
if (!m_instance && !QGuiApplication::closingDown())
m_instance = new QDragManager;
return m_instance;
}
QPixmap QDragManager::dragCursor(Qt::DropAction action) const
QObject *QDragManager::source() const
{
typedef QMap<Qt::DropAction, QPixmap>::const_iterator Iterator;
if (const QDragPrivate *d = dragPrivate()) {
const Iterator it = d->customCursors.constFind(action);
if (it != d->customCursors.constEnd())
return it.value();
}
Qt::CursorShape shape = Qt::ForbiddenCursor;
switch (action) {
case Qt::MoveAction:
shape = Qt::DragMoveCursor;
break;
case Qt::CopyAction:
shape = Qt::DragCopyCursor;
break;
case Qt::LinkAction:
shape = Qt::DragLinkCursor;
break;
default:
shape = Qt::ForbiddenCursor;
}
return QGuiApplicationPrivate::instance()->getPixmapCursor(shape);
}
Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
Qt::KeyboardModifiers modifiers) const
{
#ifdef QDND_DEBUG
qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1());
#endif
QDragPrivate *d = dragPrivate();
Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
if (defaultAction == Qt::IgnoreAction) {
//This means that the drag was initiated by QDrag::start and we need to
//preserve the old behavior
defaultAction = Qt::CopyAction;
}
if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
defaultAction = Qt::LinkAction;
else if (modifiers & Qt::ControlModifier)
defaultAction = Qt::CopyAction;
else if (modifiers & Qt::ShiftModifier)
defaultAction = Qt::MoveAction;
else if (modifiers & Qt::AltModifier)
defaultAction = Qt::LinkAction;
#ifdef QDND_DEBUG
qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
#endif
// Check if the action determined is allowed
if (!(possibleActions & defaultAction)) {
if (possibleActions & Qt::CopyAction)
defaultAction = Qt::CopyAction;
else if (possibleActions & Qt::MoveAction)
defaultAction = Qt::MoveAction;
else if (possibleActions & Qt::LinkAction)
defaultAction = Qt::LinkAction;
else
defaultAction = Qt::IgnoreAction;
}
#ifdef QDND_DEBUG
qDebug("default action : %s", dragActionsToString(defaultAction).latin1());
#endif
return defaultAction;
if (m_object)
return m_object->source();
return 0;
}
void QDragManager::setCurrentTarget(QObject *target, bool dropped)
{
if (currentDropTarget == target)
if (m_currentDropTarget == target)
return;
currentDropTarget = target;
if (!dropped && object) {
object->d_func()->target = target;
emit object->targetChanged(target);
}
}
QObject *QDragManager::currentTarget()
{
return currentDropTarget;
}
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",
};
QShapedPixmapWindow::QShapedPixmapWindow()
: QWindow()
{
setSurfaceType(RasterSurface);
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
Qt::X11BypassWindowManagerHint | Qt::WindowTransparentForInput);
create();
backingStore = new QBackingStore(this);
}
void QShapedPixmapWindow::render()
{
QRect rect(QPoint(), geometry().size());
backingStore->resize(rect.size());
backingStore->beginPaint(rect);
QPaintDevice *device = backingStore->paintDevice();
{
QPainter p(device);
p.drawPixmap(0, 0, pixmap);
}
backingStore->endPaint();
backingStore->flush(rect);
}
static Qt::KeyboardModifiers oldstate;
void QDragManager::updatePixmap()
{
if (shapedPixmapWindow) {
shapedPixmapWindow->pixmap = QPixmap();
shapedPixmapWindow->hotSpot = QPoint(default_pm_hotx,default_pm_hoty);
if (object) {
shapedPixmapWindow->pixmap = object->pixmap();
if (!shapedPixmapWindow->pixmap.isNull())
shapedPixmapWindow->hotSpot = object->hotSpot();
}
if (shapedPixmapWindow->pixmap.isNull())
shapedPixmapWindow->pixmap = QPixmap(default_pm);
shapedPixmapWindow->setGeometry(QRect(QCursor::pos() - shapedPixmapWindow->hotSpot, shapedPixmapWindow->pixmap.size()));
shapedPixmapWindow->show();
shapedPixmapWindow->render();
m_currentDropTarget = target;
if (!dropped && m_object) {
m_object->d_func()->target = target;
emit m_object->targetChanged(target);
}
}
void QDragManager::updateCursor()
QObject *QDragManager::currentTarget() const
{
if (shapedPixmapWindow) {
shapedPixmapWindow->render(); // ### Hack
shapedPixmapWindow->move(QCursor::pos() - shapedPixmapWindow->hotSpot);
}
Qt::CursorShape cursorShape = Qt::ForbiddenCursor;
if (willDrop) {
if (global_accepted_action == Qt::CopyAction) {
cursorShape = Qt::DragCopyCursor;
} else if (global_accepted_action == Qt::LinkAction) {
cursorShape = Qt::DragLinkCursor;
} else {
cursorShape = Qt::DragMoveCursor;
}
}
QCursor *cursor = qApp->overrideCursor();
if (cursor && cursorShape != cursor->shape())
qApp->changeOverrideCursor(QCursor(cursorShape));
}
bool QDragManager::eventFilter(QObject *o, QEvent *e)
{
if (beingCancelled) {
if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
qApp->removeEventFilter(this);
Q_ASSERT(object == 0);
beingCancelled = false;
eventLoop->exit();
return true; // block the key release
}
return false;
}
Q_ASSERT(object != 0);
if (!qobject_cast<QWindow *>(o))
return false;
switch(e->type()) {
case QEvent::ShortcutOverride:
// prevent accelerators from firing while dragging
e->accept();
return true;
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
cancel();
qApp->removeEventFilter(this);
beingCancelled = false;
eventLoop->exit();
} else {
// ### x11 forces move!
updateCursor();
}
return true; // Eat all key events
}
case QEvent::MouseMove:
move(static_cast<QMouseEvent *>(e));
return true; // Eat all mouse events
case QEvent::MouseButtonRelease:
qApp->removeEventFilter(this);
if (willDrop)
drop(static_cast<QMouseEvent *>(e));
else
cancel();
beingCancelled = false;
eventLoop->exit();
return true; // Eat all mouse events
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
case QEvent::Wheel:
return true;
default:
break;
}
return false;
return m_currentDropTarget;
}
Qt::DropAction QDragManager::drag(QDrag *o)
{
if (!o || object == o)
if (!o || m_object == o)
return Qt::IgnoreAction;
if (!platformDrag || !o->source()) {
if (!m_platformDrag || !o->source()) {
o->deleteLater();
return Qt::IgnoreAction;
}
if (object) {
cancel();
qApp->removeEventFilter(this);
beingCancelled = false;
if (m_object) {
qWarning("QDragManager::drag in possibly invalid state");
return Qt::IgnoreAction;
}
object = o;
if (!shapedPixmapWindow)
shapedPixmapWindow = new QShapedPixmapWindow();
oldstate = Qt::NoModifier; // #### Should use state that caused the drag
// drag_mode = mode;
m_object = o;
possible_actions = dragPrivate()->possible_actions;
m_object->d_func()->target = 0;
willDrop = false;
object->d_func()->target = 0;
qApp->installEventFilter(this);
global_accepted_action = Qt::CopyAction;
#ifndef QT_NO_CURSOR
qApp->setOverrideCursor(Qt::DragCopyCursor);
restoreCursor = true;
updateCursor();
#endif
updatePixmap();
platformDrag->startDrag();
eventLoop = new QEventLoop;
(void) eventLoop->exec();
delete eventLoop;
eventLoop = 0;
delete shapedPixmapWindow;
shapedPixmapWindow = 0;
return global_accepted_action;
}
void QDragManager::move(const QMouseEvent *me)
{
if (!platformDrag)
return;
platformDrag->move(me);
}
void QDragManager::drop(const QMouseEvent *me)
{
if (!platformDrag)
return;
#ifndef QT_NO_CURSOR
if (restoreCursor) {
QGuiApplication::restoreOverrideCursor();
restoreCursor = false;
}
#endif
willDrop = false;
platformDrag->drop(me);
if (object)
object->deleteLater();
object = 0;
}
void QDragManager::cancel(bool deleteSource)
{
if (!platformDrag)
return;
#ifndef QT_NO_CURSOR
if (restoreCursor) {
QGuiApplication::restoreOverrideCursor();
restoreCursor = false;
}
#endif
beingCancelled = true;
platformDrag->cancel();
if (object && deleteSource)
object->deleteLater();
object = 0;
global_accepted_action = Qt::IgnoreAction;
}
/*!
Called from startDrag() in QPlatformDrag implementations that do not need
the desktop-oriented stuff provided by the event filter (e.g. because their
drag is not based on mouse events). Instead, they will manage everything on
their own, will not rely on move/drop/cancel, and will call stopDrag() to stop
the event loop when the drag is over.
*/
void QDragManager::unmanageEvents()
{
qApp->removeEventFilter(this);
}
void QDragManager::stopDrag()
{
if (eventLoop)
eventLoop->exit();
const Qt::DropAction result = m_platformDrag->drag(m_object);
m_object = 0;
return result;
}
#endif // QT_NO_DRAGANDDROP

View File

@ -98,100 +98,45 @@ protected:
class QDragPrivate : public QObjectPrivate
{
public:
QDragPrivate()
: source(0)
, target(0)
, data(0)
{ }
QObject *source;
QObject *target;
QMimeData *data;
QPixmap pixmap;
QPoint hotspot;
Qt::DropActions possible_actions;
Qt::DropAction executed_action;
Qt::DropActions supported_actions;
Qt::DropAction default_action;
QMap<Qt::DropAction, QPixmap> customCursors;
Qt::DropAction defaultDropAction;
};
class QShapedPixmapWindow : public QWindow
{
public:
QShapedPixmapWindow();
void exposeEvent(QExposeEvent *)
{
render();
}
void render();
QBackingStore *backingStore;
QPixmap pixmap;
QPoint hotSpot;
};
class Q_GUI_EXPORT QDragManager : public QObject {
Q_OBJECT
// only friend classes can use QDragManager.
friend class QDrag;
friend class QDragMoveEvent;
friend class QDropEvent;
friend class QApplication;
bool eventFilter(QObject *, QEvent *);
public:
QDragManager();
~QDragManager();
static QDragManager *self();
virtual Qt::DropAction drag(QDrag *);
virtual void cancel(bool deleteSource = true);
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
void updatePixmap();
void updateCursor();
Qt::DropAction defaultAction(Qt::DropActions possibleActions,
Qt::KeyboardModifiers modifiers) const;
QPixmap dragCursor(Qt::DropAction action) const;
QDragPrivate *dragPrivate() const { return object ? object->d_func() : 0; }
inline QMimeData *dropData()
{ return object ? dragPrivate()->data : platformDropData; }
void emitActionChanged(Qt::DropAction newAction) { if (object) emit object->actionChanged(newAction); }
Qt::DropAction drag(QDrag *);
void setCurrentTarget(QObject *target, bool dropped = false);
QObject *currentTarget();
QObject *currentTarget() const;
QDrag *object;
bool beingCancelled;
bool restoreCursor;
bool willDrop;
QEventLoop *eventLoop;
Qt::DropActions possible_actions;
// Shift/Ctrl handling, and final drop status
Qt::DropAction global_accepted_action;
QShapedPixmapWindow *shapedPixmapWindow;
void unmanageEvents();
void stopDrag();
QDrag *object() const { return m_object; }
QObject *source() const;
private:
QMimeData *platformDropData;
QMimeData *m_platformDropData;
QObject *m_currentDropTarget;
QPlatformDrag *m_platformDrag;
QDrag *m_object;
Qt::DropAction currentActionForOverrideCursor;
QObject *currentDropTarget;
QPlatformDrag *platformDrag;
static QDragManager *instance;
static QDragManager *m_instance;
Q_DISABLE_COPY(QDragManager)
};

View File

@ -40,6 +40,7 @@
****************************************************************************/
#include <qdrag.h>
#include "private/qguiapplication_p.h"
#include <qpixmap.h>
#include <qpoint.h>
#include "qdnd_p.h"
@ -114,9 +115,9 @@ QDrag::QDrag(QObject *dragSource)
d->target = 0;
d->data = 0;
d->hotspot = QPoint(-10, -10);
d->possible_actions = Qt::CopyAction;
d->executed_action = Qt::IgnoreAction;
d->defaultDropAction = Qt::IgnoreAction;
d->supported_actions = Qt::IgnoreAction;
d->default_action = Qt::IgnoreAction;
}
/*!
@ -126,9 +127,6 @@ QDrag::~QDrag()
{
Q_D(QDrag);
delete d->data;
QDragManager *manager = QDragManager::self();
if (manager && manager->object == this)
manager->cancel(false);
}
/*!
@ -264,24 +262,22 @@ Qt::DropAction QDrag::exec(Qt::DropActions supportedActions, Qt::DropAction defa
qWarning("QDrag: No mimedata set before starting the drag");
return d->executed_action;
}
QDragManager *manager = QDragManager::self();
d->defaultDropAction = Qt::IgnoreAction;
d->possible_actions = supportedActions;
Qt::DropAction transformedDefaultDropAction = Qt::IgnoreAction;
if (manager) {
if (defaultDropAction == Qt::IgnoreAction) {
if (supportedActions & Qt::MoveAction) {
d->defaultDropAction = Qt::MoveAction;
} else if (supportedActions & Qt::CopyAction) {
d->defaultDropAction = Qt::CopyAction;
} else if (supportedActions & Qt::LinkAction) {
d->defaultDropAction = Qt::LinkAction;
}
} else {
d->defaultDropAction = defaultDropAction;
if (defaultDropAction == Qt::IgnoreAction) {
if (supportedActions & Qt::MoveAction) {
transformedDefaultDropAction = Qt::MoveAction;
} else if (supportedActions & Qt::CopyAction) {
transformedDefaultDropAction = Qt::CopyAction;
} else if (supportedActions & Qt::LinkAction) {
transformedDefaultDropAction = Qt::LinkAction;
}
d->executed_action = manager->drag(this);
} else {
transformedDefaultDropAction = defaultDropAction;
}
d->supported_actions = supportedActions;
d->default_action = transformedDefaultDropAction;
d->executed_action = QDragManager::self()->drag(this);
return d->executed_action;
}
@ -308,11 +304,9 @@ Qt::DropAction QDrag::start(Qt::DropActions request)
qWarning("QDrag: No mimedata set before starting the drag");
return d->executed_action;
}
QDragManager *manager = QDragManager::self();
d->defaultDropAction = Qt::IgnoreAction;
d->possible_actions = request | Qt::CopyAction;
if (manager)
d->executed_action = manager->drag(this);
d->supported_actions = request | Qt::CopyAction;
d->default_action = Qt::IgnoreAction;
d->executed_action = QDragManager::self()->drag(this);
return d->executed_action;
}
@ -335,6 +329,49 @@ void QDrag::setDragCursor(const QPixmap &cursor, Qt::DropAction action)
d->customCursors[action] = cursor;
}
/*!
Returns the drag cursor for the \a action.
\since 5.0
*/
QPixmap QDrag::dragCursor(Qt::DropAction action) const
{
typedef QMap<Qt::DropAction, QPixmap>::const_iterator Iterator;
Q_D(const QDrag);
const Iterator it = d->customCursors.constFind(action);
if (it != d->customCursors.constEnd())
return it.value();
Qt::CursorShape shape = Qt::ForbiddenCursor;
switch (action) {
case Qt::MoveAction:
shape = Qt::DragMoveCursor;
break;
case Qt::CopyAction:
shape = Qt::DragCopyCursor;
break;
case Qt::LinkAction:
shape = Qt::DragLinkCursor;
break;
default:
shape = Qt::ForbiddenCursor;
}
return QGuiApplicationPrivate::instance()->getPixmapCursor(shape);
}
Qt::DropActions QDrag::supportedActions() const
{
Q_D(const QDrag);
return d->supported_actions;
}
Qt::DropAction QDrag::defaultAction() const
{
Q_D(const QDrag);
return d->default_action;
}
/*!
\fn void QDrag::actionChanged(Qt::DropAction action)

View File

@ -56,6 +56,7 @@ class QPixmap;
class QPoint;
class QDragManager;
class Q_GUI_EXPORT QDrag : public QObject
{
Q_OBJECT
@ -81,6 +82,10 @@ public:
Qt::DropAction exec(Qt::DropActions supportedActions, Qt::DropAction defaultAction);
void setDragCursor(const QPixmap &cursor, Qt::DropAction action);
QPixmap dragCursor(Qt::DropAction action) const;
Qt::DropActions supportedActions() const;
Qt::DropAction defaultAction() const;
Q_SIGNALS:
void actionChanged(Qt::DropAction action);

View File

@ -2295,8 +2295,9 @@ QDropEvent::~QDropEvent()
*/
QObject* QDropEvent::source() const
{
QDragManager *manager = QDragManager::self();
return (manager && manager->object) ? manager->object->source() : 0;
if (const QDragManager *manager = QDragManager::self())
return manager->source();
return 0;
}

View File

@ -73,8 +73,11 @@
#include "private/qwindowsysteminterface_qpa_p.h"
#include "private/qwindow_p.h"
#include "private/qcursor_p.h"
#include "private/qdnd_p.h"
#include <private/qplatformthemefactory_qpa_p.h>
#include "qplatformdrag_qpa.h"
#ifndef QT_NO_CURSOR
#include "qplatformcursor_qpa.h"
#endif
@ -326,8 +329,7 @@ QGuiApplication::QGuiApplication(int &argc, char **argv, int flags)
QGuiApplication::QGuiApplication(QGuiApplicationPrivate &p)
: QCoreApplication(p)
{
d_func()->init();
}
d_func()->init(); }
/*!
Destructs the application.
@ -1547,49 +1549,62 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E
QCoreApplication::sendSpontaneousEvent(window, &exposeEvent);
}
Qt::DropAction QGuiApplicationPrivate::processDrag(QWindow *w, QMimeData *dropData, const QPoint &p)
QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
{
static QPointer<QWindow> currentDragWindow;
QDragManager *manager = QDragManager::self();
static Qt::DropAction lastAcceptedDropAction = Qt::IgnoreAction;
QPlatformDrag *platformDrag = platformIntegration()->drag();
if (!platformDrag) {
lastAcceptedDropAction = Qt::IgnoreAction;
return QPlatformDragQtResponse(false, lastAcceptedDropAction, QRect());
}
if (!dropData) {
if (currentDragWindow.data() == w)
currentDragWindow = 0;
QDragLeaveEvent e;
QGuiApplication::sendEvent(w, &e);
manager->global_accepted_action = Qt::IgnoreAction;
return Qt::IgnoreAction;
lastAcceptedDropAction = Qt::IgnoreAction;
return QPlatformDragQtResponse(false, lastAcceptedDropAction, QRect());
}
QDragMoveEvent me(p, manager->possible_actions, dropData,
QDragMoveEvent me(p, supportedActions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
if (w != currentDragWindow) {
lastAcceptedDropAction = Qt::IgnoreAction;
if (currentDragWindow) {
QDragLeaveEvent e;
QGuiApplication::sendEvent(currentDragWindow, &e);
manager->global_accepted_action = Qt::IgnoreAction;
}
currentDragWindow = w;
QDragEnterEvent e(p, manager->possible_actions, dropData,
QDragEnterEvent e(p, supportedActions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(w, &e);
manager->global_accepted_action = e.isAccepted() ? e.dropAction() : Qt::IgnoreAction;
if (manager->global_accepted_action != Qt::IgnoreAction) {
me.setDropAction(manager->global_accepted_action);
me.accept();
}
if (e.isAccepted() && e.dropAction() != Qt::IgnoreAction)
lastAcceptedDropAction = e.dropAction();
}
// Handling 'DragEnter' should suffice for the application.
if (lastAcceptedDropAction != Qt::IgnoreAction
&& (supportedActions & lastAcceptedDropAction)) {
me.setDropAction(lastAcceptedDropAction);
me.accept();
}
QGuiApplication::sendEvent(w, &me);
manager->global_accepted_action = me.isAccepted() ? me.dropAction() : Qt::IgnoreAction;
return manager->global_accepted_action;
lastAcceptedDropAction = me.isAccepted() ?
me.dropAction() : Qt::IgnoreAction;
return QPlatformDragQtResponse(me.isAccepted(), lastAcceptedDropAction, me.answerRect());
}
Qt::DropAction QGuiApplicationPrivate::processDrop(QWindow *w, QMimeData *dropData, const QPoint &p)
QPlatformDropQtResponse QGuiApplicationPrivate::processDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
{
QDragManager *manager = QDragManager::self();
QDropEvent de(p, manager->possible_actions, dropData,
QDropEvent de(p, supportedActions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(w, &de);
manager->global_accepted_action = de.isAccepted() ? de.dropAction() : Qt::IgnoreAction;
return manager->global_accepted_action;
Qt::DropAction acceptedAction = de.isAccepted() ? de.dropAction() : Qt::IgnoreAction;
QPlatformDropQtResponse response(de.isAccepted(),acceptedAction);
return response;
}
#ifndef QT_NO_CLIPBOARD

View File

@ -53,6 +53,8 @@
#include "private/qwindowsysteminterface_qpa_p.h"
#include "private/qshortcutmap_p.h"
#include "qplatformdrag_qpa.h"
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
@ -121,8 +123,8 @@ public:
static void processExposeEvent(QWindowSystemInterfacePrivate::ExposeEvent *e);
static Qt::DropAction processDrag(QWindow *w, QMimeData *dropData, const QPoint &p);
static Qt::DropAction processDrop(QWindow *w, QMimeData *dropData, const QPoint &p);
static QPlatformDragQtResponse processDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
static QPlatformDropQtResponse processDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
static bool processNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);

View File

@ -0,0 +1,187 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qplatformdrag_qpa.h"
#include <QtGui/private/qdnd_p.h>
#include <QtGui/QKeyEvent>
#include <QtGui/QGuiApplication>
#include <QtCore/QEventLoop>
QT_BEGIN_NAMESPACE
QPlatformDropQtResponse::QPlatformDropQtResponse(bool accepted, Qt::DropAction acceptedAction)
: m_accepted(accepted)
, m_accepted_action(acceptedAction)
{
}
bool QPlatformDropQtResponse::isAccepted() const
{
return m_accepted;
}
Qt::DropAction QPlatformDropQtResponse::acceptedAction() const
{
return m_accepted_action;
}
QPlatformDragQtResponse::QPlatformDragQtResponse(bool accepted, Qt::DropAction acceptedAction, QRect answerRect)
: QPlatformDropQtResponse(accepted,acceptedAction)
, m_answer_rect(answerRect)
{
}
QRect QPlatformDragQtResponse::answerRect() const
{
return m_answer_rect;
}
class QPlatformDragPrivate {
public:
QPlatformDragPrivate() : cursor_drop_action(Qt::IgnoreAction) {}
Qt::DropAction cursor_drop_action;
};
QPlatformDrag::QPlatformDrag() : d_ptr(new QPlatformDragPrivate)
{
}
QPlatformDrag::~QPlatformDrag()
{
}
QDrag *QPlatformDrag::currentDrag() const
{
return QDragManager::self()->object();
}
Qt::DropAction QPlatformDrag::defaultAction(Qt::DropActions possibleActions,
Qt::KeyboardModifiers modifiers) const
{
#ifdef QDND_DEBUG
qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1());
#endif
Qt::DropAction default_action = Qt::IgnoreAction;
if (currentDrag()) {
default_action = currentDrag()->defaultAction();
}
if (default_action == Qt::IgnoreAction) {
//This means that the drag was initiated by QDrag::start and we need to
//preserve the old behavior
default_action = Qt::CopyAction;
}
if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
default_action = Qt::LinkAction;
else if (modifiers & Qt::ControlModifier)
default_action = Qt::CopyAction;
else if (modifiers & Qt::ShiftModifier)
default_action = Qt::MoveAction;
else if (modifiers & Qt::AltModifier)
default_action = Qt::LinkAction;
#ifdef QDND_DEBUG
qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
#endif
// Check if the action determined is allowed
if (!(possibleActions & default_action)) {
if (possibleActions & Qt::CopyAction)
default_action = Qt::CopyAction;
else if (possibleActions & Qt::MoveAction)
default_action = Qt::MoveAction;
else if (possibleActions & Qt::LinkAction)
default_action = Qt::LinkAction;
else
default_action = Qt::IgnoreAction;
}
#ifdef QDND_DEBUG
qDebug("default action : %s", dragActionsToString(defaultAction).latin1());
#endif
return default_action;
}
/*!
\brief Called to notify QDrag about changes of the current action.
*/
void QPlatformDrag::updateAction(Qt::DropAction action)
{
Q_D(QPlatformDrag);
if (d->cursor_drop_action != action) {
d->cursor_drop_action = action;
emit currentDrag()->actionChanged(action);
}
}
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",
};
Q_GLOBAL_STATIC_WITH_ARGS(QPixmap,qt_drag_default_pixmap,(default_pm))
QPixmap QPlatformDrag::defaultPixmap()
{
return *qt_drag_default_pixmap();
}
QT_END_NAMESPACE

View File

@ -43,6 +43,7 @@
#define QPLATFORMDRAG_H
#include <QtCore/qglobal.h>
#include <QtGui/QPixmap>
QT_BEGIN_HEADER
@ -51,18 +52,54 @@ QT_BEGIN_NAMESPACE
class QMimeData;
class QMouseEvent;
class QDrag;
class QObject;
class QEvent;
class QPlatformDragPrivate;
class QPlatformDrag
class Q_GUI_EXPORT QPlatformDropQtResponse
{
public:
virtual ~QPlatformDrag() {}
QPlatformDropQtResponse(bool accepted, Qt::DropAction acceptedAction);
bool isAccepted() const;
Qt::DropAction acceptedAction() const;
private:
bool m_accepted;
Qt::DropAction m_accepted_action;
};
class Q_GUI_EXPORT QPlatformDragQtResponse : public QPlatformDropQtResponse
{
public:
QPlatformDragQtResponse(bool accepted, Qt::DropAction acceptedAction, QRect answerRect);
QRect answerRect() const;
private:
QRect m_answer_rect;
};
class Q_GUI_EXPORT QPlatformDrag
{
Q_DECLARE_PRIVATE(QPlatformDrag)
public:
QPlatformDrag();
virtual ~QPlatformDrag();
QDrag *currentDrag() const;
virtual QMimeData *platformDropData() = 0;
virtual void startDrag() {}
virtual void move(const QMouseEvent *me) = 0;
virtual void drop(const QMouseEvent *me) = 0;
virtual void cancel() = 0;
virtual Qt::DropAction drag(QDrag *m_drag) = 0;
void updateAction(Qt::DropAction action);
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const;
static QPixmap defaultPixmap();
private:
QPlatformDragPrivate *d_ptr;
};
QT_END_NAMESPACE

View File

@ -43,6 +43,7 @@
#include "private/qguiapplication_p.h"
#include "private/qtouchdevice_p.h"
#include <QAbstractEventDispatcher>
#include <QPlatformDrag>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@ -447,14 +448,14 @@ int QWindowSystemInterface::windowSystemEventsQueued()
return QWindowSystemInterfacePrivate::windowSystemEventsQueued();
}
Qt::DropAction QWindowSystemInterface::handleDrag(QWindow *w, QMimeData *dropData, const QPoint &p)
QPlatformDragQtResponse QWindowSystemInterface::handleDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
{
return QGuiApplicationPrivate::processDrag(w, dropData, p);
return QGuiApplicationPrivate::processDrag(w, dropData, p,supportedActions);
}
Qt::DropAction QWindowSystemInterface::handleDrop(QWindow *w, QMimeData *dropData, const QPoint &p)
QPlatformDropQtResponse QWindowSystemInterface::handleDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions)
{
return QGuiApplicationPrivate::processDrop(w, dropData, p);
return QGuiApplicationPrivate::processDrop(w, dropData, p,supportedActions);
}
/*!

View File

@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE
class QMimeData;
class QTouchDevice;
class QPlatformDragQtResponse;
class QPlatformDropQtResponse;
class Q_GUI_EXPORT QWindowSystemInterface
@ -122,8 +124,8 @@ public:
static void handleSynchronousExposeEvent(QWindow *tlw, const QRegion &region);
// Drag and drop. These events are sent immediately.
static Qt::DropAction handleDrag(QWindow *w, QMimeData *dropData, const QPoint &p);
static Qt::DropAction handleDrop(QWindow *w, QMimeData *dropData, const QPoint &p);
static QPlatformDragQtResponse handleDrag(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
static QPlatformDropQtResponse handleDrop(QWindow *w, const QMimeData *dropData, const QPoint &p, Qt::DropActions supportedActions);
static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);

View File

@ -1,4 +1,6 @@
HEADERS += \
$$PWD/qsimpledrag_p.h
$$PWD/qsimpledrag_p.h \
$$PWD/qshapedpixmapdndwindow_p.h
SOURCES += \
$$PWD/qsimpledrag.cpp
$$PWD/qsimpledrag.cpp \
$$PWD/qshapedpixmapdndwindow.cpp

View File

@ -0,0 +1,100 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qshapedpixmapdndwindow_p.h"
#include <QtGui/QPainter>
#include <QtGui/QCursor>
QT_BEGIN_NAMESPACE
QShapedPixmapWindow::QShapedPixmapWindow()
: QWindow(),
m_backingStore(0)
{
setSurfaceType(RasterSurface);
setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint |
Qt::X11BypassWindowManagerHint | Qt::WindowTransparentForInput);
create();
m_backingStore = new QBackingStore(this);
}
void QShapedPixmapWindow::render()
{
QRect rect(QPoint(), geometry().size());
m_backingStore->beginPaint(rect);
QPaintDevice *device = m_backingStore->paintDevice();
{
QPainter p(device);
p.drawPixmap(0, 0, m_pixmap);
}
m_backingStore->endPaint();
m_backingStore->flush(rect);
}
void QShapedPixmapWindow::setPixmap(const QPixmap &pixmap)
{
m_pixmap = pixmap;
}
void QShapedPixmapWindow::setHotspot(const QPoint &hotspot)
{
m_hotSpot = hotspot;
}
void QShapedPixmapWindow::updateGeometry()
{
QRect rect(QCursor::pos() - m_hotSpot, m_pixmap.size());
if (m_backingStore->size() != m_pixmap.size())
m_backingStore->resize(m_pixmap.size());
setGeometry(rect);
}
void QShapedPixmapWindow::exposeEvent(QExposeEvent *)
{
render();
}
QT_END_NAMESPACE

View File

@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSHAPEDPIXMAPDNDWINDOW_H
#define QSHAPEDPIXMAPDNDWINDOW_H
#include <QtGui/QWindow>
#include <QtGui/QPixmap>
#include <QtGui/QBackingStore>
QT_BEGIN_NAMESPACE
QT_BEGIN_HEADER
class QShapedPixmapWindow : public QWindow
{
public:
QShapedPixmapWindow();
void render();
void setPixmap(const QPixmap &pixmap);
void setHotspot(const QPoint &hotspot);
void updateGeometry();
protected:
void exposeEvent(QExposeEvent *);
private:
QBackingStore *m_backingStore;
QPixmap m_pixmap;
QPoint m_hotSpot;
};
QT_END_HEADER
QT_END_NAMESPACE
#endif // QSHAPEDPIXMAPDNDWINDOW_H

View File

@ -56,147 +56,283 @@
#include "qimagereader.h"
#include "qimagewriter.h"
#include <QtCore/QEventLoop>
#include <QtCore/QDebug>
#include <private/qguiapplication_p.h>
#include <private/qdnd_p.h>
#include <QtPlatformSupport/private/qshapedpixmapdndwindow_p.h>
QT_BEGIN_NAMESPACE
class QDropData : public QInternalMimeData
{
public:
QDropData();
~QDropData();
/*!
\class QBasicDrag
\brief QBasicDrag is a base class for implementing platform drag and drop.
\since 5.0
\internal
\ingroup qpa
protected:
bool hasFormat_sys(const QString &mimeType) const;
QStringList formats_sys() const;
QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const;
};
QBasicDrag implements QPlatformDrag::drag() by running a local event loop in which
it tracks mouse movements and moves the drag icon (QShapedPixmapWindow) accordingly.
It provides new virtuals allowing for querying whether the receiving window
(within the Qt application or outside) accepts the drag and sets the state accordingly.
*/
QSimpleDrag::QSimpleDrag()
QBasicDrag::QBasicDrag() :
m_restoreCursor(false), m_eventLoop(0),
m_executed_drop_action(Qt::IgnoreAction), m_can_drop(false),
m_drag(0), m_drag_icon_window(0), m_cursor_drop_action(Qt::IgnoreAction)
{
m_dropData = new QDropData();
currentWindow = 0;
}
QSimpleDrag::~QSimpleDrag()
QBasicDrag::~QBasicDrag()
{
delete m_drag_icon_window;
}
void QBasicDrag::enableEventFilter()
{
qApp->installEventFilter(this);
}
void QBasicDrag::disableEventFilter()
{
qApp->removeEventFilter(this);
}
bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
{
if (!m_drag) {
if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
disableEventFilter();
exitDndEventLoop();
return true; // block the key release
}
return false;
}
if (!qobject_cast<QWindow *>(o))
return false;
switch (e->type()) {
case QEvent::ShortcutOverride:
// prevent accelerators from firing while dragging
e->accept();
return true;
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
cancel();
resetDndState(true);
disableEventFilter();
exitDndEventLoop();
}
return true; // Eat all key events
}
case QEvent::MouseMove:
move(static_cast<QMouseEvent *>(e));
return true; // Eat all mouse events
case QEvent::MouseButtonRelease:
disableEventFilter();
if (canDrop()) {
drop(static_cast<QMouseEvent *>(e));
resetDndState(false);
} else {
cancel();
resetDndState(true);
}
exitDndEventLoop();
return true; // Eat all mouse events
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
case QEvent::Wheel:
return true;
default:
break;
}
return false;
}
Qt::DropAction QBasicDrag::drag(QDrag *o)
{
m_drag = o;
m_executed_drop_action = Qt::IgnoreAction;
m_can_drop = false;
m_restoreCursor = true;
#ifndef QT_NO_CURSOR
qApp->setOverrideCursor(Qt::DragCopyCursor);
updateCursor(m_executed_drop_action);
#endif
startDrag();
m_eventLoop = new QEventLoop;
m_eventLoop->exec();
delete m_eventLoop;
m_eventLoop = 0;
m_drag = 0;
endDrag();
return m_executed_drop_action;
}
void QBasicDrag::resetDndState(bool /* deleteSource */)
{
if (m_restoreCursor) {
#ifndef QT_NO_CURSOR
QGuiApplication::restoreOverrideCursor();
#endif
m_restoreCursor = false;
}
}
void QBasicDrag::startDrag()
{
if (!m_drag_icon_window)
m_drag_icon_window = new QShapedPixmapWindow();
m_drag_icon_window->setPixmap(m_drag->pixmap());
m_drag_icon_window->setHotspot(m_drag->hotSpot());
m_drag_icon_window->updateGeometry();
m_drag_icon_window->setVisible(true);
enableEventFilter();
}
void QBasicDrag::endDrag()
{
}
void QBasicDrag::cancel()
{
disableEventFilter();
m_drag_icon_window->setVisible(false);
}
void QBasicDrag::move(const QMouseEvent *)
{
if (m_drag)
m_drag_icon_window->updateGeometry();
}
void QBasicDrag::drop(const QMouseEvent *)
{
disableEventFilter();
m_drag_icon_window->setVisible(false);
}
void QBasicDrag::exitDndEventLoop()
{
if (m_eventLoop && m_eventLoop->isRunning())
m_eventLoop->exit();
}
void QBasicDrag::updateCursor(Qt::DropAction action)
{
Qt::CursorShape cursorShape = Qt::ForbiddenCursor;
if (canDrop()) {
switch (action) {
case Qt::CopyAction:
cursorShape = Qt::DragCopyCursor;
break;
case Qt::LinkAction:
cursorShape = Qt::DragLinkCursor;
break;
default:
cursorShape = Qt::DragMoveCursor;
break;
}
}
QCursor *cursor = qApp->overrideCursor();
if (cursor && cursorShape != cursor->shape()) {
qApp->changeOverrideCursor(QCursor(cursorShape));
}
updateAction(action);
}
/*!
\class QSimpleDrag
\brief QSimpleDrag implements QBasicDrag for Drag and Drop operations within the Qt Application itself.
\since 5.0
\internal
\ingroup qpa
The class checks whether the receiving window is a window of the Qt application
and sets the state accordingly. It does not take windows of other applications
into account.
*/
QSimpleDrag::QSimpleDrag() : m_current_window(0)
{
delete m_dropData;
}
QMimeData *QSimpleDrag::platformDropData()
{
return m_dropData;
if (drag())
return drag()->mimeData();
return 0;
}
void QSimpleDrag::startDrag()
{
QBasicDrag::startDrag();
m_current_window = QGuiApplication::topLevelAt(QCursor::pos());
if (m_current_window) {
QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_current_window, drag()->mimeData(), QCursor::pos(), drag()->supportedActions());
setCanDrop(response.isAccepted());
updateCursor(response.acceptedAction());
} else {
setCanDrop(false);
updateCursor(Qt::IgnoreAction);
}
setExecutedDropAction(Qt::IgnoreAction);
}
void QSimpleDrag::cancel()
{
QDragManager *m = QDragManager::self();
// qDebug("QDragManager::cancel");
if (m->object->target()) {
QDragLeaveEvent dle;
QCoreApplication::sendEvent(m->object->target(), &dle);
}
QBasicDrag::cancel();
if (drag())
QWindowSystemInterface::handleDrag(m_current_window, 0, QPoint(), Qt::IgnoreAction);
m_current_window = 0;
}
void QSimpleDrag::move(const QMouseEvent *me)
{
QBasicDrag::move(me);
QWindow *window = QGuiApplication::topLevelAt(me->globalPos());
QPoint pos;
if (window)
pos = me->globalPos() - window->geometry().topLeft();
if (!window)
return;
QDragManager *m = QDragManager::self();
const QPoint pos = me->globalPos() - window->geometry().topLeft();
const QPlatformDragQtResponse qt_response =
QWindowSystemInterface::handleDrag(window, drag()->mimeData(), pos, drag()->supportedActions());
if (me->buttons()) {
Qt::DropAction prevAction = m->global_accepted_action;
if (currentWindow != window) {
if (currentWindow) {
QDragLeaveEvent dle;
QCoreApplication::sendEvent(currentWindow, &dle);
m->willDrop = false;
m->global_accepted_action = Qt::IgnoreAction;
}
currentWindow = window;
if (currentWindow) {
QDragEnterEvent dee(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers());
QCoreApplication::sendEvent(currentWindow, &dee);
m->willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction;
m->global_accepted_action = m->willDrop ? dee.dropAction() : Qt::IgnoreAction;
}
m->updateCursor();
} else if (window) {
Q_ASSERT(currentWindow);
QDragMoveEvent dme(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers());
if (m->global_accepted_action != Qt::IgnoreAction) {
dme.setDropAction(m->global_accepted_action);
dme.accept();
}
QCoreApplication::sendEvent(currentWindow, &dme);
m->willDrop = dme.isAccepted();
m->global_accepted_action = m->willDrop ? dme.dropAction() : Qt::IgnoreAction;
m->updatePixmap();
m->updateCursor();
}
if (m->global_accepted_action != prevAction)
m->emitActionChanged(m->global_accepted_action);
}
updateCursor(qt_response.acceptedAction());
setCanDrop(qt_response.isAccepted());
}
void QSimpleDrag::drop(const QMouseEvent *me)
{
QDragManager *m = QDragManager::self();
QBasicDrag::drop(me);
QWindow *window = QGuiApplication::topLevelAt(me->globalPos());
if (!window)
return;
if (window) {
QPoint pos = me->globalPos() - window->geometry().topLeft();
QDropEvent de(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers());
QCoreApplication::sendEvent(window, &de);
if (de.isAccepted())
m->global_accepted_action = de.dropAction();
else
m->global_accepted_action = Qt::IgnoreAction;
const QPoint pos = me->globalPos() - window->geometry().topLeft();
const QPlatformDropQtResponse response =
QWindowSystemInterface::handleDrop(window, drag()->mimeData(),pos, drag()->supportedActions());
if (response.isAccepted()) {
setExecutedDropAction(response.acceptedAction());
} else {
setExecutedDropAction(Qt::IgnoreAction);
}
currentWindow = 0;
}
QDropData::QDropData()
: QInternalMimeData()
{
}
QDropData::~QDropData()
{
}
QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const
{
QDrag *object = QDragManager::self()->object;
if (!object)
return QVariant();
QByteArray data = object->mimeData()->data(mimetype);
if (type == QVariant::String)
return QString::fromUtf8(data);
return data;
}
bool QDropData::hasFormat_sys(const QString &format) const
{
return formats().contains(format);
}
QStringList QDropData::formats_sys() const
{
QDrag *object = QDragManager::self()->object;
if (object)
return object->mimeData()->formats();
return QStringList();
}
QT_END_NAMESPACE

View File

@ -44,32 +44,80 @@
#include <qplatformdrag_qpa.h>
#include <QtCore/QObject>
QT_BEGIN_NAMESPACE
QT_BEGIN_HEADER
class QMouseEvent;
class QWindow;
class QEventLoop;
class QDropData;
class QShapedPixmapWindow;
class QSimpleDrag : public QPlatformDrag
class QBasicDrag : public QPlatformDrag, public QObject
{
public:
QSimpleDrag();
~QSimpleDrag();
virtual ~QBasicDrag();
virtual QMimeData *platformDropData();
virtual Qt::DropAction drag(QDrag *drag);
// virtual Qt::DropAction drag(QDrag *);
virtual bool eventFilter(QObject *o, QEvent *e);
protected:
QBasicDrag();
virtual void startDrag();
virtual void cancel();
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
private:
QDropData *m_dropData;
virtual void endDrag();
QWindow *currentWindow;
QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; }
void updateCursor(Qt::DropAction action);
bool canDrop() const { return m_can_drop; }
void setCanDrop(bool c) { m_can_drop = c; }
Qt::DropAction executedDropAction() const { return m_executed_drop_action; }
void setExecutedDropAction(Qt::DropAction da) { m_executed_drop_action = da; }
QDrag *drag() const { return m_drag; }
private:
void enableEventFilter();
void disableEventFilter();
void resetDndState(bool deleteSource);
void exitDndEventLoop();
bool m_restoreCursor;
QEventLoop *m_eventLoop;
Qt::DropAction m_executed_drop_action;
bool m_can_drop;
QDrag *m_drag;
QShapedPixmapWindow *m_drag_icon_window;
Qt::DropAction m_cursor_drop_action;
};
class QSimpleDrag : public QBasicDrag
{
public:
QSimpleDrag();
virtual QMimeData *platformDropData();
protected:
virtual void startDrag();
virtual void cancel();
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
private:
QWindow *m_current_window;
};
QT_END_HEADER
QT_END_NAMESPACE
#endif

View File

@ -53,8 +53,11 @@
#include <QtGui/QPixmap>
#include <QtGui/QPainter>
#include <QtGui/QGuiApplication>
#include <QtGui/private/qwindowsysteminterface_qpa_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtCore/QDebug>
#include <QtCore/QBuffer>
#include <QtCore/QPoint>
#include <shlobj.h>
@ -300,14 +303,13 @@ private:
QWindowsDrag *m_drag;
Qt::MouseButtons m_currentButtons;
Qt::DropAction m_currentAction;
ActionCursorMap m_cursors;
ULONG m_refs;
};
QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag) :
m_drag(drag), m_currentButtons(Qt::NoButton), m_currentAction(Qt::IgnoreAction),
m_drag(drag), m_currentButtons(Qt::NoButton),
m_refs(1)
{
if (QWindowsContext::verboseOLE)
@ -321,24 +323,26 @@ QWindowsOleDropSource::~QWindowsOleDropSource()
qDebug("%s", __FUNCTION__);
}
/*!
\brief Blend custom pixmap with cursors.
*/
void QWindowsOleDropSource::createCursors()
{
QDragManager *manager = QDragManager::self();
if (!manager || !manager->object)
return;
const QPixmap pixmap = manager->object->pixmap();
const QDrag *drag = m_drag->currentDrag();
const QPixmap pixmap = drag->pixmap();
const bool hasPixmap = !pixmap.isNull();
if (!hasPixmap && manager->dragPrivate()->customCursors.isEmpty())
if (!hasPixmap)
return;
QList<Qt::DropAction> actions;
actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction;
if (hasPixmap)
actions << Qt::IgnoreAction;
const QPoint hotSpot = manager->object->hotSpot();
const QPoint hotSpot = drag->hotSpot();
for (int cnum = 0; cnum < actions.size(); ++cnum) {
const Qt::DropAction action = actions.at(cnum);
QPixmap cpm = manager->dragCursor(action);
QPixmap cpm = drag->dragCursor(action);
if (cpm.isNull())
cpm = m_drag->defaultCursor(action);
if (cpm.isNull()) {
@ -361,7 +365,7 @@ void QWindowsOleDropSource::createCursors()
const QPoint newHotSpot = hotSpot;
QPixmap newCursor(w, h);
if (hasPixmap) {
newCursor.fill(QColor(0, 0, 0, 0));
newCursor.fill(Qt::transparent);
QPainter p(&newCursor);
const QRect srcRect = pixmap.rect();
const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
@ -375,7 +379,7 @@ void QWindowsOleDropSource::createCursors()
const int hotY = hasPixmap ? qMax(0,newHotSpot.y()) : 0;
if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY))
m_cursors.insert(action, sysCursor);
m_cursors.insert(actions.at(cnum), sysCursor);
}
if (QWindowsContext::verboseOLE)
qDebug("%s %d cursors", __FUNCTION__, m_cursors.size());
@ -432,7 +436,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
HRESULT hr = S_OK;
do {
if (fEscapePressed || QWindowsDrag::instance()->dragBeingCancelled()) {
if (fEscapePressed) {
hr = ResultFromScode(DRAGDROP_S_CANCEL);
break;
}
@ -461,13 +465,11 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
} while (false);
QDragManager::self()->willDrop = hr == DRAGDROP_S_DROP;
if (QWindowsContext::verboseOLE
&& (QWindowsContext::verboseOLE > 1 || hr != S_OK))
qDebug("%s fEscapePressed=%d, grfKeyState=%lu buttons=%d willDrop = %d returns 0x%x",
qDebug("%s fEscapePressed=%d, grfKeyState=%lu buttons=%d returns 0x%x",
__FUNCTION__, fEscapePressed,grfKeyState, int(m_currentButtons),
QDragManager::self()->willDrop, int(hr));
int(hr));
return hr;
}
@ -479,16 +481,12 @@ QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
QWindowsOleDropSource::GiveFeedback(DWORD dwEffect)
{
const Qt::DropAction action = translateToQDragDropAction(dwEffect);
m_drag->updateAction(action);
if (QWindowsContext::verboseOLE > 2)
qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action);
if (m_currentAction != action) {
m_currentAction = action;
QDragManager::self()->emitActionChanged(m_currentAction);
}
const ActionCursorMap::const_iterator it = m_cursors.constFind(m_currentAction);
const ActionCursorMap::const_iterator it = m_cursors.constFind(action);
if (it != m_cursors.constEnd()) {
SetCursor(it.value());
return ResultFromScode(S_OK);
@ -510,7 +508,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect)
*/
QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) :
m_refs(1), m_window(w), m_currentWindow(0), m_chosenEffect(0), m_lastKeyState(0)
m_refs(1), m_window(w), m_chosenEffect(0), m_lastKeyState(0)
{
if (QWindowsContext::verboseOLE)
qDebug() << __FUNCTION__ << this << w;
@ -558,6 +556,38 @@ QWindow *QWindowsOleDropTarget::findDragOverWindow(const POINTL &pt) const
return m_window;
}
void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
const QPoint &point, LPDWORD pdwEffect)
{
Q_ASSERT(window);
m_lastPoint = point;
m_lastKeyState = grfKeyState;
QWindowsDrag *windowsDrag = QWindowsDrag::instance();
const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
QGuiApplicationPrivate::modifier_buttons = toQtKeyboardModifiers(grfKeyState);
QGuiApplicationPrivate::mouse_buttons = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState);
const QPlatformDragQtResponse response =
QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), m_lastPoint, actions);
m_answerRect = response.answerRect();
const Qt::DropAction action = response.acceptedAction();
if (response.isAccepted()) {
m_chosenEffect = translateToWinDragEffects(action);
} else {
m_chosenEffect = DROPEFFECT_NONE;
}
*pdwEffect = m_chosenEffect;
if (QWindowsContext::verboseOLE)
qDebug() << __FUNCTION__ << m_window
<< windowsDrag->dropData() << " supported actions=" << actions
<< " mods=" << QGuiApplicationPrivate::modifier_buttons
<< " mouse=" << QGuiApplicationPrivate::mouse_buttons
<< " accepted: " << response.isAccepted() << action
<< m_answerRect << " effect" << *pdwEffect;
}
QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
POINTL pt, LPDWORD pdwEffect)
@ -567,124 +597,28 @@ QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
QWindowsDrag::instance()->setDropDataObject(pDataObj);
pDataObj->AddRef();
m_currentWindow = m_window;
sendDragEnterEvent(m_window, grfKeyState, pt, pdwEffect);
*pdwEffect = m_chosenEffect;
const QPoint point = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
handleDrag(m_window, grfKeyState, point, pdwEffect);
return NOERROR;
}
void QWindowsOleDropTarget::sendDragEnterEvent(QWindow *dragEnterWidget,
DWORD grfKeyState,
POINTL pt, LPDWORD pdwEffect)
{
Q_ASSERT(dragEnterWidget);
m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dragEnterWidget, QPoint(pt.x,pt.y));
m_lastKeyState = grfKeyState;
m_chosenEffect = DROPEFFECT_NONE;
QDragManager *manager = QDragManager::self();
QMimeData *md = manager->dropData();
const Qt::MouseButtons mouseButtons
= QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState);
const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
const Qt::KeyboardModifiers keyMods = toQtKeyboardModifiers(grfKeyState);
QDragEnterEvent enterEvent(m_lastPoint, actions, md, mouseButtons, keyMods);
QGuiApplication::sendEvent(m_currentWindow, &enterEvent);
m_answerRect = enterEvent.answerRect();
if (QWindowsContext::verboseOLE)
qDebug() << __FUNCTION__ << " sent drag enter to " << m_window
<< *md << " actions=" << actions
<< " mods=" << keyMods << " accepted: "
<< enterEvent.isAccepted();
if (enterEvent.isAccepted())
m_chosenEffect = translateToWinDragEffects(enterEvent.dropAction());
// Documentation states that a drag move event is sent immediately after
// a drag enter event. This will honor widgets overriding dragMoveEvent only:
if (enterEvent.isAccepted()) {
QDragMoveEvent moveEvent(m_lastPoint, actions, md, mouseButtons, keyMods);
m_answerRect = enterEvent.answerRect();
moveEvent.setDropAction(enterEvent.dropAction());
moveEvent.accept(); // accept by default, since enter event was accepted.
QGuiApplication::sendEvent(dragEnterWidget, &moveEvent);
if (moveEvent.isAccepted()) {
m_answerRect = moveEvent.answerRect();
m_chosenEffect = translateToWinDragEffects(moveEvent.dropAction());
} else {
m_chosenEffect = DROPEFFECT_NONE;
}
}
}
QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
QWindow *dragOverWindow = findDragOverWindow(pt);
if (QWindowsContext::verboseOLE)
qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, dragOverWindow, grfKeyState, pt.x, pt.y);
const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(dragOverWindow, QPoint(pt.x,pt.y));
// see if we should compress this event
if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint))
&& m_lastKeyState == grfKeyState) {
*pdwEffect = m_chosenEffect;
if (QWindowsContext::verboseOLE)
qDebug("%s: compressed event", __FUNCTION__);
return NOERROR;
}
if (QWindowsContext::verboseOLE > 1)
qDebug().nospace() << '>' << __FUNCTION__ << ' ' << m_window << " current "
<< dragOverWindow << " key=" << grfKeyState
<< " pt=" <<pt.x << ',' << pt.y;
if (dragOverWindow != m_currentWindow) {
QPointer<QWindow> dragOverWindowGuard(dragOverWindow);
// Send drag leave event to the previous drag widget.
// Drag-Over widget might be deleted in DragLeave,
// (tasktracker 218353).
QDragLeaveEvent dragLeave;
if (m_currentWindow)
QGuiApplication::sendEvent(m_currentWindow, &dragLeave);
if (!dragOverWindowGuard) {
dragOverWindow = findDragOverWindow(pt);
}
// Send drag enter event to the current drag widget.
m_currentWindow = dragOverWindow;
sendDragEnterEvent(dragOverWindow, grfKeyState, pt, pdwEffect);
}
QDragManager *manager = QDragManager::self();
QMimeData *md = manager->dropData();
const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
QDragMoveEvent oldEvent(m_lastPoint, actions, md,
QWindowsMouseHandler::keyStateToMouseButtons(m_lastKeyState),
toQtKeyboardModifiers(m_lastKeyState));
m_lastPoint = tmpPoint;
m_lastKeyState = grfKeyState;
QDragMoveEvent e(tmpPoint, actions, md,
QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState),
toQtKeyboardModifiers(grfKeyState));
if (m_chosenEffect != DROPEFFECT_NONE) {
if (oldEvent.dropAction() == e.dropAction() &&
oldEvent.keyboardModifiers() == e.keyboardModifiers())
e.setDropAction(translateToQDragDropAction(m_chosenEffect));
e.accept();
}
QGuiApplication::sendEvent(dragOverWindow, &e);
m_answerRect = e.answerRect();
if (e.isAccepted())
m_chosenEffect = translateToWinDragEffects(e.dropAction());
else
m_chosenEffect = DROPEFFECT_NONE;
*pdwEffect = m_chosenEffect;
if (QWindowsContext::verboseOLE > 1)
qDebug("<%s effect=0x%lx", __FUNCTION__, m_chosenEffect);
handleDrag(dragOverWindow, grfKeyState, tmpPoint, pdwEffect);
return NOERROR;
}
@ -694,9 +628,7 @@ QWindowsOleDropTarget::DragLeave()
if (QWindowsContext::verboseOLE)
qDebug().nospace() <<__FUNCTION__ << ' ' << m_window;
m_currentWindow = 0;
QDragLeaveEvent e;
QGuiApplication::sendEvent(m_window, &e);
QWindowSystemInterface::handleDrag(m_window, 0, QPoint(), Qt::IgnoreAction);
QWindowsDrag::instance()->releaseDropDataObject();
return NOERROR;
@ -724,21 +656,15 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState,
m_lastKeyState = grfKeyState;
QWindowsDrag *windowsDrag = QWindowsDrag::instance();
QDragManager *manager = QDragManager::self();
QMimeData *md = manager->dropData();
QDropEvent e(m_lastPoint, translateToQDragDropActions(*pdwEffect), md,
QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState),
toQtKeyboardModifiers(grfKeyState));
if (m_chosenEffect != DROPEFFECT_NONE)
e.setDropAction(translateToQDragDropAction(m_chosenEffect));
QGuiApplication::sendEvent(dropWindow, &e);
if (m_chosenEffect != DROPEFFECT_NONE)
e.accept();
const QPlatformDropQtResponse response =
QWindowSystemInterface::handleDrop(dropWindow, windowsDrag->platformDropData(), m_lastPoint,
translateToQDragDropActions(*pdwEffect));
if (e.isAccepted()) {
if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) {
if (e.dropAction() == Qt::MoveAction)
if (response.isAccepted()) {
const Qt::DropAction action = response.acceptedAction();
if (action == Qt::MoveAction || action == Qt::TargetMoveAction) {
if (action == Qt::MoveAction)
m_chosenEffect = DROPEFFECT_MOVE;
else
m_chosenEffect = DROPEFFECT_COPY;
@ -760,7 +686,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState,
windowsDrag->dropDataObject()->SetData(&format, &medium, true);
}
} else {
m_chosenEffect = translateToWinDragEffects(e.dropAction());
m_chosenEffect = translateToWinDragEffects(action);
}
} else {
m_chosenEffect = DROPEFFECT_NONE;
@ -778,7 +704,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState,
\ingroup qt-lighthouse-win
*/
QWindowsDrag::QWindowsDrag() : m_dropDataObject(0), m_dragBeingCancelled(false)
QWindowsDrag::QWindowsDrag() : m_dropDataObject(0)
{
}
@ -786,6 +712,17 @@ QWindowsDrag::~QWindowsDrag()
{
}
/*!
\brief Return data for a drop in process. If it stems from a current drag, use a shortcut.
*/
QMimeData *QWindowsDrag::dropData()
{
if (const QDrag *drag = currentDrag())
return drag->mimeData();
return &m_dropData;
}
QPixmap QWindowsDrag::defaultCursor(Qt::DropAction action) const
{
switch (action) {
@ -810,69 +747,46 @@ QPixmap QWindowsDrag::defaultCursor(Qt::DropAction action) const
return m_ignoreDragCursor;
}
void QWindowsDrag::startDrag()
Qt::DropAction QWindowsDrag::drag(QDrag *drag)
{
// TODO: Accessibility handling?
QDragManager *dragManager = QDragManager::self();
QMimeData *dropData = dragManager->dropData();
m_dragBeingCancelled = false;
QMimeData *dropData = drag->mimeData();
Qt::DropAction dragResult = Qt::IgnoreAction;
DWORD resultEffect;
QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(this);
windowDropSource->createCursors();
QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData);
const Qt::DropActions possibleActions = dragManager->possible_actions;
const Qt::DropActions possibleActions = drag->supportedActions();
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
if (QWindowsContext::verboseOLE)
qDebug(">%s possible Actions=%x, effects=0x%lx", __FUNCTION__,
int(possibleActions), allowedEffects);
const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
Qt::DropAction ret = Qt::IgnoreAction;
if (r == DRAGDROP_S_DROP) {
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
ret = Qt::TargetMoveAction;
dragResult = Qt::TargetMoveAction;
resultEffect = DROPEFFECT_MOVE;
} else {
ret = translateToQDragDropAction(resultEffect);
dragResult = translateToQDragDropAction(resultEffect);
}
// Force it to be a copy if an unsupported operation occurred.
// This indicates a bug in the drop target.
if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects))
ret = Qt::CopyAction;
} else {
dragManager->setCurrentTarget(0);
if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects)) {
qWarning("%s: Forcing Qt::CopyAction", __FUNCTION__);
dragResult = Qt::CopyAction;
}
}
// clean up
dropDataObject->releaseQt();
dropDataObject->Release(); // Will delete obj if refcount becomes 0
windowDropSource->Release(); // Will delete src if refcount becomes 0
if (QWindowsContext::verboseOLE)
qDebug("<%s allowedEffects=0x%lx, reportedPerformedEffect=0x%lx, resultEffect=0x%lx, hr=0x%x, dropAction=%d",
__FUNCTION__, allowedEffects, reportedPerformedEffect, resultEffect, int(r), ret);
}
void QWindowsDrag::move(const QMouseEvent *me)
{
const QPoint pos = me->pos();
if (QWindowsContext::verboseOLE)
qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y());
}
void QWindowsDrag::drop(const QMouseEvent *me)
{
const QPoint pos = me->pos();
if (QWindowsContext::verboseOLE)
qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y());
}
void QWindowsDrag::cancel()
{
// TODO: Accessibility handling?
if (QWindowsContext::verboseOLE)
qDebug("%s", __FUNCTION__);
m_dragBeingCancelled = true;
__FUNCTION__, allowedEffects, reportedPerformedEffect,
resultEffect, int(r), dragResult);
return dragResult;
}
QWindowsDrag *QWindowsDrag::instance()

View File

@ -45,9 +45,9 @@
#include "qwindowsinternalmimedata.h"
#include <QtGui/QPlatformDrag>
#include <QtGui/QPixmap>
QT_BEGIN_NAMESPACE
class QWindowsDropMimeData : public QWindowsInternalMimeData {
public:
QWindowsDropMimeData() {}
@ -73,11 +73,10 @@ public:
private:
inline QWindow *findDragOverWindow(const POINTL &pt) const;
void sendDragEnterEvent(QWindow *to, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
void handleDrag(QWindow *window, DWORD grfKeyState, const QPoint &, LPDWORD pdwEffect);
ULONG m_refs;
QWindow *const m_window;
QWindow *m_currentWindow;
QRect m_answerRect;
QPoint m_lastPoint;
DWORD m_chosenEffect;
@ -92,25 +91,20 @@ public:
virtual QMimeData *platformDropData() { return &m_dropData; }
virtual void startDrag();
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
virtual void cancel();
virtual Qt::DropAction drag(QDrag *drag);
static QWindowsDrag *instance();
IDataObject *dropDataObject() const { return m_dropDataObject; }
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
void releaseDropDataObject();
bool dragBeingCancelled() const { return m_dragBeingCancelled; }
QMimeData *dropData();
QPixmap defaultCursor(Qt::DropAction action) const;
private:
QWindowsDropMimeData m_dropData;
IDataObject *m_dropDataObject;
bool m_dragBeingCancelled;
mutable QPixmap m_copyDragCursor;
mutable QPixmap m_moveDragCursor;

View File

@ -714,9 +714,9 @@ void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *
return;
if (event->type == atom(QXcbAtom::XdndStatus)) {
drag()->handleStatus(event, false);
drag()->handleStatus(event);
} else if (event->type == atom(QXcbAtom::XdndFinished)) {
drag()->handleFinished(event, false);
drag()->handleFinished(event);
}
QXcbWindow *window = platformWindowFromId(event->window);

View File

@ -52,6 +52,12 @@
#include <qevent.h>
#include <qguiapplication.h>
#include <qrect.h>
#include <qpainter.h>
#include <QtGui/QWindowSystemInterface>
#include <QtPlatformSupport/private/qshapedpixmapdndwindow_p.h>
#include <QtPlatformSupport/private/qsimpledrag_p.h>
QT_BEGIN_NAMESPACE
@ -109,12 +115,11 @@ static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
return proxy;
}
class QDropData : public QXcbMime
class QXcbDropData : public QXcbMime
{
public:
QDropData(QXcbDrag *d);
~QDropData();
QXcbDropData(QXcbDrag *d);
~QXcbDropData();
protected:
bool hasFormat_sys(const QString &mimeType) const;
@ -127,10 +132,9 @@ protected:
};
QXcbDrag::QXcbDrag(QXcbConnection *c)
: QXcbObject(c)
QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
{
dropData = new QDropData(this);
dropData = new QXcbDropData(this);
init();
heartbeat = -1;
@ -147,13 +151,13 @@ void QXcbDrag::init()
{
currentWindow.clear();
accepted_drop_action = Qt::IgnoreAction;
xdnd_dragsource = XCB_NONE;
last_target_accepted_action = Qt::IgnoreAction;
waiting_for_status = false;
current_target = XCB_NONE;
current_proxy_target = XCB_NONE;
xdnd_dragging = false;
source_time = XCB_CURRENT_TIME;
target_time = XCB_CURRENT_TIME;
@ -169,16 +173,17 @@ QMimeData *QXcbDrag::platformDropData()
void QXcbDrag::startDrag()
{
// #fixme enableEventFilter();
init();
heartbeat = startTimer(200);
xdnd_dragging = true;
xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(),
atom(QXcbAtom::XdndSelection), connection()->time());
QDragManager *manager = QDragManager::self();
QStringList fmts = QXcbMime::formatsHelper(manager->dropData());
QStringList fmts = QXcbMime::formatsHelper(drag()->mimeData());
for (int i = 0; i < fmts.size(); ++i) {
QList<xcb_atom_t> atoms = QXcbMime::mimeAtomsForFormat(connection(), fmts.at(i));
for (int j = 0; j < atoms.size(); ++j) {
@ -190,23 +195,16 @@ void QXcbDrag::startDrag()
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
atom(QXcbAtom::XdndTypelist),
XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData());
QPointF pos = QCursor::pos();
QMouseEvent me(QEvent::MouseMove, pos, pos, pos, Qt::LeftButton,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
move(&me);
// if (!QWidget::mouseGrabber())
// manager->shapedPixmapWindow->grabMouse();
QBasicDrag::startDrag();
}
void QXcbDrag::endDrag()
{
Q_ASSERT(heartbeat != -1);
killTimer(heartbeat);
heartbeat = -1;
xdnd_dragging = false;
if (heartbeat != -1) {
killTimer(heartbeat);
heartbeat = -1;
}
QBasicDrag::endDrag();
}
static xcb_translate_coordinates_reply_t *
@ -219,7 +217,7 @@ translateCoordinates(QXcbConnection *c, xcb_window_t from, xcb_window_t to, int
xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md)
{
if (w == QDragManager::self()->shapedPixmapWindow->handle()->winId())
if (w == shapedPixmapWindow()->handle()->winId())
return 0;
if (md) {
@ -296,9 +294,7 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
void QXcbDrag::move(const QMouseEvent *me)
{
DEBUG() << "QDragManager::move enter" << me->globalPos();
// ###
QBasicDrag::move(me);
QPoint globalPos = me->globalPos();
if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
@ -337,15 +333,13 @@ void QXcbDrag::move(const QMouseEvent *me)
::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y());
if (!translate)
return;
xcb_window_t target = translate->child;
int lx = translate->dst_x;
int ly = translate->dst_y;
free (translate);
if (target == rootwin) {
// Ok.
} else if (target) {
//me
if (target && target != rootwin) {
xcb_window_t src = rootwin;
while (target != 0) {
DNDDEBUG << "checking target for XdndAware" << target << lx << ly;
@ -376,7 +370,7 @@ void QXcbDrag::move(const QMouseEvent *me)
target = child;
}
if (!target || target == QDragManager::self()->shapedPixmapWindow->handle()->winId()) {
if (!target || target == shapedPixmapWindow()->handle()->winId()) {
DNDDEBUG << "need to find real window";
target = findRealWindow(globalPos, rootwin, 6);
DNDDEBUG << "real window found" << target;
@ -393,9 +387,6 @@ void QXcbDrag::move(const QMouseEvent *me)
target = rootwin;
}
DNDDEBUG << "and the final target is " << target;
DNDDEBUG << "the widget w is" << (w ? w->window() : 0);
xcb_window_t proxy_target = xdndProxy(connection(), target);
if (!proxy_target)
proxy_target = target;
@ -414,7 +405,6 @@ void QXcbDrag::move(const QMouseEvent *me)
free(reply);
}
DEBUG() << "target=" << target << "current_target=" << current_target;
if (target != current_target) {
if (current_target)
send_leave();
@ -447,11 +437,10 @@ void QXcbDrag::move(const QMouseEvent *me)
waiting_for_status = false;
}
}
if (waiting_for_status)
return;
QDragManager *m = QDragManager::self();
if (target) {
waiting_for_status = true;
@ -465,28 +454,21 @@ void QXcbDrag::move(const QMouseEvent *me)
move.data.data32[1] = 0; // flags
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
move.data.data32[3] = connection()->time();
move.data.data32[4] = toXdndAction(m->defaultAction(m->dragPrivate()->possible_actions, QGuiApplication::keyboardModifiers()));
move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), QGuiApplication::keyboardModifiers()));
DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window;
source_time = connection()->time();
if (w)
handle_xdnd_position(w->window(), &move, false);
handle_xdnd_position(w->window(), &move);
else
xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
} else {
if (m->willDrop) {
m->willDrop = false;
}
}
m->updateCursor();
DEBUG() << "QDragManager::move leave";
}
void QXcbDrag::drop(const QMouseEvent *)
void QXcbDrag::drop(const QMouseEvent *event)
{
endDrag();
QBasicDrag::drop(event);
if (!current_target)
return;
@ -500,14 +482,13 @@ void QXcbDrag::drop(const QMouseEvent *)
drop.data.data32[2] = connection()->time();
drop.data.data32[3] = 0;
drop.data.data32[4] = 0;
drop.data.data32[4] = currentDrag()->supportedActions();
QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target);
if (w && (w->window()->windowType() == Qt::Desktop) /*&& !w->acceptDrops()*/)
w = 0;
QDragManager *manager = QDragManager::self();
Transaction t = {
connection()->time(),
@ -515,21 +496,22 @@ void QXcbDrag::drop(const QMouseEvent *)
current_proxy_target,
(w ? w->window() : 0),
// current_embedding_widget,
manager->object
currentDrag()
};
transactions.append(t);
restartDropExpiryTimer();
if (w)
handleDrop(w->window(), &drop, false);
else
if (w) {
handleDrop(w->window(), &drop);
} else {
xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop);
}
current_target = 0;
current_proxy_target = 0;
source_time = 0;
// current_embedding_widget = 0;
manager->object = 0;
// #fixme resetDndState(false);
}
Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
@ -719,7 +701,7 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev
DEBUG() << " " << connection()->atomName(xdnd_types.at(i));
}
void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e, bool passive)
void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e)
{
QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff);
Q_ASSERT(w);
@ -727,11 +709,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t
p -= geometry.topLeft();
// ####
// if (!passive && checkEmbedded(w, e))
// return;
if (!w || (/*!w->acceptDrops() &&*/ (w->windowType() == Qt::Desktop)))
if (!w || (w->windowType() == Qt::Desktop))
return;
if (e->data.data32[0] != xdnd_dragsource) {
@ -739,12 +717,27 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t
return;
}
// timestamp from the source
if (e->data.data32[3] != XCB_NONE)
target_time /*= X11->userTime*/ = e->data.data32[3];
currentPosition = p;
currentWindow = w;
QDragManager *manager = QDragManager::self();
QMimeData *dropData = manager->dropData();
// timestamp from the source
if (e->data.data32[3] != XCB_NONE) {
target_time = e->data.data32[3];
}
QMimeData *dropData = 0;
Qt::DropActions supported_actions = Qt::IgnoreAction;
if (currentDrag()) {
dropData = currentDrag()->mimeData();
supported_actions = currentDrag()->supportedActions();
} else {
dropData = platformDropData();
supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
}
QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w,dropData,p,supported_actions);
QRect answerRect(p + geometry.topLeft(), QSize(1,1));
answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry);
xcb_client_message_event_t response;
response.response_type = XCB_CLIENT_MESSAGE;
@ -752,83 +745,33 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t
response.format = 32;
response.type = atom(QXcbAtom::XdndStatus);
response.data.data32[0] = xcb_window(w);
response.data.data32[1] = 0; // flags
response.data.data32[1] = qt_response.isAccepted(); // flags
response.data.data32[2] = 0; // x, y
response.data.data32[3] = 0; // w, h
response.data.data32[4] = 0; // action
response.data.data32[4] = toXdndAction(qt_response.acceptedAction()); // action
if (!passive) { // otherwise just reject
QRect answerRect(p + geometry.topLeft(), QSize(1,1));
if (manager->object) {
manager->possible_actions = manager->dragPrivate()->possible_actions;
} else {
manager->possible_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
}
QDragMoveEvent me(p, manager->possible_actions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
Qt::DropAction accepted_action = Qt::IgnoreAction;
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);
currentPosition = p;
if (w != currentWindow.data()) {
if (currentWindow) {
QDragLeaveEvent e;
QGuiApplication::sendEvent(currentWindow.data(), &e);
}
currentWindow = w;
last_target_accepted_action = Qt::IgnoreAction;
QDragEnterEvent de(p, manager->possible_actions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(w, &de);
if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
last_target_accepted_action = de.dropAction();
}
DEBUG() << "qt_handle_xdnd_position action=" << connection()->atomName(e->data.data32[4]);
if (last_target_accepted_action != Qt::IgnoreAction) {
me.setDropAction(last_target_accepted_action);
me.accept();
}
QGuiApplication::sendEvent(w, &me);
if (me.isAccepted()) {
response.data.data32[1] = 1; // yes
accepted_action = me.dropAction();
last_target_accepted_action = accepted_action;
} else {
response.data.data32[0] = 0;
last_target_accepted_action = Qt::IgnoreAction;
}
answerRect = me.answerRect().translated(geometry.topLeft()).intersected(geometry);
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.data32[2] = (answerRect.x() << 16) + answerRect.y();
// response.data.data32[3] = (answerRect.width() << 16) + answerRect.height();
response.data.data32[4] = toXdndAction(accepted_action);
}
response.data.data32[4] = toXdndAction(qt_response.acceptedAction());
// reset
target_time = XCB_CURRENT_TIME;
DEBUG() << "sending XdndStatus" << (xdnd_dragsource == connection()->clipboard()->owner()) << xdnd_dragsource
<< response.data.data32[1] << connection()->atomName(response.data.data32[4]);
if (xdnd_dragsource == connection()->clipboard()->owner())
handle_xdnd_status(&response, passive);
handle_xdnd_status(&response);
else
Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource,
XCB_EVENT_MASK_NO_EVENT, (const char *)&response));
@ -850,7 +793,7 @@ namespace
};
}
void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event, bool passive)
void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event)
{
xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
xcb_generic_event_t *nextEvent;
@ -861,19 +804,28 @@ void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *eve
lastEvent = (xcb_client_message_event_t *)nextEvent;
}
handle_xdnd_position(w, lastEvent, passive);
handle_xdnd_position(w, lastEvent);
if (lastEvent != event)
free(lastEvent);
}
void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event, bool)
void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event)
{
DEBUG("xdndHandleStatus");
waiting_for_status = false;
// ignore late status messages
if (event->data.data32[0] && event->data.data32[0] != current_proxy_target)
return;
Qt::DropAction newAction = (event->data.data32[1] & 0x1) ? toDropAction(event->data.data32[4]) : Qt::IgnoreAction;
const bool dropPossible = event->data.data32[1];
setCanDrop(dropPossible);
if (dropPossible) {
accepted_drop_action = toDropAction(event->data.data32[4]);
updateCursor(accepted_drop_action);
} else {
updateCursor(Qt::IgnoreAction);
}
if ((event->data.data32[1] & 2) == 0) {
QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff);
@ -882,18 +834,9 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event, bool)
} else {
source_sameanswer = QRect();
}
QDragManager *manager = QDragManager::self();
manager->willDrop = (event->data.data32[1] & 0x1);
if (manager->global_accepted_action != newAction) {
manager->global_accepted_action = newAction;
manager->emitActionChanged(newAction);
}
DEBUG() << "willDrop=" << manager->willDrop << "action=" << newAction;
manager->updateCursor();
waiting_for_status = false;
}
void QXcbDrag::handleStatus(const xcb_client_message_event_t *event, bool passive)
void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
{
if (event->window != connection()->clipboard()->owner())
return;
@ -907,13 +850,13 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event, bool passiv
lastEvent = (xcb_client_message_event_t *)nextEvent;
}
handle_xdnd_status(lastEvent, passive);
handle_xdnd_status(lastEvent);
if (lastEvent != event)
free(lastEvent);
DEBUG("xdndHandleStatus end");
}
void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/)
void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event)
{
DEBUG("xdnd leave");
if (!currentWindow || w != currentWindow.data())
@ -931,8 +874,8 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event,
DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource);
}
QDragLeaveEvent e;
QGuiApplication::sendEvent(currentWindow.data(), &e);
QWindowSystemInterface::handleDrag(w,0,QPoint(),Qt::IgnoreAction);
updateAction(Qt::IgnoreAction);
xdnd_dragsource = 0;
xdnd_types.clear();
@ -944,7 +887,6 @@ void QXcbDrag::send_leave()
if (!current_target)
return;
QDragManager *manager = QDragManager::self();
xcb_client_message_event_t leave;
leave.response_type = XCB_CLIENT_MESSAGE;
@ -963,24 +905,18 @@ void QXcbDrag::send_leave()
w = 0;
if (w)
handleLeave(w->window(), (const xcb_client_message_event_t *)&leave, false);
handleLeave(w->window(), (const xcb_client_message_event_t *)&leave);
else
xcb_send_event(xcb_connection(), false,current_proxy_target,
XCB_EVENT_MASK_NO_EVENT, (const char *)&leave);
// reset the drag manager state
manager->willDrop = false;
if (manager->global_accepted_action != Qt::IgnoreAction)
manager->emitActionChanged(Qt::IgnoreAction);
manager->global_accepted_action = Qt::IgnoreAction;
manager->updateCursor();
current_target = 0;
current_proxy_target = 0;
source_time = XCB_CURRENT_TIME;
waiting_for_status = false;
}
void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive)
void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event)
{
DEBUG("xdndHandleDrop");
if (!currentWindow) {
@ -988,16 +924,8 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bo
return; // sanity
}
// ###
// if (!passive && checkEmbedded(currentWindow, xe)){
// current_embedding_widget = 0;
// xdnd_dragsource = 0;
// currentWindow = 0;
// return;
// }
const uint32_t *l = event->data.data32;
QDragManager *manager = QDragManager::self();
DEBUG("xdnd drop");
if (l[0] != xdnd_dragsource) {
@ -1009,50 +937,39 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bo
if (l[2] != 0)
target_time = /*X11->userTime =*/ l[2];
if (!passive) {
// this could be a same-application drop, just proxied due to
// some XEMBEDding, so try to find the real QMimeData used
// based on the timestamp for this drop.
QMimeData *dropData = 0;
// ###
// int at = findXdndDropTransactionByTime(target_time);
// if (at != -1)
// dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
// if we can't find it, then use the data in the drag manager
if (!dropData)
dropData = manager->dropData();
// Drop coming from another app? Update keyboard modifiers.
// if (!qt_xdnd_dragging) {
// QApplicationPrivate::modifier_buttons = currentKeyboardModifiers();
// }
QDropEvent de(currentPosition, manager->possible_actions, dropData,
QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(currentWindow.data(), &de);
if (!de.isAccepted()) {
// Ignore a failed drag
manager->global_accepted_action = Qt::IgnoreAction;
} else {
manager->global_accepted_action = de.dropAction();
}
xcb_client_message_event_t finished;
finished.response_type = XCB_CLIENT_MESSAGE;
finished.window = xdnd_dragsource;
finished.format = 32;
finished.type = atom(QXcbAtom::XdndFinished);
DNDDEBUG << "xdndHandleDrop"
<< "currentWindow" << currentWindow.data()
<< (currentWindow ? xcb_window(currentWindow.data()) : 0);
finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
finished.data.data32[1] = de.isAccepted() ? 1 : 0; // flags
finished.data.data32[2] = toXdndAction(manager->global_accepted_action);
Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource,
XCB_EVENT_MASK_NO_EVENT, (char *)&finished));
// this could be a same-application drop, just proxied due to
// some XEMBEDding, so try to find the real QMimeData used
// based on the timestamp for this drop.
Qt::DropActions supported_drop_actions(l[4]);
QMimeData *dropData = 0;
if (currentDrag()) {
dropData = currentDrag()->mimeData();
} else {
QDragLeaveEvent e;
QGuiApplication::sendEvent(currentWindow.data(), &e);
dropData = platformDropData();
}
if (!dropData)
return;
// ###
// int at = findXdndDropTransactionByTime(target_time);
// if (at != -1)
// dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
// if we can't find it, then use the data in the drag manager
QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(currentWindow.data(),dropData,currentPosition,supported_drop_actions);
setExecutedDropAction(response.acceptedAction());
xcb_client_message_event_t finished;
finished.response_type = XCB_CLIENT_MESSAGE;
finished.window = xdnd_dragsource;
finished.format = 32;
finished.type = atom(QXcbAtom::XdndFinished);
finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
finished.data.data32[1] = response.isAccepted(); // flags
finished.data.data32[2] = toXdndAction(response.acceptedAction());
Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource,
XCB_EVENT_MASK_NO_EVENT, (char *)&finished));
xdnd_dragsource = 0;
currentWindow.clear();
waiting_for_status = false;
@ -1062,7 +979,7 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event, bo
}
void QXcbDrag::handleFinished(const xcb_client_message_event_t *event, bool)
void QXcbDrag::handleFinished(const xcb_client_message_event_t *event)
{
DEBUG("xdndHandleFinished");
if (event->window != connection()->clipboard()->owner())
@ -1099,8 +1016,8 @@ void QXcbDrag::handleFinished(const xcb_client_message_event_t *event, bool)
// current_target = 0;
// current_proxy_target = 0;
if (t.object)
t.object->deleteLater();
if (t.drag)
t.drag->deleteLater();
// current_target = target;
// current_proxy_target = proxy_target;
@ -1126,7 +1043,7 @@ void QXcbDrag::timerEvent(QTimerEvent* e)
// dnd within the same process, don't delete these
continue;
}
t.object->deleteLater();
t.drag->deleteLater();
transactions.removeAt(i--);
}
@ -1138,12 +1055,9 @@ void QXcbDrag::timerEvent(QTimerEvent* e)
void QXcbDrag::cancel()
{
DEBUG("QXcbDrag::cancel");
endDrag();
QBasicDrag::cancel();
if (current_target)
send_leave();
current_target = 0;
}
@ -1157,14 +1071,11 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event
notify.property = XCB_NONE;
notify.time = event->time;
QDragManager *manager = QDragManager::self();
QDrag *currentObject = manager->object;
// which transaction do we use? (note: -2 means use current manager->object)
int at = -1;
// figure out which data the requestor is really interested in
if (manager->object && event->time == source_time) {
if (currentDrag() && event->time == source_time) {
// requestor wants the current drag data
at = -2;
} else {
@ -1188,20 +1099,18 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event
// }
// }
}
QDrag *transactionDrag = 0;
if (at >= 0) {
restartDropExpiryTimer();
// use the drag object from an XdndDrop tansaction
manager->object = transactions.at(at).object;
} else if (at != -2) {
// no transaction found, we'll have to reject the request
manager->object = 0;
transactionDrag = transactions.at(at).drag;
}
if (manager->object) {
if (transactionDrag) {
xcb_atom_t atomFormat = event->target;
int dataFormat = 0;
QByteArray data;
if (QXcbMime::mimeDataForAtom(connection(), event->target, manager->dragPrivate()->data,
if (QXcbMime::mimeDataForAtom(connection(), event->target, transactionDrag->mimeData(),
&data, &atomFormat, &dataFormat)) {
int dataSize = data.size() / (dataFormat / 8);
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, event->requestor, event->property,
@ -1211,9 +1120,6 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event
}
}
// reset manager->object in case we modified it above
manager->object = currentObject;
xcb_send_event(xcb_connection(), false, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&notify);
}
@ -1268,20 +1174,17 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on)
}
}
QDropData::QDropData(QXcbDrag *d)
QXcbDropData::QXcbDropData(QXcbDrag *d)
: QXcbMime(),
drag(d)
{
}
QDropData::~QDropData()
QXcbDropData::~QXcbDropData()
{
}
QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
QVariant QXcbDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
{
QByteArray mime = mimetype.toLatin1();
QVariant data = /*X11->motifdnd_active
@ -1290,17 +1193,16 @@ QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type req
return data;
}
QVariant QDropData::xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const
QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QVariant::Type requestedType) const
{
QByteArray result;
QDragManager *manager = QDragManager::self();
QXcbConnection *c = drag->connection();
QXcbWindow *xcb_window = c->platformWindowFromId(drag->xdnd_dragsource);
if (xcb_window && manager->object && xcb_window->window()->windowType() != Qt::Desktop) {
QDragPrivate *o = manager->dragPrivate();
if (o->data->hasFormat(QLatin1String(format)))
result = o->data->data(QLatin1String(format));
if (xcb_window && drag->currentDrag() && xcb_window->window()->windowType() != Qt::Desktop) {
QMimeData *data = drag->currentDrag()->mimeData();
if (data->hasFormat(QLatin1String(format)))
result = data->data(QLatin1String(format));
return result;
}
@ -1320,12 +1222,12 @@ QVariant QDropData::xdndObtainData(const QByteArray &format, QVariant::Type requ
}
bool QDropData::hasFormat_sys(const QString &format) const
bool QXcbDropData::hasFormat_sys(const QString &format) const
{
return formats().contains(format);
}
QStringList QDropData::formats_sys() const
QStringList QXcbDropData::formats_sys() const
{
QStringList formats;
// if (X11->motifdnd_active) {

View File

@ -43,6 +43,7 @@
#define QXCBDRAG_H
#include <qplatformdrag_qpa.h>
#include <QtPlatformSupport/private/qsimpledrag_p.h>
#include <qxcbobject.h>
#include <xcb/xcb.h>
#include <qlist.h>
@ -51,17 +52,23 @@
#include <qsharedpointer.h>
#include <qvector.h>
#include <qpixmap.h>
#include <qbackingstore.h>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE
class QMouseEvent;
class QWindow;
class QXcbConnection;
class QXcbWindow;
class QDropData;
class QXcbDropData;
class QXcbScreen;
class QDrag;
class QShapedPixmapWindow;
class QXcbDrag : public QObject, public QXcbObject, public QPlatformDrag
class QXcbDrag : public QXcbObject, public QBasicDrag
{
public:
QXcbDrag(QXcbConnection *c);
@ -69,35 +76,36 @@ public:
virtual QMimeData *platformDropData();
// virtual Qt::DropAction drag(QDrag *);
virtual void startDrag();
virtual void cancel();
virtual void move(const QMouseEvent *me);
virtual void drop(const QMouseEvent *me);
void startDrag();
void cancel();
void move(const QMouseEvent *me);
void drop(const QMouseEvent *me);
void endDrag();
void handleEnter(QWindow *window, const xcb_client_message_event_t *event);
void handlePosition(QWindow *w, const xcb_client_message_event_t *event, bool passive);
void handleLeave(QWindow *w, const xcb_client_message_event_t *event, bool /*passive*/);
void handleDrop(QWindow *, const xcb_client_message_event_t *event, bool passive);
void handlePosition(QWindow *w, const xcb_client_message_event_t *event);
void handleLeave(QWindow *w, const xcb_client_message_event_t *event);
void handleDrop(QWindow *, const xcb_client_message_event_t *event);
void handleStatus(const xcb_client_message_event_t *event, bool passive);
void handleStatus(const xcb_client_message_event_t *event);
void handleSelectionRequest(const xcb_selection_request_event_t *event);
void handleFinished(const xcb_client_message_event_t *event, bool passive);
void handleFinished(const xcb_client_message_event_t *event);
bool dndEnable(QXcbWindow *win, bool on);
void updatePixmap();
protected:
void timerEvent(QTimerEvent* e);
private:
friend class QDropData;
friend class QXcbDropData;
void init();
void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event, bool passive);
void handle_xdnd_status(const xcb_client_message_event_t *event, bool);
void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event);
void handle_xdnd_status(const xcb_client_message_event_t *event);
void send_leave();
Qt::DropAction toDropAction(xcb_atom_t atom) const;
@ -106,7 +114,8 @@ private:
QWeakPointer<QWindow> currentWindow;
QPoint currentPosition;
QDropData *dropData;
QXcbDropData *dropData;
Qt::DropAction accepted_drop_action;
QWindow *desktop_proxy;
@ -118,7 +127,6 @@ private:
xcb_timestamp_t target_time;
xcb_timestamp_t source_time;
Qt::DropAction last_target_accepted_action;
// rectangle in which the answer will be the same
QRect source_sameanswer;
@ -132,7 +140,6 @@ private:
QXcbScreen *current_screen;
int heartbeat;
bool xdnd_dragging;
QVector<xcb_atom_t> drag_types;
@ -143,7 +150,7 @@ private:
xcb_window_t proxy_target;
QWindow *targetWindow;
// QWidget *embedding_widget;
QDrag *object;
QDrag *drag;
};
QList<Transaction> transactions;

View File

@ -1257,11 +1257,11 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
} else if (event->type == atom(QXcbAtom::XdndEnter)) {
connection()->drag()->handleEnter(window(), event);
} else if (event->type == atom(QXcbAtom::XdndPosition)) {
connection()->drag()->handlePosition(window(), event, false);
connection()->drag()->handlePosition(window(), event);
} else if (event->type == atom(QXcbAtom::XdndLeave)) {
connection()->drag()->handleLeave(window(), event, false);
connection()->drag()->handleLeave(window(), event);
} else if (event->type == atom(QXcbAtom::XdndDrop)) {
connection()->drag()->handleDrop(window(), event, false);
connection()->drag()->handleDrop(window(), event);
} else {
qWarning() << "unhandled client message:" << connection()->atomName(event->type);
}

View File

@ -44,6 +44,8 @@
#ifndef QT_NO_ITEMVIEWS
#include <qabstractitemmodel.h>
#include <qapplication.h>
#include <qplatformintegration_qpa.h>
#include <private/qguiapplication_p.h>
#include <qbrush.h>
#include <qlineedit.h>
#include <qtextedit.h>
@ -1224,8 +1226,10 @@ bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
#ifndef QT_NO_DRAGANDDROP
// The window may lose focus during an drag operation.
// i.e when dragging involves the taskbar on Windows.
if (QDragManager::self() && QDragManager::self()->object != 0)
QPlatformDrag *platformDrag = QGuiApplicationPrivate::instance()->platformIntegration()->drag();
if (platformDrag && platformDrag->currentDrag()) {
return false;
}
#endif
emit commitData(editor);

View File

@ -44,6 +44,8 @@
#ifndef QT_NO_ITEMVIEWS
#include <qabstractitemmodel.h>
#include <qapplication.h>
#include <qplatformintegration_qpa.h>
#include <private/qguiapplication_p.h>
#include <qbrush.h>
#include <qlineedit.h>
#include <qtextedit.h>
@ -689,8 +691,10 @@ bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
#ifndef QT_NO_DRAGANDDROP
// The window may lose focus during an drag operation.
// i.e when dragging involves the taskbar on Windows.
if (QDragManager::self() && QDragManager::self()->object != 0)
QPlatformDrag *platformDrag = QGuiApplicationPrivate::instance()->platformIntegration()->drag();
if (platformDrag && platformDrag->currentDrag()) {
return false;
}
#endif
emit commitData(editor);