5#include <AppKit/AppKit.h>
8#include <QtGui/private/qmacmimeregistry_p.h>
9#include <QtGui/qutimimeconverter.h>
10#include <QtGui/qclipboard.h>
11#include <QtGui/qguiapplication.h>
12#include <QtGui/qbitmap.h>
13#include <QtCore/qdatetime.h>
14#include <QtCore/qmetatype.h>
15#include <QtCore/qmimedata.h>
16#include <QtCore/qdebug.h>
17#include <QtCore/private/qcore_mac_p.h>
18#include <QtGui/qguiapplication.h>
19#include <QtGui/qevent.h>
20#include <QtCore/qurl.h>
28using namespace Qt::StringLiterals;
31
32
36OSStatus PasteboardGetItemCountSafe(PasteboardRef paste, ItemCount *cnt)
40 const OSStatus result = PasteboardGetItemCount(paste, cnt);
42 if (std::make_signed<ItemCount>::type(*cnt) < 0)
49#define PasteboardGetItemCount
60 : itemId(itemId), offset(o), converter(c), mime(m), dataRequestType(drt)
64 variantData =
static_cast<QMacMimeData *>(md)->variantData(m);
65 isPixmap = variantData.metaType().id() == QMetaType::QPixmap;
70 isPixmap = md->imageData().metaType().id() == QMetaType::QPixmap;
77 mac_mime_source =
false;
80 resolvingBeforeDestruction =
false;
86 mac_mime_source =
false;
88 OSStatus err = PasteboardCreate(
nullptr, &paste);
90 PasteboardSetPromiseKeeper(paste, promiseKeeper,
this);
92 qDebug(
"PasteBoard: Error creating pasteboard: [%d]", (
int)err);
93 resolvingBeforeDestruction =
false;
99 mac_mime_source =
false;
101 OSStatus err = PasteboardCreate(name, &paste);
103 PasteboardSetPromiseKeeper(paste, promiseKeeper,
this);
105 qDebug(
"PasteBoard: Error creating pasteboard: %s [%d]", QString::fromCFString(name).toLatin1().constData(), (
int)err);
107 resolvingBeforeDestruction =
false;
113
114
115
116 if (scope == QUtiMimeConverter::HandlerScopeFlag::DnD)
117 resolvingBeforeDestruction =
true;
118 PasteboardResolvePromises(paste);
128OSStatus
QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
129 CFStringRef uti,
void *_qpaste)
132 const long promise_id = (
long)id;
135 const QList<QUtiMimeConverter*> availableConverters = QMacMimeRegistry::all(QUtiMimeConverter::HandlerScopeFlag::All);
136 const QString utiAsQString = QString::fromCFString(uti);
138 for (
int i = 0; i < qpaste->promises.size(); i++){
140 if (!availableConverters.contains(tmp.converter)) {
150 if (tmp.itemId == promise_id && tmp.converter->canConvert(tmp.mime, utiAsQString)) {
156 if (!promise.itemId && utiAsQString ==
"com.trolltech.qt.MimeTypeName"_L1) {
160 const QCFType<CFDataRef> data = CFDataCreate(
nullptr, (UInt8*)ba.constData(), ba.size());
161 PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags);
165 if (!promise.itemId) {
168 qDebug(
"Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(utiAsQString));
169 return cantGetFlavorErr;
172 qCDebug(lcQpaClipboard,
"PasteBoard: Calling in promise for %s[%ld] [%s] [%d]", qPrintable(promise.mime), promise_id,
173 qPrintable(utiAsQString), promise.offset);
177 QVariant promiseData;
179 if (!qpaste->resolvingBeforeDestruction && !promise.mimeData.isNull()) {
180 if (promise.isPixmap && !QGuiApplication::instance()) {
181 qCWarning(lcQpaClipboard,
182 "Cannot keep promise, data contains QPixmap and requires livining QGuiApplication");
183 return cantGetFlavorErr;
185 promiseData =
static_cast<
QMacMimeData *>(promise.mimeData.data())->variantData(promise.mime);
188 promiseData = promise.variantData;
191 const QList<QByteArray> md = promise.converter->convertFromMime(promise.mime, promiseData, utiAsQString);
192 if (md.size() <= promise.offset)
193 return cantGetFlavorErr;
194 const QByteArray &ba = md[promise.offset];
195 const QCFType<CFDataRef> data = CFDataCreate(
nullptr, (UInt8*)ba.constData(), ba.size());
196 PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags);
208 if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
211 qCDebug(lcQpaClipboard,
"PasteBoard: hasUti [%s]", qPrintable(uti));
212 const QCFString c_uti(uti);
213 for (uint index = 1; index <= cnt; ++index) {
216 if (PasteboardGetItemIdentifier(paste, index, &id) != noErr)
219 PasteboardFlavorFlags flags;
220 if (PasteboardGetItemFlavorFlags(paste, id, c_uti, &flags) == noErr) {
221 qCDebug(lcQpaClipboard,
" - Found!");
225 qCDebug(lcQpaClipboard,
" - NotFound!");
238 return paste->retrieveData(format);
245 mac_mime_source =
true;
246 mime =
new QMacPasteboardMimeSource(
this);
257 if (mime == mime_src || (!mime_src && mime && mac_mime_source))
259 mac_mime_source =
false;
263 const QList<QUtiMimeConverter*> availableConverters = QMacMimeRegistry::all(scope);
264 if (mime !=
nullptr) {
266 QStringList formats = mime_src->formats();
271 QString dummyMimeType(
"application/x-qt-mime-type-name"_L1);
272 if (!formats.contains(dummyMimeType)) {
273 QByteArray dummyType = mime_src->data(dummyMimeType);
274 if (!dummyType.isEmpty())
275 formats.append(dummyMimeType);
277 for (
const auto &mimeType : formats) {
278 for (
const auto *c : availableConverters) {
282 if (c->utiForMime(
"text/html"_L1) ==
"public.rtf"_L1)
284 const QString uti(c->utiForMime(mimeType));
285 if (!uti.isEmpty()) {
287 const int numItems = c->count(mime_src);
288 for (
int item = 0; item < numItems; ++item) {
289 const NSInteger itemID = item + 1;
294 const QMacPasteboard::Promise promise(itemID, c, mimeType, mime_src, item, dataRequestType);
295 promises.append(promise);
296 PasteboardPutItemFlavor(paste,
reinterpret_cast<PasteboardItemID>(itemID), QCFString(uti), 0, kPasteboardFlavorNoFlags);
297 qCDebug(lcQpaClipboard,
" - adding %ld %s [%s] [%d]",
298 itemID, qPrintable(mimeType), qPrintable(uti), item);
309 return QStringList();
315 if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
318 qCDebug(lcQpaClipboard,
"PasteBoard: Formats [%d]", (
int)cnt);
319 for (uint index = 1; index <= cnt; ++index) {
322 if (PasteboardGetItemIdentifier(paste, index, &id) != noErr)
325 QCFType<CFArrayRef> types;
326 if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
329 const int type_count = CFArrayGetCount(types);
330 for (
int i = 0; i < type_count; ++i) {
331 const QString uti = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(types, i));
332 qCDebug(lcQpaClipboard,
" -%s", qPrintable(QString(uti)));
333 const QString mimeType = QMacMimeRegistry::flavorToMime(scope, uti);
334 if (!mimeType.isEmpty() && !ret.contains(mimeType)) {
335 qCDebug(lcQpaClipboard,
" -<%lld> %s [%s]", ret.size(), qPrintable(mimeType), qPrintable(QString(uti)));
351 if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
354 qCDebug(lcQpaClipboard,
"PasteBoard: hasFormat [%s]", qPrintable(format));
355 for (uint index = 1; index <= cnt; ++index) {
358 if (PasteboardGetItemIdentifier(paste, index, &id) != noErr)
361 QCFType<CFArrayRef> types;
362 if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
365 const int type_count = CFArrayGetCount(types);
366 for (
int i = 0; i < type_count; ++i) {
367 const QString uti = QString::fromCFString((CFStringRef)CFArrayGetValueAtIndex(types, i));
368 qCDebug(lcQpaClipboard,
" -%s [0x%x]", qPrintable(uti), uchar(scope));
369 QString mimeType = QMacMimeRegistry::flavorToMime(scope, uti);
370 if (!mimeType.isEmpty())
371 qCDebug(lcQpaClipboard,
" - %s", qPrintable(mimeType));
372 if (mimeType == format)
387 if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
390 qCDebug(lcQpaClipboard,
"Pasteboard: retrieveData [%s]", qPrintable(format));
391 const QList<QUtiMimeConverter *> availableConverters = QMacMimeRegistry::all(scope);
392 for (
const auto *c : availableConverters) {
393 const QString c_uti = c->utiForMime(format);
394 if (!c_uti.isEmpty()) {
398 if (c_uti ==
"com.apple.traditional-mac-plain-text"_L1
399 || c_uti ==
"public.utf8-plain-text"_L1
400 || c_uti ==
"public.utf16-plain-text"_L1) {
401 const QString str = qt_mac_get_pasteboardString(paste);
407 QList<QByteArray> retList;
408 for (uint index = 1; index <= cnt; ++index) {
410 if (PasteboardGetItemIdentifier(paste, index, &id) != noErr)
413 QCFType<CFArrayRef> types;
414 if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
417 const int type_count = CFArrayGetCount(types);
418 for (
int i = 0; i < type_count; ++i) {
419 const CFStringRef uti =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i));
420 if (c_uti == QString::fromCFString(uti)) {
421 QCFType<CFDataRef> macBuffer;
422 if (PasteboardCopyItemFlavorData(paste, id, uti, &macBuffer) == noErr) {
423 QByteArray buffer((
const char *)CFDataGetBytePtr(macBuffer),
424 CFDataGetLength(macBuffer));
425 if (!buffer.isEmpty()) {
426 qCDebug(lcQpaClipboard,
" - %s [%s]", qPrintable(format),
429 retList.append(buffer);
434 qCDebug(lcQpaClipboard,
" - NoMatch %s [%s]", qPrintable(c_uti),
435 qPrintable(QString::fromCFString(uti)));
440 if (!retList.isEmpty()) {
441 ret = c->convertToMime(format, retList, c_uti);
452 PasteboardClear(paste);
458 qCDebug(lcQpaClipboard,
"PasteBoard: clear!");
466 const bool fromGlobal = PasteboardSynchronize(paste) & kPasteboardModified;
472 qCDebug(lcQpaClipboard,
"Pasteboard: Synchronize!");
479 QMacAutoReleasePool pool;
480 NSPasteboard *pb = nil;
482 if (PasteboardCopyName(paste, &pbname) == noErr) {
483 pb = [NSPasteboard pasteboardWithName:
const_cast<NSString *>(
reinterpret_cast<
const NSString *>(pbname))];
486 pb = [NSPasteboard generalPasteboard];
489 NSString *text = [pb stringForType:NSPasteboardTypeString];
491 return QString::fromNSString(text);
QVariant variantData(const QString &mime)
QVariant retrieveData(const QString &format, QMetaType) const override
Returns a variant with the given type containing data for the MIME type specified by mimeType.
QMacPasteboardMimeSource(const QMacPasteboard *p)
~QMacPasteboardMimeSource()
QStringList formats() const override
Returns a list of formats supported by the object.
QMacPasteboard(QUtiMimeConverter::HandlerScope scope)
QStringList formats() const
PasteboardRef pasteBoard() const
QMimeData * mimeData() const
QMacPasteboard(PasteboardRef p, QUtiMimeConverter::HandlerScope scope=QUtiMimeConverter::HandlerScopeFlag::All)
bool hasUti(const QString &uti) const
QVariant retrieveData(const QString &format) const
bool hasFormat(const QString &format) const
QString qt_mac_get_pasteboardString(PasteboardRef paste)