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/darwin.pri4
-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.mm717
-rw-r--r--src/gui/platform/darwin/qapplekeymapper_p.h82
-rw-r--r--src/gui/platform/darwin/qmacmime.mm1046
-rw-r--r--src/gui/platform/darwin/qmacmime_p.h99
-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
11 files changed, 2372 insertions, 1149 deletions
diff --git a/src/gui/platform/darwin/darwin.pri b/src/gui/platform/darwin/darwin.pri
deleted file mode 100644
index c8d26c997b..0000000000
--- a/src/gui/platform/darwin/darwin.pri
+++ /dev/null
@@ -1,4 +0,0 @@
-HEADERS += $$PWD/qmacmime_p.h
-SOURCES += $$PWD/qmacmime.mm
-LIBS += -framework ImageIO
-macos: LIBS_PRIVATE += -framework AppKit
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
new file mode 100644
index 0000000000..b8ff5c9d6d
--- /dev/null
+++ b/src/gui/platform/darwin/qapplekeymapper.mm
@@ -0,0 +1,717 @@
+// Copyright (C) 2021 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 <qglobal.h>
+
+#ifdef Q_OS_MACOS
+#include <AppKit/AppKit.h>
+#endif
+
+#if defined(QT_PLATFORM_UIKIT)
+#include <UIKit/UIKit.h>
+#endif
+
+#include "qapplekeymapper_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys");
+
+static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers)
+{
+ if (QCoreApplication::testAttribute(Qt::AA_MacDontSwapCtrlAndMeta))
+ return modifiers;
+
+ Qt::KeyboardModifiers swappedModifiers = modifiers;
+ swappedModifiers &= ~(Qt::MetaModifier | Qt::ControlModifier);
+
+ if (modifiers & Qt::ControlModifier)
+ swappedModifiers |= Qt::MetaModifier;
+ if (modifiers & Qt::MetaModifier)
+ swappedModifiers |= Qt::ControlModifier;
+
+ return swappedModifiers;
+}
+
+#ifdef Q_OS_MACOS
+static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = {
+ { NSEventModifierFlagShift, Qt::ShiftModifier },
+ { NSEventModifierFlagControl, Qt::ControlModifier },
+ { NSEventModifierFlagCommand, Qt::MetaModifier },
+ { NSEventModifierFlagOption, Qt::AltModifier },
+ { NSEventModifierFlagNumericPad, Qt::KeypadModifier }
+};
+
+Qt::KeyboardModifiers QAppleKeyMapper::fromCocoaModifiers(NSEventModifierFlags cocoaModifiers)
+{
+ Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
+ for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) {
+ if (cocoaModifiers & cocoaModifier)
+ qtModifiers |= qtModifier;
+ }
+
+ return swapModifiersIfNeeded(qtModifiers);
+}
+
+NSEventModifierFlags QAppleKeyMapper::toCocoaModifiers(Qt::KeyboardModifiers qtModifiers)
+{
+ qtModifiers = swapModifiersIfNeeded(qtModifiers);
+
+ NSEventModifierFlags cocoaModifiers = 0;
+ for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) {
+ if (qtModifiers & qtModifier)
+ cocoaModifiers |= cocoaModifier;
+ }
+
+ return cocoaModifiers;
+}
+
+using CarbonModifiers = UInt32; // As opposed to EventModifiers which is UInt16
+
+static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers)
+{
+ qtModifiers = swapModifiersIfNeeded(qtModifiers);
+
+ static constexpr std::tuple<int, Qt::KeyboardModifier> carbonModifierMap[] = {
+ { shiftKey, Qt::ShiftModifier },
+ { controlKey, Qt::ControlModifier },
+ { cmdKey, Qt::MetaModifier },
+ { optionKey, Qt::AltModifier },
+ { kEventKeyModifierNumLockMask, Qt::KeypadModifier }
+ };
+
+ CarbonModifiers carbonModifiers = 0;
+ for (const auto &[carbonModifier, qtModifier] : carbonModifierMap) {
+ if (qtModifiers & qtModifier)
+ carbonModifiers |= carbonModifier;
+ }
+
+ return carbonModifiers;
+}
+
+// Keyboard keys (non-modifiers)
+static QHash<char16_t, Qt::Key> standardKeys = {
+ { kHomeCharCode, Qt::Key_Home },
+ { kEnterCharCode, Qt::Key_Enter },
+ { kEndCharCode, Qt::Key_End },
+ { kBackspaceCharCode, Qt::Key_Backspace },
+ { kTabCharCode, Qt::Key_Tab },
+ { kPageUpCharCode, Qt::Key_PageUp },
+ { kPageDownCharCode, Qt::Key_PageDown },
+ { kReturnCharCode, Qt::Key_Return },
+ { kEscapeCharCode, Qt::Key_Escape },
+ { kLeftArrowCharCode, Qt::Key_Left },
+ { kRightArrowCharCode, Qt::Key_Right },
+ { kUpArrowCharCode, Qt::Key_Up },
+ { kDownArrowCharCode, Qt::Key_Down },
+ { kHelpCharCode, Qt::Key_Help },
+ { kDeleteCharCode, Qt::Key_Delete },
+ // ASCII maps, for debugging
+ { ':', Qt::Key_Colon },
+ { ';', Qt::Key_Semicolon },
+ { '<', Qt::Key_Less },
+ { '=', Qt::Key_Equal },
+ { '>', Qt::Key_Greater },
+ { '?', Qt::Key_Question },
+ { '@', Qt::Key_At },
+ { ' ', Qt::Key_Space },
+ { '!', Qt::Key_Exclam },
+ { '"', Qt::Key_QuoteDbl },
+ { '#', Qt::Key_NumberSign },
+ { '$', Qt::Key_Dollar },
+ { '%', Qt::Key_Percent },
+ { '&', Qt::Key_Ampersand },
+ { '\'', Qt::Key_Apostrophe },
+ { '(', Qt::Key_ParenLeft },
+ { ')', Qt::Key_ParenRight },
+ { '*', Qt::Key_Asterisk },
+ { '+', Qt::Key_Plus },
+ { ',', Qt::Key_Comma },
+ { '-', Qt::Key_Minus },
+ { '.', Qt::Key_Period },
+ { '/', Qt::Key_Slash },
+ { '[', Qt::Key_BracketLeft },
+ { ']', Qt::Key_BracketRight },
+ { '\\', Qt::Key_Backslash },
+ { '_', Qt::Key_Underscore },
+ { '`', Qt::Key_QuoteLeft },
+ { '{', Qt::Key_BraceLeft },
+ { '}', Qt::Key_BraceRight },
+ { '|', Qt::Key_Bar },
+ { '~', Qt::Key_AsciiTilde },
+ { '^', Qt::Key_AsciiCircum }
+};
+
+static QHash<char16_t, Qt::Key> virtualKeys = {
+ { kVK_F1, Qt::Key_F1 },
+ { kVK_F2, Qt::Key_F2 },
+ { kVK_F3, Qt::Key_F3 },
+ { kVK_F4, Qt::Key_F4 },
+ { kVK_F5, Qt::Key_F5 },
+ { kVK_F6, Qt::Key_F6 },
+ { kVK_F7, Qt::Key_F7 },
+ { kVK_F8, Qt::Key_F8 },
+ { kVK_F9, Qt::Key_F9 },
+ { kVK_F10, Qt::Key_F10 },
+ { kVK_F11, Qt::Key_F11 },
+ { kVK_F12, Qt::Key_F12 },
+ { kVK_F13, Qt::Key_F13 },
+ { kVK_F14, Qt::Key_F14 },
+ { kVK_F15, Qt::Key_F15 },
+ { kVK_F16, Qt::Key_F16 },
+ { kVK_Return, Qt::Key_Return },
+ { kVK_Tab, Qt::Key_Tab },
+ { kVK_Escape, Qt::Key_Escape },
+ { kVK_Help, Qt::Key_Help },
+ { kVK_UpArrow, Qt::Key_Up },
+ { kVK_DownArrow, Qt::Key_Down },
+ { kVK_LeftArrow, Qt::Key_Left },
+ { kVK_RightArrow, Qt::Key_Right },
+ { kVK_PageUp, Qt::Key_PageUp },
+ { kVK_PageDown, Qt::Key_PageDown }
+};
+
+static QHash<char16_t, Qt::Key> functionKeys = {
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ // F1-35 function keys handled manually below
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSPrintFunctionKey, Qt::Key_Printer },
+ { NSClearDisplayFunctionKey, Qt::Key_Clear },
+ { NSInsertCharFunctionKey, Qt::Key_Insert },
+ { NSDeleteCharFunctionKey, Qt::Key_Delete },
+ { NSSelectFunctionKey, Qt::Key_Select },
+ { NSExecuteFunctionKey, Qt::Key_Execute },
+ { NSUndoFunctionKey, Qt::Key_Undo },
+ { NSRedoFunctionKey, Qt::Key_Redo },
+ { NSFindFunctionKey, Qt::Key_Find },
+ { NSHelpFunctionKey, Qt::Key_Help },
+ { NSModeSwitchFunctionKey, Qt::Key_Mode_switch }
+};
+
+static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
+{
+ qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)",
+ key.unicode(), key.unicode(), virtualKey, virtualKey);
+
+ if (key == QChar(kClearCharCode) && virtualKey == 0x47)
+ return Qt::Key_Clear;
+
+ if (key.isDigit()) {
+ qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue());
+ return key.digitValue() + Qt::Key_0;
+ }
+
+ if (key.isLetter()) {
+ qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A'));
+ return (key.toUpper().unicode() - 'A') + Qt::Key_A;
+ }
+ if (key.isSymbol()) {
+ qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode()));
+ return key.unicode();
+ }
+
+ if (auto qtKey = standardKeys.value(key.unicode())) {
+ // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed
+ if (qtKey == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) {
+ qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab");
+ return Qt::Key_Backtab;
+ }
+
+ qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey;
+ return qtKey;
+ }
+
+ // Last ditch try to match the scan code
+ if (auto qtKey = virtualKeys.value(virtualKey)) {
+ qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey;
+ return qtKey;
+ }
+
+ // Check if they belong to key codes in private unicode range
+ if (key >= QChar(NSUpArrowFunctionKey) && key <= QChar(NSModeSwitchFunctionKey)) {
+ if (auto qtKey = functionKeys.value(key.unicode())) {
+ qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey;
+ return qtKey;
+ } else if (key >= QChar(NSF1FunctionKey) && key <= QChar(NSF35FunctionKey)) {
+ auto functionKey = Qt::Key_F1 + (key.unicode() - NSF1FunctionKey) ;
+ qCDebug(lcQpaKeyMapperKeys) << "Got" << functionKey;
+ return functionKey;
+ }
+ }
+
+ qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey);
+ return Qt::Key_unknown;
+}
+
+// --------- Cocoa key mapping moved from Qt Core ---------
+
+static const int NSEscapeCharacter = 27; // not defined by Cocoa headers
+
+static const QHash<char16_t, Qt::Key> cocoaKeys = {
+ { NSEnterCharacter, Qt::Key_Enter },
+ { NSBackspaceCharacter, Qt::Key_Backspace },
+ { NSTabCharacter, Qt::Key_Tab },
+ { NSNewlineCharacter, Qt::Key_Return },
+ { NSCarriageReturnCharacter, Qt::Key_Return },
+ { NSBackTabCharacter, Qt::Key_Backtab },
+ { NSEscapeCharacter, Qt::Key_Escape },
+ { NSDeleteCharacter, Qt::Key_Backspace },
+ { NSUpArrowFunctionKey, Qt::Key_Up },
+ { NSDownArrowFunctionKey, Qt::Key_Down },
+ { NSLeftArrowFunctionKey, Qt::Key_Left },
+ { NSRightArrowFunctionKey, Qt::Key_Right },
+ { NSF1FunctionKey, Qt::Key_F1 },
+ { NSF2FunctionKey, Qt::Key_F2 },
+ { NSF3FunctionKey, Qt::Key_F3 },
+ { NSF4FunctionKey, Qt::Key_F4 },
+ { NSF5FunctionKey, Qt::Key_F5 },
+ { NSF6FunctionKey, Qt::Key_F6 },
+ { NSF7FunctionKey, Qt::Key_F7 },
+ { NSF8FunctionKey, Qt::Key_F8 },
+ { NSF9FunctionKey, Qt::Key_F9 },
+ { NSF10FunctionKey, Qt::Key_F10 },
+ { NSF11FunctionKey, Qt::Key_F11 },
+ { NSF12FunctionKey, Qt::Key_F12 },
+ { NSF13FunctionKey, Qt::Key_F13 },
+ { NSF14FunctionKey, Qt::Key_F14 },
+ { NSF15FunctionKey, Qt::Key_F15 },
+ { NSF16FunctionKey, Qt::Key_F16 },
+ { NSF17FunctionKey, Qt::Key_F17 },
+ { NSF18FunctionKey, Qt::Key_F18 },
+ { NSF19FunctionKey, Qt::Key_F19 },
+ { NSF20FunctionKey, Qt::Key_F20 },
+ { NSF21FunctionKey, Qt::Key_F21 },
+ { NSF22FunctionKey, Qt::Key_F22 },
+ { NSF23FunctionKey, Qt::Key_F23 },
+ { NSF24FunctionKey, Qt::Key_F24 },
+ { NSF25FunctionKey, Qt::Key_F25 },
+ { NSF26FunctionKey, Qt::Key_F26 },
+ { NSF27FunctionKey, Qt::Key_F27 },
+ { NSF28FunctionKey, Qt::Key_F28 },
+ { NSF29FunctionKey, Qt::Key_F29 },
+ { NSF30FunctionKey, Qt::Key_F30 },
+ { NSF31FunctionKey, Qt::Key_F31 },
+ { NSF32FunctionKey, Qt::Key_F32 },
+ { NSF33FunctionKey, Qt::Key_F33 },
+ { NSF34FunctionKey, Qt::Key_F34 },
+ { NSF35FunctionKey, Qt::Key_F35 },
+ { NSInsertFunctionKey, Qt::Key_Insert },
+ { NSDeleteFunctionKey, Qt::Key_Delete },
+ { NSHomeFunctionKey, Qt::Key_Home },
+ { NSEndFunctionKey, Qt::Key_End },
+ { NSPageUpFunctionKey, Qt::Key_PageUp },
+ { NSPageDownFunctionKey, Qt::Key_PageDown },
+ { NSPrintScreenFunctionKey, Qt::Key_Print },
+ { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
+ { NSPauseFunctionKey, Qt::Key_Pause },
+ { NSSysReqFunctionKey, Qt::Key_SysReq },
+ { NSMenuFunctionKey, Qt::Key_Menu },
+ { NSHelpFunctionKey, Qt::Key_Help },
+};
+
+QChar QAppleKeyMapper::toCocoaKey(Qt::Key key)
+{
+ // Prioritize overloaded keys
+ if (key == Qt::Key_Return)
+ return QChar(NSCarriageReturnCharacter);
+ if (key == Qt::Key_Backspace)
+ return QChar(NSBackspaceCharacter);
+
+ 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)
+ reverseCocoaKeys.insert(it.value(), it.key());
+ }
+
+ return reverseCocoaKeys.value(key);
+}
+
+Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode)
+{
+ if (auto key = cocoaKeys.value(keyCode.unicode()))
+ return key;
+
+ return Qt::Key(keyCode.toUpper().unicode());
+}
+
+// ------------------------------------------------
+
+Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers() const
+{
+ return fromCocoaModifiers(NSEvent.modifierFlags);
+}
+
+bool QAppleKeyMapper::updateKeyboard()
+{
+ QCFType<TISInputSourceRef> source = TISCopyInputMethodKeyboardLayoutOverride();
+ if (!source)
+ source = TISCopyCurrentKeyboardInputSource();
+
+ if (m_keyboardMode != NullMode && source == m_currentInputSource)
+ return false;
+
+ Q_ASSERT(source);
+ m_currentInputSource = source;
+ m_keyboardKind = LMGetKbdType();
+
+ m_keyMap.clear();
+
+ if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) {
+ const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data));
+ Q_ASSERT(uchrData);
+ m_keyboardLayoutFormat = uchrData;
+ m_keyboardMode = UnicodeMode;
+ } else {
+ m_keyboardLayoutFormat = nullptr;
+ m_keyboardMode = NullMode;
+ }
+
+ qCDebug(lcQpaKeyMapper) << "Updated keyboard to"
+ << QString::fromCFString(CFStringRef(TISGetInputSourceProperty(
+ m_currentInputSource, kTISPropertyLocalizedName)));
+
+ return true;
+}
+
+static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
+ Qt::NoModifier, // 0
+ Qt::ShiftModifier, // 1
+ Qt::ControlModifier, // 2
+ Qt::ControlModifier | Qt::ShiftModifier, // 3
+ Qt::AltModifier, // 4
+ Qt::AltModifier | Qt::ShiftModifier, // 5
+ Qt::AltModifier | Qt::ControlModifier, // 6
+ Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
+ Qt::MetaModifier, // 8
+ Qt::MetaModifier | Qt::ShiftModifier, // 9
+ Qt::MetaModifier | Qt::ControlModifier, // 10
+ Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier, // 11
+ Qt::MetaModifier | Qt::AltModifier, // 12
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13
+ Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14
+ Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15
+};
+
+/*
+ Returns a key map for the given \virtualKey based on all
+ possible modifier combinations.
+*/
+const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virtualKey) const
+{
+ static_assert(sizeof(modifierCombinations) / sizeof(Qt::KeyboardModifiers) == kNumModifierCombinations);
+
+ const_cast<QAppleKeyMapper *>(this)->updateKeyboard();
+
+ auto &keyMap = m_keyMap[virtualKey];
+ if (keyMap[Qt::NoModifier] != Qt::Key_unknown)
+ return keyMap; // Already filled
+
+ qCDebug(lcQpaKeyMapper, "Updating key map for virtual key 0x%02x", (uint)virtualKey);
+
+ // Key mapping via [NSEvent charactersByApplyingModifiers:] only works for key down
+ // events, but we might (wrongly) get into this code path for other key events such
+ // as NSEventTypeFlagsChanged.
+ const bool canMapCocoaEvent = NSApp.currentEvent.type == NSEventTypeKeyDown;
+
+ if (!canMapCocoaEvent)
+ qCWarning(lcQpaKeyMapper) << "Could not map key to character for event" << NSApp.currentEvent;
+
+ for (int i = 0; i < kNumModifierCombinations; ++i) {
+ Q_ASSERT(!i || keyMap[i] == 0);
+
+ auto qtModifiers = modifierCombinations[i];
+ auto carbonModifiers = toCarbonModifiers(qtModifiers);
+ const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF;
+
+ UInt32 deadKeyState = 0;
+ static const UniCharCount maxStringLength = 10;
+ static UniChar unicodeString[maxStringLength];
+ UniCharCount actualStringLength = 0;
+ OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, virtualKey,
+ kUCKeyActionDown, modifierKeyState, m_keyboardKind,
+ kUCKeyTranslateNoDeadKeysMask, &deadKeyState,
+ maxStringLength, &actualStringLength,
+ unicodeString);
+
+ // Use translated Unicode key if valid
+ QChar carbonUnicodeKey;
+ if (err == noErr && actualStringLength)
+ carbonUnicodeKey = QChar(unicodeString[0]);
+
+ if (@available(macOS 10.15, *)) {
+ if (canMapCocoaEvent) {
+ // Until we've verified that the Cocoa API works as expected
+ // we first run the event through the Carbon APIs and then
+ // compare the results to Cocoa.
+ auto cocoaModifiers = toCocoaModifiers(qtModifiers);
+ auto *charactersWithModifiers = [NSApp.currentEvent charactersByApplyingModifiers:cocoaModifiers];
+
+ QChar cocoaUnicodeKey;
+ if (charactersWithModifiers.length > 0)
+ cocoaUnicodeKey = QChar([charactersWithModifiers characterAtIndex:0]);
+
+ if (cocoaUnicodeKey != carbonUnicodeKey) {
+ qCWarning(lcQpaKeyMapper) << "Mismatch between Cocoa" << cocoaUnicodeKey
+ << "and Carbon" << carbonUnicodeKey << "for virtual key" << virtualKey
+ << "with" << qtModifiers;
+ }
+ }
+ }
+
+ int qtKey = toKeyCode(carbonUnicodeKey, virtualKey, qtModifiers);
+ if (qtKey == Qt::Key_unknown)
+ qtKey = carbonUnicodeKey.unicode();
+
+ keyMap[i] = qtKey;
+
+ qCDebug(lcQpaKeyMapper).verbosity(0) << "\t" << qtModifiers
+ << "+" << qUtf8Printable(QString::asprintf("0x%02x", virtualKey))
+ << "=" << qUtf8Printable(QString::asprintf("%d / 0x%02x /", qtKey, qtKey))
+ << QKeySequence(qtKey).toString();
+ }
+
+ return keyMap;
+}
+
+/*
+ Compute the possible key combinations that can map to the event's
+ virtual key and modifiers, in the current keyboard layout.
+
+ For example, given a normal US keyboard layout, the virtual key
+ 23 combined with the Alt (⌥) and Shift (⇧) modifiers, can map
+ to the following key combinations:
+
+ - Alt+Shift+5
+ - Alt+%
+ - Shift+∞
+ - fi
+
+ The function builds on a key map produced by keyMapForKey(),
+ where each modifier-key combination has been mapped to the
+ key it will produce.
+*/
+QList<QKeyCombination> QAppleKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const
+{
+ QList<QKeyCombination> ret;
+
+ const auto nativeVirtualKey = event->nativeVirtualKey();
+ if (!nativeVirtualKey)
+ return ret;
+
+ auto keyMap = keyMapForKey(nativeVirtualKey);
+
+ auto unmodifiedKey = keyMap[Qt::NoModifier];
+ Q_ASSERT(unmodifiedKey != Qt::Key_unknown);
+
+ auto eventModifiers = event->modifiers();
+
+ 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 = startingModifierLayer; i < 15; ++i) {
+ auto keyAfterApplyingModifiers = keyMap[i];
+ if (!keyAfterApplyingModifiers)
+ continue;
+
+ // Include key if the event modifiers match exactly,
+ // or are a superset of the current candidate modifiers.
+ auto candidateModifiers = modifierCombinations[i];
+ if ((eventModifiers & candidateModifiers) == candidateModifiers) {
+ // If the event includes more modifiers than the candidate they
+ // will need to be included in the resulting key combination.
+ auto additionalModifiers = eventModifiers & ~candidateModifiers;
+
+ 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;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+
+#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)
+{
+ static QHash<NSString *, Qt::Key> uiKitKeys = {
+ { UIKeyInputF1, Qt::Key_F1 },
+ { UIKeyInputF2, Qt::Key_F2 },
+ { UIKeyInputF3, Qt::Key_F3 },
+ { UIKeyInputF4, Qt::Key_F4 },
+ { UIKeyInputF5, Qt::Key_F5 },
+ { UIKeyInputF6, Qt::Key_F6 },
+ { UIKeyInputF7, Qt::Key_F7 },
+ { UIKeyInputF8, Qt::Key_F8 },
+ { UIKeyInputF9, Qt::Key_F9 },
+ { UIKeyInputF10, Qt::Key_F10 },
+ { UIKeyInputF11, Qt::Key_F11 },
+ { UIKeyInputF12, Qt::Key_F12 },
+ { UIKeyInputHome, Qt::Key_Home },
+ { UIKeyInputEnd, Qt::Key_End },
+ { UIKeyInputPageUp, Qt::Key_PageUp },
+ { UIKeyInputPageDown, Qt::Key_PageDown },
+ { UIKeyInputEscape, Qt::Key_Escape },
+ { UIKeyInputUpArrow, Qt::Key_Up },
+ { UIKeyInputDownArrow, Qt::Key_Down },
+ { UIKeyInputLeftArrow, Qt::Key_Left },
+ { UIKeyInputRightArrow, Qt::Key_Right }
+ };
+
+ if (auto key = uiKitKeys.value(keyCode))
+ return key;
+
+ return Qt::Key_unknown;
+}
+
+static constexpr std::tuple<ulong, Qt::KeyboardModifier> uiKitModifierMap[] = {
+ { UIKeyModifierShift, Qt::ShiftModifier },
+ { UIKeyModifierControl, Qt::ControlModifier },
+ { UIKeyModifierCommand, Qt::MetaModifier },
+ { UIKeyModifierAlternate, Qt::AltModifier },
+ { UIKeyModifierNumericPad, Qt::KeypadModifier }
+};
+
+ulong QAppleKeyMapper::toUIKitModifiers(Qt::KeyboardModifiers qtModifiers)
+{
+ qtModifiers = swapModifiersIfNeeded(qtModifiers);
+
+ ulong nativeModifiers = 0;
+ for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
+ if (qtModifiers & qtModifier)
+ nativeModifiers |= nativeModifier;
+ }
+
+ return nativeModifiers;
+}
+
+Qt::KeyboardModifiers QAppleKeyMapper::fromUIKitModifiers(ulong nativeModifiers)
+{
+ Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
+ for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
+ if (nativeModifiers & nativeModifier)
+ qtModifiers |= qtModifier;
+ }
+
+ return swapModifiersIfNeeded(qtModifiers);
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qapplekeymapper_p.h b/src/gui/platform/darwin/qapplekeymapper_p.h
new file mode 100644
index 0000000000..1f3494d16f
--- /dev/null
+++ b/src/gui/platform/darwin/qapplekeymapper_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2021 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 QAPPLEKEYMAPPER_H
+#define QAPPLEKEYMAPPER_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.
+//
+
+#ifdef Q_OS_MACOS
+#include <Carbon/Carbon.h>
+#endif
+
+#include <qpa/qplatformkeymapper.h>
+
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtGui/QKeyEvent>
+
+#include <QtCore/private/qcore_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QAppleKeyMapper : public QPlatformKeyMapper
+{
+public:
+ 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);
+
+ 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);
+#endif
+private:
+#ifdef Q_OS_MACOS
+ static constexpr int kNumModifierCombinations = 16;
+ struct KeyMap : std::array<char32_t, kNumModifierCombinations>
+ {
+ // Initialize first element to a sentinel that allows us
+ // to distinguish an uninitialized map from an initialized.
+ // Using 0 would not allow us to map U+0000 (NUL), however
+ // unlikely that is.
+ KeyMap() : std::array<char32_t, 16>{Qt::Key_unknown} {}
+ };
+
+ bool updateKeyboard();
+
+ using VirtualKeyCode = unsigned short;
+ const KeyMap &keyMapForKey(VirtualKeyCode virtualKey) const;
+
+ QCFType<TISInputSourceRef> m_currentInputSource = nullptr;
+
+ enum { NullMode, UnicodeMode, OtherMode } m_keyboardMode = NullMode;
+ const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr;
+ KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind;
+
+ mutable QHash<VirtualKeyCode, KeyMap> m_keyMap;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/gui/platform/darwin/qmacmime.mm b/src/gui/platform/darwin/qmacmime.mm
deleted file mode 100644
index 6e588f2ca0..0000000000
--- a/src/gui/platform/darwin/qmacmime.mm
+++ /dev/null
@@ -1,1046 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <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
-
-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 QMacPasteboardMime
- \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 interate 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 convertors.
-*/
-QMacInternalPasteboardMime::QMacInternalPasteboardMime(char t) : type(t)
-{
- qt_mac_addToGlobalMimeList(this);
-}
-
-/*!
- Destroys a conversion object, removing it from the global
- list of available convertors.
-*/
-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 QLatin1String("Any-Mime");
-}
-
-QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
-{
- // do not handle the mime type name in the drag pasteboard
- if (mime == QLatin1String("application/x-qt-mime-type-name"))
- return QString();
- QString ret = QLatin1String("com.trolltech.anymime.") + mime;
- return ret.replace(QLatin1Char('/'), QLatin1String("--"));
-}
-
-QString QMacPasteboardMimeAny::mimeFor(QString flav)
-{
- const QString any_prefix = QLatin1String("com.trolltech.anymime.");
- if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
- return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/"));
- 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 == QLatin1String("text/plain"))
- 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 == QLatin1String("text/plain"))
- 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 QLatin1String("Qt-Mime-Type");
-}
-
-QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("application/x-qt-mime-type-name"))
- return QLatin1String("com.trolltech.qt.MimeTypeName");
- 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(QLatin1String("x-qt-mime-type-name")).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 QLatin1String("PlainText (public.text)");
-}
-
-QString QMacPasteboardMimePlainTextFallback::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("text/plain"))
- return QLatin1String("public.text");
- return QString();
-}
-
-QString QMacPasteboardMimePlainTextFallback::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.text"))
- return QLatin1String("text/plain");
- 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 == QLatin1String("public.text")) {
- // 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 == QLatin1String("public.text"))
- 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 QLatin1String("UnicodeText");
-}
-
-QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("text/plain"))
- return QLatin1String("public.utf16-plain-text");
- int i = mime.indexOf(QLatin1String("charset="));
- if (i >= 0) {
- QString cs(mime.mid(i+8).toLower());
- i = cs.indexOf(QLatin1Char(';'));
- if (i>=0)
- cs = cs.left(i);
- if (cs == QLatin1String("system"))
- return QLatin1String("public.utf8-plain-text");
- else if (cs == QLatin1String("iso-10646-ucs-2")
- || cs == QLatin1String("utf16"))
- return QLatin1String("public.utf16-plain-text");
- }
- return QString();
-}
-
-QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text"))
- return QLatin1String("text/plain");
- return QString();
-}
-
-bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
-{
- return (mime == QLatin1String("text/plain")
- && (flav == QLatin1String("public.utf8-plain-text") || (flav == QLatin1String("public.utf16-plain-text"))));
-}
-
-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 == QLatin1String("public.utf8-plain-text")) {
- ret = QString::fromUtf8(firstData);
- } else if (flavor == QLatin1String("public.utf16-plain-text")) {
- 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 == QLatin1String("public.utf8-plain-text"))
- ret.append(string.toUtf8());
- else if (flavor == QLatin1String("public.utf16-plain-text")) {
- 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 QLatin1String("HTML");
-}
-
-QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("text/html"))
- return QLatin1String("public.html");
- return QString();
-}
-
-QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.html"))
- return QLatin1String("text/html");
- 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 QLatin1String("Rtf");
-}
-
-QString QMacPasteboardMimeRtfText::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("text/html"))
- return QLatin1String("public.rtf");
- return QString();
-}
-
-QString QMacPasteboardMimeRtfText::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.rtf"))
- return QLatin1String("text/html");
- 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 QLatin1String("FileURL");
-}
-
-QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
-{
- if (mime == QLatin1String("text/uri-list"))
- return QLatin1String("public.file-url");
- return QString();
-}
-
-QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.file-url"))
- return QLatin1String("text/uri-list");
- return QString();
-}
-
-bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
-{
- return mime == QLatin1String("text/uri-list") && flav == QLatin1String("public.file-url");
-}
-
-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() == QLatin1String("localhost"))
- 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(QLatin1String("file"));
- if (url.scheme() == QLatin1String("file")) {
- if (url.host().isEmpty())
- url.setHost(QLatin1String("localhost"));
- 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 QLatin1String("URL");
-}
-
-QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
-{
- if (mime.startsWith(QLatin1String("text/uri-list")))
- return QLatin1String("public.url");
- return QString();
-}
-
-QString QMacPasteboardMimeUrl::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.url"))
- return QLatin1String("text/uri-list");
- return QString();
-}
-
-bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
-{
- return flav == QLatin1String("public.url")
- && mime == QLatin1String("text/uri-list");
-}
-
-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() == QLatin1String("localhost"))
- 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(QLatin1String("file"));
- if (url.scheme() == QLatin1String("file")) {
- if (url.host().isEmpty())
- url.setHost(QLatin1String("localhost"));
- 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 QLatin1String("VCard");
-}
-
-bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
-{
- return mimeFor(flav) == mime;
-}
-
-QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
-{
- if (mime.startsWith(QLatin1String("text/vcard")))
- return QLatin1String("public.vcard");
- return QString();
-}
-
-QString QMacPasteboardMimeVCard::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.vcard"))
- return QLatin1String("text/vcard");
- return QString();
-}
-
-QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
-{
- QByteArray cards;
- if (mime == QLatin1String("text/vcard")) {
- 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 == QLatin1String("text/vcard"))
- 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 QLatin1String("Tiff");
-}
-
-QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
-{
- if (mime.startsWith(QLatin1String("application/x-qt-image")))
- return QLatin1String("public.tiff");
- return QString();
-}
-
-QString QMacPasteboardMimeTiff::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.tiff"))
- return QLatin1String("application/x-qt-image");
- return QString();
-}
-
-bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
-{
- return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
-}
-
-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 3082683834..0000000000
--- a/src/gui/platform/darwin/qmacmime_p.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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