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

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

gui: Enabled accessibility support on OS/2. Closes #39.

File size: 47.1 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#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),
702 data, mouseButtons(), keyboardModifiers());
703 if (dragData->lastDropReply == DOR_DROP)
704 de.setDropAction(dragData->lastAction);
705 sendDropEvent(dragData->lastDragOverWidget, &de);
706
707 if (dragData->lastDropReply == DOR_DROP)
708 de.accept();
709
710 if (de.isAccepted()) {
711 // We must update usOperation so that if the application changes
712 // the drop action during processing of the QDropEvent this
713 // change will be visible at the DrgDrag() end. This is odd (since
714 // it may be easily made out of sync with the visual indication)
715 // but needed for compatibility with other platforms.
716 info->usOperation = toPmDragDropOp(de.dropAction());
717 }
718
719 dragData->lastDragOverWidget = 0;
720 dragData->reset(de.isAccepted());
721
722 ULONG targetReply = de.isAccepted() ?
723 DMFL_TARGETSUCCESSFUL : DMFL_TARGETFAIL;
724
725 if (de.isAccepted() && de.dropAction() == Qt::TargetMoveAction) {
726 // Qt::TargetMoveAction means that the target will move the
727 // object (e.g. copy it to itself and delete from the source).
728 // In this case, we always send DMFL_TARGETFAIL to the source to
729 // prevent it from doing the same on its side.
730 targetReply = DMFL_TARGETFAIL;
731 }
732
733 // send DM_ENDCONVERSATION for every item
734 ULONG itemCount = DrgQueryDragitemCount(info);
735 for (ULONG i = 0; i < itemCount; ++ i) {
736 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
737 Q_ASSERT(item);
738 if (!item)
739 continue;
740 // it is possible that this item required DM_RENDERPREPARE but
741 // returned false in reply to it (so hwndItem may be NULL)
742 if (!item->hwndItem)
743 continue;
744 DrgSendTransferMsg(item->hwndItem, DM_ENDCONVERSATION,
745 MPFROMLONG(item->ulItemID),
746 MPFROMLONG(targetReply));
747 }
748
749 DrgDeleteDraginfoStrHandles(info);
750 DrgFreeDraginfo(info);
751
752 return 0;
753 }
754 default:
755 break;
756 }
757
758 return WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);
759}
760
761//---------------------------------------------------------------------
762// QDropData
763//---------------------------------------------------------------------
764
765bool QDropData::hasFormat_sys(const QString &mimeType) const
766{
767 Q_ASSERT(d);
768 if (d)
769 return d->hasFormat_sys(mimeType);
770 return false;
771}
772
773QStringList QDropData::formats_sys() const
774{
775 QStringList fmts;
776 Q_ASSERT(d);
777 if (d)
778 fmts = d->formats_sys();
779 return fmts;
780}
781
782QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
783{
784 QVariant result;
785 Q_ASSERT(d);
786 if (d)
787 result = d->retrieveData_sys(mimeType, type);
788 return result;
789}
790
791//---------------------------------------------------------------------
792// QPMCoopDragWorker
793//---------------------------------------------------------------------
794
795class QPMCoopDragWorker : public QPMMime::DragWorker, public QPMObjectWindow
796{
797public:
798 QPMCoopDragWorker() : info(0) {}
799 bool collectWorkers(QDrag *o);
800
801 // DragWorker interface
802 void init();
803 bool cleanup(bool isCancelled);
804 bool isExclusive() const { return true; }
805 ULONG itemCount() const { return 0; }
806 HWND hwnd() const { return QPMObjectWindow::hwnd(); }
807 DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps);
808
809 // QPMObjectWindow interface
810 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
811
812private:
813 QList<DragWorker*> workers;
814 DRAGINFO *info;
815};
816
817bool QPMCoopDragWorker::collectWorkers(QDrag *o)
818{
819 Q_ASSERT(o);
820 if (!o)
821 return false;
822
823 bool gotExcl = false; // got isExclusive() worker?
824 bool skipExcl = false; // skip isExclusive() or itemCount() > 1 workers?
825 ULONG coopLevel = 0; // itemCount() level for !isExclusive() workers
826
827 bool gotExclForMime = false;
828
829 // go through all formats and all converters to collect DragWorkers
830 QMimeData *mimeData = o->mimeData();
831 foreach (const QString &fmt, mimeData->formats()) {
832 DEBUG(() << "QPMCoopDragWorker: Searching for worker for mime" << fmt);
833 foreach (QPMMime *mime, QPMMime::all()) {
834 DragWorker *wrk = mime->dragWorkerFor(fmt, mimeData);
835 if (!wrk)
836 continue;
837 DEBUG(() << "QPMCoopDragWorker: Got worker" << wrk
838 << "( isExclusive" << wrk->isExclusive()
839 << "itemCount" << wrk->itemCount() << ") from convertor"
840 << mime << "( gotExclForMime" << gotExclForMime
841 << "gotExcl" << gotExcl
842 << "skipExcl" << skipExcl << "coopLevel"
843 << coopLevel << ")");
844 if (wrk->isExclusive()) {
845 if (!skipExcl && !gotExclForMime) {
846 gotExclForMime = true;
847 if (!gotExcl) {
848 gotExcl = true;
849 workers.append(wrk);
850 } else {
851 // skip everything exclusive unless it's exactly the
852 // same worker
853 skipExcl = !workers.contains(wrk);
854 }
855 }
856 // continue to search for a fall-back cooperative 1-item worker
857 // (like QPMMimeAnyMime) for the case if this worker quits
858 // the game
859 continue;
860 }
861 ULONG itemCnt = wrk->itemCount();
862 if (itemCnt == 0) {
863 DEBUG(() << "QPMCoopDragWorker: Cooperative DragWorker"
864 << wrk << "for mime " << fmt << " has "
865 << "itemCount = 0!");
866 continue;
867 }
868 if (itemCnt > 1) {
869 // coop workers with item count > 1 are also considered exclusive
870 // here, because may not co-exist with 1-item workers that should
871 // always be able to contribute
872 if (!gotExcl && !skipExcl && !gotExclForMime) {
873 gotExclForMime = true;
874 workers.append(wrk);
875 if (!coopLevel)
876 coopLevel = itemCnt;
877 // only those for the same number of items can proceed
878 if (itemCnt != coopLevel)
879 skipExcl = true;
880 }
881 // continue to search for a fall-back cooperative 1-item worker
882 // (like QPMMimeAnyMime) for the case if this worker quits
883 // the game
884 continue;
885 }
886 workers.append(wrk);
887 // Don't search for other workrers for the same mime type --
888 // we've already got a drag worker for it and adding another
889 // one would just introduce mime type duplicates on the drop
890 // target's side (where the first encountered drop worker
891 // for the given RMF would be used for actual data transfer
892 // anyway). See also QClipboard::setData().
893 break;
894 }
895 if (gotExclForMime) {
896 // ensure we have a fall-back coop (should be the last added item)
897 DragWorker *w = workers.last();
898 if (w->isExclusive() || w->itemCount() > 1) {
899 DEBUG(() << "QPMCoopDragWorker: DragWorker" << w
900 << "for" << fmt << "( isExclusive" << w->isExclusive()
901 << "itemCount" << w->itemCount()
902 <<") has no fall-back cooperative 1-item worker!");
903 workers.removeLast();
904 }
905 gotExclForMime = false;
906 } else {
907 // got a regular (non-fall-back) 1-item coop, skip evreything else
908 skipExcl = true;
909 }
910 }
911
912 // remove either all exclusive workers or all their fall-back workers
913 // (depending on skipExcl) and remove duplicates
914 for (QList<DragWorker*>::iterator it = workers.begin();
915 it < workers.end();) {
916 DragWorker *wrk = *it;
917 bool excl = wrk->isExclusive() || wrk->itemCount() > 1;
918 if (skipExcl == excl || workers.count(wrk) > 1) {
919 it = workers.erase(it);
920 } else {
921 ++it;
922 }
923 }
924
925#if defined(QDND_DEBUG)
926 foreach (DragWorker *wrk, workers) {
927 DEBUG(() << "QPMCoopDragWorker: Will use worker" << wrk
928 << "( isExclusive" << wrk->isExclusive()
929 << "itemCount" << wrk->itemCount() << ")");
930 }
931#endif
932
933 if (!mimeData->formats().count()) {
934 // The drag source doesn't provide any format, so we've got no workers.
935 // Although it may look strange, it is a supported case: for example,
936 // designer uses it a lot for in-process DnD. Instead of MIME data, it
937 // works directly with the QMimeData object from the drag source. We
938 // will handle this situation by providing a dummy DRAGINFO structure.
939 DEBUG(() << "QPMCoopDragWorker: Source provides NO data, will use "
940 "dummy DRAGINFO");
941 Q_ASSERT(!workers.count());
942 return true;
943 }
944
945 Q_ASSERT(workers.count() > 0);
946 return workers.count() > 0;
947}
948
949void QPMCoopDragWorker::init()
950{
951 Q_ASSERT(source());
952 foreach(DragWorker *wrk, workers) {
953 wrk->src = source();
954 wrk->init();
955 }
956}
957
958bool QPMCoopDragWorker::cleanup(bool isCancelled)
959{
960 bool moveDisallowed = false;
961
962 foreach(DragWorker *wrk, workers) {
963 // disallow the Move operation if at least one worker asked so
964 moveDisallowed |= wrk->cleanup(isCancelled);
965 wrk->src = 0;
966 }
967 workers.clear();
968 info = 0;
969 return moveDisallowed;
970}
971
972DRAGINFO *QPMCoopDragWorker::createDragInfo(const QString &targetName,
973 USHORT supportedOps)
974{
975 Q_ASSERT(!info);
976 if (info)
977 return 0;
978
979 if (!workers.count()) {
980 // The drag source doesn't provide any format; provide a dummy DRAGINFO
981 // with a null DRAGITEM that we will never render
982 info = DrgAllocDraginfo(1);
983 Q_ASSERT(info);
984 if (!info)
985 return 0;
986
987 DRAGITEM *item = DrgQueryDragitemPtr(info, 0);
988 Q_ASSERT(item);
989 if (!item) {
990 DrgFreeDraginfo(info);
991 info = 0;
992 return 0;
993 }
994
995 item->hwndItem = 0;
996 item->ulItemID = 0;
997 item->hstrType = 0;
998 item->hstrRMF = 0;
999 item->hstrContainerName = 0;
1000 item->hstrSourceName = 0;
1001 item->hstrTargetName = 0;
1002 item->cxOffset = 0;
1003 item->cyOffset = 0;
1004 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
1005 item->fsSupportedOps = supportedOps;
1006
1007 return info;
1008 }
1009
1010 DragWorker *firstWorker = workers.first();
1011 Q_ASSERT(firstWorker);
1012 if (!firstWorker)
1013 return 0;
1014
1015 ULONG itemCnt = firstWorker->itemCount();
1016
1017 if (firstWorker->isExclusive() && itemCnt == 0) {
1018 // this is a super exclusive worker that will do everything on its own
1019 DEBUG(() << "QPMCoopDragWorker: Will redirect to super worker"
1020 << firstWorker);
1021 return firstWorker->createDragInfo(targetName, supportedOps);
1022 }
1023
1024 // note that all workers at this place require the same amount of items
1025 // (guaranteed by collectWorkers())
1026
1027 DEBUG(() << "QPMCoopDragWorker: itemCnt" << itemCnt);
1028
1029 info = DrgAllocDraginfo(itemCnt);
1030 Q_ASSERT(info);
1031 if (!info)
1032 return 0;
1033
1034 // collect all mechanism/format pairs
1035 QByteArray allFormats;
1036 foreach (DragWorker *wrk, workers) {
1037 QByteArray formats = wrk->composeFormatString();
1038 Q_ASSERT(!formats.isNull());
1039 if (!formats.isNull()) {
1040 if (allFormats.isNull())
1041 allFormats = formats;
1042 else {
1043 allFormats += ",";
1044 allFormats += formats;
1045 }
1046 }
1047 }
1048
1049 DEBUG(() << "QPMCoopDragWorker: allFormats" << allFormats);
1050
1051 static ULONG itemID = 0;
1052
1053 QString type;
1054 QString ext;
1055 firstWorker->defaultFileType(type, ext);
1056
1057 bool ok = true;
1058 for (ULONG i = 0; i < itemCnt; ++i) {
1059 DRAGITEM *item = DrgQueryDragitemPtr(info, i);
1060 Q_ASSERT(item);
1061 if (!item) {
1062 ok = false;
1063 break;
1064 }
1065
1066 QString name;
1067 if (itemCnt == 1)
1068 name = targetName;
1069 else
1070 name = QString(QLatin1String("%1 %2")).arg(targetName).arg(i + 1);
1071
1072 if (!ext.isEmpty())
1073 name += QString(QLatin1String(".%1")).arg(ext);
1074
1075 DEBUG(() << "QPMCoopDragWorker: item" << i << ": type" << type
1076 << " name" << name);
1077
1078 // Note 1: DRAGITEM::hstrType is actually ignored by WPS,
1079 // only the target extension matters.
1080
1081 // Note 2: We're not required to fill in the hwndItem field because we
1082 // use the DC_PREPARE flag (to indicate it will be filled later, after
1083 // DM_RENDERPREPARE); however, Mozilla refuses to render if hwndItem
1084 // is initially 0. Set it to our HWND instead (we'll issue a warning if
1085 // DM_RENDER or DM_ENDCONVERSATION is erroneously sent to us)
1086
1087 item->hwndItem = hwnd();
1088 item->ulItemID = itemID ++;
1089 item->hstrType = DrgAddStrHandle(!type.isEmpty() ?
1090 QFile::encodeName(type) : DRT_UNKNOWN);
1091 item->hstrRMF = DrgAddStrHandle(allFormats);
1092 item->hstrContainerName = 0;
1093 item->hstrSourceName = 0;
1094 item->hstrTargetName = DrgAddStrHandle(QFile::encodeName(name));
1095 item->cxOffset = 0;
1096 item->cyOffset = 0;
1097 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
1098 item->fsSupportedOps = supportedOps;
1099 }
1100
1101 if (!ok) {
1102 DrgFreeDraginfo(info);
1103 info = 0;
1104 }
1105
1106 return info;
1107}
1108
1109MRESULT QPMCoopDragWorker::message(ULONG msg, MPARAM mp1, MPARAM mp2)
1110{
1111 if (msg == DM_RENDERPREPARE) {
1112 if (!info) {
1113 DEBUG(() << "Drop target sent DM_RENDERPREPARE after the DnD "
1114 "session is over!");
1115 // free the given DRAGTRANSFER structure to avoud memory leak
1116 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1117 if (xfer)
1118 qt_DrgFreeDragtransfer(xfer);
1119 return (MRESULT)FALSE;
1120 }
1121
1122 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1123 Q_ASSERT(xfer && xfer->pditem);
1124 if (!xfer || !xfer->pditem)
1125 return (MRESULT)FALSE;
1126
1127 // find the item's index (ordinal number)
1128 ULONG itemCnt = DrgQueryDragitemCount(info);
1129 ULONG index = 0;
1130 for (; index < itemCnt; ++index)
1131 if (DrgQueryDragitemPtr(info, index) == xfer->pditem)
1132 break;
1133
1134 Q_ASSERT(index < itemCnt);
1135 if (index >= itemCnt)
1136 return (MRESULT)FALSE;
1137
1138 DEBUG(() << "QPMCoopDragWorker: Got DM_RENDERPREPARE to"
1139 << QPMMime::queryHSTR(xfer->hstrSelectedRMF) << "for item"
1140 << index << "( id" << xfer->pditem->ulItemID << ")");
1141
1142 QByteArray drm, drf;
1143 if (!QPMMime::parseRMF(xfer->hstrSelectedRMF, drm, drf)) {
1144 Q_ASSERT(false);
1145 return (MRESULT)FALSE;
1146 }
1147
1148 DragWorker *wrk = 0;
1149 foreach(wrk, workers)
1150 if (wrk->prepare(drm, drf, xfer->pditem, index))
1151 break;
1152 if (!wrk) {
1153 DEBUG(() << "QPMCoopDragWorker: No suitable worker found");
1154 return (MRESULT)FALSE;
1155 }
1156
1157 xfer->pditem->hwndItem = wrk->hwnd();
1158 Q_ASSERT(xfer->pditem->hwndItem);
1159 return (MRESULT)(xfer->pditem->hwndItem ? TRUE : FALSE);
1160 }
1161
1162 if (msg == DM_RENDER) {
1163 DEBUG(() << "Drop target sent DM_RENDER to the drag source window "
1164 "instead of the drag item window, will try to forward!");
1165 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1166 Q_ASSERT(xfer && xfer->pditem);
1167 if (xfer && xfer->pditem &&
1168 xfer->pditem->hwndItem && xfer->pditem->hwndItem != hwnd())
1169 return WinSendMsg(xfer->pditem->hwndItem, msg, mp1, mp2);
1170 }
1171 else
1172 if (msg == DM_ENDCONVERSATION) {
1173 DEBUG(() << "Drop target sent DM_CONVERSATION to the drag source window "
1174 "instead of the drag item window, will try to forward!");
1175 if (info) {
1176 ULONG itemId = LONGFROMMP(mp1);
1177 // find the item by ID
1178 ULONG itemCnt = DrgQueryDragitemCount(info);
1179 for (ULONG index = 0; index < itemCnt; ++index) {
1180 PDRAGITEM item = DrgQueryDragitemPtr(info, index);
1181 if (item->ulItemID == itemId &&
1182 item->hwndItem && item->hwndItem != hwnd())
1183 return WinSendMsg(item->hwndItem, msg, mp1, mp2);
1184 }
1185 }
1186 }
1187
1188 return (MRESULT)FALSE;
1189}
1190
1191//---------------------------------------------------------------------
1192// QDragManager
1193//---------------------------------------------------------------------
1194
1195Qt::DropAction QDragManager::drag(QDrag *o)
1196
1197{
1198 DEBUG(() << "QDragManager::drag");
1199
1200 if (object == o || !o || !o->d_func()->source)
1201 return Qt::IgnoreAction;
1202
1203 if (object) {
1204 cancel();
1205 qApp->removeEventFilter(this);
1206 beingCancelled = false;
1207 }
1208
1209 // detect a mouse button to end dragging
1210 LONG vkTerminate = 0;
1211 {
1212 ULONG msg = WinQuerySysValue(HWND_DESKTOP, SV_BEGINDRAG) & 0xFFFF;
1213 switch(msg) {
1214 case WM_BUTTON1MOTIONSTART: vkTerminate = VK_BUTTON1; break;
1215 case WM_BUTTON2MOTIONSTART: vkTerminate = VK_BUTTON2; break;
1216 case WM_BUTTON3MOTIONSTART: vkTerminate = VK_BUTTON3; break;
1217 }
1218
1219 if (WinGetKeyState(HWND_DESKTOP, vkTerminate) & 0x8000) {
1220 // prefer the default button if it is pressed
1221 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) {
1222 vkTerminate = VK_BUTTON2;
1223 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) {
1224 vkTerminate = VK_BUTTON1;
1225 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) {
1226 vkTerminate = VK_BUTTON3;
1227 } else {
1228 vkTerminate = 0;
1229 }
1230 }
1231
1232 if (!vkTerminate) {
1233 DEBUG(() << "QDragManager::drag: No valid mouse button pressed, "
1234 "dragging cancelled!");
1235 o->deleteLater();
1236 return Qt::IgnoreAction;
1237 }
1238
1239 USHORT supportedOps = toPmDragDropOps(o->d_func()->possible_actions);
1240
1241 static QPMCoopDragWorker dragWorker;
1242
1243 bool ok = dragWorker.collectWorkers(o);
1244 Q_ASSERT(ok);
1245 Q_ASSERT(dragWorker.hwnd());
1246 if (!ok || !dragWorker.hwnd()) {
1247 o->deleteLater();
1248 return Qt::IgnoreAction;
1249 }
1250
1251 dragWorker.src = o->mimeData();
1252 dragWorker.init();
1253 DRAGINFO *info = dragWorker.createDragInfo(o->objectName(), supportedOps);
1254
1255 Q_ASSERT(info);
1256 if (!info) {
1257 dragWorker.cleanup(true /* isCancelled */);
1258 dragWorker.src = 0;
1259 o->deleteLater();
1260 return Qt::IgnoreAction;
1261 }
1262
1263 object = o;
1264
1265 DEBUG(() << "QDragManager::drag: actions"
1266 << dragActionsToString(dragPrivate()->possible_actions));
1267
1268 dragPrivate()->target = 0;
1269
1270#ifndef QT_NO_ACCESSIBILITY
1271 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
1272#endif
1273
1274 DRAGIMAGE img;
1275 img.cb = sizeof(DRAGIMAGE);
1276
1277 QPixmap pm = dragPrivate()->pixmap;
1278 if (!pm.isNull()) {
1279 if (pm.hasAlpha()) {
1280 // replace the black pixels with the neutral default window
1281 // background color
1282 QPixmap pm2(pm.width(), pm.height());
1283 pm2.fill(QApplication::palette().color(QPalette::Window));
1284 QPainter p(&pm2);
1285 p.drawPixmap(0, 0, pm);
1286 p.end();
1287 pm = pm2;
1288 }
1289 QPoint hot = dragPrivate()->hotspot;
1290 img.fl = DRG_BITMAP;
1291 img.hImage = pm.toPmHBITMAP();
1292 img.cxOffset = -hot.x();
1293 img.cyOffset = -(pm.height() - hot.y() - 1);
1294 } else {
1295 img.fl = DRG_ICON;
1296 img.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
1297 img.cxOffset = 0;
1298 img.cyOffset = 0;
1299 }
1300
1301 // the mouse is most likely captured by Qt at this point, uncapture it
1302 // or DrgDrag() will definitely fail
1303 WinSetCapture(HWND_DESKTOP, 0);
1304
1305 HWND target = DrgDrag(dragWorker.hwnd(), info, &img, 1, vkTerminate,
1306 (PVOID)0x80000000L); // don't lock the desktop PS
1307
1308 DEBUG(("QDragManager::drag: DrgDrag() returned %08lX (error 0x%08lX)",
1309 target, WinGetLastError(0)));
1310
1311 // we won't get any mouse release event, so manually adjust qApp state
1312 qt_pmMouseButtonUp();
1313
1314 bool moveDisallowed = dragWorker.cleanup(beingCancelled || target == 0);
1315 dragWorker.src = 0;
1316
1317 moveDisallowed |= beingCancelled || target == 0 ||
1318 info->usOperation != DO_MOVE;
1319
1320 DEBUG(() << "QDragManager::drag: moveDisallowed" << moveDisallowed);
1321
1322 Qt::DropAction ret = Qt::IgnoreAction;
1323 if (target != 0) {
1324 ret = toQDragDropAction(info->usOperation);
1325 if (moveDisallowed && info->usOperation == DO_MOVE)
1326 ret = Qt::TargetMoveAction;
1327 }
1328
1329 DEBUG(() << "QDragManager::drag: result" << dragActionsToString(ret));
1330
1331 if (target == 0)
1332 DrgDeleteDraginfoStrHandles(info);
1333 DrgFreeDraginfo(info);
1334
1335 if (!beingCancelled) {
1336 dragPrivate()->target = QWidget::find(target);
1337 cancel(); // this will delete o (object)
1338 }
1339
1340 beingCancelled = false;
1341
1342#ifndef QT_NO_ACCESSIBILITY
1343 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1344#endif
1345
1346 return ret;
1347}
1348
1349void QDragManager::cancel(bool deleteSource)
1350{
1351 // Note: the only place where this function is called with
1352 // deleteSource = false so far is QDrag::~QDrag()
1353
1354 Q_ASSERT(object && !beingCancelled);
1355 if (!object || beingCancelled)
1356 return;
1357
1358 beingCancelled = true;
1359
1360 object->setMimeData(0);
1361
1362 if (deleteSource)
1363 object->deleteLater();
1364 object = 0;
1365
1366#ifndef QT_NO_CURSOR
1367 // insert cancel code here ######## todo
1368
1369 if (restoreCursor) {
1370 QApplication::restoreOverrideCursor();
1371 restoreCursor = false;
1372 }
1373#endif
1374#ifndef QT_NO_ACCESSIBILITY
1375 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1376#endif
1377}
1378
1379void QDragManager::updatePixmap()
1380{
1381 // not used in PM implementation
1382}
1383
1384bool QDragManager::eventFilter(QObject *, QEvent *)
1385{
1386 // not used in PM implementation
1387 return false;
1388}
1389
1390void QDragManager::timerEvent(QTimerEvent*)
1391{
1392 // not used in PM implementation
1393}
1394
1395void QDragManager::move(const QPoint &)
1396{
1397 // not used in PM implementation
1398}
1399
1400void QDragManager::drop()
1401{
1402 // not used in PM implementation
1403}
1404
1405QT_END_NAMESPACE
1406
1407#endif // QT_NO_DRAGANDDROP
Note: See TracBrowser for help on using the repository browser.