diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 9c5f3b10da..2656d8ffdb 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -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 \ diff --git a/src/gui/kernel/qdnd.cpp b/src/gui/kernel/qdnd.cpp index 2fb250cf18..1ed4a96192 100644 --- a/src/gui/kernel/qdnd.cpp +++ b/src/gui/kernel/qdnd.cpp @@ -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::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(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(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(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(e)); - return true; // Eat all mouse events - - case QEvent::MouseButtonRelease: - qApp->removeEventFilter(this); - if (willDrop) - drop(static_cast(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 diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h index 857be34d10..764b73c06f 100644 --- a/src/gui/kernel/qdnd_p.h +++ b/src/gui/kernel/qdnd_p.h @@ -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 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) }; diff --git a/src/gui/kernel/qdrag.cpp b/src/gui/kernel/qdrag.cpp index 694b12a180..ead0805bd1 100644 --- a/src/gui/kernel/qdrag.cpp +++ b/src/gui/kernel/qdrag.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include +#include "private/qguiapplication_p.h" #include #include #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::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) diff --git a/src/gui/kernel/qdrag.h b/src/gui/kernel/qdrag.h index 0a1ddff2d9..de84b6588b 100644 --- a/src/gui/kernel/qdrag.h +++ b/src/gui/kernel/qdrag.h @@ -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); diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 281d4f61bc..ee121f2071 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -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; } diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 1cd448a6bb..09d4e98c3c 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -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 +#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 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 diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 68546ce0cf..d9477a3e9e 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -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); diff --git a/src/gui/kernel/qplatformdrag_qpa.cpp b/src/gui/kernel/qplatformdrag_qpa.cpp new file mode 100644 index 0000000000..832b91db7e --- /dev/null +++ b/src/gui/kernel/qplatformdrag_qpa.cpp @@ -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 +#include +#include +#include + +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 diff --git a/src/gui/kernel/qplatformdrag_qpa.h b/src/gui/kernel/qplatformdrag_qpa.h index 0c215071d3..7d22c69947 100644 --- a/src/gui/kernel/qplatformdrag_qpa.h +++ b/src/gui/kernel/qplatformdrag_qpa.h @@ -43,6 +43,7 @@ #define QPLATFORMDRAG_H #include +#include 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 diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.cpp b/src/gui/kernel/qwindowsysteminterface_qpa.cpp index 40a4ec07a6..5b77d97950 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.cpp +++ b/src/gui/kernel/qwindowsysteminterface_qpa.cpp @@ -43,6 +43,7 @@ #include "private/qguiapplication_p.h" #include "private/qtouchdevice_p.h" #include +#include #include 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); } /*! diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.h b/src/gui/kernel/qwindowsysteminterface_qpa.h index 836fb40bd7..6dae11ea81 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa.h @@ -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 ®ion); // 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); diff --git a/src/platformsupport/dnd/dnd.pri b/src/platformsupport/dnd/dnd.pri index e100dd10cb..47feb81ef2 100644 --- a/src/platformsupport/dnd/dnd.pri +++ b/src/platformsupport/dnd/dnd.pri @@ -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 diff --git a/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp b/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp new file mode 100644 index 0000000000..4eed1e7d85 --- /dev/null +++ b/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp @@ -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 +#include + +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 diff --git a/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h b/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h new file mode 100644 index 0000000000..36ca6040dd --- /dev/null +++ b/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h @@ -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 +#include +#include + +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 diff --git a/src/platformsupport/dnd/qsimpledrag.cpp b/src/platformsupport/dnd/qsimpledrag.cpp index d0d08c2445..d3cecd4e44 100644 --- a/src/platformsupport/dnd/qsimpledrag.cpp +++ b/src/platformsupport/dnd/qsimpledrag.cpp @@ -56,147 +56,283 @@ #include "qimagereader.h" #include "qimagewriter.h" +#include +#include + #include #include +#include + 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(e)->key() == Qt::Key_Escape) { + disableEventFilter(); + exitDndEventLoop(); + return true; // block the key release + } + return false; + } + + if (!qobject_cast(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(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(e)); + return true; // Eat all mouse events + + case QEvent::MouseButtonRelease: + disableEventFilter(); + + if (canDrop()) { + drop(static_cast(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 diff --git a/src/platformsupport/dnd/qsimpledrag_p.h b/src/platformsupport/dnd/qsimpledrag_p.h index 536ae241ff..7270684082 100644 --- a/src/platformsupport/dnd/qsimpledrag_p.h +++ b/src/platformsupport/dnd/qsimpledrag_p.h @@ -44,32 +44,80 @@ #include +#include + 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 diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 9f2a1dc00c..89ca6204a8 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -53,8 +53,11 @@ #include #include #include +#include +#include #include +#include #include #include @@ -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 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=" < 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() diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h index 86b5539f92..7b629baccc 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.h +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -45,9 +45,9 @@ #include "qwindowsinternalmimedata.h" #include +#include 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; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index fdc2c76fea..04c43eb97f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -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); diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index e928fe2d0a..0e3807cd7b 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -52,6 +52,12 @@ #include #include #include +#include + +#include + +#include +#include 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 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(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 *)¬ify); } @@ -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) { diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index e32e630548..710a07a5a4 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -43,6 +43,7 @@ #define QXCBDRAG_H #include +#include #include #include #include @@ -51,17 +52,23 @@ #include #include +#include +#include + +#include + 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 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 drag_types; @@ -143,7 +150,7 @@ private: xcb_window_t proxy_target; QWindow *targetWindow; // QWidget *embedding_widget; - QDrag *object; + QDrag *drag; }; QList transactions; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 067cb775c8..6b7e73d02f 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -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); } diff --git a/src/widgets/itemviews/qitemdelegate.cpp b/src/widgets/itemviews/qitemdelegate.cpp index a5351301a7..feec3fcbc5 100644 --- a/src/widgets/itemviews/qitemdelegate.cpp +++ b/src/widgets/itemviews/qitemdelegate.cpp @@ -44,6 +44,8 @@ #ifndef QT_NO_ITEMVIEWS #include #include +#include +#include #include #include #include @@ -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); diff --git a/src/widgets/itemviews/qstyleditemdelegate.cpp b/src/widgets/itemviews/qstyleditemdelegate.cpp index ca4c684e98..d54d78512a 100644 --- a/src/widgets/itemviews/qstyleditemdelegate.cpp +++ b/src/widgets/itemviews/qstyleditemdelegate.cpp @@ -44,6 +44,8 @@ #ifndef QT_NO_ITEMVIEWS #include #include +#include +#include #include #include #include @@ -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);