// 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 "qiosclipboard.h" #ifndef QT_NO_CLIPBOARD #include #include #include #include @interface UIPasteboard (QUIPasteboard) + (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode; @end @implementation UIPasteboard (QUIPasteboard) + (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode { NSString *name = (mode == QClipboard::Clipboard) ? UIPasteboardNameGeneral : UIPasteboardNameFind; return [UIPasteboard pasteboardWithName:name create:NO]; } @end // -------------------------------------------------------------------- @interface QUIClipboard : NSObject @end @implementation QUIClipboard { QIOSClipboard *m_qiosClipboard; NSInteger m_changeCountClipboard; NSInteger m_changeCountFindBuffer; } - (instancetype)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard { self = [super init]; if (self) { m_qiosClipboard = qiosClipboard; m_changeCountClipboard = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::Clipboard].changeCount; m_changeCountFindBuffer = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::FindBuffer].changeCount; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePasteboardChanged:) name:UIPasteboardChangedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePasteboardChanged:) name:UIPasteboardRemovedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePasteboardChanged:) name:UIApplicationDidBecomeActiveNotification object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIPasteboardChangedNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIPasteboardRemovedNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; [super dealloc]; } - (void)updatePasteboardChanged:(NSNotification *)notification { Q_UNUSED(notification); NSInteger changeCountClipboard = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::Clipboard].changeCount; NSInteger changeCountFindBuffer = [UIPasteboard pasteboardWithQClipboardMode:QClipboard::FindBuffer].changeCount; if (m_changeCountClipboard != changeCountClipboard) { m_changeCountClipboard = changeCountClipboard; m_qiosClipboard->emitChanged(QClipboard::Clipboard); } if (m_changeCountFindBuffer != changeCountFindBuffer) { m_changeCountFindBuffer = changeCountFindBuffer; m_qiosClipboard->emitChanged(QClipboard::FindBuffer); } } @end // -------------------------------------------------------------------- QT_BEGIN_NAMESPACE class QIOSMimeData : public QMimeData { public: QIOSMimeData(QClipboard::Mode mode) : QMimeData(), m_mode(mode) { } ~QIOSMimeData() { } QStringList formats() const override; QVariant retrieveData(const QString &mimeType, QMetaType type) const override; private: const QClipboard::Mode m_mode; }; QStringList QIOSMimeData::formats() const { QStringList foundMimeTypes; UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; NSArray *pasteboardTypes = [pb pasteboardTypes]; for (NSUInteger i = 0; i < [pasteboardTypes count]; ++i) { QString uti = QString::fromNSString([pasteboardTypes objectAtIndex:i]); QString mimeType = QMacInternalPasteboardMime::flavorToMime(QMacInternalPasteboardMime::MIME_ALL, uti); if (!mimeType.isEmpty() && !foundMimeTypes.contains(mimeType)) foundMimeTypes << mimeType; } return foundMimeTypes; } QVariant QIOSMimeData::retrieveData(const QString &mimeType, QMetaType) const { UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; NSArray *pasteboardTypes = [pb pasteboardTypes]; foreach (QMacInternalPasteboardMime *converter, QMacInternalPasteboardMime::all(QMacInternalPasteboardMime::MIME_ALL)) { if (!converter->canConvert(mimeType, converter->flavorFor(mimeType))) continue; for (NSUInteger i = 0; i < [pasteboardTypes count]; ++i) { NSString *availableUtiNSString = [pasteboardTypes objectAtIndex:i]; QString availableUti = QString::fromNSString(availableUtiNSString); if (!converter->canConvert(mimeType, availableUti)) continue; NSData *nsData = [pb dataForPasteboardType:availableUtiNSString]; QList dataList; dataList << QByteArray(reinterpret_cast([nsData bytes]), [nsData length]); return converter->convertToMime(mimeType, dataList, availableUti); } } return QVariant(); } // -------------------------------------------------------------------- QIOSClipboard::QIOSClipboard() : m_clipboard([[QUIClipboard alloc] initWithQIOSClipboard:this]) { } QIOSClipboard::~QIOSClipboard() { qDeleteAll(m_mimeData); } QMimeData *QIOSClipboard::mimeData(QClipboard::Mode mode) { Q_ASSERT(supportsMode(mode)); if (!m_mimeData.contains(mode)) return *m_mimeData.insert(mode, new QIOSMimeData(mode)); return m_mimeData[mode]; } void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) { Q_ASSERT(supportsMode(mode)); UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:mode]; if (!mimeData) { pb.items = [NSArray *> array]; return; } mimeData->deleteLater(); NSMutableDictionary *pbItem = [NSMutableDictionary dictionaryWithCapacity:mimeData->formats().size()]; foreach (const QString &mimeType, mimeData->formats()) { foreach (QMacInternalPasteboardMime *converter, QMacInternalPasteboardMime::all(QMacInternalPasteboardMime::MIME_ALL)) { QString uti = converter->flavorFor(mimeType); if (uti.isEmpty() || !converter->canConvert(mimeType, uti)) continue; QVariant mimeDataAsVariant; if (mimeData->hasImage()) { mimeDataAsVariant = mimeData->imageData(); } else if (mimeData->hasUrls()) { QVariantList urlList; for (QUrl url : mimeData->urls()) urlList << url; mimeDataAsVariant = QVariant(urlList); } else { mimeDataAsVariant = QVariant(mimeData->data(mimeType)); } QByteArray byteArray = converter->convertFromMime(mimeType, mimeDataAsVariant, uti).first(); NSData *nsData = [NSData dataWithBytes:byteArray.constData() length:byteArray.size()]; [pbItem setValue:nsData forKey:uti.toNSString()]; break; } } pb.items = @[pbItem]; } bool QIOSClipboard::supportsMode(QClipboard::Mode mode) const { return (mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer); } bool QIOSClipboard::ownsMode(QClipboard::Mode mode) const { Q_UNUSED(mode); return false; } QT_END_NAMESPACE #endif // QT_NO_CLIPBOARD