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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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