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

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

gui: DnD: Revalidate the default acceptance of the next drag move event after the operation is changed by a modifier key.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 44.6 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 if (!dragOverWidget->acceptDrops()) {
514 // We don't reply with DOR_NEVERDROP here because in this
515 // case PM will stop sending DM_DRAGOVER to the given HWND but
516 // we may have another non-native child (that has the same HWND)
517 // at a different position that accepts drops
518 DrgFreeDraginfo(info);
519 return MRFROM2SHORT(DOR_NODROP, 0);
520 }
521
522 USHORT dropReply = dragData->lastDropReply;
523
524 if (first) {
525 // determine the set of operations supported by *all* items
526 // (this implies that DRAGITEM::fsSupportedOps is a bit field)
527 dropReply = DOR_DROP;
528 ULONG itemCount = DrgQueryDragitemCount(info);
529 for (ULONG i = 0; i < itemCount; ++ i) {
530 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
531 Q_ASSERT(item);
532 if (!item) {
533 dropReply = DOR_NEVERDROP;
534 break;
535 }
536 dragData->supportedOps &= item->fsSupportedOps;
537 }
538 if (dropReply != DOR_NEVERDROP) {
539 Q_ASSERT(itemCount);
540 if (!itemCount || !dragData->supportedOps) {
541 // items don't have even a single common operation...
542 dropReply = DOR_NEVERDROP;
543 }
544 }
545 }
546
547 if (dropReply != DOR_NEVERDROP) {
548
549 if (first || dragData->lastDragOverOp != info->usOperation) {
550 // the current drop operation was changed by a modifier key
551 USHORT realOp = info->usOperation;
552 if (realOp == DO_DEFAULT || realOp == DO_UNKNOWN) {
553 // the default operation is requested
554 realOp = toPmDragDropOp(dragData->lastAction);
555 } else {
556 dragData->lastAction = toQDragDropAction(realOp);
557 }
558 dragData->sourceAllowsOp =
559 ((dragData->supportedOps & DO_MOVEABLE) && realOp == DO_MOVE) ||
560 ((dragData->supportedOps & DO_COPYABLE) && realOp == DO_COPY) ||
561 ((dragData->supportedOps & DO_LINKABLE) && realOp == DO_LINK);
562 dragData->lastDragOverOp = realOp;
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 }
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 }
611
612 dragData->lastDropReply = dropReply;
613 }
614
615 DrgFreeDraginfo(info);
616
617 return MRFROM2SHORT(dropReply, toPmDragDropOp(dragData->lastAction));
618 }
619 case DM_DRAGLEAVE: {
620 DEBUG(("DM_DRAGLEAVE: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
621
622 // Odin32 apps produce incorrect message flow, ignore
623 Q_ASSERT(dragData->lastDragOverWidget != 0);
624 if (dragData->lastDragOverWidget == 0)
625 return 0;
626
627 QDragLeaveEvent de;
628 sendDropEvent(dragData->lastDragOverWidget, &de);
629
630 dragData->lastDragOverWidget = 0;
631 dragData->reset();
632
633 return 0;
634 }
635 case DM_DROP: {
636 DEBUG(("DM_DROP: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
637
638 // sanity
639 Q_ASSERT(!dragData->canSyncRender() || qmsg.mp1);
640
641 DRAGINFO *info = 0;
642
643 if (dragData->canSyncRender() || qmsg.mp1) {
644 // Odin32 apps produce incorrect message flow, ignore
645 Q_ASSERT(dragData->lastDragOverWidget != 0);
646 if (dragData->lastDragOverWidget == 0)
647 return 0;
648
649 // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
650 // DOR_NODROP, simulate DM_DRAGLEAVE
651 Q_ASSERT(dragData->lastDropReply == DOR_DROP);
652 if (dragData->lastDropReply != DOR_DROP) {
653 QMSG qmsg2 = qmsg;
654 qmsg2.msg = DM_DRAGLEAVE;
655 dispatchDragAndDrop(widget, qmsg2);
656 return 0;
657 }
658
659 // sanity
660 Q_ASSERT((DRAGINFO *)qmsg.mp1 == dragData->info());
661
662 info = (DRAGINFO *)qmsg.mp1;
663 ok = DrgAccessDraginfo(info);
664 Q_ASSERT(ok && info);
665 if (!ok || !info)
666 return 0;
667
668 if (!dragData->canSyncRender()) {
669 // The source doesn't support syncrhonous rendering (i.e.
670 // it won't issue DM_RENDERCOMPLETE until we return from
671 // DM_DROP) so we have to post a message to ourselves to do
672 // it asynchronously. For simplicity, we reuse DM_DRAG with
673 // mp1 = 0 for this purpose.
674 WinPostMsg(qmsg.hwnd, DM_DROP, 0, 0);
675 return 0;
676 }
677 } else {
678 // we're in a fake DM_DROP we posted above
679 dragData->setInFakeDrop(true);
680 info = dragData->info();
681 Q_ASSERT(info);
682 }
683
684 // flip y coordinate
685 QPoint pnt(info->xDrop, info->yDrop);
686 pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
687 pnt = widget->mapFromGlobal(pnt);
688 if (dragData->lastDragOverWidget != widget)
689 pnt = dragData->lastDragOverWidget->mapFrom(widget, pnt);
690
691 Q_ASSERT(dragData->lastDragOverWidget->acceptDrops());
692 if (!dragData->lastDragOverWidget->acceptDrops()) {
693 DrgDeleteDraginfoStrHandles(info);
694 DrgFreeDraginfo(info);
695 return 0;
696 }
697
698 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
699
700 QDropEvent de(pnt, toQDragDropActions(dragData->supportedOps),
701 data, mouseButtons(), keyboardModifiers());
702 if (dragData->lastDropReply == DOR_DROP)
703 de.setDropAction(dragData->lastAction);
704 sendDropEvent(dragData->lastDragOverWidget, &de);
705
706 if (dragData->lastDropReply == DOR_DROP)
707 de.accept();
708
709 if (de.isAccepted()) {
710 // We must update usOperation so that if the application changes
711 // the drop action during processing of the QDropEvent this
712 // change will be visible at the DrgDrag() end. This is odd (since
713 // it may be easily made out of sync with the visual indication)
714 // but needed for compatibility with other platforms.
715 info->usOperation = toPmDragDropOp(de.dropAction());
716 }
717
718 dragData->lastDragOverWidget = 0;
719 dragData->reset(de.isAccepted());
720
721 ULONG targetReply = de.isAccepted() ?
722 DMFL_TARGETSUCCESSFUL : DMFL_TARGETFAIL;
723
724 if (de.isAccepted() && de.dropAction() == Qt::TargetMoveAction) {
725 // Qt::TargetMoveAction means that the target will move the
726 // object (e.g. copy it to itself and delete from the source).
727 // In this case, we always send DMFL_TARGETFAIL to the source to
728 // prevent it from doing the same on its side.
729 targetReply = DMFL_TARGETFAIL;
730 }
731
732 // send DM_ENDCONVERSATION for every item
733 ULONG itemCount = DrgQueryDragitemCount(info);
734 for (ULONG i = 0; i < itemCount; ++ i) {
735 PDRAGITEM item = DrgQueryDragitemPtr(info, i);
736 Q_ASSERT(item);
737 if (!item)
738 continue;
739 // it is possible that this item required DM_RENDERPREPARE but
740 // returned false in reply to it (so hwndItem may be NULL)
741 if (!item->hwndItem)
742 continue;
743 DrgSendTransferMsg(item->hwndItem, DM_ENDCONVERSATION,
744 MPFROMLONG(item->ulItemID),
745 MPFROMLONG(targetReply));
746 }
747
748 DrgDeleteDraginfoStrHandles(info);
749 DrgFreeDraginfo(info);
750
751 return 0;
752 }
753 default:
754 break;
755 }
756
757 return WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);
758}
759
760//---------------------------------------------------------------------
761// QDropData
762//---------------------------------------------------------------------
763
764bool QDropData::hasFormat_sys(const QString &mimeType) const
765{
766 Q_ASSERT(d);
767 if (d)
768 return d->hasFormat_sys(mimeType);
769 return false;
770}
771
772QStringList QDropData::formats_sys() const
773{
774 QStringList fmts;
775 Q_ASSERT(d);
776 if (d)
777 fmts = d->formats_sys();
778 return fmts;
779}
780
781QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
782{
783 QVariant result;
784 Q_ASSERT(d);
785 if (d)
786 result = d->retrieveData_sys(mimeType, type);
787 return result;
788}
789
790//---------------------------------------------------------------------
791// QPMCoopDragWorker
792//---------------------------------------------------------------------
793
794class QPMCoopDragWorker : public QPMMime::DragWorker, public QPMObjectWindow
795{
796public:
797 QPMCoopDragWorker() : info(0) {}
798 bool collectWorkers(QDrag *o);
799
800 // DragWorker interface
801 void init();
802 bool cleanup(bool isCancelled);
803 bool isExclusive() const { return true; }
804 ULONG itemCount() const { return 0; }
805 HWND hwnd() const;
806 DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps);
807
808 // QPMObjectWindow interface
809 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
810
811private:
812 QList<DragWorker*> workers;
813 // todo check workers!
814 //QPtrList<DragWorker> workers;
815 DRAGINFO *info;
816};
817
818bool QPMCoopDragWorker::collectWorkers(QDrag *o)
819{
820 Q_ASSERT(o);
821 if (!o)
822 return false;
823
824 bool gotExcl = false; // got isExclusive() worker?
825 bool skipExcl = false; // skip isExclusive() or itemCount() > 1 workers?
826 ULONG coopLevel = 0; // itemCount() level for !isExclusive() workers
827
828 bool gotExclForMime = false;
829
830 // go through all formats and all converters to collect DragWorkers
831 QMimeData *mimeData = o->mimeData();
832 foreach (const QString &fmt, mimeData->formats()) {
833 DEBUG(() << "QPMCoopDragWorker: Searching for worker for mime" << fmt);
834 foreach (QPMMime *mime, QPMMime::all()) {
835 DragWorker *wrk = mime->dragWorkerFor(fmt, mimeData);
836 if (!wrk)
837 continue;
838 DEBUG(() << "QPMCoopDragWorker: Got worker" << wrk
839 << "( isExclusive" << wrk->isExclusive()
840 << "itemCount" << wrk->itemCount() << ") from convertor"
841 << mime << "( gotExclForMime" << gotExclForMime
842 << "gotExcl" << gotExcl
843 << "skipExcl" << skipExcl << "coopLevel"
844 << coopLevel << ")");
845 if (wrk->isExclusive()) {
846 if (!skipExcl && !gotExclForMime) {
847 gotExclForMime = true;
848 if (!gotExcl) {
849 gotExcl = true;
850 workers.append(wrk);
851 } else {
852 // skip everything exclusive unless it's exactly the
853 // same worker
854 skipExcl = !workers.contains(wrk);
855 }
856 }
857 // continue to search for a fall-back cooperative 1-item worker
858 // (like QPMMimeAnyMime) for the case if this worker quits
859 // the game
860 continue;
861 }
862 ULONG itemCnt = wrk->itemCount();
863 if (itemCnt == 0) {
864 DEBUG(() << "QPMCoopDragWorker: Cooperative DragWorker"
865 << wrk << "for mime " << fmt << " has "
866 << "itemCount = 0!");
867 continue;
868 }
869 if (itemCnt > 1) {
870 // coop workers with item count > 1 are also considered exclusive
871 // here, because may not co-exist with 1-item workers that should
872 // always be able to contribute
873 if (!gotExcl && !skipExcl && !gotExclForMime) {
874 gotExclForMime = true;
875 workers.append(wrk);
876 if (!coopLevel)
877 coopLevel = itemCnt;
878 // only those for the same number of items can proceed
879 if (itemCnt != coopLevel)
880 skipExcl = true;
881 }
882 // continue to search for a fall-back cooperative 1-item worker
883 // (like QPMMimeAnyMime) for the case if this worker quits
884 // the game
885 continue;
886 }
887 workers.append(wrk);
888 // Don't search for other workrers for the same mime type --
889 // we've already got a drag worker for it and adding another
890 // one would just introduce mime type duplicates on the drop
891 // target's side (where the first encountered drop worker
892 // for the given RMF would be used for actual data transfer
893 // anyway). See also QClipboard::setData().
894 break;
895 }
896 if (gotExclForMime) {
897 // ensure we have a fall-back coop (should be the last added item)
898 DragWorker *w = workers.last();
899 if (w->isExclusive() || w->itemCount() > 1) {
900 DEBUG(() << "QPMCoopDragWorker: DragWorker" << w
901 << "for" << fmt << "( isExclusive" << w->isExclusive()
902 << "itemCount" << w->itemCount()
903 <<") has no fall-back cooperative 1-item worker!");
904 workers.removeLast();
905 }
906 gotExclForMime = false;
907 } else {
908 // got a regular (non-fall-back) 1-item coop, skip evreything else
909 skipExcl = true;
910 }
911 }
912
913 // remove either all exclusive workers or all their fall-back workers
914 // (depending on skipExcl) and remove duplicates
915 for (QList<DragWorker*>::iterator it = workers.begin();
916 it < workers.end();) {
917 DragWorker *wrk = *it;
918 bool excl = wrk->isExclusive() || wrk->itemCount() > 1;
919 if (skipExcl == excl || workers.count(wrk) > 1) {
920 it = workers.erase(it);
921 } else {
922 ++it;
923 }
924 }
925
926#if defined(QDND_DEBUG)
927 foreach (DragWorker *wrk, workers) {
928 DEBUG(() << "QPMCoopDragWorker: Will use worker" << wrk
929 << "( isExclusive" << wrk->isExclusive()
930 << "itemCount" << wrk->itemCount() << ")");
931 }
932#endif
933
934 Q_ASSERT(workers.count() > 0);
935 return workers.count() > 0;
936}
937
938HWND QPMCoopDragWorker::hwnd() const
939{
940 DragWorker *firstWorker = workers.first();
941 Q_ASSERT(firstWorker);
942 if (!firstWorker)
943 return 0;
944
945 if (firstWorker->isExclusive() && firstWorker->itemCount() == 0) {
946 // this is a super exclusive worker that will do everything on its own
947 return firstWorker->hwnd();
948 }
949
950 return QPMObjectWindow::hwnd();
951}
952
953void QPMCoopDragWorker::init()
954{
955 Q_ASSERT(source());
956 foreach(DragWorker *wrk, workers) {
957 wrk->src = source();
958 wrk->init();
959 }
960}
961
962bool QPMCoopDragWorker::cleanup(bool isCancelled)
963{
964 bool moveDisallowed = false;
965
966 foreach(DragWorker *wrk, workers) {
967 // disallow the Move operation if at least one worker asked so
968 moveDisallowed |= wrk->cleanup(isCancelled);
969 wrk->src = 0;
970 }
971 workers.clear();
972 info = 0;
973 return moveDisallowed;
974}
975
976DRAGINFO *QPMCoopDragWorker::createDragInfo(const QString &targetName,
977 USHORT supportedOps)
978{
979 Q_ASSERT(!info);
980 if (info)
981 return 0;
982
983 DragWorker *firstWorker = workers.first();
984 Q_ASSERT(firstWorker);
985 if (!firstWorker)
986 return 0;
987
988 ULONG itemCnt = firstWorker->itemCount();
989
990 if (firstWorker->isExclusive() && itemCnt == 0) {
991 // this is a super exclusive worker that will do everything on its own
992 DEBUG(() << "QPMCoopDragWorker: Will redirect to super worker"
993 << firstWorker);
994 return firstWorker->createDragInfo(targetName, supportedOps);
995 }
996
997 // note that all workers at this place require the same amount of items
998 // (guaranteed by collectWorkers())
999
1000 DEBUG(() << "QPMCoopDragWorker: itemCnt" << itemCnt);
1001
1002 info = DrgAllocDraginfo(itemCnt);
1003 Q_ASSERT(info);
1004 if (!info)
1005 return 0;
1006
1007 // collect all mechanism/format pairs
1008 QByteArray allFormats;
1009 foreach (DragWorker *wrk, workers) {
1010 QByteArray formats = wrk->composeFormatString();
1011 Q_ASSERT(!formats.isNull());
1012 if (!formats.isNull()) {
1013 if (allFormats.isNull())
1014 allFormats = formats;
1015 else {
1016 allFormats += ",";
1017 allFormats += formats;
1018 }
1019 }
1020 }
1021
1022 DEBUG(() << "QPMCoopDragWorker: allFormats" << allFormats);
1023
1024 static ULONG itemID = 0;
1025
1026 const char *type = 0;
1027 const char *ext = 0;
1028 firstWorker->defaultFileType(type, ext);
1029
1030 bool ok = true;
1031 for (ULONG i = 0; i < itemCnt; ++i) {
1032 DRAGITEM *item = DrgQueryDragitemPtr(info, i);
1033 Q_ASSERT(item);
1034 if (!item) {
1035 ok = false;
1036 break;
1037 }
1038
1039 QString name;
1040 if (itemCnt == 1)
1041 name = targetName;
1042 else
1043 name = QString(QLatin1String("%1 %2")).arg(targetName).arg(i + 1);
1044
1045 if (ext) {
1046 name += QLatin1Char('.');
1047 name += QFile::decodeName(QByteArray(ext));
1048 }
1049
1050 DEBUG(() << "QPMCoopDragWorker: item" << i << ": type" << type
1051 << " name" << name);
1052
1053 // Note 1: DRAGITEM::hstrType is actually ignored by WPS,
1054 // only the target extension matters.
1055
1056 // Note 2: We're not required to fill in the hwndItem field because we
1057 // use the DC_PREPARE flag (to indicate it will be filled later, after
1058 // DM_RENDERPREPARE); however, Mozilla refuses to render if hwndItem
1059 // is initially 0. Set it to our HWND instead (we'll issue a warning if
1060 // DM_RENDER or DM_ENDCONVERSATION is erroneously sent to us)
1061
1062 item->hwndItem = hwnd();
1063 item->ulItemID = itemID ++;
1064 item->hstrType = DrgAddStrHandle(type ? type : DRT_UNKNOWN);
1065 item->hstrRMF = DrgAddStrHandle(allFormats);
1066 item->hstrContainerName = 0;
1067 item->hstrSourceName = 0;
1068 item->hstrTargetName = DrgAddStrHandle(QFile::encodeName(name));
1069 item->cxOffset = 0;
1070 item->cyOffset = 0;
1071 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
1072 item->fsSupportedOps = supportedOps;
1073 }
1074
1075 if (!ok) {
1076 DrgFreeDraginfo(info);
1077 info = 0;
1078 }
1079
1080 return info;
1081}
1082
1083MRESULT QPMCoopDragWorker::message(ULONG msg, MPARAM mp1, MPARAM mp2)
1084{
1085 if (msg == DM_RENDERPREPARE) {
1086 if (!info) {
1087 DEBUG(() << "Drop target sent DM_RENDERPREPARE after the DnD "
1088 "session is over!");
1089 // free the given DRAGTRANSFER structure to avoud memory leak
1090 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1091 if (xfer)
1092 qt_DrgFreeDragtransfer(xfer);
1093 return (MRESULT)FALSE;
1094 }
1095
1096 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1097 Q_ASSERT(xfer && xfer->pditem);
1098 if (!xfer || !xfer->pditem)
1099 return (MRESULT)FALSE;
1100
1101 // find the item's index (ordinal number)
1102 ULONG itemCnt = DrgQueryDragitemCount(info);
1103 ULONG index = 0;
1104 for (; index < itemCnt; ++index)
1105 if (DrgQueryDragitemPtr(info, index) == xfer->pditem)
1106 break;
1107
1108 Q_ASSERT(index < itemCnt);
1109 if (index >= itemCnt)
1110 return (MRESULT)FALSE;
1111
1112 DEBUG(() << "QPMCoopDragWorker: Got DM_RENDERPREPARE to"
1113 << QPMMime::queryHSTR(xfer->hstrSelectedRMF) << "for item"
1114 << index << "( id" << xfer->pditem->ulItemID << ")");
1115
1116 QByteArray drm, drf;
1117 if (!QPMMime::parseRMF(xfer->hstrSelectedRMF, drm, drf)) {
1118 Q_ASSERT(false);
1119 return (MRESULT)FALSE;
1120 }
1121
1122 DragWorker *wrk = 0;
1123 foreach(wrk, workers)
1124 if (wrk->prepare(drm, drf, xfer->pditem, index))
1125 break;
1126 if (!wrk) {
1127 DEBUG(() << "QPMCoopDragWorker: No suitable worker found");
1128 return (MRESULT)FALSE;
1129 }
1130
1131 xfer->pditem->hwndItem = wrk->hwnd();
1132 Q_ASSERT(xfer->pditem->hwndItem);
1133 return (MRESULT)(xfer->pditem->hwndItem ? TRUE : FALSE);
1134 }
1135
1136 if (msg == DM_RENDER || msg == DM_ENDCONVERSATION) {
1137 DEBUG(() << "Drop target sent DM_RENDER or DM_ENDCONVERSATION to the "
1138 "drag source window instead of the drag item window!");
1139 if (msg == DM_RENDER) {
1140 // free the given DRAGTRANSFER structure to avoud memory leak
1141 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1;
1142 if (xfer)
1143 qt_DrgFreeDragtransfer(xfer);
1144 }
1145 }
1146
1147 return (MRESULT)FALSE;
1148}
1149
1150//---------------------------------------------------------------------
1151// QDragManager
1152//---------------------------------------------------------------------
1153
1154Qt::DropAction QDragManager::drag(QDrag *o)
1155
1156{
1157 DEBUG(() << "QDragManager::drag");
1158
1159 if (object == o || !o || !o->d_func()->source)
1160 return Qt::IgnoreAction;
1161
1162 if (object) {
1163 cancel();
1164 qApp->removeEventFilter(this);
1165 beingCancelled = false;
1166 }
1167
1168 // detect a mouse button to end dragging
1169 LONG vkTerminate = 0;
1170 {
1171 ULONG msg = WinQuerySysValue(HWND_DESKTOP, SV_BEGINDRAG) & 0xFFFF;
1172 switch(msg) {
1173 case WM_BUTTON1MOTIONSTART: vkTerminate = VK_BUTTON1; break;
1174 case WM_BUTTON2MOTIONSTART: vkTerminate = VK_BUTTON2; break;
1175 case WM_BUTTON3MOTIONSTART: vkTerminate = VK_BUTTON3; break;
1176 }
1177
1178 if (WinGetKeyState(HWND_DESKTOP, vkTerminate) & 0x8000) {
1179 // prefer the default button if it is pressed
1180 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) {
1181 vkTerminate = VK_BUTTON2;
1182 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) {
1183 vkTerminate = VK_BUTTON1;
1184 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) {
1185 vkTerminate = VK_BUTTON3;
1186 } else {
1187 vkTerminate = 0;
1188 }
1189 }
1190
1191 if (!vkTerminate) {
1192 DEBUG(() << "QDragManager::drag: No valid mouse button pressed, "
1193 "dragging cancelled!");
1194 o->deleteLater();
1195 return Qt::IgnoreAction;
1196 }
1197
1198 USHORT supportedOps = toPmDragDropOps(o->d_func()->possible_actions);
1199
1200 static QPMCoopDragWorker dragWorker;
1201
1202 bool ok = dragWorker.collectWorkers(o);
1203 Q_ASSERT(ok);
1204 Q_ASSERT(dragWorker.hwnd());
1205 if (!ok || !dragWorker.hwnd()) {
1206 o->deleteLater();
1207 return Qt::IgnoreAction;
1208 }
1209
1210 dragWorker.src = o->mimeData();
1211 dragWorker.init();
1212 DRAGINFO *info = dragWorker.createDragInfo(o->objectName(), supportedOps);
1213
1214 Q_ASSERT(info);
1215 if (!info) {
1216 dragWorker.cleanup(true /* isCancelled */);
1217 dragWorker.src = 0;
1218 o->deleteLater();
1219 return Qt::IgnoreAction;
1220 }
1221
1222 object = o;
1223
1224 DEBUG(() << "QDragManager::drag: actions"
1225 << dragActionsToString(dragPrivate()->possible_actions));
1226
1227 dragPrivate()->target = 0;
1228
1229#ifndef QT_NO_ACCESSIBILITY
1230 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
1231#endif
1232
1233 // @todo custom drag pixmap?
1234
1235 DRAGIMAGE img;
1236 img.cb = sizeof(DRAGIMAGE);
1237 img.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
1238 img.fl = DRG_ICON;
1239 img.cxOffset = 0;
1240 img.cyOffset = 0;
1241
1242 // the mouse is most likely captured by Qt at this point, uncapture it
1243 // or DrgDrag() will definitely fail
1244 WinSetCapture(HWND_DESKTOP, 0);
1245
1246 HWND target = DrgDrag(dragWorker.hwnd(), info, &img, 1, vkTerminate,
1247 (PVOID)0x80000000L); // don't lock the desktop PS
1248
1249 DEBUG(("QDragManager::drag: DrgDrag() returned %08lX (error 0x%08lX)",
1250 target, WinGetLastError(0)));
1251
1252 // we won't get any mouse release event, so manually adjust qApp state
1253 qt_pmMouseButtonUp();
1254
1255 bool moveDisallowed = dragWorker.cleanup(beingCancelled || target == 0);
1256 dragWorker.src = 0;
1257
1258 moveDisallowed |= beingCancelled || target == 0 ||
1259 info->usOperation != DO_MOVE;
1260
1261 DEBUG(() << "QDragManager::drag: moveDisallowed" << moveDisallowed);
1262
1263 Qt::DropAction ret = Qt::IgnoreAction;
1264 if (target != 0) {
1265 ret = toQDragDropAction(info->usOperation);
1266 if (moveDisallowed && info->usOperation == DO_MOVE)
1267 ret = Qt::TargetMoveAction;
1268 }
1269
1270 DEBUG(() << "QDragManager::drag: result" << dragActionsToString(ret));
1271
1272 if (target == 0)
1273 DrgDeleteDraginfoStrHandles(info);
1274 DrgFreeDraginfo(info);
1275
1276 if (!beingCancelled) {
1277 dragPrivate()->target = QWidget::find(target);
1278 cancel(); // this will delete o (object)
1279 }
1280
1281 beingCancelled = false;
1282
1283#ifndef QT_NO_ACCESSIBILITY
1284 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1285#endif
1286
1287 return ret;
1288}
1289
1290void QDragManager::cancel(bool deleteSource)
1291{
1292 // Note: the only place where this function is called with
1293 // deleteSource = false so far is QDrag::~QDrag()
1294
1295 Q_ASSERT(object && !beingCancelled);
1296 if (!object || beingCancelled)
1297 return;
1298
1299 beingCancelled = true;
1300
1301 object->setMimeData(0);
1302
1303 if (deleteSource)
1304 object->deleteLater();
1305 object = 0;
1306
1307#ifndef QT_NO_CURSOR
1308 // insert cancel code here ######## todo
1309
1310 if (restoreCursor) {
1311 QApplication::restoreOverrideCursor();
1312 restoreCursor = false;
1313 }
1314#endif
1315#ifndef QT_NO_ACCESSIBILITY
1316 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
1317#endif
1318}
1319
1320void QDragManager::updatePixmap()
1321{
1322 // not used in PM implementation
1323}
1324
1325bool QDragManager::eventFilter(QObject *, QEvent *)
1326{
1327 // not used in PM implementation
1328 return false;
1329}
1330
1331void QDragManager::timerEvent(QTimerEvent*)
1332{
1333 // not used in PM implementation
1334}
1335
1336void QDragManager::move(const QPoint &)
1337{
1338 // not used in PM implementation
1339}
1340
1341void QDragManager::drop()
1342{
1343 // not used in PM implementation
1344}
1345
1346QT_END_NAMESPACE
1347
1348#endif // QT_NO_DRAGANDDROP
Note: See TracBrowser for help on using the repository browser.