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#include "qstringconverter.h"
46
47QT_BEGIN_NAMESPACE
48
49static inline QString textUriListLiteral() { return QStringLiteral("text/uri-list"); }
50static inline QString textHtmlLiteral() { return QStringLiteral("text/html"); }
51static inline QString textPlainLiteral() { return QStringLiteral("text/plain"); }
52static inline QString textPlainUtf8Literal() { return QStringLiteral("text/plain;charset=utf-8"); }
53static inline QString applicationXColorLiteral() { return QStringLiteral("application/x-color"); }
54static inline QString applicationXQtImageLiteral() { return QStringLiteral("application/x-qt-image"); }
55
56struct QMimeDataStruct
57{
58 QString format;
59 QVariant data;
60};
61Q_DECLARE_TYPEINFO(QMimeDataStruct, Q_MOVABLE_TYPE);
62
63class QMimeDataPrivate : public QObjectPrivate
64{
65 Q_DECLARE_PUBLIC(QMimeData)
66public:
67 void removeData(const QString &format);
68 void setData(const QString &format, const QVariant &data);
69 QVariant getData(const QString &format) const;
70
71 QVariant retrieveTypedData(const QString &format, QMetaType type) const;
72
73 std::vector<QMimeDataStruct>::iterator find(const QString &format) noexcept {
74 const auto formatEquals = [](const QString &format) {
75 return [&format](const QMimeDataStruct &s) { return s.format == format; };
76 };
77 return std::find_if(dataList.begin(), dataList.end(), formatEquals(format));
78 }
79
80 std::vector<QMimeDataStruct>::const_iterator find(const QString &format) const noexcept {
81 return const_cast<QMimeDataPrivate*>(this)->find(format);
82 }
83
84 std::vector<QMimeDataStruct> dataList;
85};
86
87void QMimeDataPrivate::removeData(const QString &format)
88{
89 const auto it = find(format);
90 if (it != dataList.end())
91 dataList.erase(it);
92}
93
94void QMimeDataPrivate::setData(const QString &format, const QVariant &data)
95{
96 const auto it = find(format);
97 if (it == dataList.end())
98 dataList.push_back({format, data});
99 else
100 it->data = data;
101}
102
103
104QVariant QMimeDataPrivate::getData(const QString &format) const
105{
106 const auto it = find(format);
107 if (it == dataList.cend())
108 return {};
109 else
110 return it->data;
111}
112
113QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType type) const
114{
115 Q_Q(const QMimeData);
116 int typeId = type.id();
117
118 QVariant data = q->retrieveData(format, type);
119
120 // Text data requested: fallback to URL data if available
121 if (format == QLatin1String("text/plain") && !data.isValid()) {
122 data = retrieveTypedData(textUriListLiteral(), QMetaType(QMetaType::QVariantList));
123 if (data.metaType().id() == QMetaType::QUrl) {
124 data = QVariant(data.toUrl().toDisplayString());
125 } else if (data.metaType().id() == QMetaType::QVariantList) {
126 QString text;
127 int numUrls = 0;
128 const QList<QVariant> list = data.toList();
129 for (int i = 0; i < list.size(); ++i) {
130 if (list.at(i).metaType().id() == QMetaType::QUrl) {
131 text += list.at(i).toUrl().toDisplayString() + QLatin1Char('\n');
132 ++numUrls;
133 }
134 }
135 if (numUrls == 1)
136 text.chop(1); // no final '\n' if there's only one URL
137 data = QVariant(text);
138 }
139 }
140
141 if (data.metaType() == type || !data.isValid())
142 return data;
143
144 // provide more conversion possiblities than just what QVariant provides
145
146 // URLs can be lists as well...
147 if ((typeId == QMetaType::QUrl && data.metaType().id() == QMetaType::QVariantList)
148 || (typeId == QMetaType::QVariantList && data.metaType().id() == QMetaType::QUrl))
149 return data;
150
151 // images and pixmaps are interchangeable
152 if ((typeId == QMetaType::QPixmap && data.metaType().id() == QMetaType::QImage)
153 || (typeId == QMetaType::QImage && data.metaType().id() == QMetaType::QPixmap))
154 return data;
155
156 if (data.metaType().id() == QMetaType::QByteArray) {
157 // see if we can convert to the requested type
158 switch (typeId) {
159 case QMetaType::QString: {
160 const QByteArray ba = data.toByteArray();
161 if (format == QLatin1String("text/html")) {
162 auto encoding = QStringConverter::encodingForHtml(ba);
163 if (encoding) {
164 QStringDecoder toUtf16(*encoding);
165 return QString(toUtf16(ba));
166 }
167 // fall back to utf8
168 }
169 return QString::fromUtf8(ba);
170 }
171 case QMetaType::QColor: {
172 QVariant newData = data;
173QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
174 newData.convert(QMetaType::QColor);
175QT_WARNING_POP
176 return newData;
177 }
178 case QMetaType::QVariantList: {
179 if (format != QLatin1String("text/uri-list"))
180 break;
181 Q_FALLTHROUGH();
182 }
183 case QMetaType::QUrl: {
184 QByteArray ba = data.toByteArray();
185 // Qt 3.x will send text/uri-list with a trailing
186 // null-terminator (that is *not* sent for any other
187 // text/* mime-type), so chop it off
188 if (ba.endsWith('\0'))
189 ba.chop(1);
190
191 QList<QByteArray> urls = ba.split('\n');
192 QList<QVariant> list;
193 for (int i = 0; i < urls.size(); ++i) {
194 QByteArray ba = urls.at(i).trimmed();
195 if (!ba.isEmpty())
196 list.append(QUrl::fromEncoded(ba));
197 }
198 return list;
199 }
200 default:
201 break;
202 }
203
204 } else if (typeId == QMetaType::QByteArray) {
205
206 // try to convert to bytearray
207 switch (data.metaType().id()) {
208 case QMetaType::QByteArray:
209 case QMetaType::QColor:
210 return data.toByteArray();
211 case QMetaType::QString:
212 return data.toString().toUtf8();
213 case QMetaType::QUrl:
214 return data.toUrl().toEncoded();
215 case QMetaType::QVariantList: {
216 // has to be list of URLs
217 QByteArray result;
218 QList<QVariant> list = data.toList();
219 for (int i = 0; i < list.size(); ++i) {
220 if (list.at(i).metaType().id() == QMetaType::QUrl) {
221 result += list.at(i).toUrl().toEncoded();
222 result += "\r\n";
223 }
224 }
225 if (!result.isEmpty())
226 return result;
227 break;
228 }
229 default:
230 break;
231 }
232 }
233 return data;
234}
235
236/*!
237 \class QMimeData
238 \inmodule QtCore
239 \brief The QMimeData class provides a container for data that records information
240 about its MIME type.
241
242 QMimeData is used to describe information that can be stored in
243 the \l{QClipboard}{clipboard}, and transferred via the \l{drag
244 and drop} mechanism. QMimeData objects associate the data that
245 they hold with the corresponding MIME types to ensure that
246 information can be safely transferred between applications, and
247 copied around within the same application.
248
249 QMimeData objects are usually created using \c new and supplied
250 to QDrag or QClipboard objects. This is to enable Qt to manage
251 the memory that they use.
252
253 A single QMimeData object can store the same data using several
254 different formats at the same time. The formats() function
255 returns a list of the available formats in order of preference.
256 The data() function returns the raw data associated with a MIME
257 type, and setData() allows you to set the data for a MIME type.
258
259 For the most common MIME types, QMimeData provides convenience
260 functions to access the data:
261
262 \table
263 \header \li Tester \li Getter \li Setter \li MIME Types
264 \row \li hasText() \li text() \li setText() \li \c text/plain
265 \row \li hasHtml() \li html() \li setHtml() \li \c text/html
266 \row \li hasUrls() \li urls() \li setUrls() \li \c text/uri-list
267 \row \li hasImage() \li imageData() \li setImageData() \li \c image/ *
268 \row \li hasColor() \li colorData() \li setColorData() \li \c application/x-color
269 \endtable
270
271 For example, if your write a widget that accepts URL drags, you
272 would end up writing code like this:
273
274 \snippet code/src_corelib_kernel_qmimedata.cpp 0
275
276 There are three approaches for storing custom data in a QMimeData
277 object:
278
279 \list 1
280 \li Custom data can be stored directly in a QMimeData object as a
281 QByteArray using setData(). For example:
282
283 \snippet code/src_corelib_kernel_qmimedata.cpp 1
284
285 \li We can subclass QMimeData and reimplement hasFormat(),
286 formats(), and retrieveData().
287
288 \li If the drag and drop operation occurs within a single
289 application, we can subclass QMimeData and add extra data in
290 it, and use a qobject_cast() in the receiver's drop event
291 handler. For example:
292
293 \snippet code/src_corelib_kernel_qmimedata.cpp 2
294 \endlist
295
296 \section1 Platform-Specific MIME Types
297
298 On Windows, formats() will also return custom formats available
299 in the MIME data, using the \c{x-qt-windows-mime} subtype to
300 indicate that they represent data in non-standard formats.
301 The formats will take the following form:
302
303 \snippet code/src_corelib_kernel_qmimedata.cpp 3
304
305 The following are examples of custom MIME types:
306
307 \snippet code/src_corelib_kernel_qmimedata.cpp 4
308
309 The \c value declaration of each format describes the way in which the
310 data is encoded.
311
312 In some cases (e.g. dropping multiple email attachments), multiple data
313 values are available. They can be accessed by adding an \c index value:
314
315 \snippet code/src_corelib_kernel_qmimedata.cpp 8
316
317 On Windows, the MIME format does not always map directly to the
318 clipboard formats. Qt provides QWinMime to map clipboard
319 formats to open-standard MIME formats. Similarly, the
320 QMacPasteboardMime maps MIME to Mac flavors.
321
322 \sa QClipboard, QDragEnterEvent, QDragMoveEvent, QDropEvent, QDrag,
323 QMacPasteboardMime, {Drag and Drop}
324*/
325
326/*!
327 Constructs a new MIME data object with no data in it.
328*/
329QMimeData::QMimeData()
330 : QObject(*new QMimeDataPrivate, nullptr)
331{
332}
333
334/*!
335 Destroys the MIME data object.
336*/
337QMimeData::~QMimeData()
338{
339}
340
341/*!
342 Returns a list of URLs contained within the MIME data object.
343
344 URLs correspond to the MIME type \c text/uri-list.
345
346 \sa hasUrls(), data()
347*/
348QList<QUrl> QMimeData::urls() const
349{
350 Q_D(const QMimeData);
351 QVariant data = d->retrieveTypedData(textUriListLiteral(), QMetaType(QMetaType::QVariantList));
352 QList<QUrl> urls;
353 if (data.metaType().id() == QMetaType::QUrl)
354 urls.append(data.toUrl());
355 else if (data.metaType().id() == QMetaType::QVariantList) {
356 QList<QVariant> list = data.toList();
357 for (int i = 0; i < list.size(); ++i) {
358 if (list.at(i).metaType().id() == QMetaType::QUrl)
359 urls.append(list.at(i).toUrl());
360 }
361 }
362 return urls;
363}
364
365/*!
366 Sets the URLs stored in the MIME data object to those specified by \a urls.
367
368 URLs correspond to the MIME type \c text/uri-list.
369
370 Since Qt 5.0, setUrls also exports the urls as plain text, if setText
371 was not called before, to make it possible to drop them into any lineedit
372 and text editor.
373
374 \sa hasUrls(), setData()
375*/
376void QMimeData::setUrls(const QList<QUrl> &urls)
377{
378 Q_D(QMimeData);
379 QList<QVariant> list;
380 const int numUrls = urls.size();
381 list.reserve(numUrls);
382 for (int i = 0; i < numUrls; ++i)
383 list.append(urls.at(i));
384
385 d->setData(textUriListLiteral(), list);
386}
387
388/*!
389 Returns \c true if the object can return a list of urls; otherwise
390 returns \c false.
391
392 URLs correspond to the MIME type \c text/uri-list.
393
394 \sa setUrls(), urls(), hasFormat()
395*/
396bool QMimeData::hasUrls() const
397{
398 return hasFormat(textUriListLiteral());
399}
400
401
402/*!
403 Returns a plain text (MIME type \c text/plain) representation of
404 the data.
405
406 \sa hasText(), html(), data()
407*/
408QString QMimeData::text() const
409{
410 Q_D(const QMimeData);
411 QVariant utf8Text = d->retrieveTypedData(textPlainUtf8Literal(), QMetaType(QMetaType::QString));
412 if (!utf8Text.isNull())
413 return utf8Text.toString();
414
415 QVariant data = d->retrieveTypedData(textPlainLiteral(), QMetaType(QMetaType::QString));
416 return data.toString();
417}
418
419/*!
420 Sets \a text as the plain text (MIME type \c text/plain) used to
421 represent the data.
422
423 \sa hasText(), setHtml(), setData()
424*/
425void QMimeData::setText(const QString &text)
426{
427 Q_D(QMimeData);
428 d->setData(textPlainLiteral(), text);
429}
430
431/*!
432 Returns \c true if the object can return plain text (MIME type \c
433 text/plain); otherwise returns \c false.
434
435 \sa setText(), text(), hasHtml(), hasFormat()
436*/
437bool QMimeData::hasText() const
438{
439 return hasFormat(textPlainLiteral()) || hasUrls();
440}
441
442/*!
443 Returns a string if the data stored in the object is HTML (MIME
444 type \c text/html); otherwise returns an empty string.
445
446 \sa hasHtml(), setData()
447*/
448QString QMimeData::html() const
449{
450 Q_D(const QMimeData);
451 QVariant data = d->retrieveTypedData(textHtmlLiteral(), QMetaType(QMetaType::QString));
452 return data.toString();
453}
454
455/*!
456 Sets \a html as the HTML (MIME type \c text/html) used to
457 represent the data.
458
459 \sa hasHtml(), setText(), setData()
460*/
461void QMimeData::setHtml(const QString &html)
462{
463 Q_D(QMimeData);
464 d->setData(textHtmlLiteral(), html);
465}
466
467/*!
468 Returns \c true if the object can return HTML (MIME type \c
469 text/html); otherwise returns \c false.
470
471 \sa setHtml(), html(), hasFormat()
472*/
473bool QMimeData::hasHtml() const
474{
475 return hasFormat(textHtmlLiteral());
476}
477
478/*!
479 Returns a QVariant storing a QImage if the object can return an
480 image; otherwise returns a null variant.
481
482 A QVariant is used because QMimeData belongs to the Qt Core
483 module, whereas QImage belongs to Qt GUI. To convert the
484 QVariant to a QImage, simply use qvariant_cast(). For example:
485
486 \snippet code/src_corelib_kernel_qmimedata.cpp 5
487
488 \sa hasImage()
489*/
490QVariant QMimeData::imageData() const
491{
492 Q_D(const QMimeData);
493 return d->retrieveTypedData(applicationXQtImageLiteral(), QMetaType(QMetaType::QImage));
494}
495
496/*!
497 Sets the data in the object to the given \a image.
498
499 A QVariant is used because QMimeData belongs to the Qt Core
500 module, whereas QImage belongs to Qt GUI. The conversion
501 from QImage to QVariant is implicit. For example:
502
503 \snippet code/src_corelib_kernel_qmimedata.cpp 6
504
505 \sa hasImage(), setData()
506*/
507void QMimeData::setImageData(const QVariant &image)
508{
509 Q_D(QMimeData);
510 d->setData(applicationXQtImageLiteral(), image);
511}
512
513/*!
514 Returns \c true if the object can return an image; otherwise returns
515 false.
516
517 \sa setImageData(), imageData(), hasFormat()
518*/
519bool QMimeData::hasImage() const
520{
521 return hasFormat(applicationXQtImageLiteral());
522}
523
524/*!
525 Returns a color if the data stored in the object represents a
526 color (MIME type \c application/x-color); otherwise returns a
527 null variant.
528
529 A QVariant is used because QMimeData belongs to the Qt Core
530 module, whereas QColor belongs to Qt GUI. To convert the
531 QVariant to a QColor, simply use qvariant_cast(). For example:
532
533 \snippet code/src_corelib_kernel_qmimedata.cpp 7
534
535 \sa hasColor(), setColorData(), data()
536*/
537QVariant QMimeData::colorData() const
538{
539 Q_D(const QMimeData);
540 return d->retrieveTypedData(applicationXColorLiteral(), QMetaType(QMetaType::QColor));
541}
542
543/*!
544 Sets the color data in the object to the given \a color.
545
546 Colors correspond to the MIME type \c application/x-color.
547
548 \sa hasColor(), setData()
549*/
550void QMimeData::setColorData(const QVariant &color)
551{
552 Q_D(QMimeData);
553 d->setData(applicationXColorLiteral(), color);
554}
555
556
557/*!
558 Returns \c true if the object can return a color (MIME type \c
559 application/x-color); otherwise returns \c false.
560
561 \sa setColorData(), colorData(), hasFormat()
562*/
563bool QMimeData::hasColor() const
564{
565 return hasFormat(applicationXColorLiteral());
566}
567
568/*!
569 Returns the data stored in the object in the format described by
570 the MIME type specified by \a mimeType.
571*/
572QByteArray QMimeData::data(const QString &mimeType) const
573{
574 Q_D(const QMimeData);
575 QVariant data = d->retrieveTypedData(mimeType, QMetaType(QMetaType::QByteArray));
576 return data.toByteArray();
577}
578
579/*!
580 Sets the data associated with the MIME type given by \a mimeType
581 to the specified \a data.
582
583 For the most common types of data, you can call the higher-level
584 functions setText(), setHtml(), setUrls(), setImageData(), and
585 setColorData() instead.
586
587 Note that if you want to use a custom data type in an item view drag and drop
588 operation, you must register it as a Qt \l{QMetaType}{meta type}, using the
589 Q_DECLARE_METATYPE() macro, and implement stream operators for it.
590
591 \sa hasFormat(), QMetaType, {QMetaType::}{Q_DECLARE_METATYPE()}
592*/
593void 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*/
624bool 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*/
640QStringList 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*/
665QVariant QMimeData::retrieveData(const QString &mimeType, QMetaType 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*/
675void 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*/
686void QMimeData::removeFormat(const QString &mimeType)
687{
688 Q_D(QMimeData);
689 d->removeData(mimeType);
690}
691
692QT_END_NAMESPACE
693
694#include "moc_qmimedata.cpp"
695