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

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

gui: First DnD bits.

File size: 20.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
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 += " | ";
277 str += "IgnoreAction";
278 }
279 if (actions & Qt::LinkAction) {
280 if (!str.isEmpty())
281 str += " | ";
282 str += "LinkAction";
283 }
284 if (actions & Qt::CopyAction) {
285 if (!str.isEmpty())
286 str += " | ";
287 str += "CopyAction";
288 }
289 if (actions & Qt::MoveAction) {
290 if (!str.isEmpty())
291 str += " | ";
292 str += "MoveAction";
293 }
294 if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
295 if (!str.isEmpty())
296 str += " | ";
297 str += "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 += " | ";
308 str += Qt::ControlModifier;
309 }
310 if (moderfies & Qt::AltModifier) {
311 if (!str.isEmpty())
312 str += " | ";
313 str += Qt::AltModifier;
314 }
315 if (moderfies & Qt::ShiftModifier) {
316 if (!str.isEmpty())
317 str += " | ";
318 str += Qt::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}
361
362
363QDragManager::~QDragManager()
364{
365#ifndef QT_NO_CURSOR
366 if (restoreCursor)
367 QApplication::restoreOverrideCursor();
368#endif
369 instance = 0;
370 delete [] pm_cursor;
371 delete dropData;
372}
373
374QDragManager *QDragManager::self()
375{
376 if (!instance && qApp && !qApp->closingDown())
377 instance = new QDragManager;
378 return instance;
379}
380
381QPixmap QDragManager::dragCursor(Qt::DropAction action) const
382{
383 QDragPrivate * d = dragPrivate();
384 if (d && d->customCursors.contains(action))
385 return d->customCursors[action];
386 else if (action == Qt::MoveAction)
387 return pm_cursor[0];
388 else if (action == Qt::CopyAction)
389 return pm_cursor[1];
390 else if (action == Qt::LinkAction)
391 return pm_cursor[2];
392#ifdef Q_WS_WIN
393 else if (action == Qt::IgnoreAction)
394 return pm_cursor[3];
395#endif
396 return QPixmap();
397}
398
399bool QDragManager::hasCustomDragCursors() const
400{
401 QDragPrivate * d = dragPrivate();
402 return d && !d->customCursors.isEmpty();
403}
404
405Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
406 Qt::KeyboardModifiers modifiers) const
407{
408#ifdef QDND_DEBUG
409 qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
410 qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1());
411#endif
412
413 QDragPrivate *d = dragPrivate();
414 Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
415
416 if (defaultAction == Qt::IgnoreAction) {
417 //This means that the drag was initiated by QDrag::start and we need to
418 //preserve the old behavior
419#ifdef Q_WS_MAC
420 defaultAction = Qt::MoveAction;
421#else
422 defaultAction = Qt::CopyAction;
423#endif
424 }
425
426#ifdef Q_WS_MAC
427 if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
428 defaultAction = Qt::LinkAction;
429 else if (modifiers & Qt::AltModifier)
430 defaultAction = Qt::CopyAction;
431 else if (modifiers & Qt::ControlModifier)
432 defaultAction = Qt::MoveAction;
433#else
434 if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
435 defaultAction = Qt::LinkAction;
436 else if (modifiers & Qt::ControlModifier)
437 defaultAction = Qt::CopyAction;
438 else if (modifiers & Qt::ShiftModifier)
439 defaultAction = Qt::MoveAction;
440 else if (modifiers & Qt::AltModifier)
441 defaultAction = Qt::LinkAction;
442#endif
443
444 // if the object is set take the list of possibles from it
445 if (object)
446 possibleActions = object->d_func()->possible_actions;
447
448#ifdef QDND_DEBUG
449 qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
450#endif
451
452 // Check if the action determined is allowed
453 if (!(possibleActions & defaultAction)) {
454 if (possibleActions & Qt::CopyAction)
455 defaultAction = Qt::CopyAction;
456 else if (possibleActions & Qt::MoveAction)
457 defaultAction = Qt::MoveAction;
458 else if (possibleActions & Qt::LinkAction)
459 defaultAction = Qt::LinkAction;
460 else
461 defaultAction = Qt::IgnoreAction;
462 }
463
464#ifdef QDND_DEBUG
465 qDebug("default action : %s", dragActionsToString(defaultAction).latin1());
466#endif
467
468 return defaultAction;
469}
470
471void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
472{
473 if (currentDropTarget == target)
474 return;
475
476 currentDropTarget = target;
477 if (!dropped && object) {
478 object->d_func()->target = target;
479 emit object->targetChanged(target);
480 }
481
482}
483
484QWidget *QDragManager::currentTarget()
485{
486 return currentDropTarget;
487}
488
489#endif
490
491QDropData::QDropData()
492 : QInternalMimeData()
493{
494#ifdef Q_WS_PM
495 d = 0;
496#endif
497}
498
499QDropData::~QDropData()
500{
501}
502#endif // QT_NO_DRAGANDDROP
503
504#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
505
506static QStringList imageReadMimeFormats()
507{
508 QStringList formats;
509 QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
510 for (int i = 0; i < imageFormats.size(); ++i) {
511 QString format = QLatin1String("image/");
512 format += QString::fromLatin1(imageFormats.at(i).toLower());
513 formats.append(format);
514 }
515
516 //put png at the front because it is best
517 int pngIndex = formats.indexOf(QLatin1String("image/png"));
518 if (pngIndex != -1 && pngIndex != 0)
519 formats.move(pngIndex, 0);
520
521 return formats;
522}
523
524
525static QStringList imageWriteMimeFormats()
526{
527 QStringList formats;
528 QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
529 for (int i = 0; i < imageFormats.size(); ++i) {
530 QString format = QLatin1String("image/");
531 format += QString::fromLatin1(imageFormats.at(i).toLower());
532 formats.append(format);
533 }
534
535 //put png at the front because it is best
536 int pngIndex = formats.indexOf(QLatin1String("image/png"));
537 if (pngIndex != -1 && pngIndex != 0)
538 formats.move(pngIndex, 0);
539
540 return formats;
541}
542
543QInternalMimeData::QInternalMimeData()
544 : QMimeData()
545{
546}
547
548QInternalMimeData::~QInternalMimeData()
549{
550}
551
552bool QInternalMimeData::hasFormat(const QString &mimeType) const
553{
554 bool foundFormat = hasFormat_sys(mimeType);
555 if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
556 QStringList imageFormats = imageReadMimeFormats();
557 for (int i = 0; i < imageFormats.size(); ++i) {
558 if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
559 break;
560 }
561 }
562 return foundFormat;
563}
564
565QStringList QInternalMimeData::formats() const
566{
567 QStringList realFormats = formats_sys();
568 if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
569 QStringList imageFormats = imageReadMimeFormats();
570 for (int i = 0; i < imageFormats.size(); ++i) {
571 if (realFormats.contains(imageFormats.at(i))) {
572 realFormats += QLatin1String("application/x-qt-image");
573 break;
574 }
575 }
576 }
577 return realFormats;
578}
579
580QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
581{
582 QVariant data = retrieveData_sys(mimeType, type);
583 if (mimeType == QLatin1String("application/x-qt-image")) {
584 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
585 // try to find an image
586 QStringList imageFormats = imageReadMimeFormats();
587 for (int i = 0; i < imageFormats.size(); ++i) {
588 data = retrieveData_sys(imageFormats.at(i), type);
589 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
590 continue;
591 break;
592 }
593 }
594 // we wanted some image type, but all we got was a byte array. Convert it to an image.
595 if (data.type() == QVariant::ByteArray
596 && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
597 data = QImage::fromData(data.toByteArray());
598
599 } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
600 QColor c;
601 QByteArray ba = data.toByteArray();
602 if (ba.size() == 8) {
603 ushort * colBuf = (ushort *)ba.data();
604 c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
605 qreal(colBuf[1]) / qreal(0xFFFF),
606 qreal(colBuf[2]) / qreal(0xFFFF),
607 qreal(colBuf[3]) / qreal(0xFFFF));
608 data = c;
609 } else {
610 qWarning("Qt: Invalid color format");
611 }
612 } else if (data.type() != type && data.type() == QVariant::ByteArray) {
613 // try to use mime data's internal conversion stuf.
614 QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
615 that->setData(mimeType, data.toByteArray());
616 data = QMimeData::retrieveData(mimeType, type);
617 that->clear();
618 }
619 return data;
620}
621
622bool QInternalMimeData::canReadData(const QString &mimeType)
623{
624 return imageReadMimeFormats().contains(mimeType);
625}
626
627// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
628QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
629{
630 QStringList realFormats = data->formats();
631 if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
632 // add all supported image formats
633 QStringList imageFormats = imageWriteMimeFormats();
634 for (int i = 0; i < imageFormats.size(); ++i) {
635 if (!realFormats.contains(imageFormats.at(i)))
636 realFormats.append(imageFormats.at(i));
637 }
638 }
639 return realFormats;
640}
641
642bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
643{
644
645 bool foundFormat = data->hasFormat(mimeType);
646 if (!foundFormat) {
647 if (mimeType == QLatin1String("application/x-qt-image")) {
648 // check all supported image formats
649 QStringList imageFormats = imageWriteMimeFormats();
650 for (int i = 0; i < imageFormats.size(); ++i) {
651 if ((foundFormat = data->hasFormat(imageFormats.at(i))))
652 break;
653 }
654 } else if (mimeType.startsWith(QLatin1String("image/"))) {
655 return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
656 }
657 }
658 return foundFormat;
659}
660
661QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
662{
663 QByteArray ba;
664 if (mimeType == QLatin1String("application/x-color")) {
665 /* QMimeData can only provide colors as QColor or the name
666 of a color as a QByteArray or a QString. So we need to do
667 the conversion to application/x-color here.
668 The application/x-color format is :
669 type: application/x-color
670 format: 16
671 data[0]: red
672 data[1]: green
673 data[2]: blue
674 data[3]: opacity
675 */
676 ba.resize(8);
677 ushort * colBuf = (ushort *)ba.data();
678 QColor c = qvariant_cast<QColor>(data->colorData());
679 colBuf[0] = ushort(c.redF() * 0xFFFF);
680 colBuf[1] = ushort(c.greenF() * 0xFFFF);
681 colBuf[2] = ushort(c.blueF() * 0xFFFF);
682 colBuf[3] = ushort(c.alphaF() * 0xFFFF);
683 } else {
684 ba = data->data(mimeType);
685 if (ba.isEmpty()) {
686 if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
687 QImage image = qvariant_cast<QImage>(data->imageData());
688 QBuffer buf(&ba);
689 buf.open(QBuffer::WriteOnly);
690 // would there not be PNG ??
691 image.save(&buf, "PNG");
692 } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
693 QImage image = qvariant_cast<QImage>(data->imageData());
694 QBuffer buf(&ba);
695 buf.open(QBuffer::WriteOnly);
696 image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
697 }
698 }
699 }
700 return ba;
701}
702
703#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
704
705QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.