| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qmimedata.h" |
| |
| #include "private/qobject_p.h" |
| #include "qurl.h" |
| #include "qstringlist.h" |
| #if QT_CONFIG(textcodec) |
| #include "qtextcodec.h" |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| static inline QString textUriListLiteral() { return QStringLiteral("text/uri-list"); } |
| static inline QString textHtmlLiteral() { return QStringLiteral("text/html"); } |
| static inline QString textPlainLiteral() { return QStringLiteral("text/plain"); } |
| static inline QString textPlainUtf8Literal() { return QStringLiteral("text/plain;charset=utf-8"); } |
| static inline QString applicationXColorLiteral() { return QStringLiteral("application/x-color"); } |
| static inline QString applicationXQtImageLiteral() { return QStringLiteral("application/x-qt-image"); } |
| |
| struct QMimeDataStruct |
| { |
| QString format; |
| QVariant data; |
| }; |
| Q_DECLARE_TYPEINFO(QMimeDataStruct, Q_MOVABLE_TYPE); |
| |
| class QMimeDataPrivate : public QObjectPrivate |
| { |
| Q_DECLARE_PUBLIC(QMimeData) |
| public: |
| void removeData(const QString &format); |
| void setData(const QString &format, const QVariant &data); |
| QVariant getData(const QString &format) const; |
| |
| QVariant retrieveTypedData(const QString &format, QVariant::Type type) const; |
| |
| QVector<QMimeDataStruct> dataList; |
| }; |
| |
| void QMimeDataPrivate::removeData(const QString &format) |
| { |
| for (int i=0; i<dataList.size(); i++) { |
| if (dataList.at(i).format == format) { |
| dataList.removeAt(i); |
| return; |
| } |
| } |
| } |
| |
| void QMimeDataPrivate::setData(const QString &format, const QVariant &data) |
| { |
| // remove it first if the format is already here. |
| removeData(format); |
| QMimeDataStruct mimeData; |
| mimeData.format = format; |
| mimeData.data = data; |
| dataList += mimeData; |
| } |
| |
| |
| QVariant QMimeDataPrivate::getData(const QString &format) const |
| { |
| QVariant data; |
| for (int i=0; i<dataList.size(); i++) { |
| if (dataList.at(i).format == format) { |
| data = dataList.at(i).data; |
| break; |
| } |
| } |
| return data; |
| } |
| |
| QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QVariant::Type type) const |
| { |
| Q_Q(const QMimeData); |
| |
| QVariant data = q->retrieveData(format, type); |
| |
| // Text data requested: fallback to URL data if available |
| if (format == QLatin1String("text/plain") && !data.isValid()) { |
| data = retrieveTypedData(textUriListLiteral(), QVariant::List); |
| if (data.type() == QVariant::Url) { |
| data = QVariant(data.toUrl().toDisplayString()); |
| } else if (data.type() == QVariant::List) { |
| QString text; |
| int numUrls = 0; |
| const QList<QVariant> list = data.toList(); |
| for (int i = 0; i < list.size(); ++i) { |
| if (list.at(i).type() == QVariant::Url) { |
| text += list.at(i).toUrl().toDisplayString() + QLatin1Char('\n'); |
| ++numUrls; |
| } |
| } |
| if (numUrls == 1) |
| text.chop(1); // no final '\n' if there's only one URL |
| data = QVariant(text); |
| } |
| } |
| |
| if (data.type() == type || !data.isValid()) |
| return data; |
| |
| // provide more conversion possiblities than just what QVariant provides |
| |
| // URLs can be lists as well... |
| if ((type == QVariant::Url && data.type() == QVariant::List) |
| || (type == QVariant::List && data.type() == QVariant::Url)) |
| return data; |
| |
| // images and pixmaps are interchangeable |
| if ((type == QVariant::Pixmap && data.type() == QVariant::Image) |
| || (type == QVariant::Image && data.type() == QVariant::Pixmap)) |
| return data; |
| |
| if (data.type() == QVariant::ByteArray) { |
| // see if we can convert to the requested type |
| switch(type) { |
| #if QT_CONFIG(textcodec) |
| case QVariant::String: { |
| const QByteArray ba = data.toByteArray(); |
| QTextCodec *codec = QTextCodec::codecForName("utf-8"); |
| if (format == QLatin1String("text/html")) |
| codec = QTextCodec::codecForHtml(ba, codec); |
| return codec->toUnicode(ba); |
| } |
| #endif // textcodec |
| case QVariant::Color: { |
| QVariant newData = data; |
| newData.convert(QVariant::Color); |
| return newData; |
| } |
| case QVariant::List: { |
| if (format != QLatin1String("text/uri-list")) |
| break; |
| Q_FALLTHROUGH(); |
| } |
| case QVariant::Url: { |
| QByteArray ba = data.toByteArray(); |
| // Qt 3.x will send text/uri-list with a trailing |
| // null-terminator (that is *not* sent for any other |
| // text/* mime-type), so chop it off |
| if (ba.endsWith('\0')) |
| ba.chop(1); |
| |
| QList<QByteArray> urls = ba.split('\n'); |
| QList<QVariant> list; |
| for (int i = 0; i < urls.size(); ++i) { |
| QByteArray ba = urls.at(i).trimmed(); |
| if (!ba.isEmpty()) |
| list.append(QUrl::fromEncoded(ba)); |
| } |
| return list; |
| } |
| default: |
| break; |
| } |
| |
| } else if (type == QVariant::ByteArray) { |
| |
| // try to convert to bytearray |
| switch(data.type()) { |
| case QVariant::ByteArray: |
| case QVariant::Color: |
| return data.toByteArray(); |
| case QVariant::String: |
| return data.toString().toUtf8(); |
| case QVariant::Url: |
| return data.toUrl().toEncoded(); |
| case QVariant::List: { |
| // has to be list of URLs |
| QByteArray result; |
| QList<QVariant> list = data.toList(); |
| for (int i = 0; i < list.size(); ++i) { |
| if (list.at(i).type() == QVariant::Url) { |
| result += list.at(i).toUrl().toEncoded(); |
| result += "\r\n"; |
| } |
| } |
| if (!result.isEmpty()) |
| return result; |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| return data; |
| } |
| |
| /*! |
| \class QMimeData |
| \inmodule QtCore |
| \brief The QMimeData class provides a container for data that records information |
| about its MIME type. |
| |
| QMimeData is used to describe information that can be stored in |
| the \l{QClipboard}{clipboard}, and transferred via the \l{drag |
| and drop} mechanism. QMimeData objects associate the data that |
| they hold with the corresponding MIME types to ensure that |
| information can be safely transferred between applications, and |
| copied around within the same application. |
| |
| QMimeData objects are usually created using \c new and supplied |
| to QDrag or QClipboard objects. This is to enable Qt to manage |
| the memory that they use. |
| |
| A single QMimeData object can store the same data using several |
| different formats at the same time. The formats() function |
| returns a list of the available formats in order of preference. |
| The data() function returns the raw data associated with a MIME |
| type, and setData() allows you to set the data for a MIME type. |
| |
| For the most common MIME types, QMimeData provides convenience |
| functions to access the data: |
| |
| \table |
| \header \li Tester \li Getter \li Setter \li MIME Types |
| \row \li hasText() \li text() \li setText() \li \c text/plain |
| \row \li hasHtml() \li html() \li setHtml() \li \c text/html |
| \row \li hasUrls() \li urls() \li setUrls() \li \c text/uri-list |
| \row \li hasImage() \li imageData() \li setImageData() \li \c image/ * |
| \row \li hasColor() \li colorData() \li setColorData() \li \c application/x-color |
| \endtable |
| |
| For example, if your write a widget that accepts URL drags, you |
| would end up writing code like this: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 0 |
| |
| There are three approaches for storing custom data in a QMimeData |
| object: |
| |
| \list 1 |
| \li Custom data can be stored directly in a QMimeData object as a |
| QByteArray using setData(). For example: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 1 |
| |
| \li We can subclass QMimeData and reimplement hasFormat(), |
| formats(), and retrieveData(). |
| |
| \li If the drag and drop operation occurs within a single |
| application, we can subclass QMimeData and add extra data in |
| it, and use a qobject_cast() in the receiver's drop event |
| handler. For example: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 2 |
| \endlist |
| |
| \section1 Platform-Specific MIME Types |
| |
| On Windows, formats() will also return custom formats available |
| in the MIME data, using the \c{x-qt-windows-mime} subtype to |
| indicate that they represent data in non-standard formats. |
| The formats will take the following form: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 3 |
| |
| The following are examples of custom MIME types: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 4 |
| |
| The \c value declaration of each format describes the way in which the |
| data is encoded. |
| |
| In some cases (e.g. dropping multiple email attachments), multiple data |
| values are available. They can be accessed by adding an \c index value: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 8 |
| |
| On Windows, the MIME format does not always map directly to the |
| clipboard formats. Qt provides QWinMime to map clipboard |
| formats to open-standard MIME formats. Similarly, the |
| QMacPasteboardMime maps MIME to Mac flavors. |
| |
| \sa QClipboard, QDragEnterEvent, QDragMoveEvent, QDropEvent, QDrag, |
| QMacPasteboardMime, {Drag and Drop} |
| */ |
| |
| /*! |
| Constructs a new MIME data object with no data in it. |
| */ |
| QMimeData::QMimeData() |
| : QObject(*new QMimeDataPrivate, 0) |
| { |
| } |
| |
| /*! |
| Destroys the MIME data object. |
| */ |
| QMimeData::~QMimeData() |
| { |
| } |
| |
| /*! |
| Returns a list of URLs contained within the MIME data object. |
| |
| URLs correspond to the MIME type \c text/uri-list. |
| |
| \sa hasUrls(), data() |
| */ |
| QList<QUrl> QMimeData::urls() const |
| { |
| Q_D(const QMimeData); |
| QVariant data = d->retrieveTypedData(textUriListLiteral(), QVariant::List); |
| QList<QUrl> urls; |
| if (data.type() == QVariant::Url) |
| urls.append(data.toUrl()); |
| else if (data.type() == QVariant::List) { |
| QList<QVariant> list = data.toList(); |
| for (int i = 0; i < list.size(); ++i) { |
| if (list.at(i).type() == QVariant::Url) |
| urls.append(list.at(i).toUrl()); |
| } |
| } |
| return urls; |
| } |
| |
| /*! |
| Sets the URLs stored in the MIME data object to those specified by \a urls. |
| |
| URLs correspond to the MIME type \c text/uri-list. |
| |
| Since Qt 5.0, setUrls also exports the urls as plain text, if setText |
| was not called before, to make it possible to drop them into any lineedit |
| and text editor. |
| |
| \sa hasUrls(), setData() |
| */ |
| void QMimeData::setUrls(const QList<QUrl> &urls) |
| { |
| Q_D(QMimeData); |
| QList<QVariant> list; |
| const int numUrls = urls.size(); |
| list.reserve(numUrls); |
| for (int i = 0; i < numUrls; ++i) |
| list.append(urls.at(i)); |
| |
| d->setData(textUriListLiteral(), list); |
| } |
| |
| /*! |
| Returns \c true if the object can return a list of urls; otherwise |
| returns \c false. |
| |
| URLs correspond to the MIME type \c text/uri-list. |
| |
| \sa setUrls(), urls(), hasFormat() |
| */ |
| bool QMimeData::hasUrls() const |
| { |
| return hasFormat(textUriListLiteral()); |
| } |
| |
| |
| /*! |
| Returns a plain text (MIME type \c text/plain) representation of |
| the data. |
| |
| \sa hasText(), html(), data() |
| */ |
| QString QMimeData::text() const |
| { |
| Q_D(const QMimeData); |
| QVariant utf8Text = d->retrieveTypedData(textPlainUtf8Literal(), QVariant::String); |
| if (!utf8Text.isNull()) |
| return utf8Text.toString(); |
| |
| QVariant data = d->retrieveTypedData(textPlainLiteral(), QVariant::String); |
| return data.toString(); |
| } |
| |
| /*! |
| Sets \a text as the plain text (MIME type \c text/plain) used to |
| represent the data. |
| |
| \sa hasText(), setHtml(), setData() |
| */ |
| void QMimeData::setText(const QString &text) |
| { |
| Q_D(QMimeData); |
| d->setData(textPlainLiteral(), text); |
| } |
| |
| /*! |
| Returns \c true if the object can return plain text (MIME type \c |
| text/plain); otherwise returns \c false. |
| |
| \sa setText(), text(), hasHtml(), hasFormat() |
| */ |
| bool QMimeData::hasText() const |
| { |
| return hasFormat(textPlainLiteral()) || hasUrls(); |
| } |
| |
| /*! |
| Returns a string if the data stored in the object is HTML (MIME |
| type \c text/html); otherwise returns an empty string. |
| |
| \sa hasHtml(), setData() |
| */ |
| QString QMimeData::html() const |
| { |
| Q_D(const QMimeData); |
| QVariant data = d->retrieveTypedData(textHtmlLiteral(), QVariant::String); |
| return data.toString(); |
| } |
| |
| /*! |
| Sets \a html as the HTML (MIME type \c text/html) used to |
| represent the data. |
| |
| \sa hasHtml(), setText(), setData() |
| */ |
| void QMimeData::setHtml(const QString &html) |
| { |
| Q_D(QMimeData); |
| d->setData(textHtmlLiteral(), html); |
| } |
| |
| /*! |
| Returns \c true if the object can return HTML (MIME type \c |
| text/html); otherwise returns \c false. |
| |
| \sa setHtml(), html(), hasFormat() |
| */ |
| bool QMimeData::hasHtml() const |
| { |
| return hasFormat(textHtmlLiteral()); |
| } |
| |
| /*! |
| Returns a QVariant storing a QImage if the object can return an |
| image; otherwise returns a null variant. |
| |
| A QVariant is used because QMimeData belongs to the Qt Core |
| module, whereas QImage belongs to Qt GUI. To convert the |
| QVariant to a QImage, simply use qvariant_cast(). For example: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 5 |
| |
| \sa hasImage() |
| */ |
| QVariant QMimeData::imageData() const |
| { |
| Q_D(const QMimeData); |
| return d->retrieveTypedData(applicationXQtImageLiteral(), QVariant::Image); |
| } |
| |
| /*! |
| Sets the data in the object to the given \a image. |
| |
| A QVariant is used because QMimeData belongs to the Qt Core |
| module, whereas QImage belongs to Qt GUI. The conversion |
| from QImage to QVariant is implicit. For example: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 6 |
| |
| \sa hasImage(), setData() |
| */ |
| void QMimeData::setImageData(const QVariant &image) |
| { |
| Q_D(QMimeData); |
| d->setData(applicationXQtImageLiteral(), image); |
| } |
| |
| /*! |
| Returns \c true if the object can return an image; otherwise returns |
| false. |
| |
| \sa setImageData(), imageData(), hasFormat() |
| */ |
| bool QMimeData::hasImage() const |
| { |
| return hasFormat(applicationXQtImageLiteral()); |
| } |
| |
| /*! |
| Returns a color if the data stored in the object represents a |
| color (MIME type \c application/x-color); otherwise returns a |
| null variant. |
| |
| A QVariant is used because QMimeData belongs to the Qt Core |
| module, whereas QColor belongs to Qt GUI. To convert the |
| QVariant to a QColor, simply use qvariant_cast(). For example: |
| |
| \snippet code/src_corelib_kernel_qmimedata.cpp 7 |
| |
| \sa hasColor(), setColorData(), data() |
| */ |
| QVariant QMimeData::colorData() const |
| { |
| Q_D(const QMimeData); |
| return d->retrieveTypedData(applicationXColorLiteral(), QVariant::Color); |
| } |
| |
| /*! |
| Sets the color data in the object to the given \a color. |
| |
| Colors correspond to the MIME type \c application/x-color. |
| |
| \sa hasColor(), setData() |
| */ |
| void QMimeData::setColorData(const QVariant &color) |
| { |
| Q_D(QMimeData); |
| d->setData(applicationXColorLiteral(), color); |
| } |
| |
| |
| /*! |
| Returns \c true if the object can return a color (MIME type \c |
| application/x-color); otherwise returns \c false. |
| |
| \sa setColorData(), colorData(), hasFormat() |
| */ |
| bool QMimeData::hasColor() const |
| { |
| return hasFormat(applicationXColorLiteral()); |
| } |
| |
| /*! |
| Returns the data stored in the object in the format described by |
| the MIME type specified by \a mimeType. |
| */ |
| QByteArray QMimeData::data(const QString &mimeType) const |
| { |
| Q_D(const QMimeData); |
| QVariant data = d->retrieveTypedData(mimeType, QVariant::ByteArray); |
| return data.toByteArray(); |
| } |
| |
| /*! |
| Sets the data associated with the MIME type given by \a mimeType |
| to the specified \a data. |
| |
| For the most common types of data, you can call the higher-level |
| functions setText(), setHtml(), setUrls(), setImageData(), and |
| setColorData() instead. |
| |
| Note that if you want to use a custom data type in an item view drag and drop |
| operation, you must register it as a Qt \l{QMetaType}{meta type}, using the |
| Q_DECLARE_METATYPE() macro, and implement stream operators for it. The stream |
| operators must then be registered with the qRegisterMetaTypeStreamOperators() |
| function. |
| |
| \sa hasFormat(), QMetaType, {QMetaType::}{qRegisterMetaTypeStreamOperators()} |
| */ |
| void QMimeData::setData(const QString &mimeType, const QByteArray &data) |
| { |
| Q_D(QMimeData); |
| |
| if (mimeType == QLatin1String("text/uri-list")) { |
| QByteArray ba = data; |
| if (ba.endsWith('\0')) |
| ba.chop(1); |
| QList<QByteArray> urls = ba.split('\n'); |
| QList<QVariant> list; |
| for (int i = 0; i < urls.size(); ++i) { |
| QByteArray ba = urls.at(i).trimmed(); |
| if (!ba.isEmpty()) |
| list.append(QUrl::fromEncoded(ba)); |
| } |
| d->setData(mimeType, list); |
| } else { |
| d->setData(mimeType, QVariant(data)); |
| } |
| } |
| |
| /*! |
| Returns \c true if the object can return data for the MIME type |
| specified by \a mimeType; otherwise returns \c false. |
| |
| For the most common types of data, you can call the higher-level |
| functions hasText(), hasHtml(), hasUrls(), hasImage(), and |
| hasColor() instead. |
| |
| \sa formats(), setData(), data() |
| */ |
| bool QMimeData::hasFormat(const QString &mimeType) const |
| { |
| return formats().contains(mimeType); |
| } |
| |
| /*! |
| Returns a list of formats supported by the object. This is a list |
| of MIME types for which the object can return suitable data. The |
| formats in the list are in a priority order. |
| |
| For the most common types of data, you can call the higher-level |
| functions hasText(), hasHtml(), hasUrls(), hasImage(), and |
| hasColor() instead. |
| |
| \sa hasFormat(), setData(), data() |
| */ |
| QStringList QMimeData::formats() const |
| { |
| Q_D(const QMimeData); |
| QStringList list; |
| const int size = d->dataList.size(); |
| list.reserve(size); |
| for (int i = 0; i < size; ++i) |
| list += d->dataList.at(i).format; |
| return list; |
| } |
| |
| /*! |
| Returns a variant with the given \a type containing data for the |
| MIME type specified by \a mimeType. If the object does not |
| support the MIME type or variant type given, a null variant is |
| returned instead. |
| |
| This function is called by the general data() getter and by the |
| convenience getters (text(), html(), urls(), imageData(), and |
| colorData()). You can reimplement it if you want to store your |
| data using a custom data structure (instead of a QByteArray, |
| which is what setData() provides). You would then also need |
| to reimplement hasFormat() and formats(). |
| |
| \sa data() |
| */ |
| QVariant QMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const |
| { |
| Q_UNUSED(type); |
| Q_D(const QMimeData); |
| return d->getData(mimeType); |
| } |
| |
| /*! |
| Removes all the MIME type and data entries in the object. |
| */ |
| void QMimeData::clear() |
| { |
| Q_D(QMimeData); |
| d->dataList.clear(); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Removes the data entry for \a mimeType in the object. |
| */ |
| void QMimeData::removeFormat(const QString &mimeType) |
| { |
| Q_D(QMimeData); |
| d->removeData(mimeType); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qmimedata.cpp" |