Fix compilation with QDND_DEBUG.
[qt:qt.git] / src / gui / kernel / qdnd.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
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
67 QT_BEGIN_NAMESPACE
68
69 #ifndef QT_NO_DRAGANDDROP
70
71 //#define QDND_DEBUG
72
73 #ifdef QDND_DEBUG
74 QString dragActionsToString(Qt::DropActions actions)
75 {
76     QString str;
77     if (actions == Qt::IgnoreAction) {
78         if (!str.isEmpty())
79             str += " | ";
80         str += "IgnoreAction";
81     }
82     if (actions & Qt::LinkAction) {
83         if (!str.isEmpty())
84             str += " | ";
85         str += "LinkAction";
86     }
87     if (actions & Qt::CopyAction) {
88         if (!str.isEmpty())
89             str += " | ";
90         str += "CopyAction";
91     }
92     if (actions & Qt::MoveAction) {
93         if (!str.isEmpty())
94             str += " | ";
95         str += "MoveAction";
96     }
97     if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
98         if (!str.isEmpty())
99             str += " | ";
100         str += "TargetMoveAction";
101     }
102     return str;
103 }
104
105 QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
106 {
107     QString str;
108     if (moderfies & Qt::ControlModifier) {
109         if (!str.isEmpty())
110             str += " | ";
111         str += Qt::ControlModifier;
112     }
113     if (moderfies & Qt::AltModifier) {
114         if (!str.isEmpty())
115             str += " | ";
116         str += Qt::AltModifier;
117     }
118     if (moderfies & Qt::ShiftModifier) {
119         if (!str.isEmpty())
120             str += " | ";
121         str += Qt::ShiftModifier;
122     }
123     return str;
124 }
125 #endif
126
127
128 // the universe's only drag manager
129 QDragManager *QDragManager::instance = 0;
130
131
132 QDragManager::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 }
151
152
153 QDragManager::~QDragManager()
154 {
155 #ifndef QT_NO_CURSOR
156     if (restoreCursor)
157         QApplication::restoreOverrideCursor();
158 #endif
159     instance = 0;
160     delete dropData;
161 }
162
163 QDragManager *QDragManager::self()
164 {
165     if (!instance && !QApplication::closingDown())
166         instance = new QDragManager;
167     return instance;
168 }
169
170 QPixmap QDragManager::dragCursor(Qt::DropAction action) const
171 {
172     QDragPrivate * d = dragPrivate();
173     if (d && d->customCursors.contains(action))
174         return d->customCursors[action];
175     else if (action == Qt::MoveAction)
176         return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragMoveCursor);
177     else if (action == Qt::CopyAction)
178         return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragCopyCursor);
179     else if (action == Qt::LinkAction)
180         return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragLinkCursor);
181 #ifdef Q_WS_WIN
182     else if (action == Qt::IgnoreAction)
183         return QApplicationPrivate::instance()->getPixmapCursor(Qt::ForbiddenCursor);
184 #endif
185     return QPixmap();
186 }
187
188 bool QDragManager::hasCustomDragCursors() const
189 {
190     QDragPrivate * d = dragPrivate();
191     return d && !d->customCursors.isEmpty();
192 }
193
194 Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
195                                            Qt::KeyboardModifiers modifiers) const
196 {
197 #ifdef QDND_DEBUG
198     qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
199     qDebug("keyboard modifiers : %s", qPrintable(KeyboardModifiersToString(modifiers)));
200 #endif
201
202     QDragPrivate *d = dragPrivate();
203     Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
204
205     if (defaultAction == Qt::IgnoreAction) {
206         //This means that the drag was initiated by QDrag::start and we need to
207         //preserve the old behavior
208 #ifdef Q_WS_MAC
209         defaultAction = Qt::MoveAction;
210 #else
211         defaultAction = Qt::CopyAction;
212 #endif
213     }
214
215 #ifdef Q_WS_MAC
216     if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
217         defaultAction = Qt::LinkAction;
218     else if (modifiers & Qt::AltModifier)
219         defaultAction = Qt::CopyAction;
220     else if (modifiers & Qt::ControlModifier)
221         defaultAction = Qt::MoveAction;
222 #else
223     if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
224         defaultAction = Qt::LinkAction;
225     else if (modifiers & Qt::ControlModifier)
226         defaultAction = Qt::CopyAction;
227     else if (modifiers & Qt::ShiftModifier)
228         defaultAction = Qt::MoveAction;
229     else if (modifiers & Qt::AltModifier)
230         defaultAction = Qt::LinkAction;
231 #endif
232
233     // if the object is set take the list of possibles from it
234     if (object)
235         possibleActions = object->d_func()->possible_actions;
236
237 #ifdef QDND_DEBUG
238     qDebug("possible actions : %s", qPrintable(dragActionsToString(possibleActions)));
239 #endif
240
241     // Check if the action determined is allowed
242     if (!(possibleActions & defaultAction)) {
243         if (possibleActions & Qt::CopyAction)
244             defaultAction = Qt::CopyAction;
245         else if (possibleActions & Qt::MoveAction)
246             defaultAction = Qt::MoveAction;
247         else if (possibleActions & Qt::LinkAction)
248             defaultAction = Qt::LinkAction;
249         else
250             defaultAction = Qt::IgnoreAction;
251     }
252
253 #ifdef QDND_DEBUG
254     qDebug("default action : %s", qPrintable(dragActionsToString(defaultAction)));
255 #endif
256
257     return defaultAction;
258 }
259
260 void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
261 {
262     if (currentDropTarget == target)
263         return;
264
265     currentDropTarget = target;
266     if (!dropped && object) {
267         object->d_func()->target = target;
268         emit object->targetChanged(target);
269     }
270
271 }
272
273 QWidget *QDragManager::currentTarget()
274 {
275     return currentDropTarget;
276 }
277
278 #endif
279
280 QDropData::QDropData()
281     : QInternalMimeData()
282 {
283 }
284
285 QDropData::~QDropData()
286 {
287 }
288 #endif // QT_NO_DRAGANDDROP
289
290 #if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
291
292 static QStringList imageReadMimeFormats()
293 {
294     QStringList formats;
295     QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
296     for (int i = 0; i < imageFormats.size(); ++i) {
297         QString format = QLatin1String("image/");
298         format += QString::fromLatin1(imageFormats.at(i).toLower());
299         formats.append(format);
300     }
301
302     //put png at the front because it is best
303     int pngIndex = formats.indexOf(QLatin1String("image/png"));
304     if (pngIndex != -1 && pngIndex != 0)
305         formats.move(pngIndex, 0);
306
307     return formats;
308 }
309
310
311 static QStringList imageWriteMimeFormats()
312 {
313     QStringList formats;
314     QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
315     for (int i = 0; i < imageFormats.size(); ++i) {
316         QString format = QLatin1String("image/");
317         format += QString::fromLatin1(imageFormats.at(i).toLower());
318         formats.append(format);
319     }
320
321     //put png at the front because it is best
322     int pngIndex = formats.indexOf(QLatin1String("image/png"));
323     if (pngIndex != -1 && pngIndex != 0)
324         formats.move(pngIndex, 0);
325
326     return formats;
327 }
328
329 QInternalMimeData::QInternalMimeData()
330     : QMimeData()
331 {
332 }
333
334 QInternalMimeData::~QInternalMimeData()
335 {
336 }
337
338 bool QInternalMimeData::hasFormat(const QString &mimeType) const
339 {
340     bool foundFormat = hasFormat_sys(mimeType);
341     if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
342         QStringList imageFormats = imageReadMimeFormats();
343         for (int i = 0; i < imageFormats.size(); ++i) {
344             if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
345                 break;
346         }
347     }
348     return foundFormat;
349 }
350
351 QStringList QInternalMimeData::formats() const
352 {
353     QStringList realFormats = formats_sys();
354     if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
355         QStringList imageFormats = imageReadMimeFormats();
356         for (int i = 0; i < imageFormats.size(); ++i) {
357             if (realFormats.contains(imageFormats.at(i))) {
358                 realFormats += QLatin1String("application/x-qt-image");
359                 break;
360             }
361         }
362     }
363     return realFormats;
364 }
365
366 QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
367 {
368     QVariant data = retrieveData_sys(mimeType, type);
369     if (mimeType == QLatin1String("application/x-qt-image")) {
370         if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
371             // try to find an image
372             QStringList imageFormats = imageReadMimeFormats();
373             for (int i = 0; i < imageFormats.size(); ++i) {
374                 data = retrieveData_sys(imageFormats.at(i), type);
375                 if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
376                     continue;
377                 break;
378             }
379         }
380         // we wanted some image type, but all we got was a byte array. Convert it to an image.
381         if (data.type() == QVariant::ByteArray
382             && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
383             data = QImage::fromData(data.toByteArray());
384
385     } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
386         QColor c;
387         QByteArray ba = data.toByteArray();
388         if (ba.size() == 8) {
389             ushort * colBuf = (ushort *)ba.data();
390             c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
391                       qreal(colBuf[1]) / qreal(0xFFFF),
392                       qreal(colBuf[2]) / qreal(0xFFFF),
393                       qreal(colBuf[3]) / qreal(0xFFFF));
394             data = c;
395         } else {
396             qWarning("Qt: Invalid color format");
397         }
398     } else if (data.type() != type && data.type() == QVariant::ByteArray) {
399         // try to use mime data's internal conversion stuf.
400         QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
401         that->setData(mimeType, data.toByteArray());
402         data = QMimeData::retrieveData(mimeType, type);
403         that->clear();
404     }
405     return data;
406 }
407
408 bool QInternalMimeData::canReadData(const QString &mimeType)
409 {
410     return imageReadMimeFormats().contains(mimeType);
411 }
412
413 // helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
414 QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
415 {
416     QStringList realFormats = data->formats();
417     if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
418         // add all supported image formats
419         QStringList imageFormats = imageWriteMimeFormats();
420         for (int i = 0; i < imageFormats.size(); ++i) {
421             if (!realFormats.contains(imageFormats.at(i)))
422                 realFormats.append(imageFormats.at(i));
423         }
424     }
425     return realFormats;
426 }
427
428 bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
429 {
430
431     bool foundFormat = data->hasFormat(mimeType);
432     if (!foundFormat) {
433         if (mimeType == QLatin1String("application/x-qt-image")) {
434             // check all supported image formats
435             QStringList imageFormats = imageWriteMimeFormats();
436             for (int i = 0; i < imageFormats.size(); ++i) {
437                 if ((foundFormat = data->hasFormat(imageFormats.at(i))))
438                     break;
439             }
440         } else if (mimeType.startsWith(QLatin1String("image/"))) {
441             return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
442         }
443     }
444     return foundFormat;
445 }
446
447 QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
448 {
449     QByteArray ba;
450     if (mimeType == QLatin1String("application/x-color")) {
451         /* QMimeData can only provide colors as QColor or the name
452            of a color as a QByteArray or a QString. So we need to do
453            the conversion to application/x-color here.
454            The application/x-color format is :
455            type: application/x-color
456            format: 16
457            data[0]: red
458            data[1]: green
459            data[2]: blue
460            data[3]: opacity
461         */
462         ba.resize(8);
463         ushort * colBuf = (ushort *)ba.data();
464         QColor c = qvariant_cast<QColor>(data->colorData());
465         colBuf[0] = ushort(c.redF() * 0xFFFF);
466         colBuf[1] = ushort(c.greenF() * 0xFFFF);
467         colBuf[2] = ushort(c.blueF() * 0xFFFF);
468         colBuf[3] = ushort(c.alphaF() * 0xFFFF);
469     } else {
470         ba = data->data(mimeType);
471         if (ba.isEmpty()) {
472             if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
473                 QImage image = qvariant_cast<QImage>(data->imageData());
474                 QBuffer buf(&ba);
475                 buf.open(QBuffer::WriteOnly);
476                 // would there not be PNG ??
477                 image.save(&buf, "PNG");
478             } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
479                 QImage image = qvariant_cast<QImage>(data->imageData());
480                 QBuffer buf(&ba);
481                 buf.open(QBuffer::WriteOnly);
482                 image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
483             }
484         }
485     }
486     return ba;
487 }
488
489 #endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
490
491 QT_END_NAMESPACE