From 1df7a6a50a794721edb1bc63d268378f4f0ed7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 23 May 2013 12:44:20 +0200 Subject: Move QBasicDrag and QSimpleDrag to QtGui. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are useful as default implementations of QPlatformIntegration::drag(), instead of having it return 0 which will lead to crashes in Qt Quick 2 and widgets applications that use drag and drop. Task-number: QTBUG-31288 Change-Id: I70efa139306ced5d879def0f74e3a72d3bcd64f7 Reviewed-by: Jørgen Lind --- src/gui/kernel/kernel.pri | 4 + src/gui/kernel/qplatformintegration.cpp | 7 +- src/gui/kernel/qshapedpixmapdndwindow.cpp | 106 +++++++++ src/gui/kernel/qshapedpixmapdndwindow_p.h | 75 ++++++ src/gui/kernel/qsimpledrag.cpp | 366 ++++++++++++++++++++++++++++++ src/gui/kernel/qsimpledrag_p.h | 122 ++++++++++ 6 files changed, 679 insertions(+), 1 deletion(-) create mode 100644 src/gui/kernel/qshapedpixmapdndwindow.cpp create mode 100644 src/gui/kernel/qshapedpixmapdndwindow_p.h create mode 100644 src/gui/kernel/qsimpledrag.cpp create mode 100644 src/gui/kernel/qsimpledrag_p.h (limited to 'src/gui') diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 91374fe2dd..3c019fc5b5 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -31,6 +31,8 @@ HEADERS += \ kernel/qplatformclipboard.h \ kernel/qplatformnativeinterface.h \ kernel/qplatformmenu.h \ + kernel/qshapedpixmapdndwindow_p.h \ + kernel/qsimpledrag_p.h \ kernel/qsurfaceformat.h \ kernel/qguiapplication.h \ kernel/qguiapplication_p.h \ @@ -89,6 +91,8 @@ SOURCES += \ kernel/qplatformclipboard.cpp \ kernel/qplatformnativeinterface.cpp \ kernel/qsessionmanager.cpp \ + kernel/qshapedpixmapdndwindow.cpp \ + kernel/qsimpledrag.cpp \ kernel/qsurfaceformat.cpp \ kernel/qguiapplication.cpp \ kernel/qwindow.cpp \ diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index e82e30df80..e4f45ebb6e 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -49,6 +49,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -99,7 +100,11 @@ QPlatformClipboard *QPlatformIntegration::clipboard() const */ QPlatformDrag *QPlatformIntegration::drag() const { - return 0; + static QSimpleDrag *drag = 0; + if (!drag) { + drag = new QSimpleDrag; + } + return drag; } #endif diff --git a/src/gui/kernel/qshapedpixmapdndwindow.cpp b/src/gui/kernel/qshapedpixmapdndwindow.cpp new file mode 100644 index 0000000000..b3e64b01d0 --- /dev/null +++ b/src/gui/kernel/qshapedpixmapdndwindow.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshapedpixmapdndwindow_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QShapedPixmapWindow::QShapedPixmapWindow() + : QWindow(), + m_backingStore(0) +{ + QSurfaceFormat format; + format.setAlphaBufferSize(8); + setFormat(format); + setSurfaceType(RasterSurface); + setFlags(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.setCompositionMode(QPainter::CompositionMode_Source); + 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_pixmap.isNull()) + m_backingStore->resize(QSize(1,1)); + else 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/gui/kernel/qshapedpixmapdndwindow_p.h b/src/gui/kernel/qshapedpixmapdndwindow_p.h new file mode 100644 index 0000000000..20674b6b19 --- /dev/null +++ b/src/gui/kernel/qshapedpixmapdndwindow_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHAPEDPIXMAPDNDWINDOW_H +#define QSHAPEDPIXMAPDNDWINDOW_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QShapedPixmapWindow : public QWindow +{ + Q_OBJECT +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_NAMESPACE + +#endif // QSHAPEDPIXMAPDNDWINDOW_H diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp new file mode 100644 index 0000000000..f6912a2d57 --- /dev/null +++ b/src/gui/kernel/qsimpledrag.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsimpledrag_p.h" + +#include "qbitmap.h" +#include "qdrag.h" +#include "qpixmap.h" +#include "qevent.h" +#include "qfile.h" +#include "qtextcodec.h" +#include "qguiapplication.h" +#include "qpoint.h" +#include "qbuffer.h" +#include "qimage.h" +#include "qregexp.h" +#include "qdir.h" +#include "qimagereader.h" +#include "qimagewriter.h" + +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DRAGANDDROP + +static QWindow* topLevelAt(const QPoint &pos) +{ + QWindowList list = QGuiApplication::topLevelWindows(); + for (int i = list.count()-1; i >= 0; --i) { + QWindow *w = list.at(i); + if (w->isVisible() && w->geometry().contains(pos) && !qobject_cast(w)) + return w; + } + return 0; +} + +/*! + \class QBasicDrag + \brief QBasicDrag is a base class for implementing platform drag and drop. + \since 5.0 + \internal + \ingroup qpa + + 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. +*/ + +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) +{ +} + +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(); + 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)); + } else { + cancel(); + } + 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::restoreCursor() +{ + if (m_restoreCursor) { +#ifndef QT_NO_CURSOR + QGuiApplication::restoreOverrideCursor(); +#endif + m_restoreCursor = false; + } +} + +void QBasicDrag::startDrag() +{ + // ### TODO Check if its really necessary to have m_drag_icon_window + // when QDrag is used without a pixmap - QDrag::setPixmap() + 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(); + restoreCursor(); + 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(); + restoreCursor(); + m_drag_icon_window->setVisible(false); +} + +void QBasicDrag::exitDndEventLoop() +{ + if (m_eventLoop && m_eventLoop->isRunning()) + m_eventLoop->exit(); +} + +void QBasicDrag::updateCursor(Qt::DropAction action) +{ +#ifndef QT_NO_CURSOR + 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(); + QPixmap pixmap = m_drag->dragCursor(action); + if (!cursor) { + qApp->changeOverrideCursor((pixmap.isNull()) ? QCursor(cursorShape) : QCursor(pixmap)); + } else { + if (!pixmap.isNull()) { + if ((cursor->pixmap().cacheKey() != pixmap.cacheKey())) { + qApp->changeOverrideCursor(QCursor(pixmap)); + } + } else { + if (cursorShape != cursor->shape()) { + qApp->changeOverrideCursor(QCursor(cursorShape)); + } + } + } +#endif + 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) +{ +} + +QMimeData *QSimpleDrag::platformDropData() +{ + if (drag()) + return drag()->mimeData(); + return 0; +} + +void QSimpleDrag::startDrag() +{ + QBasicDrag::startDrag(); + m_current_window = 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() +{ + 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 = topLevelAt(me->globalPos()); + if (!window) + return; + + const QPoint pos = me->globalPos() - window->geometry().topLeft(); + const QPlatformDragQtResponse qt_response = + QWindowSystemInterface::handleDrag(window, drag()->mimeData(), pos, drag()->supportedActions()); + + updateCursor(qt_response.acceptedAction()); + setCanDrop(qt_response.isAccepted()); +} + +void QSimpleDrag::drop(const QMouseEvent *me) +{ + QBasicDrag::drop(me); + QWindow *window = topLevelAt(me->globalPos()); + if (!window) + return; + + 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); + } +} + +#endif // QT_NO_DRAGANDDROP + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h new file mode 100644 index 0000000000..36ea4c1ec5 --- /dev/null +++ b/src/gui/kernel/qsimpledrag_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIMPLEDRAG_H +#define QSIMPLEDRAG_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DRAGANDDROP + +class QMouseEvent; +class QWindow; +class QEventLoop; +class QDropData; +class QShapedPixmapWindow; + +class Q_GUI_EXPORT QBasicDrag : public QPlatformDrag, public QObject +{ +public: + virtual ~QBasicDrag(); + + virtual Qt::DropAction drag(QDrag *drag); + + 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); + virtual void endDrag(); + + 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 restoreCursor(); + 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; +}; + +class Q_GUI_EXPORT 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; +}; + +#endif // QT_NO_DRAGANDDROP + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3