Add Windows to the Lighthouse.
[profile/ivi/qtbase.git] / src / plugins / platforms / windows / qwindowsdrag.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (info@qt.nokia.com)
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qwindowsdrag.h"
43 #include "qwindowscontext.h"
44 #include "qwindowsclipboard.h"
45 #include "qwindowsintegration.h"
46 #include "qwindowsole.h"
47 #include "qtwindows_additional.h"
48 #include "qwindowswindow.h"
49 #include "qwindowsmousehandler.h"
50 #include "qwindowscursor.h"
51
52 #include <QtGui/QMouseEvent>
53 #include <QtGui/QPixmap>
54 #include <QtGui/QPainter>
55 #include <QtGui/QGuiApplication>
56
57 #include <QtCore/QDebug>
58 #include <QtCore/QPoint>
59
60 #include <shlobj.h>
61
62 QT_BEGIN_NAMESPACE
63
64 /*!
65     \class QWindowsDropMimeData
66     \brief Special mime data class for data retrieval from Drag operations.
67
68     Implementation of QWindowsInternalMimeDataBase which retrieves the
69     current drop data object from QWindowsDrag.
70
71     \sa QWindowsDrag
72     \ingroup qt-lighthouse-win
73 */
74
75 IDataObject *QWindowsDropMimeData::retrieveDataObject() const
76 {
77     return QWindowsDrag::instance()->dropDataObject();
78 }
79
80 static inline Qt::DropActions translateToQDragDropActions(DWORD pdwEffects)
81 {
82     Qt::DropActions actions = Qt::IgnoreAction;
83     if (pdwEffects & DROPEFFECT_LINK)
84         actions |= Qt::LinkAction;
85     if (pdwEffects & DROPEFFECT_COPY)
86         actions |= Qt::CopyAction;
87     if (pdwEffects & DROPEFFECT_MOVE)
88         actions |= Qt::MoveAction;
89     return actions;
90 }
91
92 static inline Qt::DropAction translateToQDragDropAction(DWORD pdwEffect)
93 {
94     if (pdwEffect & DROPEFFECT_LINK)
95         return Qt::LinkAction;
96     if (pdwEffect & DROPEFFECT_COPY)
97         return Qt::CopyAction;
98     if (pdwEffect & DROPEFFECT_MOVE)
99         return Qt::MoveAction;
100     return Qt::IgnoreAction;
101 }
102
103 static inline DWORD translateToWinDragEffects(Qt::DropActions action)
104 {
105     DWORD effect = DROPEFFECT_NONE;
106     if (action & Qt::LinkAction)
107         effect |= DROPEFFECT_LINK;
108     if (action & Qt::CopyAction)
109         effect |= DROPEFFECT_COPY;
110     if (action & Qt::MoveAction)
111         effect |= DROPEFFECT_MOVE;
112     return effect;
113 }
114
115 static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState)
116 {
117     Qt::KeyboardModifiers modifiers = Qt::NoModifier;
118
119     if (keyState & MK_SHIFT)
120         modifiers |= Qt::ShiftModifier;
121     if (keyState & MK_CONTROL)
122         modifiers |= Qt::ControlModifier;
123     if (keyState & MK_ALT)
124         modifiers |= Qt::AltModifier;
125
126     return modifiers;
127 }
128
129 /*!
130     \class QWindowsOleDropSource
131     \brief Implementation of IDropSource
132
133     Used for drag operations.
134
135     \sa QWindowsDrag
136     \ingroup qt-lighthouse-win
137 */
138
139 class QWindowsOleDropSource : public IDropSource
140 {
141 public:
142     QWindowsOleDropSource();
143     virtual ~QWindowsOleDropSource();
144
145     void createCursors();
146
147     // IUnknown methods
148     STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
149     STDMETHOD_(ULONG,AddRef)(void);
150     STDMETHOD_(ULONG,Release)(void);
151
152     // IDropSource methods
153     STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
154     STDMETHOD(GiveFeedback)(DWORD dwEffect);
155
156 private:
157     typedef QMap <Qt::DropAction, HCURSOR> ActionCursorMap;
158
159     inline void clearCursors();
160
161     Qt::MouseButtons m_currentButtons;
162     Qt::DropAction m_currentAction;
163     ActionCursorMap m_cursors;
164
165     ULONG m_refs;
166 };
167
168 QWindowsOleDropSource::QWindowsOleDropSource() :
169     m_currentButtons(Qt::NoButton), m_currentAction(Qt::IgnoreAction),
170     m_refs(1)
171 {
172     if (QWindowsContext::verboseOLE)
173         qDebug("%s", __FUNCTION__);
174 }
175
176 QWindowsOleDropSource::~QWindowsOleDropSource()
177 {
178     clearCursors();
179     if (QWindowsContext::verboseOLE)
180         qDebug("%s", __FUNCTION__);
181 }
182
183 void QWindowsOleDropSource::createCursors()
184 {
185     QDragManager *manager = QDragManager::self();
186     if (!manager || !manager->object)
187         return;
188     const QPixmap pixmap = manager->object->pixmap();
189     const bool hasPixmap = !pixmap.isNull();
190     if (!hasPixmap && manager->dragPrivate()->customCursors.isEmpty())
191         return;
192
193     QList<Qt::DropAction> actions;
194     actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction;
195     if (hasPixmap)
196         actions << Qt::IgnoreAction;
197     const QPoint hotSpot = manager->object->hotSpot();
198     for (int cnum = 0; cnum < actions.size(); ++cnum) {
199         const QPixmap cpm = manager->dragCursor(actions.at(cnum));
200         int w = cpm.width();
201         int h = cpm.height();
202
203         if (hasPixmap) {
204             const int x1 = qMin(-hotSpot.x(), 0);
205             const int x2 = qMax(pixmap.width() - hotSpot.x(), cpm.width());
206             const int y1 = qMin(-hotSpot.y(), 0);
207             const int y2 = qMax(pixmap.height() - hotSpot.y(), cpm.height());
208
209             w = x2 - x1 + 1;
210             h = y2 - y1 + 1;
211         }
212
213         const QRect srcRect = pixmap.rect();
214         const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y()));
215         const QPoint newHotSpot = hotSpot;
216         QPixmap newCursor(w, h);
217         if (hasPixmap) {
218             newCursor.fill(QColor(0, 0, 0, 0));
219             QPainter p(&newCursor);
220             p.drawPixmap(pmDest, pixmap, srcRect);
221             p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm);
222         } else {
223             newCursor = cpm;
224         }
225
226         const int hotX = hasPixmap ? qMax(0,newHotSpot.x()) : 0;
227         const int hotY = hasPixmap ? qMax(0,newHotSpot.y()) : 0;
228
229         if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY))
230             m_cursors.insert(actions.at(cnum), sysCursor);
231     }
232     if (QWindowsContext::verboseOLE)
233         qDebug("%s %d cursors", __FUNCTION__, m_cursors.size());
234 }
235
236 void QWindowsOleDropSource::clearCursors()
237 {
238     if (!m_cursors.isEmpty()) {
239         const ActionCursorMap::const_iterator cend = m_cursors.constEnd();
240         for (ActionCursorMap::const_iterator it = m_cursors.constBegin(); it != cend; ++it)
241             DestroyCursor(it.value());
242         m_cursors.clear();
243     }
244 }
245
246 //---------------------------------------------------------------------
247 //                    IUnknown Methods
248 //---------------------------------------------------------------------
249
250 STDMETHODIMP
251 QWindowsOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv)
252 {
253     if (iid == IID_IUnknown || iid == IID_IDropSource) {
254       *ppv = this;
255       ++m_refs;
256       return NOERROR;
257     }
258     *ppv = NULL;
259     return ResultFromScode(E_NOINTERFACE);
260 }
261
262 STDMETHODIMP_(ULONG)
263 QWindowsOleDropSource::AddRef(void)
264 {
265     return ++m_refs;
266 }
267
268 STDMETHODIMP_(ULONG)
269 QWindowsOleDropSource::Release(void)
270 {
271     if (--m_refs == 0) {
272       delete this;
273       return 0;
274     }
275     return m_refs;
276 }
277
278 /*!
279     \brief Check for cancel.
280 */
281
282 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
283 QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
284 {
285     HRESULT hr = S_OK;
286     do {
287         if (fEscapePressed || QWindowsDrag::instance()->dragBeingCancelled()) {
288             hr = ResultFromScode(DRAGDROP_S_CANCEL);
289             break;
290         }
291
292     // grfKeyState is broken on CE & some Windows XP versions,
293     // therefore we need to check the state manually
294     if ((GetAsyncKeyState(VK_LBUTTON) == 0)
295         && (GetAsyncKeyState(VK_MBUTTON) == 0)
296         && (GetAsyncKeyState(VK_RBUTTON) == 0)) {
297         hr = ResultFromScode(DRAGDROP_S_DROP);
298         break;
299     }
300
301     const Qt::MouseButtons buttons =  QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState);
302     if (m_currentButtons == Qt::NoButton) {
303         m_currentButtons = buttons;
304     } else {
305         // Button changed: Complete Drop operation.
306         if (!(m_currentButtons & buttons)) {
307             hr = ResultFromScode(DRAGDROP_S_DROP);
308             break;
309         }
310     }
311
312     QGuiApplication::processEvents();
313
314     } while (false);
315
316     QDragManager::self()->willDrop = hr == DRAGDROP_S_DROP;
317
318     if (QWindowsContext::verboseOLE
319         && (QWindowsContext::verboseOLE > 1 || hr != S_OK))
320         qDebug("%s fEscapePressed=%d, grfKeyState=%lu buttons=%d willDrop = %d returns 0x%x",
321                __FUNCTION__, fEscapePressed,grfKeyState, int(m_currentButtons),
322                QDragManager::self()->willDrop, int(hr));
323     return hr;
324 }
325
326 /*!
327     \brief Give feedback: Change cursor accoding to action.
328 */
329
330 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
331 QWindowsOleDropSource::GiveFeedback(DWORD dwEffect)
332 {
333     const Qt::DropAction action = translateToQDragDropAction(dwEffect);
334
335     if (QWindowsContext::verboseOLE > 2)
336         qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action);
337
338     if (m_currentAction != action) {
339         m_currentAction = action;
340         QDragManager::self()->emitActionChanged(m_currentAction);
341     }
342
343     const ActionCursorMap::const_iterator it = m_cursors.constFind(m_currentAction);
344     if (it != m_cursors.constEnd()) {
345         SetCursor(it.value());
346         return ResultFromScode(S_OK);
347     }
348
349     return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS);
350 }
351
352 /*!
353     \class QWindowsOleDropTarget
354     \brief Implementation of IDropTarget
355
356     To be registered for each window. Currently, drop sites
357     are enabled for top levels. The child window handling
358     (sending DragEnter/Leave, etc) is handled in here.
359
360     \sa QWindowsDrag
361     \ingroup qt-lighthouse-win
362 */
363
364 QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) :
365     m_refs(1), m_window(w), m_currentWindow(0), m_chosenEffect(0), m_lastKeyState(0)
366 {
367     if (QWindowsContext::verboseOLE)
368         qDebug() << __FUNCTION__ <<  this << w;
369 }
370
371 QWindowsOleDropTarget::~QWindowsOleDropTarget()
372 {
373     if (QWindowsContext::verboseOLE)
374         qDebug("%s %p", __FUNCTION__, this);
375 }
376
377 STDMETHODIMP
378 QWindowsOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv)
379 {
380     if (iid == IID_IUnknown || iid == IID_IDropTarget) {
381       *ppv = this;
382       AddRef();
383       return NOERROR;
384     }
385     *ppv = NULL;
386     return ResultFromScode(E_NOINTERFACE);
387 }
388
389 STDMETHODIMP_(ULONG)
390 QWindowsOleDropTarget::AddRef(void)
391 {
392     return ++m_refs;
393 }
394
395 STDMETHODIMP_(ULONG)
396 QWindowsOleDropTarget::Release(void)
397 {
398     if (--m_refs == 0) {
399       delete this;
400       return 0;
401     }
402     return m_refs;
403 }
404
405 QWindow *QWindowsOleDropTarget::findDragOverWindow(const POINTL &pt) const
406 {
407     if (QWindowsWindow *child =
408             QWindowsWindow::baseWindowOf(m_window)->childAtScreenPoint(QPoint(pt.x, pt.y)))
409             return child->window();
410     return m_window;
411 }
412
413 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
414 QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState,
415                                  POINTL pt, LPDWORD pdwEffect)
416 {
417     if (QWindowsContext::verboseOLE)
418         qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, m_window, grfKeyState, pt.x, pt.y);
419
420     QWindowsDrag::instance()->setDropDataObject(pDataObj);
421     pDataObj->AddRef();
422     m_currentWindow = m_window;
423     sendDragEnterEvent(m_window, grfKeyState, pt, pdwEffect);
424     *pdwEffect = m_chosenEffect;
425     return NOERROR;
426 }
427
428 void QWindowsOleDropTarget::sendDragEnterEvent(QWindow *dragEnterWidget,
429                                                DWORD grfKeyState,
430                                                POINTL pt, LPDWORD pdwEffect)
431 {
432     Q_ASSERT(dragEnterWidget);
433
434     m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dragEnterWidget, QPoint(pt.x,pt.y));
435     m_lastKeyState = grfKeyState;
436
437     m_chosenEffect = DROPEFFECT_NONE;
438
439     QDragManager *manager = QDragManager::self();
440     QMimeData *md = manager->dropData();
441     const Qt::MouseButtons mouseButtons
442         = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState);
443     const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
444     const Qt::KeyboardModifiers keyMods = toQtKeyboardModifiers(grfKeyState);
445     QDragEnterEvent enterEvent(m_lastPoint, actions, md, mouseButtons, keyMods);
446     QGuiApplication::sendEvent(m_currentWindow, &enterEvent);
447     m_answerRect = enterEvent.answerRect();
448     if (QWindowsContext::verboseOLE)
449         qDebug() << __FUNCTION__ << " sent drag enter to " << m_window
450                  << *md << " actions=" << actions
451                  << " mods=" << keyMods << " accepted: "
452                  << enterEvent.isAccepted();
453
454     if (enterEvent.isAccepted())
455         m_chosenEffect = translateToWinDragEffects(enterEvent.dropAction());
456     // Documentation states that a drag move event is sent immediately after
457     // a drag enter event. This will honor widgets overriding dragMoveEvent only:
458     if (enterEvent.isAccepted()) {
459         QDragMoveEvent moveEvent(m_lastPoint, actions, md, mouseButtons, keyMods);
460         m_answerRect = enterEvent.answerRect();
461         moveEvent.setDropAction(enterEvent.dropAction());
462         moveEvent.accept(); // accept by default, since enter event was accepted.
463
464         QGuiApplication::sendEvent(dragEnterWidget, &moveEvent);
465         if (moveEvent.isAccepted()) {
466             m_answerRect = moveEvent.answerRect();
467             m_chosenEffect = translateToWinDragEffects(moveEvent.dropAction());
468         } else {
469             m_chosenEffect = DROPEFFECT_NONE;
470         }
471     }
472 }
473
474 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
475 QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
476 {
477     QWindow *dragOverWindow = findDragOverWindow(pt);
478
479     const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(dragOverWindow, QPoint(pt.x,pt.y));
480     // see if we should compress this event
481     if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint))
482         && m_lastKeyState == grfKeyState) {
483         *pdwEffect = m_chosenEffect;
484         return NOERROR;
485     }
486
487     if (QWindowsContext::verboseOLE > 1)
488         qDebug().nospace() << '>' << __FUNCTION__ << ' ' << m_window << " current "
489                            << dragOverWindow << " key=" << grfKeyState
490                            << " pt=" <<pt.x << ',' << pt.y;
491
492     if (dragOverWindow != m_currentWindow) {
493         QPointer<QWindow> dragOverWindowGuard(dragOverWindow);
494         // Send drag leave event to the previous drag widget.
495         // Drag-Over widget might be deleted in DragLeave,
496         // (tasktracker 218353).
497         QDragLeaveEvent dragLeave;
498         if (m_currentWindow)
499             QGuiApplication::sendEvent(m_currentWindow, &dragLeave);
500         if (!dragOverWindowGuard) {
501             dragOverWindow = findDragOverWindow(pt);
502         }
503         // Send drag enter event to the current drag widget.
504         m_currentWindow = dragOverWindow;
505         sendDragEnterEvent(dragOverWindow, grfKeyState, pt, pdwEffect);
506     }
507
508     QDragManager *manager = QDragManager::self();
509     QMimeData *md = manager->dropData();
510
511     const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
512
513     QDragMoveEvent oldEvent(m_lastPoint, actions, md,
514                             QWindowsMouseHandler::keyStateToMouseButtons(m_lastKeyState),
515                             toQtKeyboardModifiers(m_lastKeyState));
516
517     m_lastPoint = tmpPoint;
518     m_lastKeyState = grfKeyState;
519
520     QDragMoveEvent e(tmpPoint, actions, md,
521                      QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState),
522                      toQtKeyboardModifiers(grfKeyState));
523     if (m_chosenEffect != DROPEFFECT_NONE) {
524         if (oldEvent.dropAction() == e.dropAction() &&
525             oldEvent.keyboardModifiers() == e.keyboardModifiers())
526             e.setDropAction(translateToQDragDropAction(m_chosenEffect));
527         e.accept();
528     }
529     QGuiApplication::sendEvent(dragOverWindow, &e);
530
531     m_answerRect = e.answerRect();
532     if (e.isAccepted())
533         m_chosenEffect = translateToWinDragEffects(e.dropAction());
534     else
535         m_chosenEffect = DROPEFFECT_NONE;
536     *pdwEffect = m_chosenEffect;
537
538     if (QWindowsContext::verboseOLE > 1)
539         qDebug("<%s effect=0x%lx", __FUNCTION__, m_chosenEffect);
540     return NOERROR;
541 }
542
543 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
544 QWindowsOleDropTarget::DragLeave()
545 {
546     if (QWindowsContext::verboseOLE)
547         qDebug().nospace() <<__FUNCTION__ << ' ' << m_window;
548
549     m_currentWindow = 0;
550     QDragLeaveEvent e;
551     QGuiApplication::sendEvent(m_window, &e);
552     QWindowsDrag::instance()->releaseDropDataObject();
553
554     return NOERROR;
555 }
556
557 #define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)
558
559 QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
560 QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState,
561                             POINTL pt, LPDWORD pdwEffect)
562 {
563     QWindow *dropWindow = findDragOverWindow(pt);
564
565     if (QWindowsContext::verboseOLE)
566         qDebug().nospace() << __FUNCTION__ << ' ' << m_window
567                            << " on " << dropWindow
568                            << " keys=" << grfKeyState << " pt="
569                            << pt.x << ',' << pt.y;
570
571     m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dropWindow, QPoint(pt.x,pt.y));
572     // grfKeyState does not all ways contain button state in the drop so if
573     // it doesn't then use the last known button state;
574     if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0)
575         grfKeyState |= m_lastKeyState & KEY_STATE_BUTTON_MASK;
576     m_lastKeyState = grfKeyState;
577
578     QWindowsDrag *windowsDrag = QWindowsDrag::instance();
579     QDragManager *manager = QDragManager::self();
580     QMimeData *md = manager->dropData();
581     QDropEvent e(m_lastPoint, translateToQDragDropActions(*pdwEffect), md,
582                  QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState),
583                  toQtKeyboardModifiers(grfKeyState));
584     if (m_chosenEffect != DROPEFFECT_NONE)
585         e.setDropAction(translateToQDragDropAction(m_chosenEffect));
586
587     QGuiApplication::sendEvent(dropWindow, &e);
588     if (m_chosenEffect != DROPEFFECT_NONE)
589         e.accept();
590
591     if (e.isAccepted()) {
592         if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) {
593             if (e.dropAction() == Qt::MoveAction)
594                 m_chosenEffect = DROPEFFECT_MOVE;
595             else
596                 m_chosenEffect = DROPEFFECT_COPY;
597             HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
598             if (hData) {
599                 DWORD *moveEffect = (DWORD *)GlobalLock(hData);;
600                 *moveEffect = DROPEFFECT_MOVE;
601                 GlobalUnlock(hData);
602                 STGMEDIUM medium;
603                 memset(&medium, 0, sizeof(STGMEDIUM));
604                 medium.tymed = TYMED_HGLOBAL;
605                 medium.hGlobal = hData;
606                 FORMATETC format;
607                 format.cfFormat = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
608                 format.tymed = TYMED_HGLOBAL;
609                 format.ptd = 0;
610                 format.dwAspect = 1;
611                 format.lindex = -1;
612                 windowsDrag->dropDataObject()->SetData(&format, &medium, true);
613             }
614         } else {
615             m_chosenEffect = translateToWinDragEffects(e.dropAction());
616         }
617     } else {
618         m_chosenEffect = DROPEFFECT_NONE;
619     }
620     *pdwEffect = m_chosenEffect;
621
622     windowsDrag->releaseDropDataObject();
623     return NOERROR;
624 }
625
626 /*!
627     \class QWindowsDrag
628     \brief Windows drag implementation.
629
630     \ingroup qt-lighthouse-win
631 */
632
633 QWindowsDrag::QWindowsDrag() : m_dropDataObject(0), m_dragBeingCancelled(false)
634 {
635 }
636
637 QWindowsDrag::~QWindowsDrag()
638 {
639 }
640
641 void QWindowsDrag::startDrag()
642 {
643     // TODO: Accessibility handling?
644     QDragManager *dragManager = QDragManager::self();
645     QMimeData *dropData = dragManager->dropData();
646     m_dragBeingCancelled = false;
647
648     DWORD resultEffect;
649     QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource();
650     windowDropSource->createCursors();
651     QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData);
652     const  Qt::DropActions possibleActions = dragManager->possible_actions;
653     const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
654     if (QWindowsContext::verboseOLE)
655           qDebug(">%s possible Actions=%x, effects=0x%lx", __FUNCTION__,
656                  int(possibleActions), allowedEffects);
657     const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
658     const DWORD  reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
659     Qt::DropAction ret = Qt::IgnoreAction;
660     if (r == DRAGDROP_S_DROP) {
661         if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
662             ret = Qt::TargetMoveAction;
663             resultEffect = DROPEFFECT_MOVE;
664         } else {
665             ret = translateToQDragDropAction(resultEffect);
666         }
667         // Force it to be a copy if an unsupported operation occurred.
668         // This indicates a bug in the drop target.
669         if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects))
670             ret = Qt::CopyAction;
671     } else {
672         dragManager->setCurrentTarget(0);
673     }
674
675     // clean up
676     dropDataObject->releaseQt();
677     dropDataObject->Release();        // Will delete obj if refcount becomes 0
678     windowDropSource->Release();        // Will delete src if refcount becomes 0
679     if (QWindowsContext::verboseOLE)
680         qDebug("<%s allowedEffects=0x%lx, reportedPerformedEffect=0x%lx, resultEffect=0x%lx, hr=0x%x, dropAction=%d",
681                __FUNCTION__, allowedEffects, reportedPerformedEffect, resultEffect, int(r), ret);
682 }
683
684 void QWindowsDrag::move(const QMouseEvent *me)
685 {
686     const QPoint pos = me->pos();
687     if (QWindowsContext::verboseOLE)
688         qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y());
689 }
690
691 void QWindowsDrag::drop(const QMouseEvent *me)
692 {
693     const QPoint pos = me->pos();
694     if (QWindowsContext::verboseOLE)
695         qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y());
696 }
697
698 void QWindowsDrag::cancel()
699 {
700     // TODO: Accessibility handling?
701     if (QWindowsContext::verboseOLE)
702         qDebug("%s", __FUNCTION__);
703     m_dragBeingCancelled = true;
704 }
705
706 QWindowsDrag *QWindowsDrag::instance()
707 {
708     return static_cast<QWindowsDrag *>(QWindowsIntegration::instance()->drag());
709 }
710
711 void QWindowsDrag::releaseDropDataObject()
712 {
713     if (QWindowsContext::verboseOLE)
714         qDebug("%s %p", __FUNCTION__, m_dropDataObject);
715     if (m_dropDataObject) {
716         m_dropDataObject->Release();
717         m_dropDataObject = 0;
718     }
719 }
720
721 QT_END_NAMESPACE