source: trunk/src/gui/kernel/qdnd_pm.cpp@ 693

Last change on this file since 693 was 659, checked in by Dmitry A. Kuminov, 16 years ago

global: Updated year to 2010 in OS/2-specific headers.

File size: 47.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2010 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qapplication.h"
45
46#include "qapplication_p.h"
47#include "qevent.h"
48#include "qpainter.h"
49#include "qwidget.h"
50#include "qbuffer.h"
51#include "qdatastream.h"
52#include "qcursor.h"
53#include "qdesktopwidget.h"
54#include "qfile.h"
55#include "qfileinfo.h"
56#include "qdnd_p.h"
57#include "qdebug.h"
58
59#ifndef QT_NO_ACCESSIBILITY
60#include "qaccessible.h"
61#endif
62
63#include "qt_os2.h"
64#include "private/qpmobjectwindow_pm_p.h"
65
66//#define QDND_DEBUG // in pair with qmime_pm.cpp
67
68#ifdef QDND_DEBUG
69# define DEBUG(a) qDebug a
70#else
71# define DEBUG(a) do {} while(0)
72#endif
73
74QT_BEGIN_NAMESPACE
75
76#if !defined(QT_NO_DRAGANDDROP) && !defined(QT_NO_CLIPBOARD)
77
78extern void qt_pmMouseButtonUp(); // defined in qapplication_pm.cpp
79extern void qt_DrgFreeDragtransfer(DRAGTRANSFER *xfer); // defined in qmime_pm.cpp
80
81#ifdef QDND_DEBUG
82extern QString dragActionsToString(Qt::DropActions actions);
83#endif
84
85/** \internal
86 * Data for QDragEnterEvent/QDragMoveEvent/QPMDropEvent.
87 */
88class QPMDragData
89{
90public:
91 QPMDragData();
92 ~QPMDragData();
93
94 void initialize(DRAGINFO *di);
95 void reset(bool isAccepted);
96 void reset() { reset(false); }
97
98 void setInFakeDrop(bool d) { fakeDrop = d; }
99 bool inFakeDrop() const { return fakeDrop; }
100
101 DRAGINFO *info() const { return di; }
102 bool canSyncRender() const { return canSyncRndr; }
103
104 bool hasFormat_sys(const QString &mimeType);
105 QStringList formats_sys();
106 QVariant retrieveData_sys(const QString &mimeType,
107 QVariant::Type preferredType);
108
109private:
110 friend class QDragManager;
111
112 void initWorkers();
113
114 bool initialized : 1;
115 bool fakeDrop : 1;
116 bool gotWorkers : 1;
117 bool canSyncRndr : 1;
118
119 DRAGINFO *di;
120 QList<QPMMime::DropWorker*> workers;
121
122 QWidget *lastDragOverWidget; // last target widget
123 USHORT lastDragOverOp; // last DM_DRAGOVER operation
124
125 USHORT supportedOps; // operations supported by all items
126 bool sourceAllowsOp : 1; // does source allow requested operation
127
128 USHORT lastDropReply; // last reply to DM_DRAGOVER
129
130 Qt::DropAction lastAction; // last target-accepted action
131 QRect lastAnswerRect; // last accepted rectangle from the target
132};
133
134QPMDragData::QPMDragData()
135 : initialized(false), fakeDrop(false)
136 , gotWorkers(false), canSyncRndr(false), di(NULL)
137 , lastDragOverWidget(0), lastDragOverOp(0)
138 , supportedOps(0), sourceAllowsOp(false)
139 , lastDropReply(DOR_NEVERDROP)
140 , lastAction(Qt::CopyAction)
141{
142}
143
144QPMDragData::~QPMDragData()
145{
146 reset();
147}
148
149void QPMDragData::initialize(DRAGINFO *info)
150{
151 Q_ASSERT(info);
152 if (initialized || !info)
153 return;
154
155 initialized = true;
156 di = info;
157
158 // Detect if the source supports synchronous rendering which means it can
159 // process DM_RENDER and reply with DM_RENDERCOMPLETE from within both
160 // DM_DRAGOVER and DM_DROP (i.e. while it is running the DnD session with
161 // DrgDrag()). Mozilla apps are known to only support synchronous rendering
162 // (an attempt to send DM_RENDER to them after DM_DROP will crash them).
163 // By the contrast, EPM only supports asyncronous rendering (it will hang
164 // if we send DM_RENDER and/or wait for DM_RENDERCOMPLETE before returning
165 // from DM_DRAGOVER/DM_DROP). WPS (e.g. desktop folders) seem to support
166 // both (we prefer the syncrhonous rendering in such cases since it lets
167 // Qt apps access dragged data right from QDragEnterEvent/QDragLeaveEvent
168 // and QDropEvent message handlers which is used by many of them for better
169 // visualization.
170
171 // get class name
172 QByteArray className(256, '\0');
173 LONG classNameLen = WinQueryClassName(info->hwndSource,
174 className.size(), className.data());
175 className.truncate(classNameLen);
176
177 DEBUG(("QPMDragData::initialize: info->hwndSource %08lX className \"%s\"",
178 info->hwndSource, QString::fromLocal8Bit(className).toUtf8().data()));
179
180 // get EXE name
181 PID pid = 0;
182 TID tid = 0;
183 QByteArray exePath(CCHMAXPATH, '\0');
184 BOOL ok = WinQueryWindowProcess(info->hwndSource, &pid, &tid);
185 if (ok) {
186 QByteArray buf(4192, '\0');
187 APIRET arc = DosQuerySysState(QS_PROCESS, 0, pid, tid,
188 buf.data(), buf.size());
189 if (arc == NO_ERROR) {
190 qsGrec_t *grec = *(qsGrec_t **)buf.data();
191 qsPrec_t *prec = (qsPrec_t *)((ULONG)grec + sizeof(qsGrec_t));
192 arc = DosQueryModuleName(prec->hMte, exePath.size(), exePath.data());
193 if (arc == NO_ERROR)
194 exePath.truncate(qstrlen(exePath));
195 }
196 }
197
198 DEBUG(("QPMDragData::initialize: pid %lu exePath \"%s\"",
199 pid, QString::fromLocal8Bit(exePath).toUtf8().data()));
200
201 QString exeName = QFileInfo(QFile::decodeName(exePath)).fileName();
202
203 // start with a positive
204 canSyncRndr = true;
205
206 if (exeName.toUpper() == QLatin1String("EPM.EXE") &&
207 qstrcmp(className, "FileWnd") == 0) {
208 // EPM only supports async rendering
209 canSyncRndr = false;
210 }
211
212 DEBUG(("QPMDragData::initialize: canSyncRender %d", canSyncRndr));
213}
214
215void QPMDragData::initWorkers()
216{
217 Q_ASSERT(initialized);
218 if (!initialized || gotWorkers)
219 return;
220
221 gotWorkers = true;
222
223 // go through all converters and collect DropWorkers to use
224 foreach (QPMMime *mime, QPMMime::all()) {
225 QPMMime::DropWorker *wrk = mime->dropWorkerFor(di);
226 if (wrk) {
227 if (wrk->isExclusive()) {
228 // ignore all other workers if some of them identified itself
229 // as exclusive
230 workers.clear();
231 workers.append(wrk);
232 break;
233 }
234 // ensure there are no duplicateseed
235 if (!workers.contains(wrk))
236 workers.append(wrk);
237 }
238 }
239
240 DEBUG(() << "QPMDragData:" << workers.count() << "drop workers for DRAGINFO" << di);
241
242 // init all workers
243 foreach (QPMMime::DropWorker *w, workers) {
244 w->nfo = di;
245 w->init();
246 }
247}
248
249void QPMDragData::reset(bool isAccepted)
250{
251 if (!initialized)
252 return;
253
254 // cleanup all workers
255 foreach (QPMMime::DropWorker *w, workers) {
256 w->cleanup(isAccepted);
257 w->nfo = 0;
258 }
259
260 workers.clear();
261 di = 0;
262 initialized = fakeDrop = gotWorkers = canSyncRndr = false;
263}
264
265bool QPMDragData::hasFormat_sys(const QString &mimeType)
266{
267 if (!gotWorkers)
268 initWorkers();
269
270 foreach (QPMMime::DropWorker *w, workers)
271 if (w->hasFormat(mimeType))
272 return true;
273
274 return false;
275}
276
277QStringList QPMDragData::formats_sys()
278{
279 QStringList mimes;
280
281 if (!gotWorkers)
282 initWorkers();
283
284 foreach (QPMMime::DropWorker *w, workers)
285 mimes << w->formats();
286
287 return mimes;
288}
289
290QVariant QPMDragData::retrieveData_sys(const QString &mimeType,
291 QVariant::Type preferredType)
292{
293 QVariant result;
294
295 // If the source doesn't support synchronous rendering, we may only do data
296 // transfer after DM_DROP is processed by us. Return shortly.
297 if (!canSyncRender() && !inFakeDrop())
298 return result;
299
300 if (!gotWorkers)
301 initWorkers();
302
303 foreach (QPMMime::DropWorker *w, workers) {
304 if (w->hasFormat(mimeType)) {
305 result = w->retrieveData(mimeType, preferredType);
306 break;
307 }
308 }
309
310 return result;
311}
312
313static Qt::DropActions toQDragDropActions(USHORT ops)
314{
315 Qt::DropActions actions = Qt::IgnoreAction;
316 if (ops & DO_LINKABLE)
317 actions |= Qt::LinkAction;
318 if (ops & DO_COPYABLE)
319 actions |= Qt::CopyAction;
320 if (ops & DO_MOVEABLE)
321 actions |= Qt::MoveAction;
322 return actions;
323}
324
325static Qt::DropAction toQDragDropAction(USHORT op)
326{
327 if (op == DO_LINK)
328 return Qt::LinkAction;
329 if (op == DO_COPY)
330 return Qt::CopyAction;
331 if (op == DO_MOVE)
332 return Qt::MoveAction;
333 return Qt::IgnoreAction;
334}
335
336static USHORT toPmDragDropOp(Qt::DropActions action)
337{
338 if (action & Qt::LinkAction)
339 return DO_LINK;
340 if (action & Qt::CopyAction)
341 return DO_COPY;
342 if (action & Qt::MoveAction)
343 return DO_MOVE;
344 return 0;
345}
346
347static USHORT toPmDragDropOps(Qt::DropActions actions)
348{
349 USHORT op = 0;
350 if (actions & Qt::LinkAction)
351 op |= DO_LINKABLE;
352 if (actions & Qt::CopyAction)
353 op |= DO_COPYABLE;
354 if (actions & Qt::MoveAction)
355 op |= DO_MOVEABLE;
356 return op;
357}
358
359// Equivalent of QApplication::mouseButtons() (mouse events aren't delivered
360// to the window during DnD in PM so this metnod returns an incorrect value).
361static Qt::MouseButtons mouseButtons()
362{
363 Qt::MouseButtons buttons = Qt::NoButton;
364
365 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000)
366 buttons |= Qt::LeftButton;
367 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000)
368 buttons |= Qt::RightButton;
369 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000)
370 buttons |= Qt::MidButton;
371
372 return buttons;
373}
374
375// Equivalent of QApplication::keyboardModifiers() (keyboard events aren't delivered
376// to the window during DnD in PM so this metnod returns an incorrect value).
377static Qt::KeyboardModifiers keyboardModifiers()
378{
379 Qt::KeyboardModifiers mods = Qt::NoModifier;
380
381 if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT ) & 0x8000)
382 mods |= Qt::ShiftModifier;
383 if ((WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000) ||
384 (WinGetKeyState(HWND_DESKTOP, VK_ALTGRAF) & 0x8000))
385 mods |= Qt::AltModifier;
386 if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)
387 mods |= Qt::ControlModifier;
388
389 // Note: we cannot properly detect LWIN/RWIN (Meta) key state here as it
390 // doesn't have a corresponding VK_ value. The best we can do is to check
391 // the physical keyboard state using the hardware scancode but it has the
392 // following problems:
393 //
394 // 1. The physical keyboard state may be out of sync with the current
395 // input message we are processing.
396 // 2. The scancode may be hardware dependent
397 //
398 // 0x7E is what I get as a scancode value for LWIN from PM here.
399 // Unfortunately, I have no keyboard with RWIN around so I can't get its
400 // scan code.
401
402 if (WinGetPhysKeyState(HWND_DESKTOP, 0x7E) & 0x8000)
403 mods |= Qt::MetaModifier;
404
405 return mods;
406}
407
408void QDragManager::init_sys()
409{
410 inDrag = false;
411
412 Q_ASSERT(dropData);
413 Q_ASSERT(!dropData->d);
414 dropData->d = new QPMDragData();
415}
416
417void QDragManager::uninit_sys()
418{
419 Q_ASSERT(dropData);
420 Q_ASSERT(dropData->d);
421 delete dropData->d;
422 dropData->d = 0;
423}
424
425void QDragManager::sendDropEvent(QWidget *receiver, QEvent *event)
426{
427 // Note: inDrag = true causes QWidget::getPS() to return a special HPS for
428 // drawing during DnD. However, if we send the message from a fake DM_DROP
429 // that we post to ourselves to render data after the DnD session ends
430 // (inFakeDrop = true), a normal HPS has to be used.
431
432 bool inFakeDrop = dropData->d->inFakeDrop();
433
434 Q_ASSERT(!inDrag);
435 if (!inFakeDrop)
436 inDrag = true;
437
438 QApplication::sendEvent(receiver, event);
439
440 if (!inFakeDrop) {
441 // make sure that all issued update requests are handled before we
442 // return from the DM_DRAGOVER/DM_DRAGLEAVE/DM_DROP message; otherwise
443 // we'll get screen corruption due to unexpected screen updates while
444 // dragging
445 QApplication::sendPostedEvents(0, QEvent::UpdateRequest);
446 inDrag = false;
447 }
448}
449
450/*!
451 * \internal
452 * Direct manipulation (Drag & Drop) event handler
453 */
454MRESULT QDragManager::dispatchDragAndDrop(QWidget *widget, const QMSG &qmsg)
455{
456 Q_ASSERT(widget);
457
458 BOOL ok = FALSE;
459
460 QDragManager *manager = QDragManager::self();
461 QPMDragData *dragData = manager->dropData->d;
462 Q_ASSERT(dragData);
463
464 // @todo use dragData->lastAnswerRect to compress DM_DRAGOVER events
465
466 switch (qmsg.msg) {
467 case DM_DRAGOVER: {
468 DEBUG(("DM_DRAGOVER: hwnd %lX di %p x %d y %d", qmsg.hwnd, qmsg.mp1,
469 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)));
470
471 DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
472 ok = DrgAccessDraginfo(info);
473 Q_ASSERT(ok && info);
474 if (!ok || !info)
475 return MRFROM2SHORT(DOR_NEVERDROP, 0);
476
477 // flip y coordinate
478 QPoint pnt(info->xDrop, info->yDrop);
479 pnt.setY(qt_display_height() - (pnt.y() + 1));
480 pnt = widget->mapFromGlobal(pnt);
481
482 QWidget *dragOverWidget = widget->childAt(pnt);
483 if (!dragOverWidget)
484 dragOverWidget = widget;
485 else
486 pnt = dragOverWidget->mapFrom(widget, pnt);
487
488 bool first = dragData->lastDragOverWidget != dragOverWidget;
489 if (first) {
490 // the first DM_DRAGOVER message
491 if (dragData->lastDragOverWidget != 0) {
492 // send drag leave to the old widget
493 dragData->reset();
494 QPointer<QWidget> dragOverWidgetGuard(dragOverWidget);
495 QDragLeaveEvent dle;
496 sendDropEvent(dragData->lastDragOverWidget, &dle);
497 if (!dragOverWidgetGuard) {
498 dragOverWidget = widget->childAt(pnt);
499 if (!dragOverWidget)
500 dragOverWidget = widget;
501 else
502 pnt = dragOverWidget->mapFrom(widget, pnt);
503 }
504 }
505 dragData->lastDragOverWidget = dragOverWidget;
506 dragData->lastDragOverOp = 0;
507 dragData->supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
508 dragData->sourceAllowsOp = false;
509 dragData->lastDropReply = DOR_NEVERDROP;
510 dragData->lastAction =
511 manager->defaultAction(toQDragDropActions(dragData->supportedOps),
512 Qt::NoModifier);
513 dragData->lastAnswerRect = QRect();
514 // ensure drag data is reset (just in case of a wrong msg flow...)
515 dragData->reset();
516 }
517
518 // Note: we don't check if dragOverWidget->acceptDrops() here since
519 // this will be checked in QApplication::notify() and the
520 // appropriate action will be taken (the message will be delivered
521 // to the parent)
522
523 USHORT dropReply = dragData->lastDropReply;
524
525 if (first) {
526 // determine the set of operations supported by *all* items
527 // (this implies that DRAGITEM::fsSupportedOps is a bit field)
528 dropReply = DOR_DROP;
529 ULONG itemCount = DrgQueryDragitemCount(info);
530 for (ULONG i = 0; i < itemCount; ++ i) {
531 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
532 Q_ASSERT(item);
533 if (!item) {
534 dropReply = DOR_NEVERDROP;
535 break;
536 }
537 dragData->supportedOps &= item->fsSupportedOps;
538 }
539 if (dropReply != DOR_NEVERDROP) {
540 Q_ASSERT(itemCount);
541 if (!itemCount || !dragData->supportedOps) {
542 // items don't have even a single common operation...
543 dropReply = DOR_NEVERDROP;
544 }
545 }
546 }
547
548 if (dropReply != DOR_NEVERDROP) {
549
550 if (first || dragData->lastDragOverOp != info->usOperation) {
551 // the current drop operation was changed by a modifier key
552 USHORT realOp = info->usOperation;
553 if (realOp == DO_DEFAULT || realOp == DO_UNKNOWN) {
554 // the default operation is requested
555 realOp = toPmDragDropOp(dragData->lastAction);
556 } else {
557 dragData->lastAction = toQDragDropAction(realOp);
558 }
559 dragData->sourceAllowsOp =
560 ((dragData->supportedOps & DO_MOVEABLE) && realOp == DO_MOVE) ||
561 ((dragData->supportedOps & DO_COPYABLE) && realOp == DO_COPY) ||
562 ((dragData->supportedOps & DO_LINKABLE) && realOp == DO_LINK);
563 dragData->lastDragOverOp = realOp;
564 // make sure the default accepted state for the next event
565 // correlates with the set of supported operations
566 if (dropReply == DOR_DROP)
567 dropReply = dragData->sourceAllowsOp ? DOR_DROP : DOR_NODROP;
568 }
569
570 // Note that if sourceAllowsOp = false here, we have to deliver
571 // events anyway (stealing them from Qt would be confusing), but
572 // we will silently ignore any accept commands and always reject
573 // the drop. Other platforms seem to do the same.
574
575 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
576
577 if (first) {
578 dragData->initialize(info);
579 QDragEnterEvent dee(pnt,
580 toQDragDropActions(dragData->supportedOps),
581 data, mouseButtons(), keyboardModifiers());
582 dee.setDropAction(dragData->lastAction);
583 sendDropEvent(dragOverWidget, &dee);
584 // if not allowed or not accepted, always reply DOR_NODROP
585 // to have DM_DRAGOVER delivered to us again for a new test
586 if (dragData->sourceAllowsOp && dee.isAccepted()) {
587 dropReply = DOR_DROP;
588 dragData->lastAction = dee.dropAction();
589 dragData->lastAnswerRect = dee.answerRect();
590 } else {
591 dropReply = DOR_NODROP;
592 }
593 DEBUG(() << "DM_DRAGOVER: QDragEnterEvent: accepted" << dee.isAccepted());
594 }
595
596 // note: according to the Qt documentation, the move event
597 // is sent immediately after the enter event, do so (only if
598 // the target accepts it)
599 if (!first || dropReply == DOR_DROP) {
600 QDragMoveEvent dme(pnt,
601 toQDragDropActions(dragData->supportedOps),
602 data, mouseButtons(), keyboardModifiers());
603 dme.setDropAction(dragData->lastAction);
604 // accept by default if the enter event was accepted
605 if (dropReply == DOR_DROP)
606 dme.accept();
607 sendDropEvent(dragOverWidget, &dme);
608 if (dragData->sourceAllowsOp && dme.isAccepted()) {
609 dropReply = DOR_DROP;
610 dragData->lastAction = dme.dropAction();
611 dragData->lastAnswerRect = dme.answerRect();
612 } else {
613 dropReply = DOR_NODROP;
614 }
615 DEBUG(() << "DM_DRAGOVER: QDragMoveEvent: accepted" << dme.isAccepted());
616 }
617
618 dragData->lastDropReply = dropReply;
619 }
620
621 DrgFreeDraginfo(info);
622
623 DEBUG(("DM_DRAGOVER: return %08x default %08x",
624 dropReply, toPmDragDropOp(dragData->lastAction)));
625 return MRFROM2SHORT(dropReply, toPmDragDropOp(dragData->lastAction));
626 }
627 case DM_DRAGLEAVE: {
628 DEBUG(("DM_DRAGLEAVE: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
629
630 // Odin32 apps produce incorrect message flow, ignore
631 Q_ASSERT(dragData->lastDragOverWidget != 0);
632 if (dragData->lastDragOverWidget == 0)
633 return 0;
634
635 QDragLeaveEvent de;
636 sendDropEvent(dragData->lastDragOverWidget, &de);
637
638 dragData->lastDragOverWidget = 0;
639 dragData->reset();
640
641 return 0;
642 }
643 case DM_DROP: {
644 DEBUG(("DM_DROP: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
645
646 // sanity
647 Q_ASSERT(!dragData->canSyncRender() || qmsg.mp1);
648
649 DRAGINFO *info = 0;
650
651 if (dragData->canSyncRender() || qmsg.mp1) {
652 // Odin32 apps produce incorrect message flow, ignore
653 Q_ASSERT(dragData->lastDragOverWidget != 0);
654 if (dragData->lastDragOverWidget == 0)
655 return 0;
656
657 // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
658 // DOR_NODROP, simulate DM_DRAGLEAVE
659 Q_ASSERT(dragData->lastDropReply == DOR_DROP);
660 if (dragData->lastDropReply != DOR_DROP) {
661 QMSG qmsg2 = qmsg;
662 qmsg2.msg = DM_DRAGLEAVE;
663 dispatchDragAndDrop(widget, qmsg2);
664 return 0;
665 }
666
667 // sanity
668 Q_ASSERT((DRAGINFO *)qmsg.mp1 == dragData->info());
669
670 info = (DRAGINFO *)qmsg.mp1;
671 ok = DrgAccessDraginfo(info);
672 Q_ASSERT(ok && info);
673 if (!ok || !info)
674 return 0;
675
676 if (!dragData->canSyncRender()) {
677 // The source doesn't support syncrhonous rendering (i.e.
678 // it won't issue DM_RENDERCOMPLETE until we return from
679 // DM_DROP) so we have to post a message to ourselves to do
680 // it asynchronously. For simplicity, we reuse DM_DRAG with
681 // mp1 = 0 for this purpose.
682 WinPostMsg(qmsg.hwnd, DM_DROP, 0, 0);
683 return 0;
684 }
685 } else {
686 // we're in a fake DM_DROP we posted above
687 dragData->setInFakeDrop(true);
688 info = dragData->info();
689 Q_ASSERT(info);
690 }
691
692 // flip y coordinate
693 QPoint pnt(info->xDrop, info->yDrop);
694 pnt.setY(qt_display_height() - (pnt.y() + 1));
695 pnt = widget->mapFromGlobal(pnt);
696 if (dragData->lastDragOverWidget != widget)
697 pnt = dragData->lastDragOverWidget->mapFrom(widget, pnt);
698
699 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
700
701 QDropEvent de(pnt, toQDragDropActions(dragData->supportedOps),