Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwindowsdrag.h"
41#include "qwindowscontext.h"
42#include "qwindowsscreen.h"
43#if QT_CONFIG(clipboard)
44# include "qwindowsclipboard.h"
45#endif
46#include "qwindowsintegration.h"
47#include "qwindowsdropdataobject.h"
48#include <QtCore/qt_windows.h>
49#include "qwindowswindow.h"
50#include "qwindowsmousehandler.h"
51#include "qwindowscursor.h"
52#include "qwindowskeymapper.h"
53
54#include <QtGui/qevent.h>
55#include <QtGui/qpixmap.h>
56#include <QtGui/qpainter.h>
57#include <QtGui/qrasterwindow.h>
58#include <QtGui/qguiapplication.h>
59#include <qpa/qwindowsysteminterface_p.h>
60#include <QtGui/private/qdnd_p.h>
61#include <QtGui/private/qguiapplication_p.h>
62#include <QtGui/private/qhighdpiscaling_p.h>
63
64#include <QtCore/qdebug.h>
65#include <QtCore/qbuffer.h>
66#include <QtCore/qpoint.h>
67
68#include <shlobj.h>
69
70QT_BEGIN_NAMESPACE
71
72/*!
73 \class QWindowsDragCursorWindow
74 \brief A toplevel window showing the drag icon in case of touch drag.
75
76 \sa QWindowsOleDropSource
77 \internal
78 \ingroup qt-lighthouse-win
79*/
80
81class QWindowsDragCursorWindow : public QRasterWindow
82{
83public:
84 explicit QWindowsDragCursorWindow(QWindow *parent = nullptr);
85
86 void setPixmap(const QPixmap &p);
87
88protected:
89 void paintEvent(QPaintEvent *) override
90 {
91 QPainter painter(this);
92 painter.drawPixmap(0, 0, m_pixmap);
93 }
94
95private:
96 QPixmap m_pixmap;
97};
98
99QWindowsDragCursorWindow::QWindowsDragCursorWindow(QWindow *parent)
100 : QRasterWindow(parent)
101{
102 QSurfaceFormat windowFormat = format();
103 windowFormat.setAlphaBufferSize(8);
104 setFormat(windowFormat);
105 setObjectName(QStringLiteral("QWindowsDragCursorWindow"));
106 setFlags(Qt::Popup | Qt::NoDropShadowWindowHint
107 | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
108 | Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput);
109}
110
111void QWindowsDragCursorWindow::setPixmap(const QPixmap &p)
112{
113 if (p.cacheKey() == m_pixmap.cacheKey())
114 return;
115 const QSize oldSize = m_pixmap.size();
116 QSize newSize = p.size();
117 qCDebug(lcQpaMime) << __FUNCTION__ << p.cacheKey() << newSize;
118 m_pixmap = p;
119 if (oldSize != newSize) {
120 const qreal pixDevicePixelRatio = p.devicePixelRatio();
121 if (pixDevicePixelRatio > 1.0 && qFuzzyCompare(pixDevicePixelRatio, devicePixelRatio()))
122 newSize /= qRound(pixDevicePixelRatio);
123 resize(newSize);
124 }
125 if (isVisible())
126 update();
127}
128
129/*!
130 \class QWindowsDropMimeData
131 \brief Special mime data class for data retrieval from Drag operations.
132
133 Implementation of QWindowsInternalMimeDataBase which retrieves the
134 current drop data object from QWindowsDrag.
135
136 \sa QWindowsDrag
137 \internal
138 \ingroup qt-lighthouse-win
139*/
140
141IDataObject *QWindowsDropMimeData::retrieveDataObject() const
142{
143 return QWindowsDrag::instance()->dropDataObject();
144}
145
146static inline Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
147{
148 Qt::DropActions actions = Qt::IgnoreAction;
149 if (pdwEffects & DROPEFFECT_LINK)
150 actions |= Qt::LinkAction;
151 if (pdwEffects & DROPEFFECT_COPY)
152 actions |= Qt::CopyAction;
153 if (pdwEffects & DROPEFFECT_MOVE)
154 actions |= Qt::MoveAction;
155 return actions;
156}
157
158static inline Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
159{
160 if (pdwEffect & DROPEFFECT_LINK)
161 return Qt::LinkAction;
162 if (pdwEffect & DROPEFFECT_COPY)
163 return Qt::CopyAction;
164 if (pdwEffect & DROPEFFECT_MOVE)
165 return Qt::MoveAction;
166 return Qt::IgnoreAction;
167}
168
169static inline DWORD translateToWinDragEffects(Qt::DropActions action)
170{
171 DWORD effect = DROPEFFECT_NONE;
172 if (action & Qt::LinkAction)
173 effect |= DROPEFFECT_LINK;
174 if (action & Qt::CopyAction)
175 effect |= DROPEFFECT_COPY;
176 if (action & Qt::MoveAction)
177 effect |= DROPEFFECT_MOVE;
178 return effect;
179}
180
181static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
182{
183 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
184
185 if (keyState & MK_SHIFT)
186 modifiers |= Qt::ShiftModifier;
187 if (keyState & MK_CONTROL)
188 modifiers |= Qt::ControlModifier;
189 if (keyState & MK_ALT)
190 modifiers |= Qt::AltModifier;
191
192 return modifiers;
193}
194
195static inline Qt::MouseButtons toQtMouseButtons(DWORD keyState)
196{
197 Qt::MouseButtons buttons = Qt::NoButton;
198
199 if (keyState & MK_LBUTTON)
200 buttons |= Qt::LeftButton;
201 if (keyState & MK_RBUTTON)
202 buttons |= Qt::RightButton;
203 if (keyState & MK_MBUTTON)
204 buttons |= Qt::MidButton;
205
206 return buttons;
207}
208
209static Qt::KeyboardModifiers lastModifiers = Qt::NoModifier;
210static Qt::MouseButtons lastButtons = Qt::NoButton;
211
212/*!
213 \class QWindowsOleDropSource
214 \brief Implementation of IDropSource
215
216 Used for drag operations.
217
218 \sa QWindowsDrag
219 \internal
220 \ingroup qt-lighthouse-win
221*/
222
223class QWindowsOleDropSource : public QWindowsComBase<IDropSource>
224{
225public:
226 enum Mode {
227 MouseDrag,
228 TouchDrag // Mouse cursor suppressed, use window as cursor.
229 };
230
231 explicit QWindowsOleDropSource(QWindowsDrag *drag);
232 ~QWindowsOleDropSource() override;
233
234 void createCursors();
235
236 // IDropSource methods
237 STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
238 STDMETHOD(GiveFeedback)(DWORD dwEffect);
239
240private:
241 struct CursorEntry {
242 CursorEntry() : cacheKey(0) {}
243 CursorEntry(const QPixmap &p, qint64 cK, const CursorHandlePtr &c, const QPoint &h) :
244 pixmap(p), cacheKey(cK), cursor(c), hotSpot(h) {}
245
246 QPixmap pixmap;
247 qint64 cacheKey; // Cache key of cursor
248 CursorHandlePtr cursor;
249 QPoint hotSpot;
250 };
251
252 typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap;
253
254 Mode m_mode;
255 QWindowsDrag *m_drag;
256 QPointer<QWindow> m_windowUnderMouse;
257 Qt::MouseButtons m_currentButtons;
258 ActionCursorMap m_cursors;
259 QWindowsDragCursorWindow *m_touchDragWindow;
260
261#ifndef QT_NO_DEBUG_STREAM
262 friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &);
263#endif
264};
265
266QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag)
267 : m_mode(QWindowsCursor::cursorState() != QWindowsCursor::State::Suppressed ? MouseDrag : TouchDrag)
268 , m_drag(drag)
269 , m_windowUnderMouse(QWindowsContext::instance()->windowUnderMouse())
270 , m_currentButtons(Qt::NoButton)
271 , m_touchDragWindow(nullptr)
272{
273 qCDebug(lcQpaMime) << __FUNCTION__ << m_mode;
274}
275
276QWindowsOleDropSource::~QWindowsOleDropSource()
277{
278 m_cursors.clear();
279 delete m_touchDragWindow;
280 qCDebug(lcQpaMime) << __FUNCTION__;
281}
282
283#ifndef QT_NO_DEBUG_STREAM
284QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e)
285{
286 d << "CursorEntry:" << e.pixmap.size() << '#' << e.cacheKey
287 << "HCURSOR" << e.cursor->handle() << "hotspot:" << e.hotSpot;
288 return d;
289}
290#endif // !QT_NO_DEBUG_STREAM
291
292/*!
293 \brief Blend custom pixmap with cursors.
294*/
295
296void QWindowsOleDropSource::createCursors()
297{
298 const QDrag *drag = m_drag->currentDrag();
299 const QPixmap pixmap = drag->pixmap();
300 const bool hasPixmap = !pixmap.isNull();
301
302 // Find screen for drag. Could be obtained from QDrag::source(), but that might be a QWidget.
303 const QPlatformScreen *platformScreen = QWindowsContext::instance()->screenManager().screenAtDp(QWindowsCursor::mousePosition());
304 if (!platformScreen) {
305 if (const QScreen *primaryScreen = QGuiApplication::primaryScreen())
306 platformScreen = primaryScreen->handle();
307 }
308 Q_ASSERT(platformScreen);
309 QPlatformCursor *platformCursor = platformScreen->cursor();
310
311 if (GetSystemMetrics (SM_REMOTESESSION) != 0) {
312 /* Workaround for RDP issues with large cursors.
313 * Touch drag window seems to work just fine...
314 * 96 pixel is a 'large' mouse cursor, according to RDP spec */
315 const int rdpLargeCursor = qRound(qreal(96) / QHighDpiScaling::factor(platformScreen));
316 if (pixmap.width() > rdpLargeCursor || pixmap.height() > rdpLargeCursor)
317 m_mode = TouchDrag;
318 }
319
320 qreal pixmapScaleFactor = 1;
321 qreal hotSpotScaleFactor = 1;
322 if (m_mode != TouchDrag) { // Touch drag: pixmap is shown in a separate QWindow, which will be scaled.)
323 hotSpotScaleFactor = QHighDpiScaling::factor(platformScreen);
324 pixmapScaleFactor = hotSpotScaleFactor / pixmap.devicePixelRatio();
325 }
326 QPixmap scaledPixmap = qFuzzyCompare(pixmapScaleFactor, 1.0)
327 ? pixmap
328 : pixmap.scaled((QSizeF(pixmap.size()) * pixmapScaleFactor).toSize(),
329 Qt::KeepAspectRatio, Qt::SmoothTransformation);
330 scaledPixmap.setDevicePixelRatio(1);
331
332 Qt::DropAction actions[] = { Qt::MoveAction, Qt::CopyAction, Qt::LinkAction, Qt::IgnoreAction };
333 int actionCount = int(sizeof(actions) / sizeof(actions[0]));
334 if (!hasPixmap)
335 --actionCount; // No Qt::IgnoreAction unless pixmap
336 const QPoint hotSpot = qFuzzyCompare(hotSpotScaleFactor, 1.0)
337 ? drag->hotSpot()
338 : (QPointF(drag->hotSpot()) * hotSpotScaleFactor).toPoint();
339 for (int cnum = 0; cnum < actionCount; ++cnum) {
340 const Qt::DropAction action = actions[cnum];
341 QPixmap cursorPixmap = drag->dragCursor(action);
342 if (cursorPixmap.isNull() && platformCursor)
343 cursorPixmap = static_cast<QWindowsCursor *>(platformCursor)->dragDefaultCursor(action);
344 const qint64 cacheKey = cursorPixmap.cacheKey();
345 const auto it = m_cursors.find(action);
346 if (it != m_cursors.end() && it.value().cacheKey == cacheKey)
347 continue;
348 if (cursorPixmap.isNull()) {
349 qWarning("%s: Unable to obtain drag cursor for %d.", __FUNCTION__, action);
350 continue;
351 }
352
353 QPoint newHotSpot(0, 0);
354 QPixmap newPixmap = cursorPixmap;
355
356 if (hasPixmap) {
357 const int x1 = qMin(-hotSpot.x(), 0);
358 const int x2 = qMax(scaledPixmap.width() - hotSpot.x(), cursorPixmap.width());
359 const int y1 = qMin(-hotSpot.y(), 0);
360 const int y2 = qMax(scaledPixmap.height() - hotSpot.y(), cursorPixmap.height());
361 QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1);
362 newCursor.fill(Qt::transparent);
363 QPainter p(&newCursor);
364 const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
365 p.drawPixmap(pmDest, scaledPixmap);
366 p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap);
367 newPixmap = newCursor;
368 newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y()));
369 }
370
371 if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newPixmap, newHotSpot)) {
372 const CursorEntry entry(newPixmap, cacheKey, CursorHandlePtr(new CursorHandle(sysCursor)), newHotSpot);
373 if (it == m_cursors.end())
374 m_cursors.insert(action, entry);
375 else
376 it.value() = entry;
377 }
378 }
379#ifndef QT_NO_DEBUG_OUTPUT
380 if (lcQpaMime().isDebugEnabled())
381 qCDebug(lcQpaMime) << __FUNCTION__ << "pixmap" << pixmap.size() << m_cursors.size() << "cursors:\n" << m_cursors;
382#endif // !QT_NO_DEBUG_OUTPUT
383}
384
385/*!
386 \brief Check for cancel.
387*/
388
389QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
390QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
391{
392 Qt::MouseButtons buttons = toQtMouseButtons(grfKeyState);
393
394 SCODE result = S_OK;
395 if (fEscapePressed || QWindowsDrag::isCanceled()) {
396 result = DRAGDROP_S_CANCEL;
397 buttons = Qt::NoButton;
398 } else {
399 if (buttons && !m_currentButtons) {
400 m_currentButtons = buttons;
401 } else if (!(m_currentButtons & buttons)) { // Button changed: Complete Drop operation.
402 result = DRAGDROP_S_DROP;
403 }
404 }
405
406 switch (result) {
407 case DRAGDROP_S_DROP:
408 case DRAGDROP_S_CANCEL:
409 if (!m_windowUnderMouse.isNull() && m_mode != TouchDrag && fEscapePressed == FALSE
410 && buttons != lastButtons) {
411 // QTBUG 66447: Synthesize a mouse release to the window under mouse at
412 // start of the DnD operation as Windows does not send any.
413 const QPoint globalPos = QWindowsCursor::mousePosition();
414 const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos);
415 QWindowSystemInterface::handleMouseEvent(m_windowUnderMouse.data(),
416 QPointF(localPos), QPointF(globalPos),
417 QWindowsMouseHandler::queryMouseButtons(),
418 Qt::LeftButton, QEvent::MouseButtonRelease);
419 }
420 m_currentButtons = Qt::NoButton;
421 break;
422
423 default:
424 QGuiApplication::processEvents();
425 break;
426 }
427
428 if (QWindowsContext::verbose > 1 || result != S_OK) {
429 qCDebug(lcQpaMime) << __FUNCTION__ << "fEscapePressed=" << fEscapePressed
430 << "grfKeyState=" << grfKeyState << "buttons" << m_currentButtons
431 << "returns 0x" << Qt::hex << int(result) << Qt::dec;
432 }
433 return ResultFromScode(result);
434}
435
436/*!
437 \brief Give feedback: Change cursor accoding to action.
438*/
439
440QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
441QWindowsOleDropSource::GiveFeedback(DWORD dwEffect)
442{
443 const Qt::DropAction action = translateToQDragDropAction(dwEffect);
444 m_drag->updateAction(action);
445
446 const qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey();
447 auto it = m_cursors.constFind(action);
448 // If a custom drag cursor is set, check its cache key to detect changes.
449 if (it == m_cursors.constEnd() || (currentCacheKey && currentCacheKey != it.value().cacheKey)) {
450 createCursors();
451 it = m_cursors.constFind(action);
452 }
453
454 if (it != m_cursors.constEnd()) {
455 const CursorEntry &e = it.value();
456 switch (m_mode) {
457 case MouseDrag:
458 SetCursor(e.cursor->handle());
459 break;
460 case TouchDrag:
461 // "Touch drag" with an unsuppressed cursor may happen with RDP (see createCursors())
462 if (QWindowsCursor::cursorState() != QWindowsCursor::State::Suppressed)
463 SetCursor(nullptr);
464 if (!m_touchDragWindow)
465 m_touchDragWindow = new QWindowsDragCursorWindow;
466 m_touchDragWindow->setPixmap(e.pixmap);
467 m_touchDragWindow->setFramePosition(QCursor::pos() - e.hotSpot);
468 if (!m_touchDragWindow->isVisible())
469 m_touchDragWindow->show();
470 break;
471 }
472 return ResultFromScode(S_OK);
473 }
474
475 return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
476}
477
478/*!
479 \class QWindowsOleDropTarget
480 \brief Implementation of IDropTarget
481
482 To be registered for each window. Currently, drop sites
483 are enabled for top levels. The child window handling
484 (sending DragEnter/Leave, etc) is handled in here.
485
486 \sa QWindowsDrag
487 \internal
488 \ingroup qt-lighthouse-win
489*/
490
491QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) : m_window(w)
492{
493 qCDebug(lcQpaMime) << __FUNCTION__ << this << w;
494}
495
496QWindowsOleDropTarget::~QWindowsOleDropTarget()
497{
498 qCDebug(lcQpaMime) << __FUNCTION__ << this;
499}
500
501void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
502 const QPoint &point, LPDWORD pdwEffect)
503{
504 Q_ASSERT(window);
505 m_lastPoint = point;
506 m_lastKeyState = grfKeyState;
507
508 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
509 const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
510
511 lastModifiers = toQtKeyboardModifiers(grfKeyState);
512 lastButtons = toQtMouseButtons(grfKeyState);
513
514 const QPlatformDragQtResponse response =
515 QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(),
516 m_lastPoint, actions,
517 lastButtons, lastModifiers);
518
519 m_answerRect = response.answerRect();
520 const Qt::DropAction action = response.acceptedAction();
521 if (response.isAccepted()) {
522 m_chosenEffect = translateToWinDragEffects(action);
523 } else {
524 m_chosenEffect = DROPEFFECT_NONE;
525 }
526 *pdwEffect = m_chosenEffect;
527 qCDebug(lcQpaMime) << __FUNCTION__ << m_window
528 << windowsDrag->dropData() << " supported actions=" << actions
529 << " mods=" << lastModifiers << " mouse=" << lastButtons
530 << " accepted: " << response.isAccepted() << action
531 << m_answerRect << " effect" << *pdwEffect;
532}
533
534QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
535QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
536 POINTL pt, LPDWORD pdwEffect)
537{
538 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
539 dh->DragEnter(reinterpret_cast<HWND>(m_window->winId()), pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
540
541 qCDebug(lcQpaMime) << __FUNCTION__ << "widget=" << m_window << " key=" << grfKeyState
542 << "pt=" << pt.x << pt.y;
543
544 QWindowsDrag::instance()->setDropDataObject(pDataObj);
545 pDataObj->AddRef();
546 const QPoint point = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
547 handleDrag(m_window, grfKeyState, point, pdwEffect);
548 return NOERROR;
549}
550
551QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
552QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
553{
554 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
555 dh->DragOver(reinterpret_cast<POINT*>(&pt), *pdwEffect);
556
557 qCDebug(lcQpaMime) << __FUNCTION__ << "m_window" << m_window << "key=" << grfKeyState
558 << "pt=" << pt.x << pt.y;
559 const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
560 // see if we should compress this event
561 if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint))
562 && m_lastKeyState == grfKeyState) {
563 *pdwEffect = m_chosenEffect;
564 qCDebug(lcQpaMime) << __FUNCTION__ << "compressed event";
565 return NOERROR;
566 }
567
568 handleDrag(m_window, grfKeyState, tmpPoint, pdwEffect);
569 return NOERROR;
570}
571
572QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
573QWindowsOleDropTarget::DragLeave()
574{
575 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
576 dh->DragLeave();
577
578 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window;
579
580 lastModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
581 lastButtons = QWindowsMouseHandler::queryMouseButtons();
582
583 QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction,
584 Qt::NoButton, Qt::NoModifier);
585
586 if (!QDragManager::self()->source())
587 m_lastKeyState = 0;
588 QWindowsDrag::instance()->releaseDropDataObject();
589
590 return NOERROR;
591}
592
593#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
594
595QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
596QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
597 POINTL pt, LPDWORD pdwEffect)
598{
599 if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper())
600 dh->Drop(pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect);
601
602 qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window
603 << "keys=" << grfKeyState << "pt=" << pt.x << ',' << pt.y;
604
605 m_lastPoint = QWindowsGeometryHint::mapFromGlobal(m_window, QPoint(pt.x,pt.y));
606
607 QWindowsDrag *windowsDrag = QWindowsDrag::instance();
608
609 lastModifiers = toQtKeyboardModifiers(grfKeyState);
610 lastButtons = toQtMouseButtons(grfKeyState);
611
612 const QPlatformDropQtResponse response =
613 QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(),
614 m_lastPoint,
615 translateToQDragDropActions(*pdwEffect),
616 lastButtons,
617 lastModifiers);
618
619 m_lastKeyState = grfKeyState;
620
621 if (response.isAccepted()) {
622 const Qt::DropAction action = response.acceptedAction();
623 if (action == Qt::MoveAction || action == Qt::TargetMoveAction) {
624 if (action == Qt::MoveAction)
625 m_chosenEffect = DROPEFFECT_MOVE;
626 else
627 m_chosenEffect = DROPEFFECT_COPY;
628 HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
629 if (hData) {
630 auto *moveEffect = reinterpret_cast<DWORD *>(GlobalLock(hData));
631 *moveEffect = DROPEFFECT_MOVE;
632 GlobalUnlock(hData);
633 STGMEDIUM medium;
634 memset(&medium, 0, sizeof(STGMEDIUM));
635 medium.tymed = TYMED_HGLOBAL;
636 medium.hGlobal = hData;
637 FORMATETC format;
638 format.cfFormat = CLIPFORMAT(RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT));
639 format.tymed = TYMED_HGLOBAL;
640 format.ptd = nullptr;
641 format.dwAspect = 1;
642 format.lindex = -1;
643 windowsDrag->dropDataObject()->SetData(&format, &medium, true);
644 }
645 } else {
646 m_chosenEffect = translateToWinDragEffects(action);
647 }
648 } else {
649 m_chosenEffect = DROPEFFECT_NONE;
650 }
651 *pdwEffect = m_chosenEffect;
652
653 windowsDrag->releaseDropDataObject();
654 return NOERROR;
655}
656
657
658/*!
659 \class QWindowsDrag
660 \brief Windows drag implementation.
661 \internal
662 \ingroup qt-lighthouse-win
663*/
664
665bool QWindowsDrag::m_canceled = false;
666bool QWindowsDrag::m_dragging = false;
667
668QWindowsDrag::QWindowsDrag() = default;
669
670QWindowsDrag::~QWindowsDrag()
671{
672 if (m_cachedDropTargetHelper)
673 m_cachedDropTargetHelper->Release();
674}
675
676/*!
677 \brief Return data for a drop in process. If it stems from a current drag, use a shortcut.
678*/
679
680QMimeData *QWindowsDrag::dropData()
681{
682 if (const QDrag *drag = currentDrag())
683 return drag->mimeData();
684 return &m_dropData;
685}
686
687/*!
688 \brief May be used to handle extended cursors functionality for drags from outside the app.
689*/
690IDropTargetHelper* QWindowsDrag::dropHelper() {
691 if (!m_cachedDropTargetHelper) {
692 CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER,
693 IID_IDropTargetHelper,
694 reinterpret_cast<void**>(&m_cachedDropTargetHelper));
695 }
696 return m_cachedDropTargetHelper;
697}
698
699Qt::DropAction QWindowsDrag::drag(QDrag *drag)
700{
701 // TODO: Accessibility handling?
702 QMimeData *dropData = drag->mimeData();
703 Qt::DropAction dragResult = Qt::IgnoreAction;
704
705 DWORD resultEffect;
706 QWindowsDrag::m_canceled = false;
707 auto *windowDropSource = new QWindowsOleDropSource(this);
708 windowDropSource->createCursors();
709 auto *dropDataObject = new QWindowsDropDataObject(dropData);
710 const Qt::DropActions possibleActions = drag->supportedActions();
711 const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
712 qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
713 << Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec;
714 // Indicate message handlers we are in DoDragDrop() event loop.
715 QWindowsDrag::m_dragging = true;
716 const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
717 QWindowsDrag::m_dragging = false;
718 const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
719 if (r == DRAGDROP_S_DROP) {
720 if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
721 dragResult = Qt::TargetMoveAction;
722 resultEffect = DROPEFFECT_MOVE;
723 } else {
724 dragResult = translateToQDragDropAction(resultEffect);
725 }
726 // Force it to be a copy if an unsupported operation occurred.
727 // This indicates a bug in the drop target.
728 if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects)) {
729 qWarning("%s: Forcing Qt::CopyAction", __FUNCTION__);
730 dragResult = Qt::CopyAction;
731 }
732 }
733 // clean up
734 dropDataObject->releaseQt();
735 dropDataObject->Release(); // Will delete obj if refcount becomes 0
736 windowDropSource->Release(); // Will delete src if refcount becomes 0
737 qCDebug(lcQpaMime) << '<' << __FUNCTION__ << Qt::hex << "allowedEffects=0x" << allowedEffects
738 << "reportedPerformedEffect=0x" << reportedPerformedEffect
739 << " resultEffect=0x" << resultEffect << "hr=0x" << int(r) << Qt::dec << "dropAction=" << dragResult;
740 return dragResult;
741}
742
743QWindowsDrag *QWindowsDrag::instance()
744{
745 return static_cast<QWindowsDrag *>(QWindowsIntegration::instance()->drag());
746}
747
748void QWindowsDrag::releaseDropDataObject()
749{
750 qCDebug(lcQpaMime) << __FUNCTION__ << m_dropDataObject;
751 if (m_dropDataObject) {
752 m_dropDataObject->Release();
753 m_dropDataObject = nullptr;
754 }
755}
756
757QT_END_NAMESPACE
758

Warning: That file was not part of the compilation database. It may have many parsing errors.