1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qmimedata.h"
41 
42 #include "private/qobject_p.h"
43 #include "qurl.h"
44 #include "qstringlist.h"
45 #if QT_CONFIG(textcodec)
46 #include "qtextcodec.h"
47 #endif
48 
49 QT_BEGIN_NAMESPACE
50 
textUriListLiteral()51 static inline QString textUriListLiteral() { return QStringLiteral("text/uri-list"); }
textHtmlLiteral()52 static inline QString textHtmlLiteral() { return QStringLiteral("text/html"); }
textPlainLiteral()53 static inline QString textPlainLiteral() { return QStringLiteral("text/plain"); }
textPlainUtf8Literal()54 static inline QString textPlainUtf8Literal() { return QStringLiteral("text/plain;charset=utf-8"); }
applicationXColorLiteral()55 static inline QString applicationXColorLiteral() { return QStringLiteral("application/x-color"); }
applicationXQtImageLiteral()56 static inline QString applicationXQtImageLiteral() { return QStringLiteral("application/x-qt-image"); }
57 
58 struct QMimeDataStruct
59 {
60     QString format;
61     QVariant data;
62 };
63 Q_DECLARE_TYPEINFO(QMimeDataStruct, Q_MOVABLE_TYPE);
64 
65 class QMimeDataPrivate : public QObjectPrivate
66 {
67     Q_DECLARE_PUBLIC(QMimeData)
68 public:
69     void removeData(const QString &format);
70     void setData(const QString &format, const QVariant &data);
71     QVariant getData(const QString &format) const;
72 
73     QVariant retrieveTypedData(const QString &format, QMetaType::Type type) const;
74 
find(const QString & format)75     std::vector<QMimeDataStruct>::iterator find(const QString &format) noexcept {
76         const auto formatEquals = [](const QString &format) {
77             return [&format](const QMimeDataStruct &s) { return s.format == format; };
78         };
79         return std::find_if(dataList.begin(), dataList.end(), formatEquals(format));
80     }
81 
find(const QString & format) const82     std::vector<QMimeDataStruct>::const_iterator find(const QString &format) const noexcept {
83         return const_cast<QMimeDataPrivate*>(this)->find(format);
84     }
85 
86     std::vector<QMimeDataStruct> dataList;
87 };
88 
removeData(const QString & format)89 void QMimeDataPrivate::removeData(const QString &format)
90 {
91     const auto it = find(format);
92     if (it != dataList.end())
93         dataList.erase(it);
94 }
95 
setData(const QString & format,const QVariant & data)96 void QMimeDataPrivate::setData(const QString &format, const QVariant &data)
97 {
98     const auto it = find(format);
99     if (it == dataList.end())
100         dataList.push_back({format, data});
101     else
102         it->data = data;
103 }
104 
105 
getData(const QString & format) const106 QVariant QMimeDataPrivate::getData(const QString &format) const
107 {
108     const auto it = find(format);
109     if (it == dataList.cend())
110         return {};
111     else
112         return it->data;
113 }
114 
retrieveTypedData(const QString & format,QMetaType::Type type) const115 QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType::Type type) const
116 {
117     Q_Q(const QMimeData);
118 
119     QVariant data = q->retrieveData(format, QVariant::Type(type));
120 
121     // Text data requested: fallback to URL data if available
122     if (format == QLatin1String("text/plain") && !data.isValid()) {
123         data = retrieveTypedData(textUriListLiteral(), QMetaType::QVariantList);
124         if (data.userType() == QMetaType::QUrl) {
125             data = QVariant(data.toUrl().toDisplayString());
126         } else if (data.userType() == QMetaType::QVariantList) {
127             QString text;
128             int numUrls = 0;
129             const QList<QVariant> list = data.toList();
130             for (int i = 0; i < list.size(); ++i) {
131                 if (list.at(i).userType() == QMetaType::QUrl) {
132                     text += list.at(i).toUrl().toDisplayString() + QLatin1Char('\n');
133                     ++numUrls;
134                 }
135             }
136             if (numUrls == 1)
137                 text.chop(1); // no final '\n' if there's only one URL
138             data = QVariant(text);
139         }
140     }
141 
142     if (data.userType() == type || !data.isValid())
143         return data;
144 
145     // provide more conversion possiblities than just what QVariant provides
146 
147     // URLs can be lists as well...
148     if ((type == QMetaType::QUrl && data.userType() == QMetaType::QVariantList)
149         || (type == QMetaType::QVariantList && data.userType() == QMetaType::QUrl))
150         return data;
151 
152     // images and pixmaps are interchangeable
153     if ((type == QMetaType::QPixmap && data.userType() == QMetaType::QImage)
154         || (type == QMetaType::QImage && data.userType() == QMetaType::QPixmap))
155         return data;
156 
157     if (data.userType() == QMetaType::QByteArray) {
158         // see if we can convert to the requested type
159         switch(type) {
160 #if QT_CONFIG(textcodec)
161         case QMetaType::QString: {
162             const QByteArray ba = data.toByteArray();
163             if (ba.isNull())
164                 return QString();
165             QTextCodec *codec = QTextCodec::codecForName("utf-8");
166             if (format == QLatin1String("text/html"))
167                 codec = QTextCodec::codecForHtml(ba, codec);
168             return codec->toUnicode(ba);
169         }
170 #endif // textcodec
171         case QMetaType::QColor: {
172             QVariant newData = data;
173             newData.convert(QMetaType::QColor);
174             return newData;
175         }
176         case QMetaType::QVariantList: {
177             if (format != QLatin1String("text/uri-list"))
178                 break;
179             Q_FALLTHROUGH();
180         }
181         case QMetaType::QUrl: {
182             QByteArray ba = data.toByteArray();
183             // Qt 3.x will send text/uri-list with a trailing
184             // null-terminator (that is *not* sent for any other
185             // text/* mime-type), so chop it off
186             if (ba.endsWith('\0'))
187                 ba.chop(1);
188 
189             QList<QByteArray> urls = ba.split('\n');
190             QList<QVariant> list;
191             for (int i = 0; i < urls.size(); ++i) {
192                 QByteArray ba = urls.at(i).trimmed();
193                 if (!ba.isEmpty())
194                     list.append(QUrl::fromEncoded(ba));
195             }
196             return list;
197         }
198         default:
199             break;
200         }
201 
202     } else if (type == QMetaType::QByteArray) {
203 
204         // try to convert to bytearray
205         switch (data.userType()) {
206         case QMetaType::QByteArray:
207         case QMetaType::QColor:
208             return data.toByteArray();
209         case QMetaType::QString:
210             return data.toString().toUtf8();
211         case QMetaType::QUrl:
212             return data.toUrl().toEncoded();
213         case QMetaType::QVariantList: {
214             // has to be list of URLs
215             QByteArray result;
216             QList<QVariant> list = data.toList();
217             for (int i = 0; i < list.size(); ++i) {
218                 if (list.at(i).userType() == QMetaType::QUrl) {
219                     result += list.at(i).toUrl().toEncoded();
220                     result += "\r\n";
221                 }
222             }
223             if (!result.isEmpty())
224                 return result;
225             break;
226         }
227         default:
228             break;
229         }
230     }
231     return data;
232 }
233 
234 /*!
235     \class QMimeData
236     \inmodule QtCore
237     \brief The QMimeData class provides a container for data that records information
238     about its MIME type.
239 
240     QMimeData is used to describe information that can be stored in
241     the \l{QClipboard}{clipboard}, and transferred via the \l{drag
242     and drop} mechanism. QMimeData objects associate the data that
243     they hold with the corresponding MIME types to ensure that
244     information can be safely transferred between applications, and
245     copied around within the same application.
246 
247     QMimeData objects are usually created using \c new and supplied
248     to QDrag or QClipboard objects. This is to enable Qt to manage
249     the memory that they use.
250 
251     A single QMimeData object can store the same data using several
252     different formats at the same time. The formats() function
253     returns a list of the available formats in order of preference.
254     The data() function returns the raw data associated with a MIME
255     type, and setData() allows you to set the data for a MIME type.
256 
257     For the most common MIME types, QMimeData provides convenience
258     functions to access the data:
259 
260     \table
261     \header \li Tester       \li Getter       \li Setter           \li MIME Types
262     \row    \li hasText()    \li text()       \li setText()        \li \c text/plain
263     \row    \li hasHtml()    \li html()       \li setHtml()        \li \c text/html
264     \row    \li hasUrls()    \li urls()       \li setUrls()        \li \c text/uri-list
265     \row    \li hasImage()   \li imageData()  \li setImageData()   \li \c image/ *
266     \row    \li hasColor()   \li colorData()  \li setColorData()   \li \c application/x-color
267     \endtable
268 
269     For example, if your write a widget that accepts URL drags, you
270     would end up writing code like this:
271 
272     \snippet code/src_corelib_kernel_qmimedata.cpp 0
273 
274     There are three approaches for storing custom data in a QMimeData
275     object:
276 
277     \list 1
278     \li  Custom data can be stored directly in a QMimeData object as a
279         QByteArray using setData(). For example:
280 
281         \snippet code/src_corelib_kernel_qmimedata.cpp 1
282 
283     \li  We can subclass QMimeData and reimplement hasFormat(),
284         formats(), and retrieveData().
285 
286     \li  If the drag and drop operation occurs within a single
287         application, we can subclass QMimeData and add extra data in
288         it, and use a qobject_cast() in the receiver's drop event
289         handler. For example:
290 
291         \snippet code/src_corelib_kernel_qmimedata.cpp 2
292     \endlist
293 
294     \section1 Platform-Specific MIME Types
295 
296     On Windows, formats() will also return custom formats available
297     in the MIME data, using the \c{x-qt-windows-mime} subtype to
298     indicate that they represent data in non-standard formats.
299     The formats will take the following form:
300 
301     \snippet code/src_corelib_kernel_qmimedata.cpp 3
302 
303     The following are examples of custom MIME types:
304 
305     \snippet code/src_corelib_kernel_qmimedata.cpp 4
306 
307     The \c value declaration of each format describes the way in which the
308     data is encoded.
309 
310     In some cases (e.g. dropping multiple email attachments), multiple data
311     values are available. They can be accessed by adding an \c index value:
312 
313     \snippet code/src_corelib_kernel_qmimedata.cpp 8
314 
315     On Windows, the MIME format does not always map directly to the
316     clipboard formats. Qt provides QWinMime to map clipboard
317     formats to open-standard MIME formats. Similarly, the
318     QMacPasteboardMime maps MIME to Mac flavors.
319 
320     \sa QClipboard, QDragEnterEvent, QDragMoveEvent, QDropEvent, QDrag,
321         QMacPasteboardMime, {Drag and Drop}
322 */
323 
324 /*!
325     Constructs a new MIME data object with no data in it.
326 */
QMimeData()327 QMimeData::QMimeData()
328     : QObject(*new QMimeDataPrivate, nullptr)
329 {
330 }
331 
332 /*!
333     Destroys the MIME data object.
334 */
~QMimeData()335 QMimeData::~QMimeData()
336 {
337 }
338 
339 /*!
340     Returns a list of URLs contained within the MIME data object.
341 
342     URLs correspond to the MIME type \c text/uri-list.
343 
344     \sa hasUrls(), data()
345 */
urls() const346 QList<QUrl> QMimeData::urls() const
347 {
348     Q_D(const QMimeData);
349     QVariant data = d->retrieveTypedData(textUriListLiteral(), QMetaType::QVariantList);
350     QList<QUrl> urls;
351     if (data.userType() == QMetaType::QUrl)
352         urls.append(data.toUrl());
353     else if (data.userType() == QMetaType::QVariantList) {
354         QList<QVariant> list = data.toList();
355         for (int i = 0; i < list.size(); ++i) {
356             if (list.at(i).userType() == QMetaType::QUrl)
357                 urls.append(list.at(i).toUrl());
358         }
359     }
360     return urls;
361 }
362 
363 /*!
364     Sets the URLs stored in the MIME data object to those specified by \a urls.
365 
366     URLs correspond to the MIME type \c text/uri-list.
367 
368     Since Qt 5.0, setUrls also exports the urls as plain text, if setText
369     was not called before, to make it possible to drop them into any lineedit
370     and text editor.
371 
372     \sa hasUrls(), setData()
373 */
setUrls(const QList<QUrl> & urls)374 void QMimeData::setUrls(const QList<QUrl> &urls)
375 {
376     Q_D(QMimeData);
377     QList<QVariant> list;
378     const int numUrls = urls.size();
379     list.reserve(numUrls);
380     for (int i = 0; i < numUrls; ++i)
381         list.append(urls.at(i));
382 
383     d->setData(textUriListLiteral(), list);
384 }
385 
386 /*!
387     Returns \c true if the object can return a list of urls; otherwise
388     returns \c false.
389 
390     URLs correspond to the MIME type \c text/uri-list.
391 
392     \sa setUrls(), urls(), hasFormat()
393 */
hasUrls() const394 bool QMimeData::hasUrls() const
395 {
396     return hasFormat(textUriListLiteral());
397 }
398 
399 
400 /*!
401     Returns a plain text (MIME type \c text/plain) representation of
402     the data.
403 
404     \sa hasText(), html(), data()
405 */
text() const406 QString QMimeData::text() const
407 {
408     Q_D(const QMimeData);
409     QVariant utf8Text = d->retrieveTypedData(textPlainUtf8Literal(), QMetaType::QString);
410     if (!utf8Text.isNull())
411         return utf8Text.toString();
412 
413     QVariant data = d->retrieveTypedData(textPlainLiteral(), QMetaType::QString);
414     return data.toString();
415 }
416 
417 /*!
418     Sets \a text as the plain text (MIME type \c text/plain) used to
419     represent the data.
420 
421     \sa hasText(), setHtml(), setData()
422 */
setText(const QString & text)423 void QMimeData::setText(const QString &text)
424 {
425     Q_D(QMimeData);
426     d->setData(textPlainLiteral(), text);
427 }
428 
429 /*!
430     Returns \c true if the object can return plain text (MIME type \c
431     text/plain); otherwise returns \c false.
432 
433     \sa setText(), text(), hasHtml(), hasFormat()
434 */
hasText() const435 bool QMimeData::hasText() const
436 {
437     return hasFormat(textPlainLiteral()) || hasUrls();
438 }
439 
440 /*!
441     Returns a string if the data stored in the object is HTML (MIME
442     type \c text/html); otherwise returns an empty string.
443 
444     \sa hasHtml(), setData()
445 */
html() const446 QString QMimeData::html() const
447 {
448     Q_D(const QMimeData);
449     QVariant data = d->retrieveTypedData(textHtmlLiteral(), QMetaType::QString);
450     return data.toString();
451 }
452 
453 /*!
454     Sets \a html as the HTML (MIME type \c text/html) used to
455     represent the data.
456 
457     \sa hasHtml(), setText(), setData()
458 */
setHtml(const QString & html)459 void QMimeData::setHtml(const QString &html)
460 {
461     Q_D(QMimeData);
462     d->setData(textHtmlLiteral(), html);
463 }
464 
465 /*!
466     Returns \c true if the object can return HTML (MIME type \c
467     text/html); otherwise returns \c false.
468 
469     \sa setHtml(), html(), hasFormat()
470 */
hasHtml() const471 bool QMimeData::hasHtml() const
472 {
473     return hasFormat(textHtmlLiteral());
474 }
475 
476 /*!
477     Returns a QVariant storing a QImage if the object can return an
478     image; otherwise returns a null variant.
479 
480     A QVariant is used because QMimeData belongs to the Qt Core
481     module, whereas QImage belongs to Qt GUI. To convert the
482     QVariant to a QImage, simply use qvariant_cast(). For example:
483 
484     \snippet code/src_corelib_kernel_qmimedata.cpp 5
485 
486     \sa hasImage()
487 */
imageData() const488 QVariant QMimeData::imageData() const
489 {
490     Q_D(const QMimeData);
491     return d->retrieveTypedData(applicationXQtImageLiteral(), QMetaType::QImage);
492 }
493 
494 /*!
495     Sets the data in the object to the given \a image.
496 
497     A QVariant is used because QMimeData belongs to the Qt Core
498     module, whereas QImage belongs to Qt GUI. The conversion
499     from QImage to QVariant is implicit. For example:
500 
501     \snippet code/src_corelib_kernel_qmimedata.cpp 6
502 
503     \sa hasImage(), setData()
504 */
setImageData(const QVariant & image)505 void QMimeData::setImageData(const QVariant &image)
506 {
507     Q_D(QMimeData);
508     d->setData(applicationXQtImageLiteral(), image);
509 }
510 
511 /*!
512     Returns \c true if the object can return an image; otherwise returns
513     false.
514 
515     \sa setImageData(), imageData(), hasFormat()
516 */
hasImage() const517 bool QMimeData::hasImage() const
518 {
519     return hasFormat(applicationXQtImageLiteral());
520 }
521 
522 /*!
523     Returns a color if the data stored in the object represents a
524     color (MIME type \c application/x-color); otherwise returns a
525     null variant.
526 
527     A QVariant is used because QMimeData belongs to the Qt Core
528     module, whereas QColor belongs to Qt GUI. To convert the
529     QVariant to a QColor, simply use qvariant_cast(). For example:
530 
531     \snippet code/src_corelib_kernel_qmimedata.cpp 7
532 
533     \sa hasColor(), setColorData(), data()
534 */
colorData() const535 QVariant QMimeData::colorData() const
536 {
537     Q_D(const QMimeData);
538     return d->retrieveTypedData(applicationXColorLiteral(), QMetaType::QColor);
539 }
540 
541 /*!
542     Sets the color data in the object to the given \a color.
543 
544     Colors correspond to the MIME type \c application/x-color.
545 
546     \sa hasColor(), setData()
547 */
setColorData(const QVariant & color)548 void QMimeData::setColorData(const QVariant &color)
549 {
550     Q_D(QMimeData);
551     d->setData(applicationXColorLiteral(), color);
552 }
553 
554 
555 /*!
556     Returns \c true if the object can return a color (MIME type \c
557     application/x-color); otherwise returns \c false.
558 
559     \sa setColorData(), colorData(), hasFormat()
560 */
hasColor() const561 bool QMimeData::hasColor() const
562 {
563     return hasFormat(applicationXColorLiteral());
564 }
565 
566 /*!
567     Returns the data stored in the object in the format described by
568     the MIME type specified by \a mimeType.
569 */
data(const QString & mimeType) const570 QByteArray QMimeData::data(const QString &mimeType) const
571 {
572     Q_D(const QMimeData);
573     QVariant data = d->retrieveTypedData(mimeType, QMetaType::QByteArray);
574     return data.toByteArray();
575 }
576 
577 /*!
578     Sets the data associated with the MIME type given by \a mimeType
579     to the specified \a data.
580 
581     For the most common types of data, you can call the higher-level
582     functions setText(), setHtml(), setUrls(), setImageData(), and
583     setColorData() instead.
584 
585     Note that if you want to use a custom data type in an item view drag and drop
586     operation, you must register it as a Qt \l{QMetaType}{meta type}, using the
587     Q_DECLARE_METATYPE() macro, and implement stream operators for it. The stream
588     operators must then be registered with the qRegisterMetaTypeStreamOperators()
589     function.
590 
591     \sa hasFormat(), QMetaType, {QMetaType::}{qRegisterMetaTypeStreamOperators()}
592 */
setData(const QString & mimeType,const QByteArray & data)593 void QMimeData::setData(const QString &mimeType, const QByteArray &data)
594 {
595     Q_D(QMimeData);
596 
597     if (mimeType == QLatin1String("text/uri-list")) {
598         QByteArray ba = data;
599         if (ba.endsWith('\0'))
600             ba.chop(1);
601         QList<QByteArray> urls = ba.split('\n');
602         QList<QVariant> list;
603         for (int i = 0; i < urls.size(); ++i) {
604             QByteArray ba = urls.at(i).trimmed();
605             if (!ba.isEmpty())
606                 list.append(QUrl::fromEncoded(ba));
607         }
608         d->setData(mimeType, list);
609     } else {
610         d->setData(mimeType, QVariant(data));
611     }
612 }
613 
614 /*!
615     Returns \c true if the object can return data for the MIME type
616     specified by \a mimeType; otherwise returns \c false.
617 
618     For the most common types of data, you can call the higher-level
619     functions hasText(), hasHtml(), hasUrls(), hasImage(), and
620     hasColor() instead.
621 
622     \sa formats(), setData(), data()
623 */
hasFormat(const QString & mimeType) const624 bool QMimeData::hasFormat(const QString &mimeType) const
625 {
626     return formats().contains(mimeType);
627 }
628 
629 /*!
630     Returns a list of formats supported by the object. This is a list
631     of MIME types for which the object can return suitable data. The
632     formats in the list are in a priority order.
633 
634     For the most common types of data, you can call the higher-level
635     functions hasText(), hasHtml(), hasUrls(), hasImage(), and
636     hasColor() instead.
637 
638     \sa hasFormat(), setData(), data()
639 */
formats() const640 QStringList QMimeData::formats() const
641 {
642     Q_D(const QMimeData);
643     QStringList list;
644     list.reserve(static_cast<int>(d->dataList.size()));
645     for (auto &e : d->dataList)
646         list += e.format;
647     return list;
648 }
649 
650 /*!
651     Returns a variant with the given \a type containing data for the
652     MIME type specified by \a mimeType. If the object does not
653     support the MIME type or variant type given, a null variant is
654     returned instead.
655 
656     This function is called by the general data() getter and by the
657     convenience getters (text(), html(), urls(), imageData(), and
658     colorData()). You can reimplement it if you want to store your
659     data using a custom data structure (instead of a QByteArray,
660     which is what setData() provides). You would then also need
661     to reimplement hasFormat() and formats().
662 
663     \sa data()
664 */
retrieveData(const QString & mimeType,QVariant::Type type) const665 QVariant QMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
666 {
667     Q_UNUSED(type);
668     Q_D(const QMimeData);
669     return d->getData(mimeType);
670 }
671 
672 /*!
673     Removes all the MIME type and data entries in the object.
674 */
clear()675 void QMimeData::clear()
676 {
677     Q_D(QMimeData);
678     d->dataList.clear();
679 }
680 
681 /*!
682     \since 4.4
683 
684     Removes the data entry for \a mimeType in the object.
685 */
removeFormat(const QString & mimeType)686 void QMimeData::removeFormat(const QString &mimeType)
687 {
688     Q_D(QMimeData);
689     d->removeData(mimeType);
690 }
691 
692 QT_END_NAMESPACE
693 
694 #include "moc_qmimedata.cpp"
695