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

Last change on this file since 503 was 473, checked in by Dmitry A. Kuminov, 15 years ago

gui: DnD: Fixed: Dropping from Qt apps to some bogus apps (e.g. EPM) didn't work because of the wrong message flow.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 47.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtGui module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
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 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** 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 are unsure which license is appropriate for your use, please
39** contact the sales department 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#include "qt_os2.h"
60
61//#define QDND_DEBUG // in pair with qmime_pm.cpp
62
63#ifdef QDND_DEBUG
64# define DEBUG(a) qDebug a
65#else
66# define DEBUG(a) do {} while(0)
67#endif
68
69QT_BEGIN_NAMESPACE
70
71#if !defined(QT_NO_DRAGANDDROP) && !defined(QT_NO_CLIPBOARD)
72
73extern void qt_pmMouseButtonUp(); // defined in qapplication_pm.cpp
74extern void qt_DrgFreeDragtransfer(DRAGTRANSFER *xfer); // defined in qmime_pm.cpp
75
76#ifdef QDND_DEBUG
77extern QString dragActionsToString(Qt::DropActions actions);
78#endif
79
80/** \internal
81 * Data for QDragEnterEvent/QDragMoveEvent/QPMDropEvent.
82 */
83class QPMDragData
84{
85public:
86 QPMDragData();
87 ~QPMDragData();
88
89 void initialize(DRAGINFO *di);
90 void reset(bool isAccepted);
91 void reset() { reset(false); }
92
93 void setInFakeDrop(bool d) { fakeDrop = d; }
94 bool inFakeDrop() const { return fakeDrop; }
95
96 DRAGINFO *info() const { return di; }
97 bool canSyncRender() const { return canSyncRndr; }
98
99 bool hasFormat_sys(const QString &mimeType);
100 QStringList formats_sys();
101 QVariant retrieveData_sys(const QString &mimeType,
102 QVariant::Type preferredType);
103
104private:
105 friend class QDragManager;
106
107 void initWorkers();
108
109 bool initialized : 1;
110 bool fakeDrop : 1;
111 bool gotWorkers : 1;
112 bool canSyncRndr : 1;
113
114 DRAGINFO *di;
115 QList<QPMMime::DropWorker*> workers;
116
117 QWidget *lastDragOverWidget; // last target widget
118 USHORT lastDragOverOp; // last DM_DRAGOVER operation
119
120 USHORT supportedOps; // operations supported by all items
121 bool sourceAllowsOp : 1; // does source allow requested operation
122
123 USHORT lastDropReply; // last reply to DM_DRAGOVER
124
125 Qt::DropAction lastAction; // last target-accepted action
126 QRect lastAnswerRect; // last accepted rectangle from the target
127};
128
129QPMDragData::QPMDragData()
130 : initialized(false), fakeDrop(false)
131 , gotWorkers(false), canSyncRndr(false), di(NULL)
132 , lastDragOverWidget(0), lastDragOverOp(0)
133 , supportedOps(0), sourceAllowsOp(false)
134 , lastDropReply(DOR_NEVERDROP)
135 , lastAction(Qt::CopyAction)
136{
137}
138
139QPMDragData::~QPMDragData()
140{
141 reset();
142}
143
144void QPMDragData::initialize(DRAGINFO *info)
145{
146 Q_ASSERT(info);
147 if (initialized || !info)
148 return;
149
150 initialized = true;
151 di = info;
152
153 // Detect if the source supports synchronous rendering which means it can
154 // process DM_RENDER and reply with DM_RENDERCOMPLETE from within both
155 // DM_DRAGOVER and DM_DROP (i.e. while it is running the DnD session with
156 // DrgDrag()). Mozilla apps are known to only support synchronous rendering
157 // (an attempt to send DM_RENDER to them after DM_DROP will crash them).
158 // By the contrast, EPM only supports asyncronous rendering (it will hang
159 // if we send DM_RENDER and/or wait for DM_RENDERCOMPLETE before returning
160 // from DM_DRAGOVER/DM_DROP). WPS (e.g. desktop folders) seem to support
161 // both (we prefer the syncrhonous rendering in such cases since it lets
162 // Qt apps access dragged data right from QDragEnterEvent/QDragLeaveEvent
163 // and QDropEvent message handlers which is used by many of them for better
164 // visualization.
165
166 // get class name
167 QByteArray className(256, '\0');
168 LONG classNameLen = WinQueryClassName(info->hwndSource,
169 className.size(), className.data());
170 className.truncate(classNameLen);
171
172 DEBUG(("QPMDragData::initialize: info->hwndSource %08lX className \"%s\"",
173 info->hwndSource, QString::fromLocal8Bit(className).toUtf8().data()));
174
175 // get EXE name
176 PID pid = 0;
177 TID tid = 0;
178 QByteArray exePath(CCHMAXPATH, '\0');
179 BOOL ok = WinQueryWindowProcess(info->hwndSource, &pid, &tid);
180 if (ok) {
181 QByteArray buf(4192, '\0');
182 APIRET arc = DosQuerySysState(QS_PROCESS, 0, pid, tid,
183 buf.data(), buf.size());
184 if (arc == NO_ERROR) {
185 qsGrec_t *grec = *(qsGrec_t **)buf.data();
186 qsPrec_t *prec = (qsPrec_t *)((ULONG)grec + sizeof(qsGrec_t));
187 arc = DosQueryModuleName(prec->hMte, exePath.size(), exePath.data());
188 if (arc == NO_ERROR)
189 exePath.truncate(qstrlen(exePath));
190 }
191 }
192
193 DEBUG(("QPMDragData::initialize: pid %lu exePath \"%s\"",
194 pid, QString::fromLocal8Bit(exePath).toUtf8().data()));
195
196 QString exeName = QFileInfo(QFile::decodeName(exePath)).fileName();
197
198 // start with a positive
199 canSyncRndr = true;
200
201 if (exeName.toUpper() == QLatin1String("EPM.EXE") &&
202 qstrcmp(className, "FileWnd") == 0) {
203 // EPM only supports async rendering
204 canSyncRndr = false;
205 }
206
207 DEBUG(("QPMDragData::initialize: canSyncRender %d", canSyncRndr));
208}
209
210void QPMDragData::initWorkers()
211{
212 Q_ASSERT(initialized);
213 if (!initialized || gotWorkers)
214 return;
215
216 gotWorkers = true;
217
218 // go through all converters and collect DropWorkers to use
219 foreach (QPMMime *mime, QPMMime::all()) {
220 QPMMime::DropWorker *wrk = mime->dropWorkerFor(di);
221 if (wrk) {
222 if (wrk->isExclusive()) {
223 // ignore all other workers if some of them identified itself
224 // as exclusive
225 workers.clear();
226 workers.append(wrk);
227 break;
228 }
229 // ensure there are no duplicateseed
230 if (!workers.contains(wrk))
231 workers.append(wrk);
232 }
233 }
234
235 DEBUG(() << "QPMDragData:" << workers.count() << "drop workers for DRAGINFO" << di);
236
237 // init all workers
238 foreach (QPMMime::DropWorker *w, workers) {
239 w->nfo = di;
240 w->init();
241 }
242}
243
244void QPMDragData::reset(bool isAccepted)
245{
246 if (!initialized)
247 return;
248
249 // cleanup all workers
250 foreach (QPMMime::DropWorker *w, workers) {
251 w->cleanup(isAccepted);
252 w->nfo = 0;
253 }
254
255 workers.clear();
256 di = 0;
257 initialized = fakeDrop = gotWorkers = canSyncRndr = false;
258}
259
260bool QPMDragData::hasFormat_sys(const QString &mimeType)
261{
262 if (!gotWorkers)
263 initWorkers();
264
265 foreach (QPMMime::DropWorker *w, workers)
266 if (w->hasFormat(mimeType))
267 return true;
268
269 return false;
270}
271
272QStringList QPMDragData::formats_sys()
273{
274 QStringList mimes;
275
276 if (!gotWorkers)
277 initWorkers();
278
279 foreach (QPMMime::DropWorker *w, workers)
280 mimes << w->formats();
281
282 return mimes;
283}
284
285QVariant QPMDragData::retrieveData_sys(const QString &mimeType,
286 QVariant::Type preferredType)
287{
288 QVariant result;
289
290 // If the source doesn't support synchronous rendering, we may only do data
291 // transfer after DM_DROP is processed by us. Return shortly.
292 if (!canSyncRender() && !inFakeDrop())
293 return result;
294
295 if (!gotWorkers)
296 initWorkers();
297
298 foreach (QPMMime::DropWorker *w, workers) {
299 if (w->hasFormat(mimeType)) {
300 result = w->retrieveData(mimeType, preferredType);
301 break;
302 }
303 }
304
305 return result;
306}
307
308static Qt::DropActions toQDragDropActions(USHORT ops)
309{
310 Qt::DropActions actions = Qt::IgnoreAction;
311 if (ops & DO_LINKABLE)
312 actions |= Qt::LinkAction;
313 if (ops & DO_COPYABLE)
314 actions |= Qt::CopyAction;
315 if (ops & DO_MOVEABLE)
316 actions |= Qt::MoveAction;
317 return actions;
318}
319
320static Qt::DropAction toQDragDropAction(USHORT op)
321{
322 if (op == DO_LINK)
323 return Qt::LinkAction;
324 if (op == DO_COPY)
325 return Qt::CopyAction;
326 if (op == DO_MOVE)
327 return Qt::MoveAction;
328 return Qt::IgnoreAction;
329}
330
331static USHORT toPmDragDropOp(Qt::DropActions action)
332{
333 if (action & Qt::LinkAction)
334 return DO_LINK;
335 if (action & Qt::CopyAction)
336 return DO_COPY;
337 if (action & Qt::MoveAction)
338 return DO_MOVE;
339 return 0;
340}
341
342static USHORT toPmDragDropOps(Qt::DropActions actions)
343{
344 USHORT op = 0;
345 if (actions & Qt::LinkAction)
346 op |= DO_LINKABLE;
347 if (actions & Qt::CopyAction)
348 op |= DO_COPYABLE;
349 if (actions & Qt::MoveAction)
350 op |= DO_MOVEABLE;
351 return op;
352}
353
354// Equivalent of QApplication::mouseButtons() (mouse events aren't delivered
355// to the window during DnD in PM so this metnod returns an incorrect value).
356static Qt::MouseButtons mouseButtons()
357{
358 Qt::MouseButtons buttons = Qt::NoButton;
359
360 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000)
361 buttons |= Qt::LeftButton;
362 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000)
363 buttons |= Qt::RightButton;
364 if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000)
365 buttons |= Qt::MidButton;
366
367 return buttons;
368}
369
370// Equivalent of QApplication::keyboardModifiers() (keyboard events aren't delivered
371// to the window during DnD in PM so this metnod returns an incorrect value).
372static Qt::KeyboardModifiers keyboardModifiers()
373{
374 Qt::KeyboardModifiers mods = Qt::NoModifier;
375
376 if (WinGetKeyState(HWND_DESKTOP, VK_SHIFT ) & 0x8000)
377 mods |= Qt::ShiftModifier;
378 if ((WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000) ||
379 (WinGetKeyState(HWND_DESKTOP, VK_ALTGRAF) & 0x8000))
380 mods |= Qt::AltModifier;
381 if (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)
382 mods |= Qt::ControlModifier;
383
384 // Note: we cannot properly detect LWIN/RWIN (Meta) key state here as it
385 // doesn't have a corresponding VK_ value. The best we can do is to check
386 // the physical keyboard state using the hardware scancode but it has the
387 // following problems:
388 //
389 // 1. The physical keyboard state may be out of sync with the current
390 // input message we are processing.
391 // 2. The scancode may be hardware dependent
392 //
393 // 0x7E is what I get as a scancode value for LWIN from PM here.
394 // Unfortunately, I have no keyboard with RWIN around so I can't get its
395 // scan code.
396
397 if (WinGetPhysKeyState(HWND_DESKTOP, 0x7E) & 0x8000)
398 mods |= Qt::MetaModifier;
399
400 return mods;
401}
402
403void QDragManager::init_sys()
404{
405 inDrag = false;
406
407 Q_ASSERT(dropData);
408 Q_ASSERT(!dropData->d);
409 dropData->d = new QPMDragData();
410}
411
412void QDragManager::uninit_sys()
413{
414 Q_ASSERT(dropData);
415 Q_ASSERT(dropData->d);
416 delete dropData->d;
417 dropData->d = 0;
418}
419
420void QDragManager::sendDropEvent(QWidget *receiver, QEvent *event)
421{
422 // Note: inDrag = true causes QWidget::getPS() to return a special HPS for
423 // drawing during DnD. However, if we send the message from a fake DM_DROP
424 // that we post to ourselves to render data after the DnD session ends
425 // (inFakeDrop = true), a normal HPS has to be used.
426
427 bool inFakeDrop = dropData->d->inFakeDrop();
428
429 Q_ASSERT(!inDrag);
430 if (!inFakeDrop)
431 inDrag = true;
432
433 QApplication::sendEvent(receiver, event);
434
435 if (!inFakeDrop) {
436 // make sure that all issued update requests are handled before we
437 // return from the DM_DRAGOVER/DM_DRAGLEAVE/DM_DROP message; otherwise
438 // we'll get screen corruption due to unexpected screen updates while
439 // dragging
440 QApplication::sendPostedEvents(0, QEvent::UpdateRequest);
441 inDrag = false;
442 }
443}
444
445/*!
446 * \internal
447 * Direct manipulation (Drag & Drop) event handler
448 */
449MRESULT QDragManager::dispatchDragAndDrop(QWidget *widget, const QMSG &qmsg)
450{
451 Q_ASSERT(widget);
452
453 BOOL ok = FALSE;
454
455 QDragManager *manager = QDragManager::self();
456 QPMDragData *dragData = manager->dropData->d;
457 Q_ASSERT(dragData);
458
459 // @todo use dragData->lastAnswerRect to compress DM_DRAGOVER events
460
461 switch (qmsg.msg) {
462 case DM_DRAGOVER: {
463 DEBUG(("DM_DRAGOVER: hwnd %lX di %p x %d y %d", qmsg.hwnd, qmsg.mp1,
464 SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)));
465
466 DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
467 ok = DrgAccessDraginfo(info);
468 Q_ASSERT(ok && info);
469 if (!ok || !info)
470 return MRFROM2SHORT(DOR_NEVERDROP, 0);
471
472 // flip y coordinate
473 QPoint pnt(info->xDrop, info->yDrop);
474 pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
475 pnt = widget->mapFromGlobal(pnt);
476
477 QWidget *dragOverWidget = widget->childAt(pnt);
478 if (!dragOverWidget)
479 dragOverWidget = widget;
480 else
481 pnt = dragOverWidget->mapFrom(widget, pnt);
482
483 bool first = dragData->lastDragOverWidget != dragOverWidget;
484 if (first) {
485 // the first DM_DRAGOVER message
486 if (dragData->lastDragOverWidget != 0) {
487 // send drag leave to the old widget
488 dragData->reset();
489 QPointer<QWidget> dragOverWidgetGuard(dragOverWidget);
490 QDragLeaveEvent dle;
491 sendDropEvent(dragData->lastDragOverWidget, &dle);
492 if (!dragOverWidgetGuard) {
493 dragOverWidget = widget->childAt(pnt);
494 if (!dragOverWidget)
495 dragOverWidget = widget;
496 else
497 pnt = dragOverWidget->mapFrom(widget, pnt);
498 }
499 }
500 dragData->lastDragOverWidget = dragOverWidget;
501 dragData->lastDragOverOp = 0;
502 dragData->supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
503 dragData->sourceAllowsOp = false;
504 dragData->lastDropReply = DOR_NEVERDROP;
505 dragData->lastAction =
506 manager->defaultAction(toQDragDropActions(dragData->supportedOps),
507 Qt::NoModifier);
508 dragData->lastAnswerRect = QRect();
509 // ensure drag data is reset (just in case of a wrong msg flow...)
510 dragData->reset();
511 }
512
513 // Note: we don't check if dragOverWidget->acceptDrops() here since
514 // this will be checked in QApplication::notify() and the
515 // appropriate action will be taken (the message will be delivered
516 // to the parent)
517
518 USHORT dropReply = dragData->lastDropReply;
519
520 if (first) {
521 // determine the set of operations supported by *all* items
522 // (this implies that DRAGITEM::fsSupportedOps is a bit field)
523 dropReply = DOR_DROP;
524 ULONG itemCount = DrgQueryDragitemCount(info);
525 for (ULONG i = 0; i < itemCount; ++ i) {
526 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
527 Q_ASSERT(item);
528 if (!item) {
529 dropReply = DOR_NEVERDROP;
530 break;
531 }
532 dragData->supportedOps &= item->fsSupportedOps;
533 }
534 if (dropReply != DOR_NEVERDROP) {
535 Q_ASSERT(itemCount);
536 if (!itemCount || !dragData->supportedOps) {
537 // items don't have even a single common operation...
538 dropReply = DOR_NEVERDROP;
539 }
540 }
541 }
542
543 if (dropReply != DOR_NEVERDROP) {
544
545 if (first || dragData->lastDragOverOp != info->usOperation) {
546 // the current drop operation was changed by a modifier key
547 USHORT realOp = info->usOperation;
548 if (realOp == DO_DEFAULT || realOp == DO_UNKNOWN) {
549 // the default operation is requested
550 realOp = toPmDragDropOp(dragData->lastAction);
551 } else {
552 dragData->lastAction = toQDragDropAction(realOp);
553 }
554 dragData->sourceAllowsOp =
555 ((dragData->supportedOps & DO_MOVEABLE) && realOp == DO_MOVE) ||
556 ((dragData->supportedOps & DO_COPYABLE) && realOp == DO_COPY) ||
557 ((dragData->supportedOps & DO_LINKABLE) && realOp == DO_LINK);
558 dragData->lastDragOverOp = realOp;
559 // make sure the default accepted state for the next event
560 // correlates with the set of supported operations
561 if (dropReply == DOR_DROP)
562 dropReply = dragData->sourceAllowsOp ? DOR_DROP : DOR_NODROP;
563 }
564
565 // Note that if sourceAllowsOp = false here, we have to deliver
566 // events anyway (stealing them from Qt would be confusing), but
567 // we will silently ignore any accept commands and always reject
568 // the drop. Other platforms seem to do the same.
569
570 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
571
572 if (first) {
573 dragData->initialize(info);
574 QDragEnterEvent dee(pnt,
575 toQDragDropActions(dragData->supportedOps),
576 data, mouseButtons(), keyboardModifiers());
577 dee.setDropAction(dragData->lastAction);
578 sendDropEvent(dragOverWidget, &dee);
579 // if not allowed or not accepted, always reply DOR_NODROP
580 // to have DM_DRAGOVER delivered to us again for a new test
581 if (dragData->sourceAllowsOp && dee.isAccepted()) {
582 dropReply = DOR_DROP;
583 dragData->lastAction = dee.dropAction();
584 dragData->lastAnswerRect = dee.answerRect();
585 } else {
586 dropReply = DOR_NODROP;
587 }
588 DEBUG(() << "DM_DRAGOVER: QDragEnterEvent: accepted" << dee.isAccepted());
589 }
590
591 // note: according to the Qt documentation, the move event
592 // is sent immediately after the enter event, do so (only if
593 // the target accepts it)
594 if (!first || dropReply == DOR_DROP) {
595 QDragMoveEvent dme(pnt,
596 toQDragDropActions(dragData->supportedOps),
597 data, mouseButtons(), keyboardModifiers());
598 dme.setDropAction(dragData->lastAction);
599 // accept by default if the enter event was accepted
600 if (dropReply == DOR_DROP)
601 dme.accept();
602 sendDropEvent(dragOverWidget, &dme);
603 if (dragData->sourceAllowsOp && dme.isAccepted()) {
604 dropReply = DOR_DROP;
605 dragData->lastAction = dme.dropAction();
606 dragData->lastAnswerRect = dme.answerRect();
607 } else {
608 dropReply = DOR_NODROP;
609 }
610 DEBUG(() << "DM_DRAGOVER: QDragMoveEvent: accepted" << dme.isAccepted());
611 }
612
613 dragData->lastDropReply = dropReply;
614 }
615
616 DrgFreeDraginfo(info);
617
618 DEBUG(("DM_DRAGOVER: return %08x default %08x",
619 dropReply, toPmDragDropOp(dragData->lastAction)));
620 return MRFROM2SHORT(dropReply, toPmDragDropOp(dragData->lastAction));
621 }
622 case DM_DRAGLEAVE: {
623 DEBUG(("DM_DRAGLEAVE: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
624
625 // Odin32 apps produce incorrect message flow, ignore
626 Q_ASSERT(dragData->lastDragOverWidget != 0);
627 if (dragData->lastDragOverWidget == 0)
628 return 0;
629
630 QDragLeaveEvent de;
631 sendDropEvent(dragData->lastDragOverWidget, &de);
632
633 dragData->lastDragOverWidget = 0;
634 dragData->reset();
635
636 return 0;
637 }
638 case DM_DROP: {
639 DEBUG(("DM_DROP: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
640
641 // sanity
642 Q_ASSERT(!dragData->canSyncRender() || qmsg.mp1);
643
644 DRAGINFO *info = 0;
645
646 if (dragData->canSyncRender() || qmsg.mp1) {
647 // Odin32 apps produce incorrect message flow, ignore
648 Q_ASSERT(dragData->lastDragOverWidget != 0);
649 if (dragData->lastDragOverWidget == 0)
650 return 0;
651
652 // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
653 // DOR_NODROP, simulate DM_DRAGLEAVE
654 Q_ASSERT(dragData->lastDropReply == DOR_DROP);
655 if (dragData->lastDropReply != DOR_DROP) {
656 QMSG qmsg2 = qmsg;
657 qmsg2.msg = DM_DRAGLEAVE;
658 dispatchDragAndDrop(widget, qmsg2);
659 return 0;
660 }
661
662 // sanity
663 Q_ASSERT((DRAGINFO *)qmsg.mp1 == dragData->info());
664
665 info = (DRAGINFO *)qmsg.mp1;
666 ok = DrgAccessDraginfo(info);
667 Q_ASSERT(ok && info);
668 if (!ok || !info)
669 return 0;
670
671 if (!dragData->canSyncRender()) {
672 // The source doesn't support syncrhonous rendering (i.e.
673 // it won't issue DM_RENDERCOMPLETE until we return from
674 // DM_DROP) so we have to post a message to ourselves to do
675 // it asynchronously. For simplicity, we reuse DM_DRAG with
676 // mp1 = 0 for this purpose.
677 WinPostMsg(qmsg.hwnd, DM_DROP, 0, 0);
678 return 0;
679 }
680 } else {
681 // we're in a fake DM_DROP we posted above
682 dragData->setInFakeDrop(true);
683 info = dragData->info();
684 Q_ASSERT(info);
685 }
686
687 // flip y coordinate
688 QPoint pnt(info->xDrop, info->yDrop);
689 pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
690 pnt = widget->mapFromGlobal(pnt);
691 if (dragData->lastDragOverWidget != widget)
692 pnt = dragData->lastDragOverWidget->mapFrom(widget, pnt);
693
694 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
695
696 QDropEvent de(pnt, toQDragDropActions(dragData->supportedOps),
697 data, mouseButtons(), keyboardModifiers());
698 if (dragData->lastDropReply == DOR_DROP)
699 de.setDropAction(dragData->lastAction);
700 sendDropEvent(dragData->lastDragOverWidget, &de);
701
702 if (dragData->lastDropReply == DOR_DROP)
703 de.accept();
704
705 if (de.isAccepted()) {
706 // We must update usOperation so that if the application changes
707 // the drop action during processing of the QDropEvent this
708 // change will be visible at the DrgDrag() end. This is odd (since
709 // it may be easily made out of sync with the visual indication)
710 // but needed for compatibility with other platforms.
711 info->usOperation = toPmDragDropOp(de.dropAction());
712 }
713
714 dragData->lastDragOverWidget = 0;
715 dragData->reset(de.isAccepted());
716
717 ULONG targetReply = de.isAccepted() ?
718 DMFL_TARGETSUCCESSFUL : DMFL_TARGETFAIL;
719
720 if (de.isAccepted() && de.dropAction() == Qt::TargetMoveAction) {
721 // Qt::TargetMoveAction means that the target will move the
722 // object (e.g. copy it to itself and delete from the source).
723 // In this case, we always send DMFL_TARGETFAIL to the source to
724 // prevent it from doing the same on its side.
725 targetReply = DMFL_TARGETFAIL;
726 }
727
728 // send DM_ENDCONVERSATION for every item
729 ULONG itemCount = DrgQueryDragitemCount(info);
730 for (ULONG i = 0; i < itemCount; ++ i) {
731 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
732 Q_ASSERT(item);
733 if (!item)
734 continue;
735 // it is possible that this item required DM_RENDERPREPARE but
736 // returned false in reply to it (so hwndItem may be NULL)
737 if (!item->hwndItem)
738 continue;
739 DrgSendTransferMsg(item->hwndItem, DM_ENDCONVERSATION,
740 MPFROMLONG(item->ulItemID),
741 MPFROMLONG(targetReply));
742 }
743
744 DrgDeleteDraginfoStrHandles(info);
745 DrgFreeDraginfo(info);
746
747 return 0;
748 }
749 default:
750 break;
751 }
752
753 return WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);
754}
755
756//---------------------------------------------------------------------
757// QDropData
758//---------------------------------------------------------------------
759
760bool QDropData::hasFormat_sys(const QString &mimeType) const
761{
762 Q_ASSERT(d);
763 if (d)
764 return d->hasFormat_sys(mimeType);
765 return false;
766}
767
768QStringList QDropData::formats_sys() const
769{
770 QStringList fmts;
771 Q_ASSERT(d);
772 if (d)
773 fmts = d->formats_sys();
774 return fmts;
775}
776
777QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
778{
779 QVariant result;
780 Q_ASSERT(d);
781 if (d)
782 result = d->retrieveData_sys(mimeType, type);
783 return result;
784}
785
786//---------------------------------------------------------------------
787// QPMCoopDragWorker
788//---------------------------------------------------------------------
789
790class QPMCoopDragWorker : public QPMMime::DragWorker, public QPMObjectWindow
791{
792public:
793 QPMCoopDragWorker() : info(0) {}
794 bool collectWorkers(QDrag *o);
795
796 // DragWorker interface
797 void init();
798 bool cleanup(bool isCancelled);
799 bool isExclusive() const { return true; }
800 ULONG itemCount() const { return 0; }
801 HWND hwnd() const { return QPMObjectWindow::hwnd(); }
802 DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps);
803
804 // QPMObjectWindow interface
805 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
806
807private:
808 QList<DragWorker*> workers;
809 DRAGINFO *info;
810};
811
812bool QPMCoopDragWorker::collectWorkers(QDrag *o)
813{
814 Q_ASSERT(o);
815 if (!o)
816 return false;
817
818 bool gotExcl = false; // got isExclusive() worker?
819 bool skipExcl = false; // skip isExclusive() or itemCount() > 1 workers?
820 ULONG coopLevel = 0; // itemCount() level for !isExclusive() workers
821
822 bool gotExclForMime = false;
823
824 // go through all formats and all converters to collect DragWorkers
825 QMimeData *mimeData = o->mimeData();
826 foreach (const QString &fmt, mimeData->formats()) {
827 DEBUG(() << "QPMCoopDragWorker: Searching for worker for mime" << fmt);
828 foreach (QPMMime *mime, QPMMime::all()) {
829 DragWorker *wrk = mime->dragWorkerFor(fmt, mimeData);
830 if (!wrk)
831 continue;
832 DEBUG(() << "QPMCoopDragWorker: Got worker" << wrk
833 << "( isExclusive" << wrk->isExclusive()
834 << "itemCount" << wrk->itemCount() << ") from convertor"
835 << mime << "( gotExclForMime" << gotExclForMime
836 << "gotExcl" << gotExcl
837 << "skipExcl" << skipExcl << "coopLevel"
838 << coopLevel << ")");
839 if (wrk->isExclusive()) {
840 if (!skipExcl && !gotExclForMime) {
841 gotExclForMime = true;
842 if (!gotExcl) {
843 gotExcl = true;
844 workers.append(wrk);
845 } else {
846 // skip everything exclusive unless it's exactly the
847 // same worker
848 skipExcl = !workers.contains(wrk);
849 }
850 }
851 // continue to search for a fall-back cooperative 1-item worker
852 // (like QPMMimeAnyMime) for the case if this worker quits
853 // the game
854 continue;
855 }
856 ULONG itemCnt = wrk->itemCount();
857 if (itemCnt == 0) {
858 DEBUG(() << "QPMCoopDragWorker: Cooperative DragWorker"
859 << wrk << "for mime " << fmt << " has "
860 << "itemCount = 0!");
861 continue;
862 }
863 if (itemCnt > 1) {
864 // coop workers with item count > 1 are also considered exclusive
865 // here, because may not co-exist with 1-item workers that should
866 // always be able to contribute
867 if (!gotExcl && !skipExcl && !gotExclForMime) {
868 gotExclForMime = true;
869 workers.append(wrk);
870 if (!coopLevel)
871 coopLevel = itemCnt;
872 // only those for the same number of items can proceed
873 if (itemCnt != coopLevel)
874 skipExcl = true;
875 }
876 // continue to search for a fall-back cooperative 1-item worker
877 // (like QPMMimeAnyMime) for the case if this worker quits
878 // the game
879 continue;
880 }
881 workers.append(wrk);
882 // Don't search for other workrers for the same mime type --
883 // we've already got a drag worker for it and adding another
884 // one would just introduce mime type duplicates on the drop
885 // target's side (where the first encountered drop worker
886 // for the given RMF would be used for actual data transfer
887 // anyway). See also QClipboard::setData().
888 break;
889 }
890 if (gotExclForMime) {
891 // ensure we have a fall-back coop (should be the last added item)
892 DragWorker *w = workers.last();
893 if (w->isExclusive() || w->itemCount() > 1) {
894 DEBUG(() << "QPMCoopDragWorker: DragWorker" << w
895 << "for" << fmt << "( isExclusive" << w->isExclusive()
896 << "itemCount" << w->itemCount()
897 <<") has no fall-back cooperative 1-item worker!");
898 workers.removeLast();
899 }
900 gotExclForMime = false;
901 } else {
902 // got a regular (non-fall-back) 1-item coop, skip evreything else
903 skipExcl = true;
904 }
905 }
906
907 // remove either all exclusive workers or all their fall-back workers
908 // (depending on skipExcl) and remove duplicates
909 for (QList<DragWorker*>::iterator it = workers.begin();
910 it < workers.end();) {
911 DragWorker *wrk = *it;
912 bool excl = wrk->isExclusive() || wrk->itemCount() > 1;
913 if (skipExcl == excl || workers.count(wrk) > 1) {
914 it = workers.erase(it);
915 } else {
916 ++it;
917 }
918 }
919
920#if defined(QDND_DEBUG)
921 foreach (DragWorker *wrk, workers) {
922 DEBUG(() << "QPMCoopDragWorker: Will use worker" << wrk
923 << "( isExclusive" << wrk->isExclusive()
924 << "itemCount" << wrk->itemCount() << ")");
925 }
926#endif
927
928 if (!mimeData->formats().count()) {
929 // The drag source doesn't provide any format, so we've got no workers.
930 // Although it may look strange, it is a supported case: for example,
931 // designer uses it a lot for in-process DnD. Instead of MIME data, it
932 // works directly with the QMimeData object from the drag source. We
933 // will handle this situation by providing a dummy DRAGINFO structure.
934 DEBUG(() << "QPMCoopDragWorker: Source provides NO data, will use "
935 "dummy DRAGINFO");
936 Q_ASSERT(!workers.count());
937 return true;
938 }
939
940 Q_ASSERT(workers.count() > 0);
941 return workers.count() > 0;
942}
943
944void QPMCoopDragWorker::init()
945{
946 Q_ASSERT(source());
947 foreach(DragWorker *wrk, workers) {
948 wrk->src = source();
949 wrk->init();
950 }
951}
952
953bool QPMCoopDragWorker::cleanup(bool isCancelled)
954{
955 bool moveDisallowed = false;
956
957 foreach(DragWorker *wrk, workers) {
958 // disallow the Move operation if at least one worker asked so
959 moveDisallowed |= wrk->cleanup(isCancelled);
960 wrk->src = 0;
961 }
962 workers.clear();
963 info = 0;
964 return moveDisallowed;
965}
966
967DRAGINFO *QPMCoopDragWorker::createDragInfo(const QString &targetName,
968 USHORT supportedOps)
969{
970 Q_ASSERT(!info);
971 if (info)
972 return 0;
973
974 if (!workers.count()) {
975 // The drag source doesn't provide any format; provide a dummy DRAGINFO
976 // with a null DRAGITEM that we will never render
977 info = DrgAllocDraginfo(1);
978 Q_ASSERT(info);
979 if (!info)
980 return 0;
981
982 DRAGITEM *item = DrgQueryDragitemPtr(info, 0);
983 Q_ASSERT(item);
984 if (!item) {
985 DrgFreeDraginfo(info);
986 info = 0;
987 return 0;
988 }
989
990 item->hwndItem = 0;
991 item->ulItemID = 0;
992 item->hstrType = 0;
993 item->hstrRMF = 0;
994 item->hstrContainerName = 0;
995 item->hstrSourceName = 0;
996 item->hstrTargetName = 0;
997 item->cxOffset = 0;
998 item->cyOffset = 0;
999 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
1000 item->fsSupportedOps = supportedOps;
1001
1002 return info;
1003 }
1004
1005 DragWorker *firstWorker = workers.first();
1006 Q_ASSERT(firstWorker);
1007 if (!firstWorker)
1008 return 0;
1009
1010 ULONG itemCnt = firstWorker->itemCount();
1011
1012 if (firstWorker->isExclusive() && itemCnt == 0) {
1013 // this is a super exclusive worker that will do everything on its own
1014 DEBUG(() << "QPMCoopDragWorker: Will redirect to super worker"
1015 << firstWorker);
1016 return firstWorker->createDragInfo(targetName, supportedOps);
1017 }
1018
1019 // note that all workers at this place require the same amount of items
1020 // (guaranteed by collectWorkers())
1021
1022 DEBUG(() << "QPMCoopDragWorker: itemCnt" << itemCnt);
1023
1024 info = DrgAllocDraginfo(itemCnt);
1025 Q_ASSERT(info);
1026 if (!info)
1027 return 0;
1028
1029 // collect all mechanism/format pairs
1030 QByteArray allFormats;
1031 foreach (DragWorker *wrk, workers) {
1032 QByteArray formats = wrk->composeFormatString();
1033 Q_ASSERT(!formats.isNull());
1034 if (!formats.isNull()) {
1035 if (allFormats.isNull())
1036 allFormats = formats;
1037 else {
1038 allFormats += ",";
1039 allFormats += formats;
1040 }
1041 }
1042 }
1043
1044 DEBUG(() << "QPMCoopDragWorker: allFormats" << allFormats);
1045
1046 static ULONG itemID = 0;
1047
1048 QString type;
1049 QString ext;
1050 firstWorker->defaultFileType(type, ext);
1051
1052 bool ok = true;
1053 for (ULONG i = 0; i < itemCnt; ++i) {
1054 DRAGITEM *item = DrgQueryDragitemPtr(info, i);
1055 Q_ASSERT(item);
1056 if (!item) {
1057 ok = false;
1058 break;
1059 }
1060
1061 QString name;
1062 if (itemCnt == 1)
1063 name = targetName;
1064 else
1065 name = QString(QLatin1String("%1 %2")).arg(targetName).arg(i + 1);
1066
1067 if (!ext.isEmpty())
1068 name += QString(QLatin1String(".%1")).arg(ext);
1069
1070 DEBUG(() << "QPMCoopDragWorker: item" << i << ": type" << type
1071 << " name" << name);
1072
1073 // Note 1: DRAGITEM::hstrType is actually ignored by WPS,
1074 // only the target extension matters.
1075
1076 // Note 2: We're not required to fill in the hwndItem field because we
1077 // use the DC_PREPARE flag (to indicate it will be filled later, after
1078 // DM_RENDERPREPARE); however, Mozilla refuses to render if hwndItem
1079 // is initially 0. Set it to our HWND instead (we'll issue a warning if
1080 // DM_RENDER or DM_ENDCONVERSATION is erroneously sent to us)
1081
1082 item->hwndItem = hwnd();
1083 item->ulItemID = itemID ++;
1084 item->hstrType = DrgAddStrHandle(!type.isEmpty() ?
1085 QFile::encodeName(type) : DRT_UNKNOWN);
1086 item->hstrRMF = DrgAddStrHandle(allFormats);
1087 item->hstrContainerName = 0;
1088 item->hstrSourceName = 0;
1089 item->hstrTargetName = DrgAddStrHandle(QFile::encodeName(name));
1090 item->cxOffset = 0;
1091 item->cyOffset = 0;
1092 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
1093 item->fsSupportedOps = supportedOps;
1094 }
1095
1096 if (!ok) {
1097 DrgFreeDraginfo(info);
1098 info = 0;
1099 }
1100
1101 return info;
1102}
1103
1104MRESULT QPMCoopDragWorker::message(ULONG msg, MPARAM mp1, MPARAM mp2)
1105{
1106 if (msg == DM_RENDERPREPARE) {
1107 if (!info) {
1108 DEBUG(() << "Drop target sent DM_RENDERPREPARE after the DnD "
1109 "session is over!");
1110 // free the given DRAGTRANSFER structure to avoud memory leak
1111 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1112 if (xfer)
1113 qt_DrgFreeDragtransfer(xfer);
1114 return (MRESULT)FALSE;
1115 }
1116
1117 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1118 Q_ASSERT(xfer && xfer->pditem);
1119 if (!xfer || !xfer->pditem)
1120 return (MRESULT)FALSE;
1121
1122 // find the item's index (ordinal number)
1123 ULONG itemCnt = DrgQueryDragitemCount(info);
1124 ULONG index = 0;
1125 for (; index < itemCnt; ++index)
1126 if (DrgQueryDragitemPtr(info, index) == xfer->pditem)
1127 break;
1128
1129 Q_ASSERT(index < itemCnt);
1130 if (index >= itemCnt)
1131 return (MRESULT)FALSE;
1132
1133 DEBUG(() << "QPMCoopDragWorker: Got DM_RENDERPREPARE to"
1134 << QPMMime::queryHSTR(xfer->hstrSelectedRMF) << "for item"
1135 << index << "( id" << xfer->pditem->ulItemID << ")");
1136
1137 QByteArray drm, drf;
1138 if (!QPMMime::parseRMF(xfer->hstrSelectedRMF, drm, drf)) {
1139 Q_ASSERT(false);
1140 return (MRESULT)FALSE;
1141 }
1142
1143 DragWorker *wrk = 0;
1144 foreach(wrk, workers)
1145 if (wrk->prepare(drm, drf, xfer->pditem, index))
1146 break;
1147 if (!wrk) {
1148 DEBUG(() << "QPMCoopDragWorker: No suitable worker found");
1149 return (MRESULT)FALSE;
1150 }
1151
1152 xfer->pditem->hwndItem = wrk->hwnd();
1153 Q_ASSERT(xfer->pditem->hwndItem);
1154 return (MRESULT)(xfer->pditem->hwndItem ? TRUE : FALSE);
1155 }
1156
1157 if (msg == DM_RENDER) {
1158 DEBUG(() << "Drop target sent DM_RENDER to the drag source window "
1159 "instead of the drag item window, will try to forward!");
1160 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1161 Q_ASSERT(xfer && xfer->pditem);
1162 if (xfer && xfer->pditem &&
1163 xfer->pditem->hwndItem && xfer->pditem->hwndItem != hwnd())
1164 return WinSendMsg(xfer->pditem->hwndItem, msg, mp1, mp2);
1165 }
1166 else
1167 if (msg == DM_ENDCONVERSATION) {
1168 DEBUG(() << "Drop target sent DM_CONVERSATION to the drag source window "
1169 "instead of the drag item window, will try to forward!");
1170 if (info) {
1171 ULONG itemId = LONGFROMMP(mp1);
1172 // find the item by ID
1173 ULONG itemCnt = DrgQueryDragitemCount(info);
1174 for (ULONG index = 0; index < itemCnt; ++index) {
1175 PDRAGITEM item = DrgQueryDragitemPtr(info, index);
1176 if (item->ulItemID == itemId &&
1177 item->hwndItem && item->hwndItem != hwnd())
1178 return WinSendMsg(item->hwndItem, msg, mp1, mp2);
1179 }
1180 }
1181 }
1182
1183 return (MRESULT)FALSE;
1184}
1185
1186//---------------------------------------------------------------------
1187// QDragManager
1188//---------------------------------------------------------------------
1189
1190Qt::DropAction QDragManager::drag(QDrag *o)
1191
1192{
1193 DEBUG(() << "QDragManager::drag");
1194
1195 if (object == o || !o || !o->d_func()->source)
1196 return Qt::IgnoreAction;
1197
1198 if (object) {
1199 cancel();
1200 qApp->removeEventFilter(this);
1201 beingCancelled = false;
1202 }
1203
1204 // detect a mouse button to end dragging
1205 LONG vkTerminate = 0;
1206 {
1207 ULONG msg = WinQuerySysValue(HWND_DESKTOP, SV_BEGINDRAG) & 0xFFFF;
1208 switch(msg) {
1209 case WM_BUTTON1MOTIONSTART: vkTerminate = VK_BUTTON1; break;
1210 case WM_BUTTON2MOTIONSTART: vkTerminate = VK_BUTTON2; break;
1211 case WM_BUTTON3MOTIONSTART: vkTerminate = VK_BUTTON3; break;
1212 }
1213
1214 if (WinGetKeyState(HWND_DESKTOP, vkTerminate) & 0x8000) {
1215 // prefer the default button if it is pressed
1216 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) {
1217 vkTerminate = VK_BUTTON2;
1218 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) {
1219 vkTerminate = VK_BUTTON1;
1220 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) {
1221 vkTerminate = VK_BUTTON3;
1222 } else {
1223 vkTerminate = 0;
1224 }
1225 }
1226
1227 if (!vkTerminate) {
1228 DEBUG(() << "QDragManager::drag: No valid mouse button pressed, "
1229 "dragging cancelled!");
1230 o->deleteLater();
1231 return Qt::IgnoreAction;
1232 }
1233
1234 USHORT supportedOps = toPmDragDropOps(o->d_func()->possible_actions);
1235
1236 static QPMCoopDragWorker dragWorker;
1237
1238 bool ok = dragWorker.collectWorkers(o);
1239 Q_ASSERT(ok);
1240 Q_ASSERT(dragWorker.hwnd());
1241 if (!ok || !dragWorker.hwnd()) {
1242 o->deleteLater();
1243 return Qt::IgnoreAction;
1244 }
1245
1246 dragWorker.src = o->mimeData();
1247 dragWorker.init();
1248 DRAGINFO *info = dragWorker.createDragInfo(o->objectName(), supportedOps);
1249
1250 Q_ASSERT(info);
1251 if (!info) {
1252 dragWorker.cleanup(true /* isCancelled */);
1253 dragWorker.src = 0;
1254 o->deleteLater();
1255 return Qt::IgnoreAction;
1256 }
1257
1258 object = o;
1259
1260 DEBUG(() << "QDragManager::drag: actions"
1261 << dragActionsToString(dragPrivate()->possible_actions));
1262
1263 dragPrivate()->target = 0;
1264
1265#ifndef QT_NO_ACCESSIBILITY
1266 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
1267#endif
1268
1269 DRAGIMAGE img;
1270 img.cb = sizeof(DRAGIMAGE);
1271
1272 QPixmap pm = dragPrivate()->pixmap;
1273 if (!pm.isNull()) {
1274 if (pm.hasAlpha()) {
1275 // replace the black pixels with the neutral default window
1276 // background color
1277 QPixmap pm2(pm.width(), pm.height());
1278 pm2.fill(QApplication::palette().color(QPalette::Window));
1279 QPainter p(&pm2);
1280 p.drawPixmap(0, 0, pm);
1281 p.end();
1282 pm = pm2;
1283 }
1284 QPoint hot = dragPrivate()->hotspot;
1285 img.fl = DRG_BITMAP;
1286 img.hImage = pm.toPmHBITMAP();
1287 img.cxOffset = -hot.x();
1288 img.cyOffset = -(pm.height() - hot.y() - 1);
1289 } else {
1290 img.fl = DRG_ICON;
1291 img.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
1292 img.cxOffset = 0;
1293 img.cyOffset = 0;
1294 }
1295
1296 // the mouse is most likely captured by Qt at this point, uncapture it
1297 // or DrgDrag() will definitely fail
1298 WinSetCapture(HWND_DESKTOP, 0);
1299
1300 HWND target = DrgDrag(dragWorker.hwnd(), info, &img, 1, vkTerminate,
1301 (PVOID)0x80000000L); // don't lock the desktop PS
1302
1303 DEBUG(("QDragManager::drag: DrgDrag() returned %08lX (error 0x%08lX)",
1304 target, WinGetLastError(0)));
1305
1306 // we won't get any mouse release event, so manually adjust qApp state
1307 qt_pmMouseButtonUp();
1308
1309 bool moveDisallowed = dragWorker.cleanup(beingCancelled || target == 0);
1310 dragWorker.src = 0;
1311
1312 moveDisallowed |= beingCancelled || target == 0 ||
1313 info->usOperation != DO_MOVE;
1314
1315 DEBUG(() << "QDragManager::drag: moveDisallowed" << moveDisallowed);
1316
1317 Qt::DropAction ret = Qt::IgnoreAction;
1318 if (target != 0) {
1319 ret = toQDragDropAction(info->usOperation);
1320 if (moveDisallowed && info->usOperation == DO_MOVE)
1321 ret = Qt::TargetMoveAction;
1322 }
1323
1324 DEBUG(() << "QDragManager::drag: result" << dragActionsToString(ret));
1325
1326 if (target == 0)
1327 DrgDeleteDraginfoStrHandles(info);
1328 DrgFreeDraginfo(info);
1329
1330 if (!beingCancelled) {
1331 dragPrivate()->target = QWidget::find(target);
1332 cancel(); // this will delete o (object)
1333 }
1334
1335 beingCancelled = false;
1336
1337#ifndef QT_NO_ACCESSIBILITY
1338 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1339#endif
1340
1341 return ret;
1342}
1343
1344void QDragManager::cancel(bool deleteSource)
1345{
1346 // Note: the only place where this function is called with
1347 // deleteSource = false so far is QDrag::~QDrag()
1348
1349 Q_ASSERT(object && !beingCancelled);
1350 if (!object || beingCancelled)
1351 return;
1352
1353 beingCancelled = true;
1354
1355 object->setMimeData(0);
1356
1357 if (deleteSource)
1358 object->deleteLater();
1359 object = 0;
1360
1361#ifndef QT_NO_CURSOR
1362 // insert cancel code here ######## todo
1363
1364 if (restoreCursor) {
1365 QApplication::restoreOverrideCursor();
1366 restoreCursor = false;
1367 }
1368#endif
1369#ifndef QT_NO_ACCESSIBILITY
1370 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1371#endif
1372}
1373
1374void QDragManager::updatePixmap()
1375{
1376 // not used in PM implementation
1377}
1378
1379bool QDragManager::eventFilter(QObject *, QEvent *)
1380{
1381 // not used in PM implementation
1382 return false;
1383}
1384
1385void QDragManager::timerEvent(QTimerEvent*)
1386{
1387 // not used in PM implementation
1388}
1389
1390void QDragManager::move(const QPoint &)
1391{
1392 // not used in PM implementation
1393}
1394
1395void QDragManager::drop()
1396{
1397 // not used in PM implementation
1398}
1399
1400QT_END_NAMESPACE
1401
1402#endif // QT_NO_DRAGANDDROP
Note: See TracBrowser for help on using the repository browser.