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

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

gui: Removed qmime.h dependency on private qpmobjectwindow_pm_p.h and on qt_os2.h (which drags in non-portable OS/2 API).

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