diff options
Diffstat (limited to 'src/plugins/platforms/windows/qwindowsdrag.cpp')
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdrag.cpp | 308 |
1 files changed, 202 insertions, 106 deletions
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index fedda750d9..60c3daff23 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -54,6 +54,9 @@ #include <QtGui/QMouseEvent> #include <QtGui/QPixmap> #include <QtGui/QPainter> +#include <QtGui/QPaintDevice> +#include <QtGui/QBackingStore> +#include <QtGui/QWindow> #include <QtGui/QGuiApplication> #include <qpa/qwindowsysteminterface_p.h> #include <QtGui/private/qguiapplication_p.h> @@ -207,6 +210,77 @@ static const char * const ignoreDragCursorXpmC[] = { "...............XXXX....."}; /*! + \class QWindowsDragCursorWindow + \brief A toplevel window showing the drag icon in case of touch drag. + + \sa QWindowsOleDropSource + \internal + \ingroup qt-lighthouse-win +*/ + +class QWindowsDragCursorWindow : public QWindow +{ +public: + explicit QWindowsDragCursorWindow(QWindow *parent = 0); + + void setPixmap(const QPixmap &p); + +protected: + void exposeEvent(QExposeEvent *); + +private: + void render(); + + QBackingStore m_backingStore; + QPixmap m_pixmap; +}; + +QWindowsDragCursorWindow::QWindowsDragCursorWindow(QWindow *parent) + : QWindow(parent) + , m_backingStore(this) +{ + QSurfaceFormat windowFormat = format(); + windowFormat.setAlphaBufferSize(8); + setFormat(windowFormat); + setObjectName(QStringLiteral("QWindowsDragCursorWindow")); + setFlags(Qt::Popup | Qt::NoDropShadowWindowHint + | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint + | Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput); +} + +void QWindowsDragCursorWindow::setPixmap(const QPixmap &p) +{ + if (p.cacheKey() == m_pixmap.cacheKey()) + return; + const QSize oldSize = m_pixmap.size(); + const QSize newSize = p.size(); + qCDebug(lcQpaMime) << __FUNCTION__ << p.cacheKey() << newSize; + m_pixmap = p; + if (oldSize != newSize) { + resize(newSize); + m_backingStore.resize(newSize); + } + if (isVisible()) + render(); +} + +void QWindowsDragCursorWindow::exposeEvent(QExposeEvent *) +{ + Q_ASSERT(!m_pixmap.isNull()); + render(); +} + +void QWindowsDragCursorWindow::render() +{ + const QRect rect(QPoint(0, 0), m_pixmap.size()); + m_backingStore.beginPaint(rect); + QPainter painter(m_backingStore.paintDevice()); + painter.drawPixmap(0, 0, m_pixmap); + m_backingStore.endPaint(); + m_backingStore.flush(rect); +} + +/*! \class QWindowsDropMimeData \brief Special mime data class for data retrieval from Drag operations. @@ -286,6 +360,11 @@ static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState) class QWindowsOleDropSource : public IDropSource { public: + enum Mode { + MouseDrag, + TouchDrag // Mouse cursor suppressed, use window as cursor. + }; + explicit QWindowsOleDropSource(QWindowsDrag *drag); virtual ~QWindowsOleDropSource(); @@ -304,35 +383,65 @@ private: class DragCursorHandle { Q_DISABLE_COPY(DragCursorHandle) public: - DragCursorHandle(HCURSOR c, qint64 k) : cursor(c), cacheKey(k) {} + DragCursorHandle(HCURSOR c) : cursor(c) {} ~DragCursorHandle() { DestroyCursor(cursor); } - HCURSOR cursor; - qint64 cacheKey; + const HCURSOR cursor; }; - typedef QMap <Qt::DropAction, QSharedPointer<DragCursorHandle> > ActionCursorMap; + typedef QSharedPointer<DragCursorHandle> DragCursorHandlePtr; + struct CursorEntry { + CursorEntry() : cacheKey(0) {} + CursorEntry(const QPixmap &p, qint64 cK, const DragCursorHandlePtr &c, const QPoint &h) : + pixmap(p), cacheKey(cK), cursor(c), hotSpot(h) {} + + QPixmap pixmap; + qint64 cacheKey; // Cache key of cursor + DragCursorHandlePtr cursor; + QPoint hotSpot; + }; + + typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap; + typedef ActionCursorMap::Iterator ActionCursorMapIt; + typedef ActionCursorMap::ConstIterator ActionCursorMapConstIt; + + const Mode m_mode; QWindowsDrag *m_drag; Qt::MouseButtons m_currentButtons; ActionCursorMap m_cursors; + QWindowsDragCursorWindow *m_touchDragWindow; ULONG m_refs; +#ifndef QT_NO_DEBUG_OUTPUT + friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &); +#endif }; -QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag) : - m_drag(drag), m_currentButtons(Qt::NoButton), - m_refs(1) +QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag) + : m_mode(QWindowsCursor::cursorState() != QWindowsCursor::CursorSuppressed ? MouseDrag : TouchDrag) + , m_drag(drag) + , m_currentButtons(Qt::NoButton) + , m_touchDragWindow(0) + , m_refs(1) { - if (QWindowsContext::verboseOLE) - qDebug("%s", __FUNCTION__); + qCDebug(lcQpaMime) << __FUNCTION__ << m_mode; } QWindowsOleDropSource::~QWindowsOleDropSource() { m_cursors.clear(); - if (QWindowsContext::verboseOLE) - qDebug("%s", __FUNCTION__); + delete m_touchDragWindow; + qCDebug(lcQpaMime) << __FUNCTION__; } +#ifndef QT_NO_DEBUG_OUTPUT +QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e) +{ + d << "CursorEntry:" << e.pixmap.size() << '#' << e.cacheKey + << "HCURSOR" << e.cursor->cursor << "hotspot:" << e.hotSpot; + return d; +} +#endif // !QT_NO_DEBUG_OUTPUT + /*! \brief Blend custom pixmap with cursors. */ @@ -343,59 +452,56 @@ void QWindowsOleDropSource::createCursors() const QPixmap pixmap = drag->pixmap(); const bool hasPixmap = !pixmap.isNull(); - QList<Qt::DropAction> actions; - actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction; - if (hasPixmap) - actions << Qt::IgnoreAction; + Qt::DropAction actions[] = { Qt::MoveAction, Qt::CopyAction, Qt::LinkAction, Qt::IgnoreAction }; + int actionCount = int(sizeof(actions) / sizeof(actions[0])); + if (!hasPixmap) + --actionCount; // No Qt::IgnoreAction unless pixmap const QPoint hotSpot = drag->hotSpot(); - for (int cnum = 0; cnum < actions.size(); ++cnum) { - const Qt::DropAction action = actions.at(cnum); - QPixmap cpm = drag->dragCursor(action); - if (cpm.isNull()) - cpm = m_drag->defaultCursor(action); - QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); - if (!cursorHandler.isNull() && cpm.cacheKey() == cursorHandler->cacheKey) + for (int cnum = 0; cnum < actionCount; ++cnum) { + const Qt::DropAction action = actions[cnum]; + QPixmap cursorPixmap = drag->dragCursor(action); + if (cursorPixmap.isNull()) + cursorPixmap = m_drag->defaultCursor(action); + const qint64 cacheKey = cursorPixmap.cacheKey(); + const ActionCursorMapIt it = m_cursors.find(action); + if (it != m_cursors.end() && it.value().cacheKey == cacheKey) continue; - if (cpm.isNull()) { + if (cursorPixmap.isNull()) { qWarning("%s: Unable to obtain drag cursor for %d.", __FUNCTION__, action); continue; } - int w = cpm.width(); - int h = cpm.height(); + QPoint newHotSpot(0, 0); + QPixmap newPixmap = cursorPixmap; if (hasPixmap) { const int x1 = qMin(-hotSpot.x(), 0); - const int x2 = qMax(pixmap.width() - hotSpot.x(), cpm.width()); + const int x2 = qMax(pixmap.width() - hotSpot.x(), cursorPixmap.width()); const int y1 = qMin(-hotSpot.y(), 0); - const int y2 = qMax(pixmap.height() - hotSpot.y(), cpm.height()); - - w = x2 - x1 + 1; - h = y2 - y1 + 1; - } - - const QPoint newHotSpot = hotSpot; - QPixmap newCursor(w, h); - if (hasPixmap) { + const int y2 = qMax(pixmap.height() - hotSpot.y(), cursorPixmap.height()); + QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1); newCursor.fill(Qt::transparent); QPainter p(&newCursor); const QRect srcRect = pixmap.rect(); const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y())); p.drawPixmap(pmDest, pixmap, srcRect); - p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm); - } else { - newCursor = cpm; + p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap); + newPixmap = newCursor; + newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y())); } - const int hotX = hasPixmap ? qMax(0,newHotSpot.x()) : 0; - const int hotY = hasPixmap ? qMax(0,newHotSpot.y()) : 0; - - if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY)) { - m_cursors.insert(action, QSharedPointer<DragCursorHandle>(new DragCursorHandle(sysCursor, cpm.cacheKey()))); + if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newPixmap, newHotSpot.x(), newHotSpot.y())) { + const CursorEntry entry(newPixmap, cacheKey, DragCursorHandlePtr(new DragCursorHandle(sysCursor)), newHotSpot); + if (it == m_cursors.end()) + m_cursors.insert(action, entry); + else + it.value() = entry; } } - if (QWindowsContext::verboseOLE) - qDebug("%s %d cursors", __FUNCTION__, m_cursors.size()); +#ifndef QT_NO_DEBUG_OUTPUT + if (lcQpaMime().isDebugEnabled()) + qCDebug(lcQpaMime) << __FUNCTION__ << "pixmap" << pixmap.size() << m_cursors.size() << "cursors:\n" << m_cursors; +#endif // !QT_NO_DEBUG_OUTPUT } //--------------------------------------------------------------------- @@ -468,11 +574,11 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) } while (false); - if (QWindowsContext::verboseOLE - && (QWindowsContext::verboseOLE > 1 || hr != S_OK)) - qDebug("%s fEscapePressed=%d, grfKeyState=%lu buttons=%d returns 0x%x", - __FUNCTION__, fEscapePressed,grfKeyState, int(m_currentButtons), - int(hr)); + if (QWindowsContext::verbose > 1 || hr != S_OK) { + qCDebug(lcQpaMime) << __FUNCTION__ << "fEscapePressed=" << fEscapePressed + << "grfKeyState=" << grfKeyState << "buttons" << m_currentButtons + << "returns 0x" << hex <<int(hr) << dec; + } return hr; } @@ -486,17 +592,29 @@ 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); - - QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); - qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); - if (cursorHandler.isNull() || currentCacheKey != cursorHandler->cacheKey) + const qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); + ActionCursorMapConstIt it = m_cursors.constFind(action); + // If a custom drag cursor is set, check its cache key to detect changes. + if (it == m_cursors.constEnd() || (currentCacheKey && currentCacheKey != it.value().cacheKey)) { createCursors(); + it = m_cursors.constFind(action); + } - const ActionCursorMap::const_iterator it = m_cursors.constFind(action); if (it != m_cursors.constEnd()) { - SetCursor(it.value()->cursor); + const CursorEntry &e = it.value(); + switch (m_mode) { + case MouseDrag: + SetCursor(e.cursor->cursor); + break; + case TouchDrag: + if (!m_touchDragWindow) + m_touchDragWindow = new QWindowsDragCursorWindow; + m_touchDragWindow->setPixmap(e.pixmap); + m_touchDragWindow->setFramePosition(QWindowsCursor::mousePosition() - e.hotSpot); + if (!m_touchDragWindow->isVisible()) + m_touchDragWindow->show(); + break; + } return ResultFromScode(S_OK); } @@ -519,14 +637,12 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) : m_refs(1), m_window(w), m_chosenEffect(0), m_lastKeyState(0) { - if (QWindowsContext::verboseOLE) - qDebug() << __FUNCTION__ << this << w; + qCDebug(lcQpaMime) << __FUNCTION__ << this << w; } QWindowsOleDropTarget::~QWindowsOleDropTarget() { - if (QWindowsContext::verboseOLE) - qDebug("%s %p", __FUNCTION__, this); + qCDebug(lcQpaMime) << __FUNCTION__ << this; } STDMETHODIMP @@ -557,14 +673,6 @@ QWindowsOleDropTarget::Release(void) return m_refs; } -QWindow *QWindowsOleDropTarget::findDragOverWindow(const POINTL &pt) const -{ - if (QWindowsWindow *child = - QWindowsWindow::baseWindowOf(m_window)->childAtScreenPoint(QPoint(pt.x, pt.y))) - return child->window(); - return m_window; -} - void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, const QPoint &point, LPDWORD pdwEffect) { @@ -588,13 +696,12 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, 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; + qCDebug(lcQpaMime) << __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 @@ -604,8 +711,8 @@ QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) dh->DragEnter(reinterpret_cast<HWND>(m_window->winId()), pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect); - if (QWindowsContext::verboseOLE) - qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, m_window, grfKeyState, pt.x, pt.y); + qCDebug(lcQpaMime) << __FUNCTION__ << "widget=" << m_window << " key=" << grfKeyState + << "pt=" << pt.x << pt.y; QWindowsDrag::instance()->setDropDataObject(pDataObj); pDataObj->AddRef(); @@ -620,20 +727,18 @@ QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) dh->DragOver(reinterpret_cast<POINT*>(&pt), *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)); + qCDebug(lcQpaMime) << __FUNCTION__ << "m_window" << m_window << "key=" << grfKeyState + << "pt=" << pt.x << pt.y; + const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(m_window, 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__); + qCDebug(lcQpaMime) << __FUNCTION__ << "compressed event"; return NOERROR; } - handleDrag(dragOverWindow, grfKeyState, tmpPoint, pdwEffect); + handleDrag(m_window, grfKeyState, tmpPoint, pdwEffect); return NOERROR; } @@ -643,8 +748,7 @@ QWindowsOleDropTarget::DragLeave() if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) dh->DragLeave(); - if (QWindowsContext::verboseOLE) - qDebug().nospace() <<__FUNCTION__ << ' ' << m_window; + qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window; QWindowSystemInterface::handleDrag(m_window, 0, QPoint(), Qt::IgnoreAction); QWindowsDrag::instance()->releaseDropDataObject(); @@ -661,15 +765,10 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) dh->Drop(pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect); - QWindow *dropWindow = findDragOverWindow(pt); - - if (QWindowsContext::verboseOLE) - qDebug().nospace() << __FUNCTION__ << ' ' << m_window - << " on " << dropWindow - << " keys=" << grfKeyState << " pt=" - << pt.x << ',' << pt.y; + qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window + << "keys=" << grfKeyState << "pt=" << pt.x << ',' << pt.y; - m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dropWindow, QPoint(pt.x,pt.y)); + m_lastPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y)); // grfKeyState does not all ways contain button state in the drop so if // it doesn't then use the last known button state; if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0) @@ -679,7 +778,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, QWindowsDrag *windowsDrag = QWindowsDrag::instance(); const QPlatformDropQtResponse response = - QWindowSystemInterface::handleDrop(dropWindow, windowsDrag->dropData(), m_lastPoint, + QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), m_lastPoint, translateToQDragDropActions(*pdwEffect)); if (response.isAccepted()) { @@ -796,9 +895,8 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData); 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); + qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x" + << hex << int(possibleActions) << "effects=0x" << allowedEffects << dec; const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); if (r == DRAGDROP_S_DROP) { @@ -819,10 +917,9 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag) 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), dragResult); + qCDebug(lcQpaMime) << '<' << __FUNCTION__ << hex << "allowedEffects=0x" << allowedEffects + << "reportedPerformedEffect=0x" << reportedPerformedEffect + << " resultEffect=0x" << resultEffect << "hr=0x" << int(r) << dec << "dropAction=" << dragResult; return dragResult; } @@ -833,8 +930,7 @@ QWindowsDrag *QWindowsDrag::instance() void QWindowsDrag::releaseDropDataObject() { - if (QWindowsContext::verboseOLE) - qDebug("%s %p", __FUNCTION__, m_dropDataObject); + qCDebug(lcQpaMime) << __FUNCTION__ << m_dropDataObject; if (m_dropDataObject) { m_dropDataObject->Release(); m_dropDataObject = 0; |