source: trunk/src/gui/kernel/qdnd.cpp@ 786

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

trunk: Merged in qt 4.6.2 sources.

File size: 20.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qplatformdefs.h"
43
44#include "qbitmap.h"
45#include "qdrag.h"
46#include "qpixmap.h"
47#include "qevent.h"
48#include "qfile.h"
49#include "qtextcodec.h"
50#include "qapplication.h"
51#include "qpoint.h"
52#include "qwidget.h"
53#include "qbuffer.h"
54#include "qimage.h"
55#include "qregexp.h"
56#include "qdir.h"
57#include "qdnd_p.h"
58#include "qimagereader.h"
59#include "qimagewriter.h"
60#include "qdebug.h"
61#include <ctype.h>
62
63#ifndef QT_NO_DRAGANDDROP
64
65QT_BEGIN_NAMESPACE
66
67// These pixmaps approximate the images in the Windows User Interface Guidelines.
68
69// XPM
70
71static const char * const move_xpm[] = {
72"11 20 3 1",
73". c None",
74#if defined(Q_WS_WIN)
75"a c #000000",
76"X c #FFFFFF", // Windows cursor is traditionally white
77#else
78"a c #FFFFFF",
79"X c #000000", // X11 cursor is traditionally black
80#endif
81"aa.........",
82"aXa........",
83"aXXa.......",
84"aXXXa......",
85"aXXXXa.....",
86"aXXXXXa....",
87"aXXXXXXa...",
88"aXXXXXXXa..",
89"aXXXXXXXXa.",
90"aXXXXXXXXXa",
91"aXXXXXXaaaa",
92"aXXXaXXa...",
93"aXXaaXXa...",
94"aXa..aXXa..",
95"aa...aXXa..",
96"a.....aXXa.",
97"......aXXa.",
98".......aXXa",
99".......aXXa",
100"........aa."};
101
102#if defined(Q_WS_WIN) || defined(Q_WS_PM)
103/* XPM */
104static const char * const ignore_xpm[] = {
105"24 30 3 1",
106". c None",
107#if defined(Q_WS_WIN)
108"a c #000000",
109"X c #FFFFFF", // Windows cursor is traditionally white
110#else
111"a c #FFFFFF",
112"X c #000000", // OS/2 cursor is traditionally black
113#endif
114"aa......................",
115"aXa.....................",
116"aXXa....................",
117"aXXXa...................",
118"aXXXXa..................",
119"aXXXXXa.................",
120"aXXXXXXa................",
121"aXXXXXXXa...............",
122"aXXXXXXXXa..............",
123"aXXXXXXXXXa.............",
124"aXXXXXXaaaa.............",
125"aXXXaXXa................",
126"aXXaaXXa................",
127"aXa..aXXa...............",
128"aa...aXXa...............",
129"a.....aXXa..............",
130"......aXXa.....XXXX.....",
131".......aXXa..XXaaaaXX...",
132".......aXXa.XaaaaaaaaX..",
133"........aa.XaaaXXXXaaaX.",
134"...........XaaaaX..XaaX.",
135"..........XaaXaaaX..XaaX",
136"..........XaaXXaaaX.XaaX",
137"..........XaaX.XaaaXXaaX",
138"..........XaaX..XaaaXaaX",
139"...........XaaX..XaaaaX.",
140"...........XaaaXXXXaaaX.",
141"............XaaaaaaaaX..",
142".............XXaaaaXX...",
143"...............XXXX....."};
144#endif
145
146/* XPM */
147static const char * const copy_xpm[] = {
148"24 30 3 1",
149". c None",
150"a c #000000",
151"X c #FFFFFF",
152#if defined(Q_WS_WIN) // Windows cursor is traditionally white
153"aa......................",
154"aXa.....................",
155"aXXa....................",
156"aXXXa...................",
157"aXXXXa..................",
158"aXXXXXa.................",
159"aXXXXXXa................",
160"aXXXXXXXa...............",
161"aXXXXXXXXa..............",
162"aXXXXXXXXXa.............",
163"aXXXXXXaaaa.............",
164"aXXXaXXa................",
165"aXXaaXXa................",
166"aXa..aXXa...............",
167"aa...aXXa...............",
168"a.....aXXa..............",
169"......aXXa..............",
170".......aXXa.............",
171".......aXXa.............",
172"........aa...aaaaaaaaaaa",
173#else
174"XX......................",
175"XaX.....................",
176"XaaX....................",
177"XaaaX...................",
178"XaaaaX..................",
179"XaaaaaX.................",
180"XaaaaaaX................",
181"XaaaaaaaX...............",
182"XaaaaaaaaX..............",
183"XaaaaaaaaaX.............",
184"XaaaaaaXXXX.............",
185"XaaaXaaX................",
186"XaaXXaaX................",
187"XaX..XaaX...............",
188"XX...XaaX...............",
189"X.....XaaX..............",
190"......XaaX..............",
191".......XaaX.............",
192".......XaaX.............",
193"........XX...aaaaaaaaaaa",
194#endif
195".............aXXXXXXXXXa",
196".............aXXXXXXXXXa",
197".............aXXXXaXXXXa",
198".............aXXXXaXXXXa",
199".............aXXaaaaaXXa",
200".............aXXXXaXXXXa",
201".............aXXXXaXXXXa",
202".............aXXXXXXXXXa",
203".............aXXXXXXXXXa",
204".............aaaaaaaaaaa"};
205
206/* XPM */
207static const char * const link_xpm[] = {
208"24 30 3 1",
209". c None",
210"a c #000000",
211"X c #FFFFFF",
212#if defined(Q_WS_WIN) // Windows cursor is traditionally white
213"aa......................",
214"aXa.....................",
215"aXXa....................",
216"aXXXa...................",
217"aXXXXa..................",
218"aXXXXXa.................",
219"aXXXXXXa................",
220"aXXXXXXXa...............",
221"aXXXXXXXXa..............",
222"aXXXXXXXXXa.............",
223"aXXXXXXaaaa.............",
224"aXXXaXXa................",
225"aXXaaXXa................",
226"aXa..aXXa...............",
227"aa...aXXa...............",
228"a.....aXXa..............",
229"......aXXa..............",
230".......aXXa.............",
231".......aXXa.............",
232"........aa...aaaaaaaaaaa",
233#else
234"XX......................",
235"XaX.....................",
236"XaaX....................",
237"XaaaX...................",
238"XaaaaX..................",
239"XaaaaaX.................",
240"XaaaaaaX................",
241"XaaaaaaaX...............",
242"XaaaaaaaaX..............",
243"XaaaaaaaaaX.............",
244"XaaaaaaXXXX.............",
245"XaaaXaaX................",
246"XaaXXaaX................",
247"XaX..XaaX...............",
248"XX...XaaX...............",
249"X.....XaaX..............",
250"......XaaX..............",
251".......XaaX.............",
252".......XaaX.............",
253"........XX...aaaaaaaaaaa",
254#endif
255".............aXXXXXXXXXa",
256".............aXXXaaaaXXa",
257".............aXXXXaaaXXa",
258".............aXXXaaaaXXa",
259".............aXXaaaXaXXa",
260".............aXXaaXXXXXa",
261".............aXXaXXXXXXa",
262".............aXXXaXXXXXa",
263".............aXXXXXXXXXa",
264".............aaaaaaaaaaa"};
265
266#ifndef QT_NO_DRAGANDDROP
267
268//#define QDND_DEBUG
269
270#ifdef QDND_DEBUG
271QString dragActionsToString(Qt::DropActions actions)
272{
273 QString str;
274 if (actions == Qt::IgnoreAction) {
275 if (!str.isEmpty())
276 str += QLatin1String(" | ");
277 str += QLatin1String("IgnoreAction");
278 }
279 if (actions & Qt::LinkAction) {
280 if (!str.isEmpty())
281 str += QLatin1String(" | ");
282 str += QLatin1String("LinkAction");
283 }
284 if (actions & Qt::CopyAction) {
285 if (!str.isEmpty())
286 str += QLatin1String(" | ");
287 str += QLatin1String("CopyAction");
288 }
289 if (actions & Qt::MoveAction) {
290 if (!str.isEmpty())
291 str += QLatin1String(" | ");
292 str += QLatin1String("MoveAction");
293 }
294 if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
295 if (!str.isEmpty())
296 str += QLatin1String(" | ");
297 str += QLatin1String("TargetMoveAction");
298 }
299 return str;
300}
301
302QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
303{
304 QString str;
305 if (moderfies & Qt::ControlModifier) {
306 if (!str.isEmpty())
307 str += QLatin1String(" | ");
308 str += QLatin1String("ControlModifier");
309 }
310 if (moderfies & Qt::AltModifier) {
311 if (!str.isEmpty())
312 str += QLatin1String(" | ");
313 str += QLatin1String("AltModifier");
314 }
315 if (moderfies & Qt::ShiftModifier) {
316 if (!str.isEmpty())
317 str += QLatin1String(" | ");
318 str += QLatin1String("ShiftModifier");
319 }
320 return str;
321}
322#endif
323
324
325// the universe's only drag manager
326QDragManager *QDragManager::instance = 0;
327
328
329QDragManager::QDragManager()
330 : QObject(qApp)
331{
332 Q_ASSERT(!instance);
333
334#if defined(Q_WS_WIN) || defined(Q_WS_PM)
335 n_cursor = 4;
336#else
337 n_cursor = 3;
338#endif
339
340#ifdef Q_WS_QWS
341 currentActionForOverrideCursor = Qt::IgnoreAction;
342#endif
343 pm_cursor = new QPixmap[n_cursor];
344 pm_cursor[0] = QPixmap((const char **)move_xpm);
345 pm_cursor[1] = QPixmap((const char **)copy_xpm);
346 pm_cursor[2] = QPixmap((const char **)link_xpm);
347#if defined(Q_WS_WIN) || defined(Q_WS_PM)
348 pm_cursor[3] = QPixmap((const char **)ignore_xpm);
349#endif
350 object = 0;
351 beingCancelled = false;
352 restoreCursor = false;
353 willDrop = false;
354 eventLoop = 0;
355 dropData = new QDropData();
356 currentDropTarget = 0;
357#ifdef Q_WS_X11
358 xdndMimeTransferedPixmapIndex = 0;
359#endif
360#ifdef Q_WS_PM
361 init_sys();
362#endif
363}
364
365
366QDragManager::~QDragManager()
367{
368#ifdef Q_WS_PM
369 uninit_sys();
370#endif
371#ifndef QT_NO_CURSOR
372 if (restoreCursor)
373 QApplication::restoreOverrideCursor();
374#endif
375 instance = 0;
376 delete [] pm_cursor;
377 delete dropData;
378}
379
380QDragManager *QDragManager::self()
381{
382 if (!instance && !QApplication::closingDown())
383 instance = new QDragManager;
384 return instance;
385}
386
387QPixmap QDragManager::dragCursor(Qt::DropAction action) const
388{
389 QDragPrivate * d = dragPrivate();
390 if (d && d->customCursors.contains(action))
391 return d->customCursors[action];
392 else if (action == Qt::MoveAction)
393 return pm_cursor[0];
394 else if (action == Qt::CopyAction)
395 return pm_cursor[1];
396 else if (action == Qt::LinkAction)
397 return pm_cursor[2];
398#ifdef Q_WS_WIN
399 else if (action == Qt::IgnoreAction)
400 return pm_cursor[3];
401#endif
402 return QPixmap();
403}
404
405bool QDragManager::hasCustomDragCursors() const
406{
407 QDragPrivate * d = dragPrivate();
408 return d && !d->customCursors.isEmpty();
409}
410
411Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
412 Qt::KeyboardModifiers modifiers) const
413{
414#ifdef QDND_DEBUG
415 qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
416 qDebug("keyboard modifiers : %ls", KeyboardModifiersToString(modifiers).utf16());
417#endif
418
419 QDragPrivate *d = dragPrivate();
420 Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
421
422 if (defaultAction == Qt::IgnoreAction) {
423 //This means that the drag was initiated by QDrag::start and we need to
424 //preserve the old behavior
425#ifdef Q_WS_MAC
426 defaultAction = Qt::MoveAction;
427#else
428 defaultAction = Qt::CopyAction;
429#endif
430 }
431
432#ifdef Q_WS_MAC
433 if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
434 defaultAction = Qt::LinkAction;
435 else if (modifiers & Qt::AltModifier)
436 defaultAction = Qt::CopyAction;
437 else if (modifiers & Qt::ControlModifier)
438 defaultAction = Qt::MoveAction;
439#else
440 if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
441 defaultAction = Qt::LinkAction;
442 else if (modifiers & Qt::ControlModifier)
443 defaultAction = Qt::CopyAction;
444 else if (modifiers & Qt::ShiftModifier)
445 defaultAction = Qt::MoveAction;
446 else if (modifiers & Qt::AltModifier)
447 defaultAction = Qt::LinkAction;
448#endif
449
450 // if the object is set take the list of possibles from it
451 if (object)
452 possibleActions = object->d_func()->possible_actions;
453
454#ifdef QDND_DEBUG
455 qDebug("possible actions : %ls", dragActionsToString(possibleActions).utf16());
456#endif
457
458 // Check if the action determined is allowed
459 if (!(possibleActions & defaultAction)) {
460 if (possibleActions & Qt::CopyAction)
461 defaultAction = Qt::CopyAction;
462 else if (possibleActions & Qt::MoveAction)
463 defaultAction = Qt::MoveAction;
464 else if (possibleActions & Qt::LinkAction)
465 defaultAction = Qt::LinkAction;
466 else
467 defaultAction = Qt::IgnoreAction;
468 }
469
470#ifdef QDND_DEBUG
471 qDebug("default action : %ls", dragActionsToString(defaultAction).utf16());
472#endif
473
474 return defaultAction;
475}
476
477void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
478{
479 if (currentDropTarget == target)
480 return;
481
482 currentDropTarget = target;
483 if (!dropped && object) {
484 object->d_func()->target = target;
485 emit object->targetChanged(target);
486 }
487
488}
489
490QWidget *QDragManager::currentTarget()
491{
492 return currentDropTarget;
493}
494
495#endif
496
497QDropData::QDropData()
498 : QInternalMimeData()
499{
500#ifdef Q_WS_PM
501 d = 0;
502#endif
503}
504
505QDropData::~QDropData()
506{
507}
508#endif // QT_NO_DRAGANDDROP
509
510#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
511
512static QStringList imageReadMimeFormats()
513{
514 QStringList formats;
515 QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
516 for (int i = 0; i < imageFormats.size(); ++i) {
517 QString format = QLatin1String("image/");
518 format += QString::fromLatin1(imageFormats.at(i).toLower());
519 formats.append(format);
520 }
521
522 //put png at the front because it is best
523 int pngIndex = formats.indexOf(QLatin1String("image/png"));
524 if (pngIndex != -1 && pngIndex != 0)
525 formats.move(pngIndex, 0);
526
527 return formats;
528}
529
530
531static QStringList imageWriteMimeFormats()
532{
533 QStringList formats;
534 QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
535 for (int i = 0; i < imageFormats.size(); ++i) {
536 QString format = QLatin1String("image/");
537 format += QString::fromLatin1(imageFormats.at(i).toLower());
538 formats.append(format);
539 }
540
541 //put png at the front because it is best
542 int pngIndex = formats.indexOf(QLatin1String("image/png"));
543 if (pngIndex != -1 && pngIndex != 0)
544 formats.move(pngIndex, 0);
545
546 return formats;
547}
548
549QInternalMimeData::QInternalMimeData()
550 : QMimeData()
551{
552}
553
554QInternalMimeData::~QInternalMimeData()
555{
556}
557
558bool QInternalMimeData::hasFormat(const QString &mimeType) const
559{
560 bool foundFormat = hasFormat_sys(mimeType);
561 if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
562 QStringList imageFormats = imageReadMimeFormats();
563 for (int i = 0; i < imageFormats.size(); ++i) {
564 if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
565 break;
566 }
567 }
568 return foundFormat;
569}
570
571QStringList QInternalMimeData::formats() const
572{
573 QStringList realFormats = formats_sys();
574 if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
575 QStringList imageFormats = imageReadMimeFormats();
576 for (int i = 0; i < imageFormats.size(); ++i) {
577 if (realFormats.contains(imageFormats.at(i))) {
578 realFormats += QLatin1String("application/x-qt-image");
579 break;
580 }
581 }
582 }
583 return realFormats;
584}
585
586QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
587{
588 QVariant data = retrieveData_sys(mimeType, type);
589 if (mimeType == QLatin1String("application/x-qt-image")) {
590 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
591 // try to find an image
592 QStringList imageFormats = imageReadMimeFormats();
593 for (int i = 0; i < imageFormats.size(); ++i) {
594 data = retrieveData_sys(imageFormats.at(i), type);
595 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
596 continue;
597 break;
598 }
599 }
600 // we wanted some image type, but all we got was a byte array. Convert it to an image.
601 if (data.type() == QVariant::ByteArray
602 && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
603 data = QImage::fromData(data.toByteArray());
604
605 } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
606 QColor c;
607 QByteArray ba = data.toByteArray();
608 if (ba.size() == 8) {
609 ushort * colBuf = (ushort *)ba.data();
610 c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
611 qreal(colBuf[1]) / qreal(0xFFFF),
612 qreal(colBuf[2]) / qreal(0xFFFF),
613 qreal(colBuf[3]) / qreal(0xFFFF));
614 data = c;
615 } else {
616 qWarning("Qt: Invalid color format");
617 }
618 } else if (data.type() != type && data.type() == QVariant::ByteArray) {
619 // try to use mime data's internal conversion stuf.
620 QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
621 that->setData(mimeType, data.toByteArray());
622 data = QMimeData::retrieveData(mimeType, type);
623 that->clear();
624 }
625 return data;
626}
627
628bool QInternalMimeData::canReadData(const QString &mimeType)
629{
630 return imageReadMimeFormats().contains(mimeType);
631}
632
633// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
634QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
635{
636 QStringList realFormats = data->formats();
637 if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
638 // add all supported image formats
639 QStringList imageFormats = imageWriteMimeFormats();
640 for (int i = 0; i < imageFormats.size(); ++i) {
641 if (!realFormats.contains(imageFormats.at(i)))
642 realFormats.append(imageFormats.at(i));
643 }
644 }
645 return realFormats;
646}
647
648bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
649{
650
651 bool foundFormat = data->hasFormat(mimeType);
652 if (!foundFormat) {
653 if (mimeType == QLatin1String("application/x-qt-image")) {
654 // check all supported image formats
655 QStringList imageFormats = imageWriteMimeFormats();
656 for (int i = 0; i < imageFormats.size(); ++i) {
657 if ((foundFormat = data->hasFormat(imageFormats.at(i))))
658 break;
659 }
660 } else if (mimeType.startsWith(QLatin1String("image/"))) {
661 return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
662 }
663 }
664 return foundFormat;
665}
666
667QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
668{
669 QByteArray ba;
670 if (mimeType == QLatin1String("application/x-color")) {
671 /* QMimeData can only provide colors as QColor or the name
672 of a color as a QByteArray or a QString. So we need to do
673 the conversion to application/x-color here.
674 The application/x-color format is :
675 type: application/x-color
676 format: 16
677 data[0]: red
678 data[1]: green
679 data[2]: blue
680 data[3]: opacity
681 */
682 ba.resize(8);
683 ushort * colBuf = (ushort *)ba.data();
684 QColor c = qvariant_cast<QColor>(data->colorData());
685 colBuf[0] = ushort(c.redF() * 0xFFFF);
686 colBuf[1] = ushort(c.greenF() * 0xFFFF);
687 colBuf[2] = ushort(c.blueF() * 0xFFFF);
688 colBuf[3] = ushort(c.alphaF() * 0xFFFF);
689 } else {
690 ba = data->data(mimeType);
691 if (ba.isEmpty()) {
692 if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
693 QImage image = qvariant_cast<QImage>(data->imageData());
694 QBuffer buf(&ba);
695 buf.open(QBuffer::WriteOnly);
696 // would there not be PNG ??
697 image.save(&buf, "PNG");
698 } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
699 QImage image = qvariant_cast<QImage>(data->imageData());
700 QBuffer buf(&ba);
701 buf.open(QBuffer::WriteOnly);
702 image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
703 }
704 }
705 }
706 return ba;
707}
708
709#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
710
711QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.