summaryrefslogtreecommitdiffstats
path: root/src/gui/platform/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/platform/darwin')
-rw-r--r--src/gui/platform/darwin/qappleiconengine.mm464
-rw-r--r--src/gui/platform/darwin/qappleiconengine_p.h64
-rw-r--r--src/gui/platform/darwin/qapplekeymapper.mm163
-rw-r--r--src/gui/platform/darwin/qapplekeymapper_p.h14
-rw-r--r--src/gui/platform/darwin/qmacmime.mm1013
-rw-r--r--src/gui/platform/darwin/qmacmime_p.h63
-rw-r--r--src/gui/platform/darwin/qmacmimeregistry.mm118
-rw-r--r--src/gui/platform/darwin/qmacmimeregistry_p.h42
-rw-r--r--src/gui/platform/darwin/qutimimeconverter.h62
-rw-r--r--src/gui/platform/darwin/qutimimeconverter.mm823
10 files changed, 1689 insertions, 1137 deletions
diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm
new file mode 100644
index 0000000000..7e0ed184dc
--- /dev/null
+++ b/src/gui/platform/darwin/qappleiconengine.mm
@@ -0,0 +1,464 @@
+// Copyright (C) 2023 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 "qappleiconengine_p.h"
+
+#if defined(Q_OS_MACOS)
+# include <AppKit/AppKit.h>
+#elif defined(QT_PLATFORM_UIKIT)
+# include <UIKit/UIKit.h>
+#endif
+
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpalette.h>
+#include <QtGui/qstylehints.h>
+
+#include <QtGui/private/qcoregraphics_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace {
+auto *loadImage(const QString &iconName)
+{
+ static constexpr std::pair<QLatin1StringView, NSString *> iconMap[] = {
+ {"address-book-new"_L1, @"book.closed"},
+ {"application-exit"_L1, @"xmark.circle"},
+ {"appointment-new"_L1, @"calendar.badge.plus"},
+ {"call-start"_L1, @"phone.arrow.up.right"},
+ {"call-stop"_L1, @"phone.down"},
+ {"contact-new"_L1, @"person.crop.circle.badge.plus"},
+ {"document-new"_L1, @"doc.badge.plus"},
+ {"document-open"_L1, @"folder"},
+ {"document-open-recent"_L1, @"doc.badge.clock"},
+ {"document-page-setup"_L1, @"doc.badge.gearshape"},
+ {"document-print"_L1, @"printer"},
+ //{"document-print-preview"_L1, @""},
+ {"document-properties"_L1, @"doc.badge.ellipsis"},
+ //{"document-revert"_L1, @""},
+ {"document-save"_L1, @"square.and.arrow.down"},
+ //{"document-save-as"_L1, @""},
+ {"document-send"_L1, @"paperplane"},
+ {"edit-clear"_L1, @"xmark.circle"},
+ {"edit-copy"_L1, @"doc.on.doc"},
+ {"edit-cut"_L1, @"scissors"},
+ {"edit-delete"_L1, @"delete.left"},
+ {"edit-find"_L1, @"magnifyingglass"},
+ //{"edit-find-replace"_L1, @"arrow.up.left.and.down.right.magnifyingglass"},
+ {"edit-paste"_L1, @"clipboard"},
+ {"edit-redo"_L1, @"arrowshape.turn.up.right"},
+ //{"edit-select-all"_L1, @""},
+ {"edit-undo"_L1, @"arrowshape.turn.up.left"},
+ {"folder-new"_L1, @"folder.badge.plus"},
+ {"format-indent-less"_L1, @"decrease.indent"},
+ {"format-indent-more"_L1, @"increase.indent"},
+ {"format-justify-center"_L1, @"text.aligncenter"},
+ {"format-justify-fill"_L1, @"text.justify"},
+ {"format-justify-left"_L1, @"text.justify.left"},
+ {"format-justify-right"_L1, @"text.justify.right"},
+ {"format-text-direction-ltr"_L1, @"text.justify.leading"},
+ {"format-text-direction-rtl"_L1, @"text.justify.trailing"},
+ {"format-text-bold"_L1, @"bold"},
+ {"format-text-italic"_L1, @"italic"},
+ {"format-text-underline"_L1, @"underline"},
+ {"format-text-strikethrough"_L1, @"strikethrough"},
+ //{"go-bottom"_L1, @""},
+ {"go-down"_L1, @"arrowshape.down"},
+ {"go-first"_L1, @"increase.indent"},
+ {"go-home"_L1, @"house"},
+ //{"go-jump"_L1, @""},
+ //{"go-last"_L1, @""},
+ {"go-next"_L1, @"arrowshape.right"},
+ {"go-previous"_L1, @"arrowshape.left"},
+ //{"go-top"_L1, @""},
+ {"go-up"_L1, @"arrowshape.up"},
+ {"help-about"_L1, @"info.circle"},
+ //{"help-contents"_L1, @""},
+ {"help-faq"_L1, @"questionmark.app"},
+ {"insert-image"_L1, @"photo.badge.plus"},
+ {"insert-link"_L1, @"link.badge.plus"},
+ //{"insert-object"_L1, @""},
+ {"insert-text"_L1, @"textformat"},
+ {"list-add"_L1, @"plus.circle"},
+ {"list-remove"_L1, @"minus.circle"},
+ {"mail-forward"_L1, @"arrowshape.turn.up.right"},
+ {"mail-mark-important"_L1, @"star"},
+ {"mail-mark-junk"_L1, @"xmark.bin"},
+ {"mail-mark-notjunk"_L1, @"trash.slash"},
+ {"mail-mark-read"_L1, @"envelope.open"},
+ {"mail-mark-unread"_L1, @"envelope.fill"},
+ {"mail-message-new"_L1, @"square.and.pencil"},
+ {"mail-reply-all"_L1, @"arrowshape.turn.up.left.2"},
+ {"mail-reply-sender"_L1, @"arrowshape.turn.up.left"},
+ {"mail-send"_L1, @"paperplane"},
+ {"mail-send-receive"_L1, @"envelope.arrow.triangle.branch"},
+ {"media-eject"_L1, @"eject"},
+ {"media-playback-pause"_L1, @"pause"},
+ {"media-playback-start"_L1, @"play"},
+ {"media-playback-stop"_L1, @"stop"},
+ {"media-record"_L1, @"record.circle"},
+ {"media-seek-backward"_L1, @"backward"},
+ {"media-seek-forward"_L1, @"forward"},
+ {"media-skip-backward"_L1, @"backward.end.alt"},
+ {"media-skip-forward"_L1, @"forward.end.alt"},
+ {"object-flip-horizontal"_L1, @"rectangle.landscape.rotate"},
+ {"object-flip-vertical"_L1, @"rectangle.portrait.rotate"},
+ {"object-rotate-left"_L1, @"rotate.left"},
+ {"object-rotate-right"_L1, @"rotate.right"},
+ {"process-stop"_L1, @"stop.circle"},
+ {"system-lock-screen"_L1, @"lock.display"},
+ {"system-log-out"_L1, @"door.left.hand.open"},
+ //{"system-run"_L1, @""},
+ {"system-search"_L1, @"magnifyingglass"},
+ //{"system-reboot"_L1, @""},
+ {"system-shutdown"_L1, @"power"},
+ //{"tools-check-spelling"_L1, @""},
+ {"view-fullscreen"_L1, @"arrow.up.left.and.arrow.down.right"},
+ {"view-refresh"_L1, @"arrow.clockwise"},
+ {"view-restore"_L1, @"arrow.down.right.and.arrow.up.left"},
+ //{"view-sort-ascending"_L1, @""},
+ //{"view-sort-descending"_L1, @""},
+ {"window-close"_L1, @"xmark.circle"},
+ {"window-new"_L1, @"macwindow.badge.plus"},
+ {"zoom-fit-best"_L1, @"square.arrowtriangle.4.outward"},
+ {"zoom-in"_L1, @"plus.magnifyingglass"},
+ //{"zoom-original"_L1, @""},
+ {"zoom-out"_L1, @"minus.magnifyingglass"},
+ {"process-working"_L1, @"circle.dotted"},
+ //{"accessories-calculator"_L1, @""},
+ //{"accessories-character-map"_L1, @""},
+ {"accessories-dictionary"_L1, @"character.book.closed"},
+ {"accessories-text-editor"_L1, @"textformat"},
+ {"help-browser"_L1, @"folder.badge.questionmark"},
+ {"multimedia-volume-control"_L1, @"speaker.wave.3"},
+ {"preferences-desktop-accessibility"_L1, @"accessibility"},
+ //{"preferences-desktop-font"_L1, @""},
+ {"preferences-desktop-keyboard"_L1, @"keyboard.badge.ellipsis"},
+ //{"preferences-desktop-locale"_L1, @""},
+ //{"preferences-desktop-multimedia"_L1, @""},
+ //{"preferences-desktop-screensaver"_L1, @""},
+ //{"preferences-desktop-theme"_L1, @""},
+ //{"preferences-desktop-wallpaper"_L1, @""},
+ {"system-file-manager"_L1, @"folder.badge.gearshape"},
+ //{"system-software-install"_L1, @""},
+ //{"system-software-update"_L1, @""}, d
+ //{"utilities-system-monitor"_L1, @""},
+ {"utilities-terminal"_L1, @"apple.terminal"},
+ //{"applications-accessories"_L1, @""},
+ //{"applications-development"_L1, @""},
+ //{"applications-engineering"_L1, @""},
+ {"applications-games"_L1, @"gamecontroller"},
+ //{"applications-graphics"_L1, @""},
+ {"applications-internet"_L1, @"network"},
+ {"applications-multimedia"_L1, @"tv.and.mediabox"},
+ //{"applications-office"_L1, @""},
+ //{"applications-other"_L1, @""},
+ {"applications-science"_L1, @"atom"},
+ //{"applications-system"_L1, @""},
+ //{"applications-utilities"_L1, @""},
+ {"preferences-desktop"_L1, @"menubar.dock.rectangle"},
+ //{"preferences-desktop-peripherals"_L1, @""},
+ //{"preferences-desktop-personal"_L1, @""},
+ //{"preferences-other"_L1, @""},
+ //{"preferences-system"_L1, @""},
+ {"preferences-system-network"_L1, @"network"},
+ {"system-help"_L1, @"questionmark.diamond"},
+ {"audio-card"_L1, @"waveform.circle"},
+ {"audio-input-microphone"_L1, @"mic"},
+ {"battery"_L1, @"battery.100percent"},
+ {"camera-photo"_L1, @"camera"},
+ {"camera-video"_L1, @"video"},
+ {"camera-web"_L1, @"web.camera"},
+ {"computer"_L1, @"desktopcomputer"},
+ {"drive-harddisk"_L1, @"internaldrive"},
+ {"drive-optical"_L1, @"opticaldiscdrive"},
+ {"drive-removable-media"_L1, @"externaldrive"},
+ {"input-gaming"_L1, @"gamecontroller"}, // "games" also using this one
+ {"input-keyboard"_L1, @"keyboard"},
+ {"input-mouse"_L1, @"computermouse"},
+ {"input-tablet"_L1, @"ipad"},
+ {"media-flash"_L1, @"mediastick"},
+ //{"media-floppy"_L1, @""},
+ //{"media-optical"_L1, @""},
+ {"media-tape"_L1, @"recordingtape"},
+ //{"modem"_L1, @""},
+ {"multimedia-player"_L1, @"play.rectangle"},
+ {"network-wired"_L1, @"app.connected.to.app.below.fill"},
+ {"network-wireless"_L1, @"wifi"},
+ //{"pda"_L1, @""},
+ {"phone"_L1, @"iphone"},
+ {"printer"_L1, @"printer"},
+ {"scanner"_L1, @"scanner"},
+ {"video-display"_L1, @"play.display"},
+ //{"emblem-default"_L1, @""},
+ {"emblem-documents"_L1, @"doc.circle"},
+ {"emblem-downloads"_L1, @"arrow.down.circle"},
+ {"emblem-favorite"_L1, @"star"},
+ {"emblem-important"_L1, @"exclamationmark.bubble.circle"},
+ {"emblem-mail"_L1, @"envelope"},
+ {"emblem-photos"_L1, @"photo.stack"},
+ //{"emblem-readonly"_L1, @""},
+ {"emblem-shared"_L1, @"folder.badge.person.crop"},
+ {"emblem-symbolic-link"_L1, @"link.circle"},
+ {"emblem-synchronized"_L1, @"arrow.triangle.2.circlepath.circle"},
+ {"emblem-system"_L1, @"gear"},
+ //{"emblem-unreadable"_L1, @""},
+ {"folder"_L1, @"folder"},
+ //{"folder-remote"_L1, @""},
+ {"network-server"_L1, @"server.rack"},
+ //{"network-workgroup"_L1, @""},
+ //{"start-here"_L1, @""},
+ {"user-bookmarks"_L1, @"bookmark.circle"},
+ {"user-desktop"_L1, @"desktopcomputer"}, //"computer" also using this one
+ {"user-home"_L1, @"house"}, //"go-home" also using this one
+ {"user-trash"_L1, @"trash"},
+ {"appointment-missed"_L1, @"calendar.badge.exclamationmark"},
+ {"appointment-soon"_L1, @"calendar.badge.clock"},
+ {"audio-volume-high"_L1, @"speaker.wave.3"},
+ {"audio-volume-low"_L1, @"speaker.wave.1"},
+ {"audio-volume-medium"_L1, @"speaker.wave.2"},
+ {"audio-volume-muted"_L1, @"speaker.slash"},
+ {"battery-caution"_L1, @"minus.plus.batteryblock.exclamationmark"},
+ {"battery-low"_L1, @"battery.25percent"}, // there are different levels that can be low battery
+ {"dialog-error"_L1, @"exclamationmark.bubble"},
+ {"dialog-information"_L1, @"info.circle"},
+ {"dialog-password"_L1, @"lock"},
+ {"dialog-question"_L1, @"questionmark.circle"},
+ {"dialog-warning"_L1, @"exclamationmark.octagon"},
+ {"folder-drag-accept"_L1, @"plus.rectangle.on.folder"},
+ //{"folder-open"_L1, @""},
+ {"folder-visiting"_L1, @"folder.circle"},
+ {"image-loading"_L1, @"photo.circle"},
+ {"image-missing"_L1, @"photo"},
+ {"mail-attachment"_L1, @"paperclip"},
+ {"mail-unread"_L1, @"envelope.badge"},
+ {"mail-read"_L1, @"envelope.open"},
+ {"mail-replied"_L1, @"arrowshape.turn.up.left"},
+ //{"mail-signed"_L1, @""},
+ //{"mail-signed-verified"_L1, @""},
+ {"media-playlist-repeat"_L1, @"repet"},
+ {"media-playlist-shuffle"_L1, @"shuffle"},
+ //{"network-error"_L1, @""},
+ //{"network-idle"_L1, @""},
+ {"network-offline"_L1, @"network.slash"},
+ //{"network-receive"_L1, @""},
+ //{"network-transmit"_L1, @""},
+ //{"network-transmit-receive"_L1, @""},
+ //{"printer-error"_L1, @""},
+ {"printer-printing"_L1, @"printer.dotmatrix.filled.and.paper"}, // not sure
+ {"security-high"_L1, @"lock.shield"},
+ //{"security-medium"_L1, @""},
+ {"security-low"_L1, @"lock.trianglebadge.exclamationmark"},
+ {"software-update-available"_L1, @"arrowshape.up.circle"},
+ {"software-update-urgent"_L1, @"exclamationmark.transmission"},
+ {"sync-error"_L1, @"exclamationmark.arrow.triangle.2.circlepath"},
+ {"sync-synchronizing"_L1, @"arrow.triangle.2.circlepath"},
+ {"task-due"_L1, @"clock.badge.exclamationmark"},
+ {"task-past-due"_L1, @"clock.badge.xmark"},
+ {"user-available"_L1, @"person.crop.circle.badge.checkmark"},
+ {"user-away"_L1, @"person.crop.circle.badge.clock"},
+ //{"user-idle"_L1, @""},
+ {"user-offline"_L1, @"person.crop.circle.badge.xmark"},
+ //{"user-trash-full"_L1, @""},
+ {"weather-clear"_L1, @"sun.max"},
+ {"weather-clear-night"_L1, @"moon"},
+ {"weather-few-clouds"_L1, @"cloud.sun"},
+ {"weather-few-clouds-night"_L1, @"cloud.moon"},
+ {"weather-fog"_L1, @"cloud.fog"},
+ {"weather-overcast"_L1, @"cloud"},
+ //{"weather-severe-alert"_L1, @""},
+ {"weather-showers"_L1, @"cloud.rain"},
+ //{"weather-showers-scattered"_L1, @""},
+ {"weather-snow"_L1, @"cloud.snow"},
+ {"weather-storm"_L1, @"tropicalstorm"},
+ };
+ const auto it = std::find_if(std::begin(iconMap), std::end(iconMap), [iconName](const auto &c){
+ return c.first == iconName;
+ });
+ NSString *systemIconName = it != std::end(iconMap) ? it->second : iconName.toNSString();
+#if defined(Q_OS_MACOS)
+ return [NSImage imageWithSystemSymbolName:systemIconName accessibilityDescription:nil];
+#elif defined(QT_PLATFORM_UIKIT)
+ return [UIImage systemImageNamed:systemIconName];
+#endif
+}
+}
+
+QAppleIconEngine::QAppleIconEngine(const QString &iconName)
+ : m_iconName(iconName), m_image(loadImage(iconName))
+{
+ if (m_image)
+ [m_image retain];
+}
+
+QAppleIconEngine::~QAppleIconEngine()
+{
+ if (m_image)
+ [m_image release];
+}
+
+QIconEngine *QAppleIconEngine::clone() const
+{
+ return new QAppleIconEngine(m_iconName);
+}
+
+QString QAppleIconEngine::key() const
+{
+ return u"QAppleIconEngine"_s;
+}
+
+QString QAppleIconEngine::iconName()
+{
+ return m_iconName;
+}
+
+bool QAppleIconEngine::isNull()
+{
+ return m_image == nullptr;
+}
+
+QList<QSize> QAppleIconEngine::availableIconSizes(double aspectRatio)
+{
+ const qreal devicePixelRatio = qGuiApp->devicePixelRatio();
+ const QList<QSize> sizes = {
+ {qRound(16 * devicePixelRatio), qRound(16. * devicePixelRatio / aspectRatio)},
+ {qRound(32 * devicePixelRatio), qRound(32. * devicePixelRatio / aspectRatio)},
+ {qRound(64 * devicePixelRatio), qRound(64. * devicePixelRatio / aspectRatio)},
+ {qRound(128 * devicePixelRatio), qRound(128. * devicePixelRatio / aspectRatio)},
+ {qRound(256 * devicePixelRatio), qRound(256. * devicePixelRatio / aspectRatio)},
+ };
+ return sizes;
+}
+
+QList<QSize> QAppleIconEngine::availableSizes(QIcon::Mode, QIcon::State)
+{
+ const double aspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height;
+ return availableIconSizes(aspectRatio);
+}
+
+QSize QAppleIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/)
+{
+ const double inputAspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height;
+ const double outputAspectRatio = size.width() / size.height();
+ QSize result = size;
+ if (outputAspectRatio > inputAspectRatio)
+ result.rwidth() = result.height() * inputAspectRatio;
+ else
+ result.rheight() = result.width() / inputAspectRatio;
+ return result;
+}
+
+QPixmap QAppleIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+namespace {
+#if defined(Q_OS_MACOS)
+auto *configuredImage(const NSImage *image, const QColor &color)
+{
+ auto *config = [NSImageSymbolConfiguration configurationWithPointSize:48
+ weight:NSFontWeightRegular
+ scale:NSImageSymbolScaleLarge];
+ if (@available(macOS 12, *)) {
+ auto *primaryColor = [NSColor colorWithSRGBRed:color.redF()
+ green:color.greenF()
+ blue:color.blueF()
+ alpha:color.alphaF()];
+
+ auto *colorConfig = [NSImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor];
+ config = [config configurationByApplyingConfiguration:colorConfig];
+ }
+
+ return [image imageWithSymbolConfiguration:config];
+}
+#elif defined(QT_PLATFORM_UIKIT)
+auto *configuredImage(const UIImage *image, const QColor &color)
+{
+ auto *config = [UIImageSymbolConfiguration configurationWithPointSize:48
+ weight:UIImageSymbolWeightRegular
+ scale:UIImageSymbolScaleLarge];
+
+ if (@available(iOS 15, *)) {
+ auto *primaryColor = [UIColor colorWithRed:color.redF()
+ green:color.greenF()
+ blue:color.blueF()
+ alpha:color.alphaF()];
+
+ auto *colorConfig = [UIImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor];
+ config = [config configurationByApplyingConfiguration:colorConfig];
+ }
+ return [image imageByApplyingSymbolConfiguration:config];
+}
+#endif
+}
+
+QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ const quint64 cacheKey = calculateCacheKey(mode, state);
+ if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
+ const QSize paintSize = actualSize(size, mode, state);
+ const QSize paintOffset = paintSize != size
+ ? (QSizeF(size - paintSize) * 0.5).toSize()
+ : QSize();
+
+ m_pixmap = QPixmap(size * scale);
+ m_pixmap.setDevicePixelRatio(scale);
+ m_pixmap.fill(Qt::transparent);
+
+ QPainter painter(&m_pixmap);
+ paint(&painter, QRect(paintOffset.width(), paintOffset.height(),
+ paintSize.width(), paintSize.height()), mode, state);
+
+ m_cacheKey = cacheKey;
+ }
+ return m_pixmap;
+}
+
+void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ QColor color;
+ const QPalette palette;
+ switch (mode) {
+ case QIcon::Normal:
+ color = palette.color(QPalette::Inactive, QPalette::Text);
+ break;
+ case QIcon::Disabled:
+ color = palette.color(QPalette::Disabled, QPalette::Text);
+ break;
+ case QIcon::Active:
+ color = palette.color(QPalette::Active, QPalette::Text);
+ break;
+ case QIcon::Selected:
+ color = palette.color(QPalette::Active, QPalette::HighlightedText);
+ break;
+ }
+ const auto *image = configuredImage(m_image, color);
+
+ QMacCGContext ctx(painter);
+
+#if defined(Q_OS_MACOS)
+ NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:gc];
+
+ const NSSize pixmapSize = NSMakeSize(rect.width(), rect.height());
+ [image setSize:pixmapSize];
+ const NSRect sourceRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height);
+ const NSRect iconRect = NSMakeRect(rect.x(), rect.y(), pixmapSize.width, pixmapSize.height);
+
+ [image drawInRect:iconRect fromRect:sourceRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES hints:nil];
+ [NSGraphicsContext restoreGraphicsState];
+#elif defined(QT_PLATFORM_UIKIT)
+ UIGraphicsPushContext(ctx);
+ const CGRect cgrect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ [image drawInRect:cgrect];
+ UIGraphicsPopContext();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qappleiconengine_p.h b/src/gui/platform/darwin/qappleiconengine_p.h
new file mode 100644
index 0000000000..2a4ff7fc64
--- /dev/null
+++ b/src/gui/platform/darwin/qappleiconengine_p.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 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 QAPPLEICONENGINE_P_H
+#define QAPPLEICONENGINE_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 <QtGui/qiconengine.h>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(UIImage);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSImage);
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QAppleIconEngine : public QIconEngine
+{
+public:
+ QAppleIconEngine(const QString &iconName);
+ ~QAppleIconEngine();
+ QIconEngine *clone() const override;
+ QString key() const override;
+ QString iconName() override;
+ bool isNull() override;
+
+ QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override;
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
+
+ static QList<QSize> availableIconSizes(double aspectRatio = 1.0);
+
+private:
+ static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state)
+ {
+ return (quint64(mode) << 32) | state;
+ }
+
+ const QString m_iconName;
+#if defined(Q_OS_MACOS)
+ const NSImage *m_image;
+#elif defined(QT_PLATFORM_UIKIT)
+ const UIImage *m_image;
+#endif
+ mutable QPixmap m_pixmap;
+ mutable quint64 m_cacheKey = {};
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QAPPLEICONENGINE_P_H
diff --git a/src/gui/platform/darwin/qapplekeymapper.mm b/src/gui/platform/darwin/qapplekeymapper.mm
index 2b0335fef4..b8ff5c9d6d 100644
--- a/src/gui/platform/darwin/qapplekeymapper.mm
+++ b/src/gui/platform/darwin/qapplekeymapper.mm
@@ -18,7 +18,6 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcQpaKeyMapper, "qt.qpa.keymapper");
Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys");
static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers)
@@ -37,36 +36,6 @@ static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers m
return swappedModifiers;
}
-Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
- NSString *charactersIgnoringModifiers, QString &text)
-{
- if ([characters isEqualToString:@"\t"]) {
- if (qtModifiers & Qt::ShiftModifier)
- return Qt::Key_Backtab;
- return Qt::Key_Tab;
- } else if ([characters isEqualToString:@"\r"]) {
- if (qtModifiers & Qt::KeypadModifier)
- return Qt::Key_Enter;
- return Qt::Key_Return;
- }
- if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
- QChar ch;
- if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
- ([charactersIgnoringModifiers length] != 0)) {
- ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
- } else if ([characters length] != 0) {
- ch = QChar([characters characterAtIndex:0]);
- }
- if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) &&
- (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) {
- text = QString::fromNSString(characters);
- }
- if (!ch.isNull())
- return Qt::Key(ch.toUpper().unicode());
- }
- return Qt::Key_unknown;
-}
-
#ifdef Q_OS_MACOS
static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = {
{ NSEventModifierFlagShift, Qt::ShiftModifier },
@@ -360,11 +329,11 @@ QChar QAppleKeyMapper::toCocoaKey(Qt::Key key)
{
// Prioritize overloaded keys
if (key == Qt::Key_Return)
- return QChar(NSNewlineCharacter);
+ return QChar(NSCarriageReturnCharacter);
if (key == Qt::Key_Backspace)
return QChar(NSBackspaceCharacter);
- static QHash<Qt::Key, char16_t> reverseCocoaKeys;
+ Q_CONSTINIT static QHash<Qt::Key, char16_t> reverseCocoaKeys;
if (reverseCocoaKeys.isEmpty()) {
reverseCocoaKeys.reserve(cocoaKeys.size());
for (auto it = cocoaKeys.begin(); it != cocoaKeys.end(); ++it)
@@ -384,7 +353,7 @@ Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode)
// ------------------------------------------------
-Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers()
+Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers() const
{
return fromCocoaModifiers(NSEvent.modifierFlags);
}
@@ -538,11 +507,9 @@ const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virt
where each modifier-key combination has been mapped to the
key it will produce.
*/
-QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
+QList<QKeyCombination> QAppleKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const
{
- QList<int> ret;
-
- qCDebug(lcQpaKeyMapper) << "Computing possible keys for" << event;
+ QList<QKeyCombination> ret;
const auto nativeVirtualKey = event->nativeVirtualKey();
if (!nativeVirtualKey)
@@ -555,16 +522,49 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
auto eventModifiers = event->modifiers();
- // The complete set of event modifiers, along with the
- // unmodified key, is always a valid key combination,
- // and the first priority.
- ret << int(eventModifiers) + int(unmodifiedKey);
+ int startingModifierLayer = 0;
+ if (toCocoaModifiers(eventModifiers) & NSEventModifierFlagCommand) {
+ // When the Command key is pressed AppKit seems to do key equivalent
+ // matching using a Latin/Roman interpretation of the current keyboard
+ // layout. For example, for a Greek layout, pressing Option+Command+C
+ // produces a key event with chars="ç" and unmodchars="ψ", but AppKit
+ // still treats this as a match for a key equivalent of Option+Command+C.
+ // We can't do the same by just applying the modifiers to our key map,
+ // as that too contains "ψ" for the Option+Command combination. What we
+ // can do instead is take advantage of the fact that the Command
+ // modifier layer in all/most keyboard layouts contains a Latin
+ // layer. We then combine that with the modifiers of the event
+ // to produce the resulting "Latin" key combination.
+ static constexpr int kCommandLayer = 2;
+ ret << QKeyCombination::fromCombined(
+ int(eventModifiers) + int(keyMap[kCommandLayer]));
+
+ // If the unmodified key is outside of Latin1, we also treat
+ // that as a valid key combination, even if AppKit natively
+ // does not. For example, for a Greek layout, we still want
+ // to support Option+Command+ψ as a key combination, as it's
+ // unlikely to clash with the Latin key combination we added
+ // above.
+
+ // However, if the unmodified key is within Latin1, we skip
+ // it, to avoid these types of conflicts. For example, in
+ // the same Greek layout, pressing the key next to Tab will
+ // produce a Latin ';' symbol, but we've already treated that
+ // as 'q' above, thanks to the Command modifier, so we skip
+ // the potential Command+; key combination. This is also in
+ // line with what AppKit natively does.
+
+ // Skipping Latin1 unmodified keys also handles the case of
+ // a Latin layout, where the unmodified and modified keys
+ // are the same.
+
+ if (unmodifiedKey <= 0xff)
+ startingModifierLayer = 1;
+ }
// FIXME: We only compute the first 8 combinations. Why?
- for (int i = 1; i < 8; ++i) {
+ for (int i = startingModifierLayer; i < 15; ++i) {
auto keyAfterApplyingModifiers = keyMap[i];
- if (keyAfterApplyingModifiers == unmodifiedKey)
- continue;
if (!keyAfterApplyingModifiers)
continue;
@@ -575,18 +575,39 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
// If the event includes more modifiers than the candidate they
// will need to be included in the resulting key combination.
auto additionalModifiers = eventModifiers & ~candidateModifiers;
- ret << int(additionalModifiers) + int(keyAfterApplyingModifiers);
- }
- }
- if (lcQpaKeyMapper().isDebugEnabled()) {
- qCDebug(lcQpaKeyMapper) << "Possible keys:";
- for (int keyAndModifiers : ret) {
- auto keyCombination = QKeyCombination::fromCombined(keyAndModifiers);
- auto keySequence = QKeySequence(keyCombination);
- qCDebug(lcQpaKeyMapper).verbosity(0) << "\t-"
- << keyCombination << "/" << keySequence << "/"
- << qUtf8Printable(keySequence.toString(QKeySequence::NativeText));
+ auto keyCombination = QKeyCombination::fromCombined(
+ int(additionalModifiers) + int(keyAfterApplyingModifiers));
+
+ // If there's an existing key combination with the same key,
+ // but a different set of modifiers, we want to choose only
+ // one of them, by priority (see below).
+ const auto existingCombination = std::find_if(
+ ret.begin(), ret.end(), [&](auto existingCombination) {
+ return existingCombination.key() == keyAfterApplyingModifiers;
+ });
+
+ if (existingCombination != ret.end()) {
+ // We prioritize the combination with the more specific
+ // modifiers. In the case where the number of modifiers
+ // are the same, we want to prioritize Command over Option
+ // over Control over Shift. Unfortunately the order (and
+ // hence value) of the modifiers in Qt::KeyboardModifier
+ // does not match our preferred order when Control and
+ // Meta is switched, but we can work around that by
+ // explicitly swapping the modifiers and using that
+ // for the comparison. This also works when the
+ // Qt::AA_MacDontSwapCtrlAndMeta application attribute
+ // is set, as the incoming modifiers are then left
+ // as is, and we can still trust the order.
+ auto existingModifiers = swapModifiersIfNeeded(existingCombination->keyboardModifiers());
+ auto replacementModifiers = swapModifiersIfNeeded(additionalModifiers);
+ if (replacementModifiers > existingModifiers)
+ *existingCombination = keyCombination;
+ } else {
+ // All is good, no existing combination has this key
+ ret << keyCombination;
+ }
}
}
@@ -597,6 +618,36 @@ QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
#else // iOS
+Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
+ NSString *charactersIgnoringModifiers, QString &text)
+{
+ if ([characters isEqualToString:@"\t"]) {
+ if (qtModifiers & Qt::ShiftModifier)
+ return Qt::Key_Backtab;
+ return Qt::Key_Tab;
+ } else if ([characters isEqualToString:@"\r"]) {
+ if (qtModifiers & Qt::KeypadModifier)
+ return Qt::Key_Enter;
+ return Qt::Key_Return;
+ }
+ if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
+ QChar ch;
+ if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
+ ([charactersIgnoringModifiers length] != 0)) {
+ ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
+ } else if ([characters length] != 0) {
+ ch = QChar([characters characterAtIndex:0]);
+ }
+ if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) &&
+ (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) {
+ text = QString::fromNSString(characters);
+ }
+ if (!ch.isNull())
+ return Qt::Key(ch.toUpper().unicode());
+ }
+ return Qt::Key_unknown;
+}
+
// Keyboard keys (non-modifiers)
API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode)
{
diff --git a/src/gui/platform/darwin/qapplekeymapper_p.h b/src/gui/platform/darwin/qapplekeymapper_p.h
index 34557c8ede..1f3494d16f 100644
--- a/src/gui/platform/darwin/qapplekeymapper_p.h
+++ b/src/gui/platform/darwin/qapplekeymapper_p.h
@@ -19,6 +19,8 @@
#include <Carbon/Carbon.h>
#endif
+#include <qpa/qplatformkeymapper.h>
+
#include <QtCore/QList>
#include <QtCore/QHash>
#include <QtGui/QKeyEvent>
@@ -27,13 +29,12 @@
QT_BEGIN_NAMESPACE
-class Q_GUI_EXPORT QAppleKeyMapper
+class Q_GUI_EXPORT QAppleKeyMapper : public QPlatformKeyMapper
{
public:
- static Qt::KeyboardModifiers queryKeyboardModifiers();
- QList<int> possibleKeys(const QKeyEvent *event) const;
- static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters,
- NSString *charactersIgnoringModifiers, QString &text);
+ Qt::KeyboardModifiers queryKeyboardModifiers() const override;
+ QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *event) const override;
+
#ifdef Q_OS_MACOS
static Qt::KeyboardModifiers fromCocoaModifiers(NSEventModifierFlags cocoaModifiers);
static NSEventModifierFlags toCocoaModifiers(Qt::KeyboardModifiers);
@@ -41,6 +42,9 @@ public:
static QChar toCocoaKey(Qt::Key key);
static Qt::Key fromCocoaKey(QChar keyCode);
#else
+ static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters,
+ NSString *charactersIgnoringModifiers, QString &text);
+
static Qt::Key fromUIKitKey(NSString *keyCode);
static Qt::KeyboardModifiers fromUIKitModifiers(ulong uikitModifiers);
static ulong toUIKitModifiers(Qt::KeyboardModifiers);
diff --git a/src/gui/platform/darwin/qmacmime.mm b/src/gui/platform/darwin/qmacmime.mm
deleted file mode 100644
index fe323bf60d..0000000000
--- a/src/gui/platform/darwin/qmacmime.mm
+++ /dev/null
@@ -1,1013 +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 <ImageIO/ImageIO.h>
-
-#include <QtCore/qsystemdetection.h>
-#include <QtCore/qurl.h>
-#include <QtGui/qimage.h>
-#include <QtCore/qmimedata.h>
-#include <QtCore/qstringconverter.h>
-
-#if defined(Q_OS_MACOS)
-#import <AppKit/AppKit.h>
-#else
-#include <MobileCoreServices/MobileCoreServices.h>
-#endif
-
-#if defined(QT_PLATFORM_UIKIT)
-#import <UIKit/UIKit.h>
-#endif
-
-#include "qmacmime_p.h"
-#include "qguiapplication.h"
-#include "private/qcore_mac_p.h"
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-typedef QList<QMacInternalPasteboardMime*> MimeList;
-Q_GLOBAL_STATIC(MimeList, globalMimeList)
-Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
-
-void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime)
-{
- // globalMimeList is in decreasing priority order. Recently added
- // converters take prioity over previously added converters: prepend
- // to the list.
- globalMimeList()->prepend(macMime);
-}
-
-void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime)
-{
- if (!QGuiApplication::closingDown())
- globalMimeList()->removeAll(macMime);
-}
-
-/*!
- \fn void qRegisterDraggedTypes(const QStringList &types)
- \relates QMacPasteboardMime
-
- 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 QMacPasteboardMime subclass implementation. By default
- drag and drop is enabled for all standard pasteboard types.
-
- \sa QMacPasteboardMime
-*/
-void qt_mac_registerDraggedTypes(const QStringList &types)
-{
- (*globalDraggedTypesList()) += types;
-}
-
-const QStringList& qt_mac_enabledDraggedTypes()
-{
- return (*globalDraggedTypesList());
-}
-
-/*****************************************************************************
- QDnD debug facilities
- *****************************************************************************/
-//#define DEBUG_MIME_MAPS
-
-/*!
- \class QMacInternalPasteboardMime
- \internal
- \brief The QMacPasteboardMime class converts between a MIME type and a
- \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform
- Type Identifier (UTI)} format.
- \since 4.2
-
- \ingroup draganddrop
- \inmodule QtWidgets
-
- 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.
-
- QMacPasteboardMime'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 QMacPasteboardMime 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 QMacPasteboardMime to
- find an instance that can convert to, or from, a specific MIME type. It will do this by calling
- canConvert() on each instance, starting with (and choosing) the last created instance first.
- The actual conversions will be done by using convertToMime() and convertFromMime().
-
- \note The API uses the term "flavor" in some cases. This is for backwards
- compatibility reasons, and should now be understood as UTIs.
-*/
-
-/*
- \enum QMacPasteboardMime::QMacPasteboardMimeType
- \internal
-*/
-
-/*
- Constructs a new conversion object of type \a t, adding it to the
- globally accessed list of available converters.
-*/
-QMacInternalPasteboardMime::QMacInternalPasteboardMime(char t) : type(t)
-{
- qt_mac_addToGlobalMimeList(this);
-}
-
-/*
- Destroys a conversion object, removing it from the global
- list of available converters.
-*/
-QMacInternalPasteboardMime::~QMacInternalPasteboardMime()
-{
- qt_mac_removeFromGlobalMimeList(this);
-}
-
-/*
- Returns the item count for the given \a mimeData
-*/
-int QMacInternalPasteboardMime::count(QMimeData *mimeData)
-{
- Q_UNUSED(mimeData);
- return 1;
-}
-
-class QMacPasteboardMimeAny : public QMacInternalPasteboardMime {
-private:
-
-public:
- QMacPasteboardMimeAny() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
- }
- ~QMacPasteboardMimeAny() {
- }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeAny::convertorName()
-{
- return "Any-Mime"_L1;
-}
-
-QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
-{
- // 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 QMacPasteboardMimeAny::mimeFor(QString flav)
-{
- const QString any_prefix = "com.trolltech.anymime."_L1;
- if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
- return flav.mid(any_prefix.length()).replace("--"_L1, "/"_L1);
- return QString();
-}
-
-bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
-{
- return mimeFor(flav) == mime;
-}
-
-QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
- QVariant ret;
- if (mime == "text/plain"_L1)
- ret = QString::fromUtf8(data.first());
- else
- ret = data.first();
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
-{
- QList<QByteArray> ret;
- if (mime == "text/plain"_L1)
- ret.append(data.toString().toUtf8());
- else
- ret.append(data.toByteArray());
- return ret;
-}
-
-class QMacPasteboardMimeTypeName : public QMacInternalPasteboardMime {
-private:
-
-public:
- QMacPasteboardMimeTypeName() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
- }
- ~QMacPasteboardMimeTypeName() {
- }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeTypeName::convertorName()
-{
- return "Qt-Mime-Type"_L1;
-}
-
-QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
-{
- if (mime == "application/x-qt-mime-type-name"_L1)
- return "com.trolltech.qt.MimeTypeName"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeTypeName::mimeFor(QString)
-{
- return QString();
-}
-
-bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
-{
- return false;
-}
-
-QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
-{
- QVariant ret;
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
-{
- QList<QByteArray> ret;
- ret.append(QString("x-qt-mime-type-name"_L1).toUtf8());
- return ret;
-}
-
-class QMacPasteboardMimePlainTextFallback : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimePlainTextFallback() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimePlainTextFallback::convertorName()
-{
- return "PlainText (public.text)"_L1;
-}
-
-QString QMacPasteboardMimePlainTextFallback::flavorFor(const QString &mime)
-{
- if (mime == "text/plain"_L1)
- return "public.text"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimePlainTextFallback::mimeFor(QString flav)
-{
- if (flav == "public.text"_L1)
- return "text/plain"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimePlainTextFallback::canConvert(const QString &mime, QString flav)
-{
- return mime == mimeFor(flav);
-}
-
-QVariant QMacPasteboardMimePlainTextFallback::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimePlainTextFallback: Cannot handle multiple member data");
-
- if (flavor == "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<const UInt8 *>(firstData.constData()),
- firstData.size(), kCFStringEncodingUTF8, false)));
- } else {
- qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
- }
- return QVariant();
-}
-
-QList<QByteArray> QMacPasteboardMimePlainTextFallback::convertFromMime(const QString &, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- QString string = data.toString();
- if (flavor == "public.text"_L1)
- ret.append(string.toUtf8());
- return ret;
-}
-
-class QMacPasteboardMimeUnicodeText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeUnicodeText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeUnicodeText::convertorName()
-{
- return "UnicodeText"_L1;
-}
-
-QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
-{
- if (mime == "text/plain"_L1)
- return "public.utf16-plain-text"_L1;
- int i = mime.indexOf("charset="_L1);
- if (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 QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
-{
- if (flav == "public.utf16-plain-text"_L1 || flav == "public.utf8-plain-text"_L1)
- return "text/plain"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
-{
- return (mime == "text/plain"_L1
- && (flav == "public.utf8-plain-text"_L1 || (flav == "public.utf16-plain-text"_L1)));
-}
-
-QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeUnicodeText: 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 (flavor == "public.utf8-plain-text"_L1) {
- ret = QString::fromUtf8(firstData);
- } else if (flavor == "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<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- QString string = data.toString();
- if (flavor == "public.utf8-plain-text"_L1)
- ret.append(string.toUtf8());
- else if (flavor == "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 QMacPasteboardMimeHTMLText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeHTMLText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeHTMLText::convertorName()
-{
- return "HTML"_L1;
-}
-
-QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
-{
- if (mime == "text/html"_L1)
- return "public.html"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
-{
- if (flav == "public.html"_L1)
- return "text/html"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
-{
- return flavorFor(mime) == flav;
-}
-
-QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
-{
- if (!canConvert(mimeType, flavor))
- return QVariant();
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
- return data.first();
-}
-
-QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flavor))
- return ret;
- ret.append(data.toByteArray());
- return ret;
-}
-
-class QMacPasteboardMimeRtfText : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeRtfText() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeRtfText::convertorName()
-{
- return "Rtf"_L1;
-}
-
-QString QMacPasteboardMimeRtfText::flavorFor(const QString &mime)
-{
- if (mime == "text/html"_L1)
- return "public.rtf"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeRtfText::mimeFor(QString flav)
-{
- if (flav == "public.rtf"_L1)
- return "text/html"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeRtfText::canConvert(const QString &mime, QString flav)
-{
- return mime == mimeFor(flav);
-}
-
-QVariant QMacPasteboardMimeRtfText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
-{
- if (!canConvert(mimeType, flavor))
- return QVariant();
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeHTMLText: 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<QByteArray> QMacPasteboardMimeRtfText::convertFromMime(const QString &mime, QVariant data, QString flavor)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flavor))
- 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 QMacPasteboardMimeFileUri : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeFileUri() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
- int count(QMimeData *mimeData);
-};
-
-QString QMacPasteboardMimeFileUri::convertorName()
-{
- return "FileURL"_L1;
-}
-
-QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
-{
- if (mime == "text/uri-list"_L1)
- return "public.file-url"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
-{
- if (flav == "public.file-url"_L1)
- return "text/uri-list"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
-{
- return mime == "text/uri-list"_L1 && flav == "public.file-url"_L1;
-}
-
-QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (!canConvert(mime, flav))
- return QVariant();
- QList<QVariant> 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<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flav))
- return ret;
- QList<QVariant> 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 QMacPasteboardMimeFileUri::count(QMimeData *mimeData)
-{
- return mimeData->urls().count();
-}
-
-class QMacPasteboardMimeUrl : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeUrl() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeUrl::convertorName()
-{
- return "URL"_L1;
-}
-
-QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
-{
- if (mime.startsWith("text/uri-list"_L1))
- return "public.url"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeUrl::mimeFor(QString flav)
-{
- if (flav == "public.url"_L1)
- return "text/uri-list"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
-{
- return flav == "public.url"_L1
- && mime == "text/uri-list"_L1;
-}
-
-QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (!canConvert(mime, flav))
- return QVariant();
-
- QList<QVariant> ret;
- for (int i=0; i<data.size(); ++i) {
- QUrl url = QUrl::fromEncoded(data.at(i));
- if (url.host().toLower() == "localhost"_L1)
- url.setHost(QString());
- url.setPath(url.path().normalized(QString::NormalizationForm_C));
- ret.append(url);
- }
- return QVariant(ret);
-}
-
-QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flav))
- return ret;
-
- QList<QVariant> 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));
- }
- ret.append(url.toEncoded());
- }
- return ret;
-}
-
-class QMacPasteboardMimeVCard : public QMacInternalPasteboardMime
-{
-public:
- QMacPasteboardMimeVCard() : QMacInternalPasteboardMime(MIME_ALL){ }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeVCard::convertorName()
-{
- return "VCard"_L1;
-}
-
-bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
-{
- return mimeFor(flav) == mime;
-}
-
-QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
-{
- if (mime.startsWith("text/vcard"_L1))
- return "public.vcard"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeVCard::mimeFor(QString flav)
-{
- if (flav == "public.vcard"_L1)
- return "text/vcard"_L1;
- return QString();
-}
-
-QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
-{
- QByteArray cards;
- if (mime == "text/vcard"_L1) {
- for (int i=0; i<data.size(); ++i)
- cards += data[i];
- }
- return QVariant(cards);
-}
-
-QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString)
-{
- QList<QByteArray> 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 QMacPasteboardMimeTiff : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeTiff() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeTiff::convertorName()
-{
- return "Tiff"_L1;
-}
-
-QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
-{
- if (mime.startsWith("application/x-qt-image"_L1))
- return "public.tiff"_L1;
- return QString();
-}
-
-QString QMacPasteboardMimeTiff::mimeFor(QString flav)
-{
- if (flav == "public.tiff"_L1)
- return "application/x-qt-image"_L1;
- return QString();
-}
-
-bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
-{
- return flav == "public.tiff"_L1 && mime == "application/x-qt-image"_L1;
-}
-
-QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
-
- if (!canConvert(mime, flav))
- return QVariant();
-
- QCFType<CFDataRef> tiffData = data.first().toRawCFData();
- QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
-
- if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0))
- return QVariant(qt_mac_toQImage(image));
-
- return QVariant();
-}
-
-QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
-{
- if (!canConvert(mime, flav))
- return QList<QByteArray>();
-
- QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
- QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
-
- if (!imageDestination)
- return QList<QByteArray>();
-
- QImage img = qvariant_cast<QImage>(variant);
- NSDictionary *props = @{
- static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()),
- static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height())
- };
-
- CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), static_cast<CFDictionaryRef>(props));
- CGImageDestinationFinalize(imageDestination);
-
- return QList<QByteArray>() << QByteArray::fromCFData(data);
-}
-
-/*!
- \internal
-
- This is an internal function.
-*/
-void QMacInternalPasteboardMime::initializeMimeTypes()
-{
- if (globalMimeList()->isEmpty()) {
- // Create QMacPasteboardMimeAny first to put it at the end of globalMimeList
- // with lowest priority. (the constructor prepends to the list)
- new QMacPasteboardMimeAny;
-
- //standard types that we wrap
- new QMacPasteboardMimeTiff;
- new QMacPasteboardMimePlainTextFallback;
- new QMacPasteboardMimeUnicodeText;
- new QMacPasteboardMimeRtfText;
- new QMacPasteboardMimeHTMLText;
- new QMacPasteboardMimeFileUri;
- new QMacPasteboardMimeUrl;
- new QMacPasteboardMimeTypeName;
- new QMacPasteboardMimeVCard;
- }
-}
-
-/*!
- \internal
-*/
-void QMacInternalPasteboardMime::destroyMimeTypes()
-{
- MimeList *mimes = globalMimeList();
- while (!mimes->isEmpty())
- delete mimes->takeFirst();
-}
-
-/*
- Returns the most-recently created QMacPasteboardMime of type \a t that can convert
- between the \a mime and \a flav formats. Returns 0 if no such convertor
- exists.
-*/
-QMacInternalPasteboardMime*
-QMacInternalPasteboardMime::convertor(uchar t, const QString &mime, QString flav)
-{
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
-#ifdef DEBUG_MIME_MAPS
- qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]",
- (*it)->convertorName().toLatin1().constData(),
- (*it)->type & t, mime.toLatin1().constData(),
- flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
- (*it)->canConvert(mime,flav));
- for (int i = 0; i < (*it)->countFlavors(); ++i) {
- int f = (*it)->flavor(i);
- qDebug(" %d) %d[%c%c%c%c] [%s]", i, f,
- (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF,
- (*it)->convertorName().toLatin1().constData());
- }
-#endif
- if (((*it)->type & t) && (*it)->canConvert(mime, flav))
- return (*it);
- }
- return 0;
-}
-/*
- Returns a MIME type of type \a t for \a flav, or 0 if none exists.
-*/
-QString QMacInternalPasteboardMime::flavorToMime(uchar t, QString flav)
-{
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
-#ifdef DEBUG_MIME_MAPS
- qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]",
- (*it)->convertorName().toLatin1().constData(),
- (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
- (*it)->mimeFor(flav).toLatin1().constData());
-
-#endif
- if ((*it)->type & t) {
- QString mimeType = (*it)->mimeFor(flav);
- if (!mimeType.isNull())
- return mimeType;
- }
- }
- return QString();
-}
-
-/*
- Returns a list of all currently defined QMacPasteboardMime objects of type \a t.
-*/
-QList<QMacInternalPasteboardMime*> QMacInternalPasteboardMime::all(uchar t)
-{
- MimeList ret;
- MimeList *mimes = globalMimeList();
- for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
- if ((*it)->type & t)
- ret.append((*it));
- }
- return ret;
-}
-
-
-/*
- \fn QString QMacPasteboardMime::convertorName()
-
- Returns a name for the convertor.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav)
-
- Returns \c true if the convertor can convert (both ways) between
- \a mime and \a flav; otherwise returns \c false.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QString QMacPasteboardMime::mimeFor(QString flav)
-
- Returns the MIME UTI used for Mac flavor \a flav, or 0 if this
- convertor does not support \a flav.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QString QMacPasteboardMime::flavorFor(const QString &mime)
-
- Returns the Mac UTI used for MIME type \a mime, or 0 if this
- convertor does not support \a mime.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-
- Returns \a data converted from Mac UTI \a flav to MIME type \a
- mime.
-
- Note that Mac flavors must all be self-terminating. The input \a
- data may contain trailing data.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*
- \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav)
-
- Returns \a data converted from MIME type \a mime
- to Mac UTI \a flav.
-
- Note that Mac flavors must all be self-terminating. The return
- value may contain trailing data.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-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 bd926f59fe..0000000000
--- a/src/gui/platform/darwin/qmacmime_p.h
+++ /dev/null
@@ -1,63 +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 <QtGui/private/qtguiglobal_p.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-QT_BEGIN_NAMESPACE
-
-// Duplicate of QMacPasteboardMime in QtMacExtras. Keep in sync!
-class Q_GUI_EXPORT QMacInternalPasteboardMime {
- char type;
-public:
- enum QMacPasteboardMimeType { MIME_DND=0x01,
- MIME_CLIP=0x02,
- MIME_QT_CONVERTOR=0x04,
- MIME_QT3_CONVERTOR=0x08,
- MIME_ALL=MIME_DND|MIME_CLIP
- };
- explicit QMacInternalPasteboardMime(char);
- virtual ~QMacInternalPasteboardMime();
-
- static void initializeMimeTypes();
- static void destroyMimeTypes();
-
- static QList<QMacInternalPasteboardMime*> all(uchar);
- static QMacInternalPasteboardMime *convertor(uchar, const QString &mime, QString flav);
- static QString flavorToMime(uchar, QString flav);
-
- virtual QString convertorName() = 0;
-
- virtual bool canConvert(const QString &mime, QString flav) = 0;
- virtual QString mimeFor(QString flav) = 0;
- virtual QString flavorFor(const QString &mime) = 0;
- virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) = 0;
- virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) = 0;
- virtual int count(QMimeData *mimeData);
-};
-
-Q_GUI_EXPORT void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime);
-Q_GUI_EXPORT void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime);
-Q_GUI_EXPORT void qt_mac_registerDraggedTypes(const QStringList &types);
-Q_GUI_EXPORT const QStringList& qt_mac_enabledDraggedTypes();
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/gui/platform/darwin/qmacmimeregistry.mm b/src/gui/platform/darwin/qmacmimeregistry.mm
new file mode 100644
index 0000000000..6710a0656f
--- /dev/null
+++ b/src/gui/platform/darwin/qmacmimeregistry.mm
@@ -0,0 +1,118 @@
+// 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 <QtCore/qmimedata.h>
+
+#include "qutimimeconverter.h"
+#include "qmacmimeregistry_p.h"
+#include "qguiapplication.h"
+#include "private/qcore_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QMacMimeRegistry {
+
+typedef QList<QUtiMimeConverter*> MimeList;
+Q_GLOBAL_STATIC(MimeList, globalMimeList)
+Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
+
+// implemented in qutimimeconverter.mm
+void registerBuiltInTypes();
+
+void registerDraggedTypes(const QStringList &types)
+{
+ (*globalDraggedTypesList()) += types;
+}
+
+const QStringList& enabledDraggedTypes()
+{
+ return (*globalDraggedTypesList());
+}
+
+/*****************************************************************************
+ QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_MIME_MAPS
+
+/*!
+ \class QMacMimeRegistry
+ \internal
+ \ingroup draganddrop
+*/
+
+/*!
+ \internal
+
+ This is an internal function.
+*/
+void initializeMimeTypes()
+{
+ if (globalMimeList()->isEmpty())
+ registerBuiltInTypes();
+}
+
+/*!
+ \internal
+*/
+void destroyMimeTypes()
+{
+ MimeList *mimes = globalMimeList();
+ while (!mimes->isEmpty())
+ delete mimes->takeFirst();
+}
+
+/*
+ Returns a MIME type of for scope \a scope for \a uti, or \nullptr if none exists.
+*/
+QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &uti)
+{
+ const MimeList &mimes = *globalMimeList();
+ for (const auto &mime : mimes) {
+ const bool relevantScope = mime->scope() & scope;
+#ifdef DEBUG_MIME_MAPS
+ qDebug("QMacMimeRegistry::flavorToMime: attempting (%d) for uti %s [%s]",
+ relevantScope, qPrintable(uti), qPrintable((*it)->mimeForUti(uti)));
+#endif
+ if (relevantScope) {
+ const QString mimeType = mime->mimeForUti(uti);
+ if (!mimeType.isNull())
+ return mimeType;
+ }
+ }
+ return QString();
+}
+
+void registerMimeConverter(QUtiMimeConverter *macMime)
+{
+ // globalMimeList is in decreasing priority order. Recently added
+ // converters take prioity over previously added converters: prepend
+ // to the list.
+ globalMimeList()->prepend(macMime);
+}
+
+void unregisterMimeConverter(QUtiMimeConverter *macMime)
+{
+ if (!QGuiApplication::closingDown())
+ globalMimeList()->removeAll(macMime);
+}
+
+
+/*
+ Returns a list of all currently defined QUtiMimeConverter objects for scope \a scope.
+*/
+QList<QUtiMimeConverter *> all(QUtiMimeConverter::HandlerScope scope)
+{
+ MimeList ret;
+ const MimeList &mimes = *globalMimeList();
+ for (const auto &mime : mimes) {
+ if (mime->scope() & scope)
+ ret.append(mime);
+ }
+ return ret;
+}
+
+} // namespace QMacMimeRegistry
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qmacmimeregistry_p.h b/src/gui/platform/darwin/qmacmimeregistry_p.h
new file mode 100644
index 0000000000..5928b81959
--- /dev/null
+++ b/src/gui/platform/darwin/qmacmimeregistry_p.h
@@ -0,0 +1,42 @@
+// 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
+
+#ifndef QMACMIMEREGISTRY_H
+#define QMACMIMEREGISTRY_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 <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/qutimimeconverter.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QMacMimeRegistry {
+ Q_GUI_EXPORT void initializeMimeTypes();
+ Q_GUI_EXPORT void destroyMimeTypes();
+
+ Q_GUI_EXPORT void registerMimeConverter(QUtiMimeConverter *);
+ Q_GUI_EXPORT void unregisterMimeConverter(QUtiMimeConverter *);
+
+ Q_GUI_EXPORT QList<QUtiMimeConverter *> 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();
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACMIMEREGISTRY_H
diff --git a/src/gui/platform/darwin/qutimimeconverter.h b/src/gui/platform/darwin/qutimimeconverter.h
new file mode 100644
index 0000000000..e9297b5fa0
--- /dev/null
+++ b/src/gui/platform/darwin/qutimimeconverter.h
@@ -0,0 +1,62 @@
+// 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 <QtGui/qtguiglobal.h>
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QString;
+class QVariant;
+class QMimeData;
+
+class Q_GUI_EXPORT QUtiMimeConverter
+{
+ Q_DISABLE_COPY(QUtiMimeConverter)
+public:
+ enum class HandlerScopeFlag : uint8_t
+ {
+ DnD = 0x01,
+ Clipboard = 0x02,
+ Qt_compatible = 0x04,
+ Qt3_compatible = 0x08,
+ All = DnD|Clipboard,
+ AllCompatible = All|Qt_compatible
+ };
+ Q_DECLARE_FLAGS(HandlerScope, HandlerScopeFlag)
+
+ QUtiMimeConverter();
+ 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<QByteArray> 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<QByteArray> &data, const QString &uti) const = 0;
+ virtual int count(const QMimeData *mimeData) const;
+
+private:
+ friend class QMacMimeTypeName;
+ friend class QMacMimeAny;
+
+ explicit QUtiMimeConverter(HandlerScope scope);
+
+ const HandlerScope m_scope;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QUtiMimeConverter::HandlerScope)
+
+
+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..ee643fd0c6
--- /dev/null
+++ b/src/gui/platform/darwin/qutimimeconverter.mm
@@ -0,0 +1,823 @@
+// 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 <ImageIO/ImageIO.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <UniformTypeIdentifiers/UTCoreTypes.h>
+
+#include <QtCore/qsystemdetection.h>
+#include <QtCore/qurl.h>
+#include <QtGui/qimage.h>
+#include <QtCore/qmimedata.h>
+#include <QtCore/qstringconverter.h>
+
+#if defined(Q_OS_MACOS)
+#import <AppKit/AppKit.h>
+#else
+#include <MobileCoreServices/MobileCoreServices.h>
+#endif
+
+#if defined(QT_PLATFORM_UIKIT)
+#import <UIKit/UIKit.h>
+#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
+ \brief The QUtiMimeConverter class converts between a MIME type and a
+ \l{https://developer.apple.com/documentation/uniformtypeidentifiers}
+ {Uniform Type Identifier (UTI)} format.
+ \since 6.5
+
+ \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.
+
+ Construct an instance of your converter implementation after instantiating
+ QGuiApplication:
+
+ \code
+ int main(int argc, char **argv)
+ {
+ QGuiApplication app(argc, argv);
+ JsonMimeConverter jsonConverter;
+ }
+ \endcode
+
+ Destroying the instance will unregister the converter and remove support
+ for the conversion. It is also valid to heap-allocate the converter
+ instance; Qt takes ownership and will delete the converter object during
+ QGuiApplication shut-down.
+
+ 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
+*/
+
+/*!
+ \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.
+
+ Call this constructor after QGuiApplication has been created.
+*/
+QUtiMimeConverter::QUtiMimeConverter()
+ : QUtiMimeConverter(HandlerScopeFlag::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(const QString &uti) const
+
+ 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) const
+
+ 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<QByteArray> &data, const QString &uti) const
+
+ 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<QByteArray> QUtiMimeConverter::convertFromMime(const QString &mime,
+ const QVariant &data, const QString & uti) const
+
+ 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(HandlerScopeFlag::AllCompatible) {}
+
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &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<QByteArray> QMacMimeAny::convertFromMime(const QString &mime, const QVariant &data,
+ const QString &) const
+{
+ QList<QByteArray> 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(HandlerScopeFlag::AllCompatible) {}
+
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &, const QString &) const
+{
+ QVariant ret;
+ return ret;
+}
+
+QList<QByteArray> QMacMimeTypeName::convertFromMime(const QString &, const QVariant &, const QString &) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &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<const UInt8 *>(firstData.constData()),
+ firstData.size(), kCFStringEncodingUTF8, false)));
+ } else {
+ qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
+ }
+ return QVariant();
+}
+
+QList<QByteArray>
+QMacMimePlainTextFallback::convertFromMime(const QString &, const QVariant &data,
+ const QString &uti) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &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<QByteArray>
+QMacMimeUnicodeText::convertFromMime(const QString &, const QVariant &data,
+ const QString &uti) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &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<QByteArray>
+QMacMimeHTMLText::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &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<QByteArray>
+QMacMimeRtfText::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+ QList<QVariant> 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<QByteArray>
+QMacMimeFileUri::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+ QList<QVariant> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+
+ QList<QVariant> ret;
+ for (int i=0; i<data.size(); ++i) {
+ QUrl url = QUrl::fromEncoded(data.at(i));
+ if (url.host().toLower() == "localhost"_L1)
+ url.setHost(QString());
+ url.setPath(url.path().normalized(QString::NormalizationForm_C));
+ ret.append(url);
+ }
+ return QVariant(ret);
+}
+
+QList<QByteArray> QMacMimeUrl::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> ret;
+ if (!canConvert(mime, uti))
+ return ret;
+
+ QList<QVariant> 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));
+ }
+ ret.append(url.toEncoded());
+ }
+ return ret;
+}
+
+class QMacMimeVCard : public QUtiMimeConverter
+{
+public:
+ QString utiForMime(const QString &mime) const override;
+ QString mimeForUti(const QString &uti) const override;
+ QVariant convertToMime(const QString &mime, const QList<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &data, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QVariant();
+ QByteArray cards;
+ if (uti == "public.vcard"_L1) {
+ for (int i=0; i<data.size(); ++i)
+ cards += data[i];
+ }
+ return QVariant(cards);
+}
+
+QList<QByteArray> QMacMimeVCard::convertFromMime(const QString &mime,
+ const QVariant &data, const QString &uti) const
+{
+ QList<QByteArray> 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<QByteArray> &data,
+ const QString &uti) const override;
+ QList<QByteArray> 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<QByteArray> &data, const QString &uti) const
+{
+ if (data.count() > 1)
+ qWarning("QMacMimeTiff: Cannot handle multiple member data");
+
+ if (!canConvert(mime, uti))
+ return QVariant();
+
+ QCFType<CFDataRef> tiffData = data.first().toRawCFData();
+ QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
+
+ if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0))
+ return QVariant(qt_mac_toQImage(image));
+
+ return QVariant();
+}
+
+QList<QByteArray> QMacMimeTiff::convertFromMime(const QString &mime,
+ const QVariant &variant, const QString &uti) const
+{
+ if (!canConvert(mime, uti))
+ return QList<QByteArray>();
+
+ QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
+ QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data,
+ (CFStringRef)UTTypeTIFF.identifier, 1, 0);
+
+ if (!imageDestination)
+ return QList<QByteArray>();
+
+ QImage img = qvariant_cast<QImage>(variant);
+ NSDictionary *props = @{
+ static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()),
+ static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height())
+ };
+
+ CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img),
+ static_cast<CFDictionaryRef>(props));
+ CGImageDestinationFinalize(imageDestination);
+
+ return QList<QByteArray>() << 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