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

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 15.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@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#include <private/qapplication_p.h>
64
65#ifndef QT_NO_DRAGANDDROP
66
67QT_BEGIN_NAMESPACE
68
69#ifndef QT_NO_DRAGANDDROP
70
71//#define QDND_DEBUG
72
73#ifdef QDND_DEBUG
74QString dragActionsToString(Qt::DropActions actions)
75{
76 QString str;
77 if (actions == Qt::IgnoreAction) {
78 if (!str.isEmpty())
79 str += QLatin1String(" | ");
80 str += QLatin1String("IgnoreAction");
81 }
82 if (actions & Qt::LinkAction) {
83 if (!str.isEmpty())
84 str += QLatin1String(" | ");
85 str += QLatin1String("LinkAction");
86 }
87 if (actions & Qt::CopyAction) {
88 if (!str.isEmpty())
89 str += QLatin1String(" | ");
90 str += QLatin1String("CopyAction");
91 }
92 if (actions & Qt::MoveAction) {
93 if (!str.isEmpty())
94 str += QLatin1String(" | ");
95 str += QLatin1String("MoveAction");
96 }
97 if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
98 if (!str.isEmpty())
99 str += QLatin1String(" | ");
100 str += QLatin1String("TargetMoveAction");
101 }
102 return str;
103}
104
105QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
106{
107 QString str;
108 if (moderfies & Qt::ControlModifier) {
109 if (!str.isEmpty())
110 str += QLatin1String(" | ");
111 str += QLatin1String("ControlModifier");
112 }
113 if (moderfies & Qt::AltModifier) {
114 if (!str.isEmpty())
115 str += QLatin1String(" | ");
116 str += QLatin1String("AltModifier");
117 }
118 if (moderfies & Qt::ShiftModifier) {
119 if (!str.isEmpty())
120 str += QLatin1String(" | ");
121 str += QLatin1String("ShiftModifier");
122 }
123 return str;
124}
125#endif
126
127
128// the universe's only drag manager
129QDragManager *QDragManager::instance = 0;
130
131
132QDragManager::QDragManager()
133 : QObject(qApp)
134{
135 Q_ASSERT(!instance);
136
137#ifdef Q_WS_QWS
138 currentActionForOverrideCursor = Qt::IgnoreAction;
139#endif
140 object = 0;
141 beingCancelled = false;
142 restoreCursor = false;
143 willDrop = false;
144 eventLoop = 0;
145 dropData = new QDropData();
146 currentDropTarget = 0;
147#ifdef Q_WS_X11
148 xdndMimeTransferedPixmapIndex = 0;
149#endif
150#ifdef Q_WS_PM
151 init_sys();
152#endif
153}
154
155
156QDragManager::~QDragManager()
157{
158#ifdef Q_WS_PM
159 uninit_sys();
160#endif
161#ifndef QT_NO_CURSOR
162 if (restoreCursor)
163 QApplication::restoreOverrideCursor();
164#endif
165 instance = 0;
166 delete dropData;
167}
168
169QDragManager *QDragManager::self()
170{
171 if (!instance && !QApplication::closingDown())
172 instance = new QDragManager;
173 return instance;
174}
175
176QPixmap QDragManager::dragCursor(Qt::DropAction action) const
177{
178 QDragPrivate * d = dragPrivate();
179 if (d && d->customCursors.contains(action))
180 return d->customCursors[action];
181 else if (action == Qt::MoveAction)
182 return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragMoveCursor);
183 else if (action == Qt::CopyAction)
184 return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragCopyCursor);
185 else if (action == Qt::LinkAction)
186 return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragLinkCursor);
187#ifdef Q_WS_WIN
188 else if (action == Qt::IgnoreAction)
189 return QApplicationPrivate::instance()->getPixmapCursor(Qt::ForbiddenCursor);
190#endif
191 return QPixmap();
192}
193
194bool QDragManager::hasCustomDragCursors() const
195{
196 QDragPrivate * d = dragPrivate();
197 return d && !d->customCursors.isEmpty();
198}
199
200Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
201 Qt::KeyboardModifiers modifiers) const
202{
203#ifdef QDND_DEBUG
204 qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
205 qDebug("keyboard modifiers : %ls", KeyboardModifiersToString(modifiers).utf16());
206#endif
207
208 QDragPrivate *d = dragPrivate();
209 Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
210
211 if (defaultAction == Qt::IgnoreAction) {
212 //This means that the drag was initiated by QDrag::start and we need to
213 //preserve the old behavior
214#ifdef Q_WS_MAC
215 defaultAction = Qt::MoveAction;
216#else
217 defaultAction = Qt::CopyAction;
218#endif
219 }
220
221#ifdef Q_WS_MAC
222 if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
223 defaultAction = Qt::LinkAction;
224 else if (modifiers & Qt::AltModifier)
225 defaultAction = Qt::CopyAction;
226 else if (modifiers & Qt::ControlModifier)
227 defaultAction = Qt::MoveAction;
228#else
229 if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
230 defaultAction = Qt::LinkAction;
231 else if (modifiers & Qt::ControlModifier)
232 defaultAction = Qt::CopyAction;
233 else if (modifiers & Qt::ShiftModifier)
234 defaultAction = Qt::MoveAction;
235 else if (modifiers & Qt::AltModifier)
236 defaultAction = Qt::LinkAction;
237#endif
238
239 // if the object is set take the list of possibles from it
240 if (object)
241 possibleActions = object->d_func()->possible_actions;
242
243#ifdef QDND_DEBUG
244 qDebug("possible actions : %ls", dragActionsToString(possibleActions).utf16());
245#endif
246
247 // Check if the action determined is allowed
248 if (!(possibleActions & defaultAction)) {
249 if (possibleActions & Qt::CopyAction)
250 defaultAction = Qt::CopyAction;
251 else if (possibleActions & Qt::MoveAction)
252 defaultAction = Qt::MoveAction;
253 else if (possibleActions & Qt::LinkAction)
254 defaultAction = Qt::LinkAction;
255 else
256 defaultAction = Qt::IgnoreAction;
257 }
258
259#ifdef QDND_DEBUG
260 qDebug("default action : %ls", dragActionsToString(defaultAction).utf16());
261#endif
262
263 return defaultAction;
264}
265
266void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
267{
268 if (currentDropTarget == target)
269 return;
270
271 currentDropTarget = target;
272 if (!dropped && object) {
273 object->d_func()->target = target;
274 emit object->targetChanged(target);
275 }
276
277}
278
279QWidget *QDragManager::currentTarget()
280{
281 return currentDropTarget;
282}
283
284#endif
285
286QDropData::QDropData()
287 : QInternalMimeData()
288{
289#ifdef Q_WS_PM
290 d = 0;
291#endif
292}
293
294QDropData::~QDropData()
295{
296}
297#endif // QT_NO_DRAGANDDROP
298
299#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
300
301static QStringList imageReadMimeFormats()
302{
303 QStringList formats;
304 QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
305 for (int i = 0; i < imageFormats.size(); ++i) {
306 QString format = QLatin1String("image/");
307 format += QString::fromLatin1(imageFormats.at(i).toLower());
308 formats.append(format);
309 }
310
311 //put png at the front because it is best
312 int pngIndex = formats.indexOf(QLatin1String("image/png"));
313 if (pngIndex != -1 && pngIndex != 0)
314 formats.move(pngIndex, 0);
315
316 return formats;
317}
318
319
320static QStringList imageWriteMimeFormats()
321{
322 QStringList formats;
323 QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
324 for (int i = 0; i < imageFormats.size(); ++i) {
325 QString format = QLatin1String("image/");
326 format += QString::fromLatin1(imageFormats.at(i).toLower());
327 formats.append(format);
328 }
329
330 //put png at the front because it is best
331 int pngIndex = formats.indexOf(QLatin1String("image/png"));
332 if (pngIndex != -1 && pngIndex != 0)
333 formats.move(pngIndex, 0);
334
335 return formats;
336}
337
338QInternalMimeData::QInternalMimeData()
339 : QMimeData()
340{
341}
342
343QInternalMimeData::~QInternalMimeData()
344{
345}
346
347bool QInternalMimeData::hasFormat(const QString &mimeType) const
348{
349 bool foundFormat = hasFormat_sys(mimeType);
350 if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
351 QStringList imageFormats = imageReadMimeFormats();
352 for (int i = 0; i < imageFormats.size(); ++i) {
353 if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
354 break;
355 }
356 }
357 return foundFormat;
358}
359
360QStringList QInternalMimeData::formats() const
361{
362 QStringList realFormats = formats_sys();
363 if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
364 QStringList imageFormats = imageReadMimeFormats();
365 for (int i = 0; i < imageFormats.size(); ++i) {
366 if (realFormats.contains(imageFormats.at(i))) {
367 realFormats += QLatin1String("application/x-qt-image");
368 break;
369 }
370 }
371 }
372 return realFormats;
373}
374
375QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
376{
377 QVariant data = retrieveData_sys(mimeType, type);
378 if (mimeType == QLatin1String("application/x-qt-image")) {
379 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
380 // try to find an image
381 QStringList imageFormats = imageReadMimeFormats();
382 for (int i = 0; i < imageFormats.size(); ++i) {
383 data = retrieveData_sys(imageFormats.at(i), type);
384 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
385 continue;
386 break;
387 }
388 }
389 // we wanted some image type, but all we got was a byte array. Convert it to an image.
390 if (data.type() == QVariant::ByteArray
391 && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
392 data = QImage::fromData(data.toByteArray());
393
394 } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
395 QColor c;
396 QByteArray ba = data.toByteArray();
397 if (ba.size() == 8) {
398 ushort * colBuf = (ushort *)ba.data();
399 c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
400 qreal(colBuf[1]) / qreal(0xFFFF),
401 qreal(colBuf[2]) / qreal(0xFFFF),
402 qreal(colBuf[3]) / qreal(0xFFFF));
403 data = c;
404 } else {
405 qWarning("Qt: Invalid color format");
406 }
407 } else if (data.type() != type && data.type() == QVariant::ByteArray) {
408 // try to use mime data's internal conversion stuf.
409 QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
410 that->setData(mimeType, data.toByteArray());
411 data = QMimeData::retrieveData(mimeType, type);
412 that->clear();
413 }
414 return data;
415}
416
417bool QInternalMimeData::canReadData(const QString &mimeType)
418{
419 return imageReadMimeFormats().contains(mimeType);
420}
421
422// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
423QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
424{
425 QStringList realFormats = data->formats();
426 if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
427 // add all supported image formats
428 QStringList imageFormats = imageWriteMimeFormats();
429 for (int i = 0; i < imageFormats.size(); ++i) {
430 if (!realFormats.contains(imageFormats.at(i)))
431 realFormats.append(imageFormats.at(i));
432 }
433 }
434 return realFormats;
435}
436
437bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
438{
439
440 bool foundFormat = data->hasFormat(mimeType);
441 if (!foundFormat) {
442 if (mimeType == QLatin1String("application/x-qt-image")) {
443 // check all supported image formats
444 QStringList imageFormats = imageWriteMimeFormats();
445 for (int i = 0; i < imageFormats.size(); ++i) {
446 if ((foundFormat = data->hasFormat(imageFormats.at(i))))
447 break;
448 }
449 } else if (mimeType.startsWith(QLatin1String("image/"))) {
450 return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
451 }
452 }
453 return foundFormat;
454}
455
456QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
457{
458 QByteArray ba;
459 if (mimeType == QLatin1String("application/x-color")) {
460 /* QMimeData can only provide colors as QColor or the name
461 of a color as a QByteArray or a QString. So we need to do
462 the conversion to application/x-color here.
463 The application/x-color format is :
464 type: application/x-color
465 format: 16
466 data[0]: red
467 data[1]: green
468 data[2]: blue
469 data[3]: opacity
470 */
471 ba.resize(8);
472 ushort * colBuf = (ushort *)ba.data();
473 QColor c = qvariant_cast<QColor>(data->colorData());
474 colBuf[0] = ushort(c.redF() * 0xFFFF);
475 colBuf[1] = ushort(c.greenF() * 0xFFFF);
476 colBuf[2] = ushort(c.blueF() * 0xFFFF);
477 colBuf[3] = ushort(c.alphaF() * 0xFFFF);
478 } else {
479 ba = data->data(mimeType);
480 if (ba.isEmpty()) {
481 if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
482 QImage image = qvariant_cast<QImage>(data->imageData());
483 QBuffer buf(&ba);
484 buf.open(QBuffer::WriteOnly);
485 // would there not be PNG ??
486 image.save(&buf, "PNG");
487 } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
488 QImage image = qvariant_cast<QImage>(data->imageData());
489 QBuffer buf(&ba);
490 buf.open(QBuffer::WriteOnly);
491 image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
492 }
493 }
494 }
495 return ba;
496}
497
498#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
499
500QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.