From 649dccf57b18626f6a0790d46e5e619e4e603078 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 15 Nov 2022 11:36:51 +0100 Subject: Reintroduce converter APIs for supporting native clipboard formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Qt 5, QWin(dows)Mime and QMacMime lived in the respective Extras modules, which were removed and partially folded into the relevant modules in Qt. QWindowsMime and QMacMime continued to provide the abstraction for implementing built-in support for native clipboard formats and UTIs within Qt, but only as private APIs. After the recent clean up of those APIs and respective infrastructure, we can now bring them back as public converter interfaces. Application developers can subclass those and instantiate an instance of their implementation to add support for platform or application specific data formats. These interfaces are not in the QNativeInterface namespace, as applications don't call into Windows or macOS using those interfaces. I.e. there is no class on which an application would call auto *converter= nativeInterface(); Also, since applications override those converter types, we do want to guarantee binary and source compatibility. [ChangeLog][QtGui][QWindowsMimeConverter] Reintroduced to allow applications to add support for conversion from and to Windows-native clipboard formats to MIME-encoded data. [ChangeLog][QtGui][QUtiMimeConverter] Reintroduced to allow applications to add support for conversion from and to clipboard data on macOS and iOS to MIME-encoded data. Fixes: QTBUG-93632 Change-Id: Iebd909c3970015d203f59d5ab15e306b3d312f6e Reviewed-by: Yuhang Zhao <2546789017@qq.com> Reviewed-by: Tor Arne Vestbø --- src/gui/platform/darwin/qmacmime.mm | 805 -------------------- src/gui/platform/darwin/qmacmime_p.h | 61 -- src/gui/platform/darwin/qmacmimeregistry.mm | 22 +- src/gui/platform/darwin/qmacmimeregistry_p.h | 10 +- src/gui/platform/darwin/qutimimeconverter.h | 54 ++ src/gui/platform/darwin/qutimimeconverter.mm | 806 +++++++++++++++++++++ src/gui/platform/windows/qwindowsmime.cpp | 157 ---- src/gui/platform/windows/qwindowsmime_p.h | 53 -- src/gui/platform/windows/qwindowsmimeconverter.cpp | 152 ++++ src/gui/platform/windows/qwindowsmimeconverter.h | 41 ++ .../platform/windows/qwindowsnativeinterface.cpp | 9 +- 11 files changed, 1073 insertions(+), 1097 deletions(-) delete mode 100644 src/gui/platform/darwin/qmacmime.mm delete mode 100644 src/gui/platform/darwin/qmacmime_p.h create mode 100644 src/gui/platform/darwin/qutimimeconverter.h create mode 100644 src/gui/platform/darwin/qutimimeconverter.mm delete mode 100644 src/gui/platform/windows/qwindowsmime.cpp delete mode 100644 src/gui/platform/windows/qwindowsmime_p.h create mode 100644 src/gui/platform/windows/qwindowsmimeconverter.cpp create mode 100644 src/gui/platform/windows/qwindowsmimeconverter.h (limited to 'src/gui/platform') diff --git a/src/gui/platform/darwin/qmacmime.mm b/src/gui/platform/darwin/qmacmime.mm deleted file mode 100644 index 4e2a65cf76..0000000000 --- a/src/gui/platform/darwin/qmacmime.mm +++ /dev/null @@ -1,805 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include - -#include -#include -#include -#include -#include - -#if defined(Q_OS_MACOS) -#import -#else -#include -#endif - -#if defined(QT_PLATFORM_UIKIT) -#import -#endif - -#include "qmacmime_p.h" -#include "qmacmimeregistry_p.h" -#include "qguiapplication.h" -#include "private/qcore_mac_p.h" - -QT_BEGIN_NAMESPACE - -using namespace Qt::StringLiterals; - -/***************************************************************************** - QDnD debug facilities - *****************************************************************************/ -//#define DEBUG_MIME_MAPS - -/*! - \class QMacMime - \internal - \brief The QMacMime class converts between a MIME type and a - \l{https://developer.apple.com/documentation/uniformtypeidentifiers} - {Uniform Type Identifier (UTI)} format. - \since 4.2 - - \ingroup draganddrop - \inmodule QtGui - - Qt's drag and drop and clipboard facilities use the MIME - standard. On X11, this maps trivially to the Xdnd protocol. On - Mac, although some applications use MIME to describe clipboard - contents, it is more common to use Apple's UTI format. - - QMacMime's role is to bridge the gap between MIME and UTI; - By subclasses this class, one can extend Qt's drag and drop - and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. - - A subclass of QMacMime will automatically be registered, and active, upon instantiation. - - Qt has predefined support for the following UTIs: - \list - \li public.utf8-plain-text - converts to "text/plain" - \li public.utf16-plain-text - converts to "text/plain" - \li public.text - converts to "text/plain" - \li public.html - converts to "text/html" - \li public.url - converts to "text/uri-list" - \li public.file-url - converts to "text/uri-list" - \li public.tiff - converts to "application/x-qt-image" - \li public.vcard - converts to "text/plain" - \li com.apple.traditional-mac-plain-text - converts to "text/plain" - \li com.apple.pict - converts to "application/x-qt-image" - \endlist - - When working with MIME data, Qt will iterate through all instances of QMacMime to find - find an instance that can convert to, or from, a specific MIME type. It will do this by calling - mimeForUti() or utiForMime() on each instance, starting with (and choosing) the last created - instance first. The actual conversions will be done by using convertToMime() and convertFromMime(). -*/ - -/*! - \enum QMacMime::HandlerScope - \internal -*/ - -/*! - Constructs a new conversion object of type \a scope, adding it to the - globally accessed list of available converters. -*/ -QMacMime::QMacMime(HandlerScope scope) - : m_scope(scope) -{ - QMacMimeRegistry::registerMimeConverter(this); -} - -/*! - Constructs a new conversion object and adds it to the - globally accessed list of available converters. -*/ -QMacMime::QMacMime() - : QMacMime(HandlerScope::All) -{ -} - -/*! - Destroys a conversion object, removing it from the global - list of available converters. -*/ -QMacMime::~QMacMime() -{ - QMacMimeRegistry::unregisterMimeConverter(this); -} - -/*! - Returns the item count for the given \a mimeData -*/ -int QMacMime::count(const QMimeData *mimeData) const -{ - Q_UNUSED(mimeData); - return 1; -} - -/*! - \fn bool QMacMime::canConvert(const QString &mime, const QString &uti) const - - Returns \c true if the converter can convert (both ways) between - \a mime and \a uti; otherwise returns \c false. -*/ - -/*! - \fn QString QMacMime::mimeForUti(QString uti) - - Returns the MIME type used for Mac UTI \a uti, or an empty string if - this converter does not support converting from \a uti. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QString QMacMime::utiForMime(const QString &mime) - - Returns the Mac UTI used for MIME type \a mime, or an empty string if - this converter does not support converting from \a mime. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QVariant QMacMime::convertToMime(const QString &mime, - const QList &data, const QString &uti) - - Returns \a data converted from Mac UTI \a uti to MIME type \a mime. - - Note that Mac UTIs must all be self-terminating. The input \a data - may contain trailing data. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QList QMacMime::convertFromMime(const QString &mime, - const QVariant &data, const QString & uti) - - Returns \a data converted from MIME type \a mime to Mac UTI \a uti. - - Note that Mac UTIs must all be self-terminating. The return - value may contain trailing data. - - All subclasses must reimplement this pure virtual function. -*/ - - -class QMacMimeAny : public QMacMime { -public: - QMacMimeAny() : QMacMime(HandlerScope::AllCompatible) {} - - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeAny::utiForMime(const QString &mime) const -{ - // do not handle the mime type name in the drag pasteboard - if (mime == "application/x-qt-mime-type-name"_L1) - return QString(); - QString ret = "com.trolltech.anymime."_L1 + mime; - return ret.replace(u'/', "--"_L1); -} - -QString QMacMimeAny::mimeForUti(const QString &uti) const -{ - const QString any_prefix = "com.trolltech.anymime."_L1; - if (uti.size() > any_prefix.length() && uti.startsWith(any_prefix)) - return uti.mid(any_prefix.length()).replace("--"_L1, "/"_L1); - return QString(); -} - -QVariant QMacMimeAny::convertToMime(const QString &mime, const QList &data, - const QString &) const -{ - if (data.count() > 1) - qWarning("QMacMimeAny: Cannot handle multiple member data"); - QVariant ret; - if (mime == "text/plain"_L1) - ret = QString::fromUtf8(data.first()); - else - ret = data.first(); - return ret; -} - -QList QMacMimeAny::convertFromMime(const QString &mime, const QVariant &data, - const QString &) const -{ - QList ret; - if (mime == "text/plain"_L1) - ret.append(data.toString().toUtf8()); - else - ret.append(data.toByteArray()); - return ret; -} - -class QMacMimeTypeName : public QMacMime { -private: - -public: - QMacMimeTypeName(): QMacMime(HandlerScope::AllCompatible) {} - - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const override; -}; - -QString QMacMimeTypeName::utiForMime(const QString &mime) const -{ - if (mime == "application/x-qt-mime-type-name"_L1) - return u"com.trolltech.qt.MimeTypeName"_s; - return QString(); -} - -QString QMacMimeTypeName::mimeForUti(const QString &) const -{ - return QString(); -} - -QVariant QMacMimeTypeName::convertToMime(const QString &, const QList &, const QString &) const -{ - QVariant ret; - return ret; -} - -QList QMacMimeTypeName::convertFromMime(const QString &, const QVariant &, const QString &) const -{ - QList ret; - ret.append(QString("x-qt-mime-type-name"_L1).toUtf8()); - return ret; -} - -class QMacMimePlainTextFallback : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimePlainTextFallback::utiForMime(const QString &mime) const -{ - if (mime == "text/plain"_L1) - return "public.text"_L1; - return QString(); -} - -QString QMacMimePlainTextFallback::mimeForUti(const QString &uti) const -{ - if (uti == "public.text"_L1) - return "text/plain"_L1; - return QString(); -} - -QVariant -QMacMimePlainTextFallback::convertToMime(const QString &mimetype, - const QList &data, const QString &uti) const -{ - if (data.count() > 1) - qWarning("QMacMimePlainTextFallback: Cannot handle multiple member data"); - - if (uti == "public.text"_L1) { - // Note that public.text is documented by Apple to have an undefined encoding. From - // testing it seems that utf8 is normally used, at least by Safari on iOS. - const QByteArray &firstData = data.first(); - return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault, - reinterpret_cast(firstData.constData()), - firstData.size(), kCFStringEncodingUTF8, false))); - } else { - qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); - } - return QVariant(); -} - -QList -QMacMimePlainTextFallback::convertFromMime(const QString &, const QVariant &data, - const QString &uti) const -{ - QList ret; - QString string = data.toString(); - if (uti == "public.text"_L1) - ret.append(string.toUtf8()); - return ret; -} - -class QMacMimeUnicodeText : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeUnicodeText::utiForMime(const QString &mime) const -{ - if (mime == "text/plain"_L1) - return "public.utf16-plain-text"_L1; - if (qsizetype i = mime.indexOf("charset="_L1); i >= 0) { - QString cs(mime.mid(i + 8).toLower()); - i = cs.indexOf(u';'); - if (i >= 0) - cs = cs.left(i); - if (cs == "system"_L1) - return "public.utf8-plain-text"_L1; - else if (cs == "iso-10646-ucs-2"_L1 || cs == "utf16"_L1) - return "public.utf16-plain-text"_L1; - } - return QString(); -} - -QString QMacMimeUnicodeText::mimeForUti(const QString &uti) const -{ - if (uti == "public.utf16-plain-text"_L1 || uti == "public.utf8-plain-text"_L1) - return "text/plain"_L1; - return QString(); -} - -QVariant -QMacMimeUnicodeText::convertToMime(const QString &mimetype, - const QList &data, const QString &uti) const -{ - if (data.count() > 1) - qWarning("QMacMimeUnicodeText: Cannot handle multiple member data"); - const QByteArray &firstData = data.first(); - // I can only handle two types (system and unicode) so deal with them that way - QVariant ret; - if (uti == "public.utf8-plain-text"_L1) { - ret = QString::fromUtf8(firstData); - } else if (uti == "public.utf16-plain-text"_L1) { - QString str = QStringDecoder(QStringDecoder::Utf16)(firstData); - ret = str; - } else { - qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); - } - return ret; -} - -QList -QMacMimeUnicodeText::convertFromMime(const QString &, const QVariant &data, - const QString &uti) const -{ - QList ret; - QString string = data.toString(); - if (uti == "public.utf8-plain-text"_L1) - ret.append(string.toUtf8()); - else if (uti == "public.utf16-plain-text"_L1) { - QStringEncoder::Flags f; -#if defined(Q_OS_MACOS) - // Some applications such as Microsoft Excel, don't deal well with - // a BOM present, so we follow the traditional approach of Qt on - // macOS to not generate public.utf16-plain-text with a BOM. - f = QStringEncoder::Flag::Default; -#else - // Whereas iOS applications will fail to paste if we do _not_ - // include a BOM in the public.utf16-plain-text content, most - // likely due to converting the data using NSUTF16StringEncoding - // which assumes big-endian byte order if there is no BOM. - f = QStringEncoder::Flag::WriteBom; -#endif - QStringEncoder encoder(QStringEncoder::Utf16, f); - ret.append(encoder(string)); - } - return ret; -} - -class QMacMimeHTMLText : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeHTMLText::utiForMime(const QString &mime) const -{ - if (mime == "text/html"_L1) - return "public.html"_L1; - return QString(); -} - -QString QMacMimeHTMLText::mimeForUti(const QString &uti) const -{ - if (uti == "public.html"_L1) - return "text/html"_L1; - return QString(); -} - -QVariant -QMacMimeHTMLText::convertToMime(const QString &mimeType, - const QList &data, const QString &uti) const -{ - if (!canConvert(mimeType, uti)) - return QVariant(); - if (data.count() > 1) - qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); - return data.first(); -} - -QList -QMacMimeHTMLText::convertFromMime(const QString &mime, - const QVariant &data, const QString &uti) const -{ - QList ret; - if (!canConvert(mime, uti)) - return ret; - ret.append(data.toByteArray()); - return ret; -} - -class QMacMimeRtfText : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeRtfText::utiForMime(const QString &mime) const -{ - if (mime == "text/html"_L1) - return "public.rtf"_L1; - return QString(); -} - -QString QMacMimeRtfText::mimeForUti(const QString &uti) const -{ - if (uti == "public.rtf"_L1) - return "text/html"_L1; - return QString(); -} - -QVariant -QMacMimeRtfText::convertToMime(const QString &mimeType, - const QList &data, const QString &uti) const -{ - if (!canConvert(mimeType, uti)) - return QVariant(); - if (data.count() > 1) - qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); - - // Read RTF into to NSAttributedString, then convert the string to HTML - NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData() - options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} - documentAttributes:nil - error:nil]; - - NSError *error; - NSRange range = NSMakeRange(0, [string length]); - NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; - NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error]; - return QByteArray::fromNSData(htmlData); -} - -QList -QMacMimeRtfText::convertFromMime(const QString &mime, - const QVariant &data, const QString &uti) const -{ - QList ret; - if (!canConvert(mime, uti)) - return ret; - - NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData() - options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} - documentAttributes:nil - error:nil]; - - NSError *error; - NSRange range = NSMakeRange(0, [string length]); - NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}; - NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error]; - ret << QByteArray::fromNSData(rtfData); - return ret; -} - -class QMacMimeFileUri : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; - int count(const QMimeData *mimeData) const override; -}; - -QString QMacMimeFileUri::utiForMime(const QString &mime) const -{ - if (mime == "text/uri-list"_L1) - return "public.file-url"_L1; - return QString(); -} - -QString QMacMimeFileUri::mimeForUti(const QString &uti) const -{ - if (uti == "public.file-url"_L1) - return "text/uri-list"_L1; - return QString(); -} - -QVariant -QMacMimeFileUri::convertToMime(const QString &mime, - const QList &data, const QString &uti) const -{ - if (!canConvert(mime, uti)) - return QVariant(); - QList ret; - for (int i = 0; i < data.size(); ++i) { - const QByteArray &a = data.at(i); - NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size() - encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; - NSURL *nsurl = [NSURL URLWithString:urlString]; - QUrl url; - // OS X 10.10 sends file references instead of file paths - if ([nsurl isFileReferenceURL]) { - url = QUrl::fromNSURL([nsurl filePathURL]); - } else { - url = QUrl::fromNSURL(nsurl); - } - - if (url.host().toLower() == "localhost"_L1) - url.setHost(QString()); - - url.setPath(url.path().normalized(QString::NormalizationForm_C)); - ret.append(url); - } - return QVariant(ret); -} - -QList -QMacMimeFileUri::convertFromMime(const QString &mime, - const QVariant &data, const QString &uti) const -{ - QList ret; - if (!canConvert(mime, uti)) - return ret; - QList urls = data.toList(); - for (int i = 0; i < urls.size(); ++i) { - QUrl url = urls.at(i).toUrl(); - if (url.scheme().isEmpty()) - url.setScheme("file"_L1); - if (url.scheme() == "file"_L1) { - if (url.host().isEmpty()) - url.setHost("localhost"_L1); - url.setPath(url.path().normalized(QString::NormalizationForm_D)); - } - if (url.isLocalFile()) - ret.append(url.toEncoded()); - } - return ret; -} - -int QMacMimeFileUri::count(const QMimeData *mimeData) const -{ - return mimeData->urls().count(); -} - -class QMacMimeUrl : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeUrl::utiForMime(const QString &mime) const -{ - if (mime.startsWith("text/uri-list"_L1)) - return "public.url"_L1; - return QString(); -} - -QString QMacMimeUrl::mimeForUti(const QString &uti) const -{ - if (uti == "public.url"_L1) - return "text/uri-list"_L1; - return QString(); -} - -QVariant QMacMimeUrl::convertToMime(const QString &mime, - const QList &data, const QString &uti) const -{ - if (!canConvert(mime, uti)) - return QVariant(); - - QList ret; - for (int i=0; i QMacMimeUrl::convertFromMime(const QString &mime, - const QVariant &data, const QString &uti) const -{ - QList ret; - if (!canConvert(mime, uti)) - return ret; - - QList urls = data.toList(); - for (int i=0; i &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeVCard::utiForMime(const QString &mime) const -{ - if (mime.startsWith("text/vcard"_L1)) - return "public.vcard"_L1; - return QString(); -} - -QString QMacMimeVCard::mimeForUti(const QString &uti) const -{ - if (uti == "public.vcard"_L1) - return "text/vcard"_L1; - return QString(); -} - -QVariant QMacMimeVCard::convertToMime(const QString &mime, - const QList &data, const QString &uti) const -{ - if (!canConvert(mime, uti)) - return QVariant(); - QByteArray cards; - if (uti == "public.vcard"_L1) { - for (int i=0; i QMacMimeVCard::convertFromMime(const QString &mime, - const QVariant &data, const QString &uti) const -{ - QList ret; - if (!canConvert(mime, uti)) - return ret; - - if (mime == "text/vcard"_L1) - ret.append(data.toString().toUtf8()); - return ret; -} - -extern QImage qt_mac_toQImage(CGImageRef image); -extern CGImageRef qt_mac_toCGImage(const QImage &qImage); - -class QMacMimeTiff : public QMacMime -{ -public: - QString utiForMime(const QString &mime) const override; - QString mimeForUti(const QString &uti) const override; - QVariant convertToMime(const QString &mime, const QList &data, - const QString &uti) const override; - QList convertFromMime(const QString &mime, const QVariant &data, - const QString &uti) const override; -}; - -QString QMacMimeTiff::utiForMime(const QString &mime) const -{ - if (mime.startsWith("application/x-qt-image"_L1)) - return "public.tiff"_L1; - return QString(); -} - -QString QMacMimeTiff::mimeForUti(const QString &uti) const -{ - if (uti == "public.tiff"_L1) - return "application/x-qt-image"_L1; - return QString(); -} - -QVariant QMacMimeTiff::convertToMime(const QString &mime, - const QList &data, const QString &uti) const -{ - if (data.count() > 1) - qWarning("QMacMimeTiff: Cannot handle multiple member data"); - - if (!canConvert(mime, uti)) - return QVariant(); - - QCFType tiffData = data.first().toRawCFData(); - QCFType imageSource = CGImageSourceCreateWithData(tiffData, 0); - - if (QCFType image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0)) - return QVariant(qt_mac_toQImage(image)); - - return QVariant(); -} - -QList QMacMimeTiff::convertFromMime(const QString &mime, - const QVariant &variant, const QString &uti) const -{ - if (!canConvert(mime, uti)) - return QList(); - - QCFType data = CFDataCreateMutable(0, 0); - QCFType imageDestination = CGImageDestinationCreateWithData(data, - kUTTypeTIFF, 1, 0); - - if (!imageDestination) - return QList(); - - QImage img = qvariant_cast(variant); - NSDictionary *props = @{ - static_cast(kCGImagePropertyPixelWidth): @(img.width()), - static_cast(kCGImagePropertyPixelHeight): @(img.height()) - }; - - CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), - static_cast(props)); - CGImageDestinationFinalize(imageDestination); - - return QList() << QByteArray::fromCFData(data); -} - -namespace QMacMimeRegistry { - -void registerBuiltInTypes() -{ - // Create QMacMimeAny first to put it at the end of globalMimeList - // with lowest priority. (the constructor prepends to the list) - new QMacMimeAny; - - //standard types that we wrap - new QMacMimeTiff; - new QMacMimePlainTextFallback; - new QMacMimeUnicodeText; - new QMacMimeRtfText; - new QMacMimeHTMLText; - new QMacMimeFileUri; - new QMacMimeUrl; - new QMacMimeTypeName; - new QMacMimeVCard; -} - -} - -QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/qmacmime_p.h b/src/gui/platform/darwin/qmacmime_p.h deleted file mode 100644 index 2b03528836..0000000000 --- a/src/gui/platform/darwin/qmacmime_p.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef QMACMIME_H -#define QMACMIME_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - - -#include - -#include - -QT_BEGIN_NAMESPACE - -class Q_GUI_EXPORT QMacMime -{ -public: - enum class HandlerScope : uchar - { - DnD = 0x01, - Clipboard = 0x02, - Qt_compatible = 0x04, - Qt3_compatible = 0x08, - All = DnD|Clipboard, - AllCompatible = All|Qt_compatible - }; - - QMacMime(); - explicit QMacMime(HandlerScope scope); // internal - virtual ~QMacMime(); - - HandlerScope scope() const { return m_scope; } - bool canConvert(const QString &mime, const QString &uti) const { return mimeForUti(uti) == mime; } - - // for converting from Qt - virtual QList convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const = 0; - virtual QString utiForMime(const QString &mime) const = 0; - - // for converting to Qt - virtual QString mimeForUti(const QString &uti) const = 0; - virtual QVariant convertToMime(const QString &mime, const QList &data, const QString &uti) const = 0; - virtual int count(const QMimeData *mimeData) const; - -private: - const HandlerScope m_scope; -}; - -QT_END_NAMESPACE - -#endif - diff --git a/src/gui/platform/darwin/qmacmimeregistry.mm b/src/gui/platform/darwin/qmacmimeregistry.mm index c08172abcc..abf013248f 100644 --- a/src/gui/platform/darwin/qmacmimeregistry.mm +++ b/src/gui/platform/darwin/qmacmimeregistry.mm @@ -3,7 +3,7 @@ #include -#include "qmacmime_p.h" +#include "qutimimeconverter.h" #include "qmacmimeregistry_p.h" #include "qguiapplication.h" #include "private/qcore_mac_p.h" @@ -14,25 +14,25 @@ using namespace Qt::StringLiterals; namespace QMacMimeRegistry { -typedef QList MimeList; +typedef QList MimeList; Q_GLOBAL_STATIC(MimeList, globalMimeList) Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList) -// implemented in qmacmime.mm +// implemented in qutimimeconverter.mm void registerBuiltInTypes(); /*! \fn void qRegisterDraggedTypes(const QStringList &types) - \relates QMacMime + \relates QUtiMimeConverter Registers the given \a types as custom pasteboard types. This function should be called to enable the Drag and Drop events for custom pasteboard types on Cocoa implementations. This is required - in addition to a QMacMime subclass implementation. By default + in addition to a QUtiMimeConverter subclass implementation. By default drag and drop is enabled for all standard pasteboard types. - \sa QMacMime + \sa QUtiMimeConverter */ void registerDraggedTypes(const QStringList &types) @@ -80,7 +80,7 @@ void destroyMimeTypes() /* Returns a MIME type of for scope \a scope for \a uti, or \nullptr if none exists. */ -QString flavorToMime(QMacMime::HandlerScope scope, const QString &uti) +QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &uti) { MimeList *mimes = globalMimeList(); for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { @@ -98,7 +98,7 @@ QString flavorToMime(QMacMime::HandlerScope scope, const QString &uti) return QString(); } -void registerMimeConverter(QMacMime *macMime) +void registerMimeConverter(QUtiMimeConverter *macMime) { // globalMimeList is in decreasing priority order. Recently added // converters take prioity over previously added converters: prepend @@ -106,7 +106,7 @@ void registerMimeConverter(QMacMime *macMime) globalMimeList()->prepend(macMime); } -void unregisterMimeConverter(QMacMime *macMime) +void unregisterMimeConverter(QUtiMimeConverter *macMime) { if (!QGuiApplication::closingDown()) globalMimeList()->removeAll(macMime); @@ -114,9 +114,9 @@ void unregisterMimeConverter(QMacMime *macMime) /* - Returns a list of all currently defined QMacMime objects for scope \a scope. + Returns a list of all currently defined QUtiMimeConverter objects for scope \a scope. */ -QList all(QMacMime::HandlerScope scope) +QList all(QUtiMimeConverter::HandlerScope scope) { MimeList ret; MimeList *mimes = globalMimeList(); diff --git a/src/gui/platform/darwin/qmacmimeregistry_p.h b/src/gui/platform/darwin/qmacmimeregistry_p.h index fb35bd8304..5928b81959 100644 --- a/src/gui/platform/darwin/qmacmimeregistry_p.h +++ b/src/gui/platform/darwin/qmacmimeregistry_p.h @@ -17,7 +17,7 @@ #include -#include +#include #include @@ -27,11 +27,11 @@ namespace QMacMimeRegistry { Q_GUI_EXPORT void initializeMimeTypes(); Q_GUI_EXPORT void destroyMimeTypes(); - Q_GUI_EXPORT void registerMimeConverter(QMacMime *); - Q_GUI_EXPORT void unregisterMimeConverter(QMacMime *); + Q_GUI_EXPORT void registerMimeConverter(QUtiMimeConverter *); + Q_GUI_EXPORT void unregisterMimeConverter(QUtiMimeConverter *); - Q_GUI_EXPORT QList all(QMacMime::HandlerScope scope); - Q_GUI_EXPORT QString flavorToMime(QMacMime::HandlerScope scope, const QString &flav); + Q_GUI_EXPORT QList all(QUtiMimeConverter::HandlerScope scope); + Q_GUI_EXPORT QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &flav); Q_GUI_EXPORT void registerDraggedTypes(const QStringList &types); Q_GUI_EXPORT const QStringList& enabledDraggedTypes(); diff --git a/src/gui/platform/darwin/qutimimeconverter.h b/src/gui/platform/darwin/qutimimeconverter.h new file mode 100644 index 0000000000..f3de4c0663 --- /dev/null +++ b/src/gui/platform/darwin/qutimimeconverter.h @@ -0,0 +1,54 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QUTIMIMECONVERTER_H +#define QUTIMIMECONVERTER_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QByteArray; +class QString; +class QVariant; +class QMimeData; + +class Q_GUI_EXPORT QUtiMimeConverter +{ +public: + enum class HandlerScope : uchar + { + DnD = 0x01, + Clipboard = 0x02, + Qt_compatible = 0x04, + Qt3_compatible = 0x08, + All = DnD|Clipboard, + AllCompatible = All|Qt_compatible + }; + + QUtiMimeConverter(); + explicit QUtiMimeConverter(HandlerScope scope); // internal + virtual ~QUtiMimeConverter(); + + HandlerScope scope() const { return m_scope; } + bool canConvert(const QString &mime, const QString &uti) const { return mimeForUti(uti) == mime; } + + // for converting from Qt + virtual QList convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const = 0; + virtual QString utiForMime(const QString &mime) const = 0; + + // for converting to Qt + virtual QString mimeForUti(const QString &uti) const = 0; + virtual QVariant convertToMime(const QString &mime, const QList &data, const QString &uti) const = 0; + virtual int count(const QMimeData *mimeData) const; + +private: + const HandlerScope m_scope; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/gui/platform/darwin/qutimimeconverter.mm b/src/gui/platform/darwin/qutimimeconverter.mm new file mode 100644 index 0000000000..8f8e348c0d --- /dev/null +++ b/src/gui/platform/darwin/qutimimeconverter.mm @@ -0,0 +1,806 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include +#include + +#include +#include +#include +#include +#include + +#if defined(Q_OS_MACOS) +#import +#else +#include +#endif + +#if defined(QT_PLATFORM_UIKIT) +#import +#endif + +#include "qutimimeconverter.h" +#include "qmacmimeregistry_p.h" +#include "qguiapplication.h" +#include "private/qcore_mac_p.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_MIME_MAPS + +/*! + \class QUtiMimeConverter + \internal + \brief The QUtiMimeConverter class converts between a MIME type and a + \l{https://developer.apple.com/documentation/uniformtypeidentifiers} + {Uniform Type Identifier (UTI)} format. + \since 4.2 + + \ingroup draganddrop + \inmodule QtGui + + Qt's drag and drop and clipboard facilities use the MIME + standard. On X11, this maps trivially to the Xdnd protocol. On + Mac, although some applications use MIME to describe clipboard + contents, it is more common to use Apple's UTI format. + + QUtiMimeConverter's role is to bridge the gap between MIME and UTI; + By subclasses this class, one can extend Qt's drag and drop + and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. + + A subclass of QUtiMimeConverter will automatically be registered, and active, upon instantiation. + + Qt has predefined support for the following UTIs: + \list + \li public.utf8-plain-text - converts to "text/plain" + \li public.utf16-plain-text - converts to "text/plain" + \li public.text - converts to "text/plain" + \li public.html - converts to "text/html" + \li public.url - converts to "text/uri-list" + \li public.file-url - converts to "text/uri-list" + \li public.tiff - converts to "application/x-qt-image" + \li public.vcard - converts to "text/plain" + \li com.apple.traditional-mac-plain-text - converts to "text/plain" + \li com.apple.pict - converts to "application/x-qt-image" + \endlist + + When working with MIME data, Qt will iterate through all instances of QUtiMimeConverter to find + find an instance that can convert to, or from, a specific MIME type. It will do this by calling + mimeForUti() or utiForMime() on each instance, starting with (and choosing) the last created + instance first. The actual conversions will be done by using convertToMime() and convertFromMime(). +*/ + +/*! + \enum QUtiMimeConverter::HandlerScope + \internal +*/ + +/*! + Constructs a new conversion object of type \a scope, adding it to the + globally accessed list of available converters. +*/ +QUtiMimeConverter::QUtiMimeConverter(HandlerScope scope) + : m_scope(scope) +{ + QMacMimeRegistry::registerMimeConverter(this); +} + +/*! + Constructs a new conversion object and adds it to the + globally accessed list of available converters. +*/ +QUtiMimeConverter::QUtiMimeConverter() + : QUtiMimeConverter(HandlerScope::All) +{ +} + +/*! + Destroys a conversion object, removing it from the global + list of available converters. +*/ +QUtiMimeConverter::~QUtiMimeConverter() +{ + QMacMimeRegistry::unregisterMimeConverter(this); +} + +/*! + Returns the item count for the given \a mimeData +*/ +int QUtiMimeConverter::count(const QMimeData *mimeData) const +{ + Q_UNUSED(mimeData); + return 1; +} + +/*! + \fn bool QUtiMimeConverter::canConvert(const QString &mime, const QString &uti) const + + Returns \c true if the converter can convert (both ways) between + \a mime and \a uti; otherwise returns \c false. +*/ + +/*! + \fn QString QUtiMimeConverter::mimeForUti(QString uti) + + Returns the MIME type used for Mac UTI \a uti, or an empty string if + this converter does not support converting from \a uti. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QUtiMimeConverter::utiForMime(const QString &mime) + + Returns the Mac UTI used for MIME type \a mime, or an empty string if + this converter does not support converting from \a mime. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QUtiMimeConverter::convertToMime(const QString &mime, + const QList &data, const QString &uti) + + Returns \a data converted from Mac UTI \a uti to MIME type \a mime. + + Note that Mac UTIs must all be self-terminating. The input \a data + may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QList QUtiMimeConverter::convertFromMime(const QString &mime, + const QVariant &data, const QString & uti) + + Returns \a data converted from MIME type \a mime to Mac UTI \a uti. + + Note that Mac UTIs must all be self-terminating. The return + value may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + + +class QMacMimeAny : public QUtiMimeConverter { +public: + QMacMimeAny() : QUtiMimeConverter(HandlerScope::AllCompatible) {} + + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeAny::utiForMime(const QString &mime) const +{ + // do not handle the mime type name in the drag pasteboard + if (mime == "application/x-qt-mime-type-name"_L1) + return QString(); + QString ret = "com.trolltech.anymime."_L1 + mime; + return ret.replace(u'/', "--"_L1); +} + +QString QMacMimeAny::mimeForUti(const QString &uti) const +{ + const QString any_prefix = "com.trolltech.anymime."_L1; + if (uti.size() > any_prefix.length() && uti.startsWith(any_prefix)) + return uti.mid(any_prefix.length()).replace("--"_L1, "/"_L1); + return QString(); +} + +QVariant QMacMimeAny::convertToMime(const QString &mime, const QList &data, + const QString &) const +{ + if (data.count() > 1) + qWarning("QMacMimeAny: Cannot handle multiple member data"); + QVariant ret; + if (mime == "text/plain"_L1) + ret = QString::fromUtf8(data.first()); + else + ret = data.first(); + return ret; +} + +QList QMacMimeAny::convertFromMime(const QString &mime, const QVariant &data, + const QString &) const +{ + QList ret; + if (mime == "text/plain"_L1) + ret.append(data.toString().toUtf8()); + else + ret.append(data.toByteArray()); + return ret; +} + +class QMacMimeTypeName : public QUtiMimeConverter { +private: + +public: + QMacMimeTypeName(): QUtiMimeConverter(HandlerScope::AllCompatible) {} + + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const override; +}; + +QString QMacMimeTypeName::utiForMime(const QString &mime) const +{ + if (mime == "application/x-qt-mime-type-name"_L1) + return u"com.trolltech.qt.MimeTypeName"_s; + return QString(); +} + +QString QMacMimeTypeName::mimeForUti(const QString &) const +{ + return QString(); +} + +QVariant QMacMimeTypeName::convertToMime(const QString &, const QList &, const QString &) const +{ + QVariant ret; + return ret; +} + +QList QMacMimeTypeName::convertFromMime(const QString &, const QVariant &, const QString &) const +{ + QList ret; + ret.append(QString("x-qt-mime-type-name"_L1).toUtf8()); + return ret; +} + +class QMacMimePlainTextFallback : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimePlainTextFallback::utiForMime(const QString &mime) const +{ + if (mime == "text/plain"_L1) + return "public.text"_L1; + return QString(); +} + +QString QMacMimePlainTextFallback::mimeForUti(const QString &uti) const +{ + if (uti == "public.text"_L1) + return "text/plain"_L1; + return QString(); +} + +QVariant +QMacMimePlainTextFallback::convertToMime(const QString &mimetype, + const QList &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimePlainTextFallback: Cannot handle multiple member data"); + + if (uti == "public.text"_L1) { + // Note that public.text is documented by Apple to have an undefined encoding. From + // testing it seems that utf8 is normally used, at least by Safari on iOS. + const QByteArray &firstData = data.first(); + return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(firstData.constData()), + firstData.size(), kCFStringEncodingUTF8, false))); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return QVariant(); +} + +QList +QMacMimePlainTextFallback::convertFromMime(const QString &, const QVariant &data, + const QString &uti) const +{ + QList ret; + QString string = data.toString(); + if (uti == "public.text"_L1) + ret.append(string.toUtf8()); + return ret; +} + +class QMacMimeUnicodeText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeUnicodeText::utiForMime(const QString &mime) const +{ + if (mime == "text/plain"_L1) + return "public.utf16-plain-text"_L1; + if (qsizetype i = mime.indexOf("charset="_L1); i >= 0) { + QString cs(mime.mid(i + 8).toLower()); + i = cs.indexOf(u';'); + if (i >= 0) + cs = cs.left(i); + if (cs == "system"_L1) + return "public.utf8-plain-text"_L1; + else if (cs == "iso-10646-ucs-2"_L1 || cs == "utf16"_L1) + return "public.utf16-plain-text"_L1; + } + return QString(); +} + +QString QMacMimeUnicodeText::mimeForUti(const QString &uti) const +{ + if (uti == "public.utf16-plain-text"_L1 || uti == "public.utf8-plain-text"_L1) + return "text/plain"_L1; + return QString(); +} + +QVariant +QMacMimeUnicodeText::convertToMime(const QString &mimetype, + const QList &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimeUnicodeText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + // I can only handle two types (system and unicode) so deal with them that way + QVariant ret; + if (uti == "public.utf8-plain-text"_L1) { + ret = QString::fromUtf8(firstData); + } else if (uti == "public.utf16-plain-text"_L1) { + QString str = QStringDecoder(QStringDecoder::Utf16)(firstData); + ret = str; + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList +QMacMimeUnicodeText::convertFromMime(const QString &, const QVariant &data, + const QString &uti) const +{ + QList ret; + QString string = data.toString(); + if (uti == "public.utf8-plain-text"_L1) + ret.append(string.toUtf8()); + else if (uti == "public.utf16-plain-text"_L1) { + QStringEncoder::Flags f; +#if defined(Q_OS_MACOS) + // Some applications such as Microsoft Excel, don't deal well with + // a BOM present, so we follow the traditional approach of Qt on + // macOS to not generate public.utf16-plain-text with a BOM. + f = QStringEncoder::Flag::Default; +#else + // Whereas iOS applications will fail to paste if we do _not_ + // include a BOM in the public.utf16-plain-text content, most + // likely due to converting the data using NSUTF16StringEncoding + // which assumes big-endian byte order if there is no BOM. + f = QStringEncoder::Flag::WriteBom; +#endif + QStringEncoder encoder(QStringEncoder::Utf16, f); + ret.append(encoder(string)); + } + return ret; +} + +class QMacMimeHTMLText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeHTMLText::utiForMime(const QString &mime) const +{ + if (mime == "text/html"_L1) + return "public.html"_L1; + return QString(); +} + +QString QMacMimeHTMLText::mimeForUti(const QString &uti) const +{ + if (uti == "public.html"_L1) + return "text/html"_L1; + return QString(); +} + +QVariant +QMacMimeHTMLText::convertToMime(const QString &mimeType, + const QList &data, const QString &uti) const +{ + if (!canConvert(mimeType, uti)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); + return data.first(); +} + +QList +QMacMimeHTMLText::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList ret; + if (!canConvert(mime, uti)) + return ret; + ret.append(data.toByteArray()); + return ret; +} + +class QMacMimeRtfText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeRtfText::utiForMime(const QString &mime) const +{ + if (mime == "text/html"_L1) + return "public.rtf"_L1; + return QString(); +} + +QString QMacMimeRtfText::mimeForUti(const QString &uti) const +{ + if (uti == "public.rtf"_L1) + return "text/html"_L1; + return QString(); +} + +QVariant +QMacMimeRtfText::convertToMime(const QString &mimeType, + const QList &data, const QString &uti) const +{ + if (!canConvert(mimeType, uti)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); + + // Read RTF into to NSAttributedString, then convert the string to HTML + NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData() + options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} + documentAttributes:nil + error:nil]; + + NSError *error; + NSRange range = NSMakeRange(0, [string length]); + NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; + NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error]; + return QByteArray::fromNSData(htmlData); +} + +QList +QMacMimeRtfText::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList ret; + if (!canConvert(mime, uti)) + return ret; + + NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData() + options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} + documentAttributes:nil + error:nil]; + + NSError *error; + NSRange range = NSMakeRange(0, [string length]); + NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}; + NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error]; + ret << QByteArray::fromNSData(rtfData); + return ret; +} + +class QMacMimeFileUri : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; + int count(const QMimeData *mimeData) const override; +}; + +QString QMacMimeFileUri::utiForMime(const QString &mime) const +{ + if (mime == "text/uri-list"_L1) + return "public.file-url"_L1; + return QString(); +} + +QString QMacMimeFileUri::mimeForUti(const QString &uti) const +{ + if (uti == "public.file-url"_L1) + return "text/uri-list"_L1; + return QString(); +} + +QVariant +QMacMimeFileUri::convertToMime(const QString &mime, + const QList &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + QList ret; + for (int i = 0; i < data.size(); ++i) { + const QByteArray &a = data.at(i); + NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size() + encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; + NSURL *nsurl = [NSURL URLWithString:urlString]; + QUrl url; + // OS X 10.10 sends file references instead of file paths + if ([nsurl isFileReferenceURL]) { + url = QUrl::fromNSURL([nsurl filePathURL]); + } else { + url = QUrl::fromNSURL(nsurl); + } + + if (url.host().toLower() == "localhost"_L1) + url.setHost(QString()); + + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList +QMacMimeFileUri::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList ret; + if (!canConvert(mime, uti)) + return ret; + QList urls = data.toList(); + for (int i = 0; i < urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme("file"_L1); + if (url.scheme() == "file"_L1) { + if (url.host().isEmpty()) + url.setHost("localhost"_L1); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + if (url.isLocalFile()) + ret.append(url.toEncoded()); + } + return ret; +} + +int QMacMimeFileUri::count(const QMimeData *mimeData) const +{ + return mimeData->urls().count(); +} + +class QMacMimeUrl : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeUrl::utiForMime(const QString &mime) const +{ + if (mime.startsWith("text/uri-list"_L1)) + return "public.url"_L1; + return QString(); +} + +QString QMacMimeUrl::mimeForUti(const QString &uti) const +{ + if (uti == "public.url"_L1) + return "text/uri-list"_L1; + return QString(); +} + +QVariant QMacMimeUrl::convertToMime(const QString &mime, + const QList &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + + QList ret; + for (int i=0; i QMacMimeUrl::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList ret; + if (!canConvert(mime, uti)) + return ret; + + QList urls = data.toList(); + for (int i=0; i &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeVCard::utiForMime(const QString &mime) const +{ + if (mime.startsWith("text/vcard"_L1)) + return "public.vcard"_L1; + return QString(); +} + +QString QMacMimeVCard::mimeForUti(const QString &uti) const +{ + if (uti == "public.vcard"_L1) + return "text/vcard"_L1; + return QString(); +} + +QVariant QMacMimeVCard::convertToMime(const QString &mime, + const QList &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + QByteArray cards; + if (uti == "public.vcard"_L1) { + for (int i=0; i QMacMimeVCard::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList ret; + if (!canConvert(mime, uti)) + return ret; + + if (mime == "text/vcard"_L1) + ret.append(data.toString().toUtf8()); + return ret; +} + +extern QImage qt_mac_toQImage(CGImageRef image); +extern CGImageRef qt_mac_toCGImage(const QImage &qImage); + +class QMacMimeTiff : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList &data, + const QString &uti) const override; + QList convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeTiff::utiForMime(const QString &mime) const +{ + if (mime.startsWith("application/x-qt-image"_L1)) + return "public.tiff"_L1; + return QString(); +} + +QString QMacMimeTiff::mimeForUti(const QString &uti) const +{ + if (uti == "public.tiff"_L1) + return "application/x-qt-image"_L1; + return QString(); +} + +QVariant QMacMimeTiff::convertToMime(const QString &mime, + const QList &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimeTiff: Cannot handle multiple member data"); + + if (!canConvert(mime, uti)) + return QVariant(); + + QCFType tiffData = data.first().toRawCFData(); + QCFType imageSource = CGImageSourceCreateWithData(tiffData, 0); + + if (QCFType image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0)) + return QVariant(qt_mac_toQImage(image)); + + return QVariant(); +} + +QList QMacMimeTiff::convertFromMime(const QString &mime, + const QVariant &variant, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QList(); + + QCFType data = CFDataCreateMutable(0, 0); + QCFType imageDestination = CGImageDestinationCreateWithData(data, + kUTTypeTIFF, 1, 0); + + if (!imageDestination) + return QList(); + + QImage img = qvariant_cast(variant); + NSDictionary *props = @{ + static_cast(kCGImagePropertyPixelWidth): @(img.width()), + static_cast(kCGImagePropertyPixelHeight): @(img.height()) + }; + + CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), + static_cast(props)); + CGImageDestinationFinalize(imageDestination); + + return QList() << QByteArray::fromCFData(data); +} + +namespace QMacMimeRegistry { + +void registerBuiltInTypes() +{ + // Create QMacMimeAny first to put it at the end of globalMimeList + // with lowest priority. (the constructor prepends to the list) + new QMacMimeAny; + + //standard types that we wrap + new QMacMimeTiff; + new QMacMimePlainTextFallback; + new QMacMimeUnicodeText; + new QMacMimeRtfText; + new QMacMimeHTMLText; + new QMacMimeFileUri; + new QMacMimeUrl; + new QMacMimeTypeName; + new QMacMimeVCard; +} + +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsmime.cpp b/src/gui/platform/windows/qwindowsmime.cpp deleted file mode 100644 index 74e083658d..0000000000 --- a/src/gui/platform/windows/qwindowsmime.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "qwindowsmime_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QNativeInterface::Private; - -/*! - \class QWindowsMime - \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. - \internal - - Qt's drag-and-drop and clipboard facilities use the MIME standard. - On X11, this maps trivially to the Xdnd protocol, but on Windows - although some applications use MIME types to describe clipboard - formats, others use arbitrary non-standardized naming conventions, - or unnamed built-in formats of Windows. - - By instantiating subclasses of QWindowsMime that provide conversions - between Windows Clipboard and MIME formats, you can convert - proprietary clipboard formats to MIME formats. - - Qt has predefined support for the following Windows Clipboard formats: - - \table - \header \li Windows Format \li Equivalent MIME type - \row \li \c CF_UNICODETEXT \li \c text/plain - \row \li \c CF_TEXT \li \c text/plain - \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is - a \l{QImageWriter::supportedImageFormats()}{Qt image format} - \row \li \c CF_HDROP \li \c text/uri-list - \row \li \c CF_INETURL \li \c text/uri-list - \row \li \c CF_HTML \li \c text/html - \endtable - - An example use of this class would be to map the Windows Metafile - clipboard format (\c CF_METAFILEPICT) to and from the MIME type - \c{image/x-wmf}. This conversion might simply be adding or removing - a header, or even just passing on the data. See \l{Drag and Drop} - for more information on choosing and definition MIME types. - - You can check if a MIME type is convertible using canConvertFromMime() and - can perform conversions with convertToMime() and convertFromMime(). - - \sa QWindowsMimeRegistry -*/ - - -/*! - \fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const - \internal - - Returns \c true if the converter can convert from the \a mimeData to - the format specified in \a formatetc. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const - \internal - - Returns \c true if the converter can convert to the \a mimeType from - the available formats in \a pDataObj. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const - \internal - - Returns the mime type that will be created form the format specified - in \a formatetc, or an empty string if this converter does not support - \a formatetc. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QList QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const - \internal - - Returns a QList of FORMATETC structures representing the different windows clipboard - formats that can be provided for the \a mimeType from the \a mimeData. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj, - QMetaType preferredType) const - \internal - - Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. - If possible the QVariant should be of the \a preferredType to avoid needless conversions. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const - \internal - - Convert the \a mimeData to the format specified in \a formatetc. - The converted data should then be placed in \a pmedium structure. - - Return true if the conversion was successful. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - Constructs a QWindowsMime instance. - - The instance is automatically registered, and will be called to convert data during - clipboard or drag'n'drop operations. -*/ -QWindowsMime::QWindowsMime() -{ - auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); - Q_ASSERT(nativeWindowsApp); - nativeWindowsApp->registerMime(this); -} - -/*! - Constructs a QWindowsMime instance. - - The instance is automatically unregistered. -*/ -QWindowsMime::~QWindowsMime() -{ - auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); - Q_ASSERT(nativeWindowsApp); - nativeWindowsApp->unregisterMime(this); -} - -/*! - Registers the MIME type \a mime, and returns an ID number - identifying the format on Windows. - - A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be - registered as the clipboard format for \c WindowsType. -*/ -int QWindowsMime::registerMimeType(const QString &mimeType) -{ - auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); - Q_ASSERT(nativeWindowsApp); - return nativeWindowsApp->registerMimeType(mimeType); -} - -QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsmime_p.h b/src/gui/platform/windows/qwindowsmime_p.h deleted file mode 100644 index f097a618ef..0000000000 --- a/src/gui/platform/windows/qwindowsmime_p.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef QWINDOWSMIME_P_H -#define QWINDOWSMIME_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QMimeData; - -namespace QNativeInterface::Private { - -class Q_GUI_EXPORT QWindowsMime -{ -public: - QWindowsMime(); - virtual ~QWindowsMime(); - - static int registerMimeType(const QString &mimeType); - - // for converting from Qt - virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0; - virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0; - virtual QList formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0; - - // for converting to Qt - virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0; - virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const = 0; - virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; -}; - -} // QNativeInterface::Private - -QT_END_NAMESPACE - -#endif // QWINDOWSMIME_P_H diff --git a/src/gui/platform/windows/qwindowsmimeconverter.cpp b/src/gui/platform/windows/qwindowsmimeconverter.cpp new file mode 100644 index 0000000000..d7998a3eb7 --- /dev/null +++ b/src/gui/platform/windows/qwindowsmimeconverter.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsmimeconverter.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsMimeConverter + \brief The QWindowsMimeConverter class maps open-standard MIME to Window Clipboard formats. + \inmodule QtGui + + Qt's drag-and-drop and clipboard facilities use the MIME standard. + On X11, this maps trivially to the Xdnd protocol, but on Windows + although some applications use MIME types to describe clipboard + formats, others use arbitrary non-standardized naming conventions, + or unnamed built-in formats of Windows. + + By instantiating subclasses of QWindowsMimeConverter that provide + conversions between Windows Clipboard and MIME formats, you can convert + proprietary clipboard formats to MIME formats. + + Qt has predefined support for the following Windows Clipboard formats: + + \table + \header \li Windows Format \li Equivalent MIME type + \row \li \c CF_UNICODETEXT \li \c text/plain + \row \li \c CF_TEXT \li \c text/plain + \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is + a \l{QImageWriter::supportedImageFormats()}{Qt image format} + \row \li \c CF_HDROP \li \c text/uri-list + \row \li \c CF_INETURL \li \c text/uri-list + \row \li \c CF_HTML \li \c text/html + \endtable + + An example use of this class would be to map the Windows Metafile + clipboard format (\c CF_METAFILEPICT) to and from the MIME type + \c{image/x-wmf}. This conversion might simply be adding or removing + a header, or even just passing on the data. See \l{Drag and Drop} + for more information on choosing and definition MIME types. + + You can check if a MIME type is convertible using canConvertFromMime() and + can perform conversions with convertToMime() and convertFromMime(). +*/ + + +/*! + \fn bool QWindowsMimeConverter::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const + + Returns \c true if the converter can convert from the \a mimeData to + the format specified in \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QWindowsMimeConverter::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const + + Returns \c true if the converter can convert to the \a mimeType from + the available formats in \a pDataObj. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QWindowsMimeConverter::mimeForFormat(const FORMATETC &formatetc) const + + Returns the mime type that will be created form the format specified + in \a formatetc, or an empty string if this converter does not support + \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QList QWindowsMimeConverter::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const + + Returns a QList of FORMATETC structures representing the different windows clipboard + formats that can be provided for the \a mimeType from the \a mimeData. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QWindowsMimeConverter::convertToMime(const QString &mimeType, IDataObject *pDataObj, + QMetaType preferredType) const + + Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. + If possible the QVariant should be of the \a preferredType to avoid needless conversions. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QWindowsMimeConverter::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const + + Convert the \a mimeData to the format specified in \a formatetc. + The converted data should then be placed in \a pmedium structure. + + Return true if the conversion was successful. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + Constructs a QWindowsMimeConverter instance. + + The instance is automatically registered, and will be called to convert data during + clipboard or drag'n'drop operations. +*/ +QWindowsMimeConverter::QWindowsMimeConverter() +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + nativeWindowsApp->registerMime(this); +} + +/*! + Constructs a QWindowsMimeConverter instance. + + The instance is automatically unregistered. +*/ +QWindowsMimeConverter::~QWindowsMimeConverter() +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + nativeWindowsApp->unregisterMime(this); +} + +/*! + Registers the MIME type \a mimeType, and returns an ID number + identifying the format on Windows. + + A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be + registered as the clipboard format for \c WindowsType. +*/ +int QWindowsMimeConverter::registerMimeType(const QString &mimeType) +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + return nativeWindowsApp->registerMimeType(mimeType); +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsmimeconverter.h b/src/gui/platform/windows/qwindowsmimeconverter.h new file mode 100644 index 0000000000..c1b80c1fef --- /dev/null +++ b/src/gui/platform/windows/qwindowsmimeconverter.h @@ -0,0 +1,41 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSMIMECONVERTER_P_H +#define QWINDOWSMIMECONVERTER_P_H + +#include + +QT_BEGIN_NAMESPACE + +class QMetaType; +class QMimeData; +class QVariant; +struct tagFORMATETC; +using FORMATETC = tagFORMATETC; +struct tagSTGMEDIUM; +using STGMEDIUM = tagSTGMEDIUM; +struct IDataObject; + +class Q_GUI_EXPORT QWindowsMimeConverter +{ +public: + QWindowsMimeConverter(); + virtual ~QWindowsMimeConverter(); + + static int registerMimeType(const QString &mimeType); + + // for converting from Qt + virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0; + virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0; + virtual QList formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0; + + // for converting to Qt + virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0; + virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const = 0; + virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSMIMECONVERTER_H diff --git a/src/gui/platform/windows/qwindowsnativeinterface.cpp b/src/gui/platform/windows/qwindowsnativeinterface.cpp index fe6f4e96dd..86c6593f5d 100644 --- a/src/gui/platform/windows/qwindowsnativeinterface.cpp +++ b/src/gui/platform/windows/qwindowsnativeinterface.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -218,21 +217,21 @@ QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsScreen); */ /*! - \fn bool QNativeInterface::Private::QWindowsApplication::registerMime(QWindowsMime *mime) + \fn bool QNativeInterface::Private::QWindowsApplication::registerMime(QWindowsMimeConverter *mime) \internal Registers the converter \a mime to the system. - \sa QNativeInterface::Private::QWindowsMime, unregisterMime() + \sa QWindowsMimeConverter, unregisterMime() */ /*! - \fn void QNativeInterface::Private::QWindowsApplication::unregisterMime(QWindowsMime *mime) + \fn void QNativeInterface::Private::QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime) \internal Unregisters the converter \a mime from the system. - \sa QNativeInterface::Private::QWindowsMime, registerMime() + \sa QWindowsMimeConverter, registerMime() */ /*! -- cgit v1.2.3