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