8#include <QtGui/private/qinternalmimedata_p.h>
9#include <QtCore/qbytearraymatcher.h>
10#include <QtCore/qmap.h>
11#include <QtCore/qurl.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qdebug.h>
14#include <QtCore/qbuffer.h>
15#include <QtGui/qimagereader.h>
16#include <QtGui/qimagewriter.h>
23using namespace Qt::StringLiterals;
26
27
28
29#define BMP_LCS_sRGB 0x73524742
30#define BMP_LCS_GM_IMAGES 0x00000004L
71 QByteArray msg = func;
72 msg +=
": Unable to convert DIB image. The image converter plugin for '";
74 msg +=
"' is not available. Available formats: ";
75 const auto &formats = QImageReader::supportedImageFormats();
76 for (
const QByteArray &af : formats) {
83static inline bool readDib(QBuffer &buffer, QImage &img)
86 if (!reader.canRead()) {
87 qWarning(
"%s", msgConversionError(
__FUNCTION__, dibFormatC).constData());
94#if QT_CONFIG(imageformat_bmp)
95static QByteArray writeDib(
const QImage &img)
99 buffer.open(QIODevice::ReadWrite);
100 QImageWriter writer(&buffer, dibFormatC);
101 if (!writer.canWrite()) {
102 qWarning(
"%s", msgConversionError(
__FUNCTION__, dibFormatC).constData());
105 if (!writer.write(img))
110static bool qt_write_dibv5(QDataStream &s, QImage image)
112 QIODevice* d = s.device();
113 if (!d->isWritable())
117 qsizetype bpl_bmp = qsizetype(image.width()) * 4;
118 qsizetype size = bpl_bmp * image.height();
119 if (qsizetype(DWORD(size)) != size)
122 BMP_BITMAPV5HEADER bi;
123 ZeroMemory(&bi,
sizeof(bi));
124 bi.bV5Size =
sizeof(BMP_BITMAPV5HEADER);
125 bi.bV5Width = image.width();
126 bi.bV5Height = image.height();
129 bi.bV5Compression = BI_BITFIELDS;
130 bi.bV5SizeImage = DWORD(bpl_bmp * image.height());
131 bi.bV5XPelsPerMeter = 0;
132 bi.bV5YPelsPerMeter = 0;
134 bi.bV5ClrImportant = 0;
135 bi.bV5BlueMask = 0x000000ff;
136 bi.bV5GreenMask = 0x0000ff00;
137 bi.bV5RedMask = 0x00ff0000;
138 bi.bV5AlphaMask = 0xff000000;
139 bi.bV5CSType = BMP_LCS_sRGB;
140 bi.bV5Intent = BMP_LCS_GM_IMAGES;
142 d->write(
reinterpret_cast<
const char*>(&bi), bi.bV5Size);
143 if (s.status() != QDataStream::Ok)
146 d->write(
reinterpret_cast<
const char *>(&bi.bV5RedMask),
sizeof(bi.bV5RedMask));
147 if (s.status() != QDataStream::Ok)
150 d->write(
reinterpret_cast<
const char *>(&bi.bV5GreenMask),
sizeof(bi.bV5GreenMask));
151 if (s.status() != QDataStream::Ok)
154 d->write(
reinterpret_cast<
const char *>(&bi.bV5BlueMask),
sizeof(bi.bV5BlueMask));
155 if (s.status() != QDataStream::Ok)
158 if (image.format() != QImage::Format_ARGB32)
159 image = std::move(image).convertToFormat(QImage::Format_ARGB32);
161 auto *buf =
new uchar[bpl_bmp];
163 memset(buf, 0, size_t(bpl_bmp));
164 for (
int y=image.height()-1; y>=0; y--) {
166 const QRgb *p =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
167 const QRgb *end = p + image.width();
170 int alpha = qAlpha(*p);
172 *b++ = uchar(qBlue(*p));
173 *b++ = uchar(qGreen(*p));
174 *b++ = uchar(qRed(*p));
184 d->write(
reinterpret_cast<
const char *>(buf), bpl_bmp);
185 if (s.status() != QDataStream::Ok) {
197static int getCf(
const FORMATETC &formatetc)
199 return formatetc.cfFormat;
205 formatetc.cfFormat = CLIPFORMAT(cf);
206 formatetc.dwAspect = DVASPECT_CONTENT;
207 formatetc.lindex = -1;
208 formatetc.ptd =
nullptr;
209 formatetc.tymed = TYMED_HGLOBAL;
213static bool setData(
const QByteArray &data, STGMEDIUM *pmedium)
215 HGLOBAL hData = GlobalAlloc(0, SIZE_T(data.size()));
219 void *out = GlobalLock(hData);
220 memcpy(out, data.data(), size_t(data.size()));
222 pmedium->tymed = TYMED_HGLOBAL;
223 pmedium->hGlobal = hData;
224 pmedium->pUnkForRelease =
nullptr;
231 FORMATETC formatetc = setCf(cf);
232 formatetc.lindex = lindex;
234 if (pDataObj->GetData(&formatetc, &s) == S_OK) {
235 const void *val = GlobalLock(s.hGlobal);
236 data = QByteArray::fromRawData(
reinterpret_cast<
const char *>(val),
int(GlobalSize(s.hGlobal)));
238 GlobalUnlock(s.hGlobal);
239 ReleaseStgMedium(&s);
242 formatetc.tymed = TYMED_ISTREAM;
243 if (pDataObj->GetData(&formatetc, &s) == S_OK) {
245 ULONG actualRead = 0;
246 LARGE_INTEGER pos = {{0, 0}};
248 HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET,
nullptr);
249 while(SUCCEEDED(hr)){
250 hr = s.pstm->Read(szBuffer,
sizeof(szBuffer), &actualRead);
251 if (SUCCEEDED(hr) && actualRead > 0) {
252 data += QByteArray::fromRawData(szBuffer,
int(actualRead));
254 if (actualRead !=
sizeof(szBuffer))
258 ReleaseStgMedium(&s);
266 FORMATETC formatetc = setCf(cf);
267 if (pDataObj->QueryGetData(&formatetc) != S_OK){
268 formatetc.tymed = TYMED_ISTREAM;
269 return pDataObj->QueryGetData(&formatetc) == S_OK;
274#ifndef QT_NO_DEBUG_STREAM
275QDebug operator<<(QDebug d,
const FORMATETC &tc)
277 QDebugStateSaver saver(d);
279 d <<
"FORMATETC(cfFormat=" << tc.cfFormat <<
' ';
280 switch (tc.cfFormat) {
300 d <<
"CF_UNICODETEXT";
303 d <<
"CF_ENHMETAFILE";
306 d << QWindowsMimeRegistry::clipboardFormatName(tc.cfFormat);
309 d <<
", dwAspect=" << tc.dwAspect <<
", lindex=" << tc.lindex
310 <<
", tymed=" << tc.tymed <<
", ptd=" << tc.ptd <<
')';
314QDebug operator<<(QDebug d, IDataObject *dataObj)
316 QDebugStateSaver saver(d);
321 IEnumFORMATETC *enumFormatEtc;
322 if (SUCCEEDED(dataObj->EnumFormatEtc(DATADIR_GET, &enumFormatEtc)) && enumFormatEtc) {
323 FORMATETC formatEtc[1];
325 if (SUCCEEDED(enumFormatEtc->Reset())) {
326 while (SUCCEEDED(enumFormatEtc->Next(1, formatEtc, &fetched)) && fetched)
327 d << formatEtc[0] <<
',';
328 enumFormatEtc->Release();
346 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM *pmedium)
const override;
352 int cf = getCf(formatetc);
353 return (cf == CF_UNICODETEXT || (cf == CF_TEXT && GetACP() != CP_UTF8)) && mimeData->hasText();
357
358
359
360
363 if (canConvertFromMime(formatetc, mimeData)) {
365 int cf = getCf(formatetc);
367 data = mimeData->text().toLocal8Bit();
369 int maxsize=data.size()+data.size()/40+3;
370 QByteArray r(maxsize,
'\0');
372 const char* d = data.data();
373 const int s = data.size();
376 for (
int i=0; i<s; i++) {
388 if (j+3 >= maxsize) {
389 maxsize += maxsize/4;
395 return setData(r, pmedium);
397 if (cf == CF_UNICODETEXT) {
398 QString str = mimeData->text();
399 const QChar *u = str.unicode();
401 const int s = str.length();
402 int maxsize = s + s/40 + 3;
406 for (
int i=0; i < s; ++i) {
410 if (*u == u'\n' && !cr)
415 if (ri+3 >= maxsize) {
416 maxsize += maxsize/4;
422 const int byteLength = res.length() *
int(
sizeof(ushort));
423 QByteArray r(byteLength + 2,
'\0');
424 memcpy(r.data(), res.unicode(), size_t(byteLength));
427 return setData(r, pmedium);
435 return mimeType.startsWith(u"text/plain")
436 && (canGetData(CF_UNICODETEXT, pDataObj)
437 || canGetData(CF_TEXT, pDataObj));
442 int cf = getCf(formatetc);
443 if (cf == CF_UNICODETEXT || cf == CF_TEXT)
444 return u"text/plain"_s;
451 QList<FORMATETC> formatics;
452 if (mimeType.startsWith(u"text/plain") && mimeData->hasText()) {
453 formatics += setCf(CF_UNICODETEXT);
454 if (GetACP() != CP_UTF8)
455 formatics += setCf(CF_TEXT);
464 if (canConvertToMime(mime, pDataObj)) {
466 QByteArray data = getData(CF_UNICODETEXT, pDataObj);
467 if (!data.isEmpty()) {
468 str = QString::fromWCharArray(
reinterpret_cast<
const wchar_t *>(data.constData()));
469 str.replace(
"\r\n"_L1,
"\n"_L1);
471 data = getData(CF_TEXT, pDataObj);
472 if (!data.isEmpty()) {
473 const char* d = data.data();
474 const unsigned s =
unsigned(qstrlen(d));
475 QByteArray r(data.size()+1,
'\0');
478 for (
unsigned i = 0; i < s; ++i) {
484 str = QString::fromLocal8Bit(r);
487 if (preferredType.id() == QMetaType::QString)
490 ret =
std::move(str).toUtf8();
492 qCDebug(lcQpaMime) <<
__FUNCTION__ << ret;
504 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM *pmedium)
const override;
513 CF_INETURL_W = registerMimeType(u"UniformResourceLocatorW"_s);
514 CF_INETURL = registerMimeType(u"UniformResourceLocator"_s);
519 if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) {
520 const auto urls = mimeData->urls();
521 for (
const QUrl &url : urls) {
522 if (url.isLocalFile())
526 return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls();
531 if (canConvertFromMime(formatetc, mimeData)) {
532 if (getCf(formatetc) == CF_HDROP) {
533 const auto &urls = mimeData->urls();
534 QStringList fileNames;
535 size_t size =
sizeof(DROPFILES) + 2;
536 for (
const QUrl &url : urls) {
537 const QString fn = QDir::toNativeSeparators(url.toLocalFile());
539 size +=
sizeof(ushort) * size_t(fn.length() + 1);
540 fileNames.append(fn);
544 QByteArray result(
int(size),
'\0');
545 auto* d =
reinterpret_cast<DROPFILES *>(result.data());
546 d->pFiles =
sizeof(DROPFILES);
547 GetCursorPos(&d->pt);
549 char *files = (
reinterpret_cast<
char*>(d)) + d->pFiles;
552 auto *f =
reinterpret_cast<
wchar_t *>(files);
553 for (
int i=0; i<fileNames.size(); i++) {
554 const auto l = size_t(fileNames.at(i).length());
555 memcpy(f, fileNames.at(i).data(), l *
sizeof(ushort));
561 return setData(result, pmedium);
563 if (getCf(formatetc) == CF_INETURL_W) {
564 const auto urls = mimeData->urls();
566 if (!urls.isEmpty()) {
567 const QString url = urls.at(0).toString();
568 result = QByteArray(
reinterpret_cast<
const char *>(url.data()),
569 url.length() *
int(
sizeof(ushort)));
573 return setData(result, pmedium);
575 if (getCf(formatetc) == CF_INETURL) {
576 const auto urls = mimeData->urls();
579 result = urls.at(0).toString().toLocal8Bit();
580 return setData(result, pmedium);
589 return mimeType == u"text/uri-list"
590 && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
596 if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
597 format = u"text/uri-list"_s;
603 QList<FORMATETC> formatics;
604 if (mimeType == u"text/uri-list") {
605 if (canConvertFromMime(setCf(CF_HDROP), mimeData))
606 formatics += setCf(CF_HDROP);
607 if (canConvertFromMime(setCf(CF_INETURL_W), mimeData))
608 formatics += setCf(CF_INETURL_W);
609 if (canConvertFromMime(setCf(CF_INETURL), mimeData))
610 formatics += setCf(CF_INETURL);
617 if (mimeType == u"text/uri-list") {
618 if (canGetData(CF_HDROP, pDataObj)) {
619 QList<QVariant> urls;
621 QByteArray data = getData(CF_HDROP, pDataObj);
625 const auto *hdrop =
reinterpret_cast<
const DROPFILES *>(data.constData());
627 const auto *filesw =
reinterpret_cast<
const wchar_t *>(data.constData() + hdrop->pFiles);
630 QString fileurl = QString::fromWCharArray(filesw + i);
631 urls += QUrl::fromLocalFile(fileurl);
632 i += fileurl.length()+1;
635 const char* files =
reinterpret_cast<
const char *>(data.constData() + hdrop->pFiles);
638 urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
639 i +=
int(strlen(files+i))+1;
643 if (preferredType.id() == QMetaType::QUrl && urls.size() == 1)
647 }
else if (canGetData(CF_INETURL_W, pDataObj)) {
648 QByteArray data = getData(CF_INETURL_W, pDataObj);
651 return QUrl(QString::fromWCharArray(
reinterpret_cast<
const wchar_t *>(data.constData())));
652 }
else if (canGetData(CF_INETURL, pDataObj)) {
653 QByteArray data = getData(CF_INETURL, pDataObj);
656 return QUrl(QString::fromLocal8Bit(data.constData()));
669 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM * pmedium)
const override;
683 CF_HTML = registerMimeType(u"HTML Format"_s);
688 QList<FORMATETC> formatetcs;
689 if (mimeType == u"text/html" && (!mimeData->html().isEmpty()))
690 formatetcs += setCf(CF_HTML);
696 if (getCf(formatetc) == CF_HTML)
697 return u"text/html"_s;
703 return mimeType == u"text/html" && canGetData(CF_HTML, pDataObj);
709 return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
713
714
715
716
717
718
719
720
721
722
723
726 Q_UNUSED(preferredType);
728 if (canConvertToMime(mime, pDataObj)) {
729 QByteArray html = getData(CF_HTML, pDataObj);
730 static constexpr auto startMatcher = qMakeStaticByteArrayMatcher(
"StartHTML:");
731 static constexpr auto endMatcher = qMakeStaticByteArrayMatcher(
"EndHTML:");
732 qCDebug(lcQpaMime) <<
__FUNCTION__ <<
"raw:" << html;
733 int start = startMatcher.indexIn(html);
734 int end = endMatcher.indexIn(html);
737 int startOffset = start + 10;
739 while (html.at(i) !=
'\r' && html.at(i) !=
'\n')
741 QByteArray bytecount = html.mid(startOffset, i - startOffset);
742 start = bytecount.toInt();
746 int endOffset = end + 8;
748 while (html.at(i) !=
'\r' && html.at(i) !=
'\n')
750 QByteArray bytecount = html.mid(endOffset , i - endOffset);
751 end = bytecount.toInt();
754 if (end > start && start > 0) {
755 html = html.mid(start, end - start);
756 html.replace(
'\r',
"");
757 result = QString::fromUtf8(html);
765 if (canConvertFromMime(formatetc, mimeData)) {
766 QByteArray data = mimeData->html().toUtf8();
769 "StartHTML:0000000107\r\n"
770 "EndHTML:0000000000\r\n"
771 "StartFragment:0000000000\r\n"
772 "EndFragment:0000000000\r\n\r\n";
774 static constexpr auto startFragmentMatcher = qMakeStaticByteArrayMatcher(
"<!--StartFragment-->");
775 static constexpr auto endFragmentMatcher = qMakeStaticByteArrayMatcher(
"<!--EndFragment-->");
777 if (startFragmentMatcher.indexIn(data) == -1)
778 result +=
"<!--StartFragment-->";
780 if (endFragmentMatcher.indexIn(data) == -1)
781 result +=
"<!--EndFragment-->";
784 QByteArray pos = QByteArray::number(result.size());
785 memcpy(
reinterpret_cast<
char *>(result.data() + 53 - pos.length()), pos.constData(), size_t(pos.length()));
788 pos = QByteArray::number(startFragmentMatcher.indexIn(result) + 20);
789 memcpy(
reinterpret_cast<
char *>(result.data() + 79 - pos.length()), pos.constData(), size_t(pos.length()));
790 pos = QByteArray::number(endFragmentMatcher.indexIn(result));
791 memcpy(
reinterpret_cast<
char *>(result.data() + 103 - pos.length()), pos.constData(), size_t(pos.length()));
793 return setData(result, pmedium);
799#if QT_CONFIG(imageformat_bmp)
800class QWindowsMimeImage :
public QWindowsMimeConverter
805 bool canConvertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData)
const override;
806 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM * pmedium)
const override;
807 QList<FORMATETC> formatsForMime(
const QString &mimeType,
const QMimeData *mimeData)
const override;
810 bool canConvertToMime(
const QString &mimeType, IDataObject *pDataObj)
const override;
811 QVariant convertToMime(
const QString &mime, IDataObject *pDataObj, QMetaType preferredType)
const override;
812 QString mimeForFormat(
const FORMATETC &formatetc)
const override;
814 bool hasOriginalDIBV5(IDataObject *pDataObj)
const;
818QWindowsMimeImage::QWindowsMimeImage()
820 CF_PNG = RegisterClipboardFormat(L"PNG");
823QList<FORMATETC> QWindowsMimeImage::formatsForMime(
const QString &mimeType,
const QMimeData *mimeData)
const
825 QList<FORMATETC> formatetcs;
826 if (mimeData->hasImage() && mimeType == u"application/x-qt-image") {
828 auto image = qvariant_cast<QImage>(mimeData->imageData());
829 if (!image.isNull() && image.hasAlphaChannel())
830 formatetcs += setCf(CF_DIBV5);
831 formatetcs += setCf(CF_DIB);
833 if (!formatetcs.isEmpty())
834 qCDebug(lcQpaMime) <<
__FUNCTION__ << mimeType << formatetcs;
838QString QWindowsMimeImage::mimeForFormat(
const FORMATETC &formatetc)
const
840 int cf = getCf(formatetc);
841 if (cf == CF_DIB || cf == CF_DIBV5 || cf ==
int(CF_PNG))
842 return u"application/x-qt-image"_s;
846bool QWindowsMimeImage::canConvertToMime(
const QString &mimeType, IDataObject *pDataObj)
const
848 return mimeType == u"application/x-qt-image"
849 && (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj));
852bool QWindowsMimeImage::canConvertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData)
const
854 int cf = getCf(formatetc);
855 if (!mimeData->hasImage())
857 const QImage image = qvariant_cast<QImage>(mimeData->imageData());
862 return cf == CF_DIBV5 || cf == CF_DIB
863 || (cf ==
int(CF_PNG) && image.hasAlphaChannel());
866bool QWindowsMimeImage::convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM * pmedium)
const
868 int cf = getCf(formatetc);
869 if ((cf == CF_DIB || cf == CF_DIBV5 || cf ==
int(CF_PNG)) && mimeData->hasImage()) {
870 auto img = qvariant_cast<QImage>(mimeData->imageData());
875 if (img.format() > QImage::Format_ARGB32)
876 img = std::move(img).convertToFormat(QImage::Format_RGB32);
877 const QByteArray ba = writeDib(img);
879 return setData(ba, pmedium);
880 }
else if (cf ==
int(CF_PNG)) {
882 const bool written = buffer.open(QIODevice::WriteOnly) && img.save(&buffer,
"PNG");
885 return setData(ba, pmedium);
887 QDataStream s(&ba, QIODevice::WriteOnly);
888 s.setByteOrder(QDataStream::LittleEndian);
889 if (qt_write_dibv5(s, std::move(img)))
890 return setData(ba, pmedium);
896bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj)
const
898 bool isSynthesized =
true;
899 IEnumFORMATETC *pEnum =
nullptr;
900 HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
901 if (res == S_OK && pEnum) {
903 while ((res = pEnum->Next(1, &fc,
nullptr)) == S_OK) {
905 CoTaskMemFree(fc.ptd);
906 if (fc.cfFormat == CF_DIB)
908 if (fc.cfFormat == CF_DIBV5) {
909 isSynthesized =
false;
915 return !isSynthesized;
918QVariant QWindowsMimeImage::convertToMime(
const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType)
const
920 Q_UNUSED(preferredType);
922 if (mimeType != u"application/x-qt-image")
926 const bool canGetDibV5 = canGetData(CF_DIBV5, pDataObj);
927 const bool hasOrigDibV5 = canGetDibV5 ? hasOriginalDIBV5(pDataObj) :
false;
928 qCDebug(lcQpaMime) <<
"canGetDibV5:" << canGetDibV5 <<
"hasOrigDibV5:" << hasOrigDibV5;
930 if (!hasOrigDibV5 && canGetData(CF_PNG, pDataObj)) {
931 qCDebug(lcQpaMime) <<
"Decoding PNG";
933 QByteArray data = getData(CF_PNG, pDataObj);
934 if (img.loadFromData(data,
"PNG")) {
939 if (canGetDibV5 || canGetData(CF_DIB, pDataObj)) {
940 qCDebug(lcQpaMime) <<
"Decoding DIB";
942 QByteArray data = getData(canGetDibV5 ? CF_DIBV5 : CF_DIB, pDataObj);
943 QBuffer buffer(&data);
944 if (readDib(buffer, img))
959 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM * pmedium)
const override;
968 QMap<
int, QString> outFormats;
969 QMap<
int, QString> inFormats;
975 outFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s);
976 inFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s);
982 return formatetc.tymed & TYMED_HGLOBAL
983 && outFormats.contains(formatetc.cfFormat)
984 && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
989 if (canConvertFromMime(formatetc, mimeData)) {
991 if (outFormats.value(getCf(formatetc)) == u"text/html") {
993 QString html = mimeData->html();
995 const QChar *u = html.unicode();
997 const int s = html.length();
998 int maxsize = s + s/40 + 3;
1002 for (
int i=0; i < s; ++i) {
1006 if (*u == u'\n' && !cr)
1011 if (ri+3 >= maxsize) {
1012 maxsize += maxsize/4;
1013 res.resize(maxsize);
1018 const int byteLength = res.length() *
int(
sizeof(ushort));
1019 QByteArray r(byteLength + 2,
'\0');
1020 memcpy(r.data(), res.unicode(), size_t(byteLength));
1022 r[byteLength+1] = 0;
1025#if QT_CONFIG(draganddrop)
1026 data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
1029 return setData(data, pmedium);
1036 QList<FORMATETC> formatetcs;
1037 const auto mit =
std::find(outFormats.cbegin(), outFormats.cend(), mimeType);
1038 if (mit != outFormats.cend() && mimeData->formats().contains(mimeType))
1039 formatetcs += setCf(mit.key());
1045 const auto mit =
std::find(inFormats.cbegin(), inFormats.cend(), mimeType);
1046 return mit != inFormats.cend() && canGetData(mit.key(), pDataObj);
1052 if (canConvertToMime(mimeType, pDataObj)) {
1053 QByteArray data = getData(inFormats.key(mimeType), pDataObj);
1054 if (!data.isEmpty()) {
1055 qCDebug(lcQpaMime) <<
__FUNCTION__;
1056 if (mimeType == u"text/html" && preferredType == QMetaType(QMetaType::QString)) {
1058 val = QString::fromWCharArray(
reinterpret_cast<
const wchar_t *>(data.constData()));
1069 return inFormats.value(getCf(formatetc));
1080 bool convertFromMime(
const FORMATETC &formatetc,
const QMimeData *mimeData, STGMEDIUM * pmedium)
const override;
1089 mutable QMap<
int, QString> formats;
1090 static QStringList ianaTypes;
1091 static QStringList excludeList;
1100 if (ianaTypes.isEmpty()) {
1101 ianaTypes.append(u"application/"_s);
1102 ianaTypes.append(u"audio/"_s);
1103 ianaTypes.append(u"example/"_s);
1104 ianaTypes.append(u"image/"_s);
1105 ianaTypes.append(u"message/"_s);
1106 ianaTypes.append(u"model/"_s);
1107 ianaTypes.append(u"multipart/"_s);
1108 ianaTypes.append(u"text/"_s);
1109 ianaTypes.append(u"video/"_s);
1112 if (excludeList.isEmpty()) {
1113 excludeList.append(u"HTML Format"_s);
1114 excludeList.append(u"UniformResourceLocator"_s);
1115 excludeList.append(u"text/html"_s);
1116 excludeList.append(u"text/plain"_s);
1117 excludeList.append(u"text/uri-list"_s);
1118 excludeList.append(u"application/x-qt-image"_s);
1119 excludeList.append(u"application/x-color"_s);
1126#if QT_CONFIG(draganddrop)
1127 return formatetc.tymed & TYMED_HGLOBAL
1128 && (formats.contains(formatetc.cfFormat)
1129 && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
1132 Q_UNUSED(formatetc);
1133 return formatetc.tymed & TYMED_HGLOBAL
1134 && formats.contains(formatetc.cfFormat);
1140#if QT_CONFIG(draganddrop)
1141 return canConvertFromMime(formatetc, mimeData)
1142 && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
1145 Q_UNUSED(formatetc);
1153 QList<FORMATETC> formatetcs;
1154 auto mit =
std::find(formats.begin(), formats.end(), mimeType);
1156 if (mit == formats.end() && !excludeList.contains(mimeType, Qt::CaseInsensitive))
1157 mit = formats.insert(registerMimeType(mimeType), mimeType);
1158 if (mit != formats.end())
1159 formatetcs += setCf(mit.key());
1161 if (!formatetcs.isEmpty())
1162 qCDebug(lcQpaMime) <<
__FUNCTION__ << mimeType << formatetcs;
1169 return mimeType.startsWith(QLatin1StringView(
x_qt_windows_mime), Qt::CaseInsensitive);
1175 int n = mimeType.lastIndexOf(u'\"') - len;
1176 QString ret = mimeType.mid(len, n);
1178 const int beginPos = mimeType.indexOf(u";index=");
1179 if (beginPos > -1) {
1180 const int endPos = mimeType.indexOf(u';', beginPos + 1);
1181 const int indexStartPos = beginPos + 7;
1183 *lindex = QStringView{mimeType}.mid(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt();
1193 if (isCustomMimeType(mimeType)) {
1195 QString clipFormat = customMimeType(mimeType);
1196 const UINT cf = RegisterClipboardFormat(
reinterpret_cast<
const wchar_t *> (clipFormat.utf16()));
1197 return canGetData(
int(cf), pDataObj);
1200 const auto mit =
std::find(formats.cbegin(), formats.cend(), mimeType);
1201 const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType);
1202 return canGetData(cf, pDataObj);
1207 Q_UNUSED(preferredType);
1209 if (canConvertToMime(mimeType, pDataObj)) {
1211 if (isCustomMimeType(mimeType)) {
1213 QString clipFormat = customMimeType(mimeType, &lindex);
1214 const UINT cf = RegisterClipboardFormat(
reinterpret_cast<
const wchar_t *> (clipFormat.utf16()));
1215 data = getData(
int(cf), pDataObj, lindex);
1217 const auto mit =
std::find(formats.cbegin(), formats.cend(), mimeType);
1218 const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType);
1219 data = getData(cf, pDataObj);
1221 if (!data.isEmpty())
1229 QString format = formats.value(getCf(formatetc));
1230 if (!format.isEmpty())
1233 const QString clipFormat = QWindowsMimeRegistry::clipboardFormatName(getCf(formatetc));
1234 if (!clipFormat.isEmpty()) {
1235#if QT_CONFIG(draganddrop)
1236 if (QInternalMimeData::canReadData(clipFormat))
1237 format = clipFormat;
1238 else if ((formatetc.cfFormat >= 0xC000)){
1240 if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
1242 bool ianaType =
false;
1243 int sz = ianaTypes.size();
1244 for (
int i = 0; i < sz; i++) {
1245 if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) {
1251 format = QLatin1StringView(x_qt_windows_mime) + clipFormat + u'"';
1253 format = clipFormat;
1263
1264
1265
1266
1267
1273 qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount);
1278 ensureInitialized();
1279 for (
int i = m_mimes.size()-1; i >= 0; --i) {
1280 if (m_mimes.at(i)->canConvertToMime(mimeType, pDataObj))
1281 return m_mimes.at(i);
1288 qCDebug(lcQpaMime) <<
"QWindowsMimeConverter::allMimesForFormats()";
1289 ensureInitialized();
1290 QStringList formats;
1291 LPENUMFORMATETC FAR fmtenum;
1292 HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum);
1294 if (hr == NOERROR) {
1296 while (S_OK == fmtenum->Next(1, &fmtetc,
nullptr)) {
1297 for (
int i= m_mimes.size() - 1; i >= 0; --i) {
1298 QString format = m_mimes.at(i)->mimeForFormat(fmtetc);
1299 if (!format.isEmpty() && !formats.contains(format)) {
1302 qCDebug(lcQpaMime) <<
__FUNCTION__ << fmtetc << format;
1307 CoTaskMemFree(fmtetc.ptd);
1311 qCDebug(lcQpaMime) << pDataObj << formats;
1317 ensureInitialized();
1318 qCDebug(lcQpaMime) <<
__FUNCTION__ << formatetc;
1319 for (
int i = m_mimes.size()-1; i >= 0; --i) {
1320 if (m_mimes.at(i)->canConvertFromMime(formatetc, mimeData))
1321 return m_mimes.at(i);
1328 ensureInitialized();
1329 QList<FORMATETC> formatics;
1330#if !QT_CONFIG(draganddrop)
1333 formatics.reserve(20);
1334 const QStringList formats = QInternalMimeData::formatsHelper(mimeData);
1335 for (
int f = 0; f < formats.size(); ++f) {
1336 for (
int i = m_mimes.size() - 1; i >= 0; --i)
1337 formatics += m_mimes.at(i)->formatsForMime(formats.at(f), mimeData);
1345 if (m_internalMimeCount == 0) {
1346 m_internalMimeCount = -1;
1347#if QT_CONFIG(imageformat_bmp)
1348 (
void)
new QWindowsMimeImage;
1355 m_internalMimeCount = m_mimes.size();
1356 Q_ASSERT(m_internalMimeCount > 0);
1362 wchar_t buf[256] = {0};
1363 return GetClipboardFormatName(UINT(cf), buf, 255)
1364 ? QString::fromWCharArray(buf) : QString();
1368 IDataObject *pDataObj,
1369 QMetaType preferredType,
1370 QString *formatIn )
const
1372 for (
const QString &format : mimeTypes) {
1373 if (
const QWindowsMimeConverter *converter = converterToMime(format, pDataObj)) {
1374 if (converter->canConvertToMime(format, pDataObj)) {
1375 const QVariant dataV = converter->convertToMime(format, pDataObj, preferredType);
1376 if (dataV.isValid()) {
1377 qCDebug(lcQpaMime) <<
__FUNCTION__ << mimeTypes <<
"\nFormat: "
1378 << format << pDataObj <<
" returns " << dataV;
1386 qCDebug(lcQpaMime) <<
__FUNCTION__ <<
"fails" << mimeTypes << pDataObj << preferredType.id();
1392 ensureInitialized();
1393 m_mimes.append(mime);
1397
1398
1399
1400
1401
1402
1405 const QString mimeType = isCustomMimeType(mime) ? customMimeType(mime) : mime;
1406 const UINT f = RegisterClipboardFormat(
reinterpret_cast<
const wchar_t *> (mimeType.utf16()));
1408 qErrnoWarning(
"QWindowsMimeRegistry::registerMimeType: Failed to register clipboard format "
1409 "for %s", qPrintable(mime));
QList< FORMATETC > formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override
Returns a QList of FORMATETC structures representing the different windows clipboard formats that can...
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override
Returns true if the converter can convert to the mimeType from the available formats in pDataObj.
QString mimeForFormat(const FORMATETC &formatetc) const override
Returns the mime type that will be created form the format specified in formatetc,...
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override
Convert the mimeData to the format specified in formatetc.
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override
Returns a QVariant containing the converted data for mimeType from pDataObj.
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override
Returns true if the converter can convert from the mimeData to the format specified in formatetc.
QString mimeForFormat(const FORMATETC &formatetc) const override
Returns the mime type that will be created form the format specified in formatetc,...
QList< FORMATETC > formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override
Returns a QList of FORMATETC structures representing the different windows clipboard formats that can...
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override
Returns true if the converter can convert to the mimeType from the available formats in pDataObj.
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override
Returns a QVariant containing the converted data for mimeType from pDataObj.
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override
Convert the mimeData to the format specified in formatetc.
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override
Returns true if the converter can convert from the mimeData to the format specified in formatetc.
Singleton container for all relevant information.
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override
Returns true if the converter can convert from the mimeData to the format specified in formatetc.
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override
Convert the mimeData to the format specified in formatetc.
QString mimeForFormat(const FORMATETC &formatetc) const override
Returns the mime type that will be created form the format specified in formatetc,...
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override
Returns true if the converter can convert to the mimeType from the available formats in pDataObj.
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override
Returns a QVariant containing the converted data for mimeType from pDataObj.
QList< FORMATETC > formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override
Returns a QList of FORMATETC structures representing the different windows clipboard formats that can...
Manages the list of QWindowsMimeConverter instances.
QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QMetaType preferredType, QString *format=nullptr) const
QWindowsMimeConverter * converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
void registerMime(QWindowsMimeConverter *mime)
QStringList allMimesForFormats(IDataObject *pDataObj) const
QList< FORMATETC > allFormatsForMime(const QMimeData *mimeData) const
QWindowsMimeConverter * converterToMime(const QString &mimeType, IDataObject *pDataObj) const
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override
Returns true if the converter can convert to the mimeType from the available formats in pDataObj.
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override
Returns true if the converter can convert from the mimeData to the format specified in formatetc.
QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override
Convert the mimeData to the format specified in formatetc.
QList< FORMATETC > formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override
Returns a QList of FORMATETC structures representing the different windows clipboard formats that can...
QString mimeForFormat(const FORMATETC &formatetc) const override
Returns the mime type that will be created form the format specified in formatetc,...
QList< FORMATETC > formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override
Returns a QList of FORMATETC structures representing the different windows clipboard formats that can...
QString mimeForFormat(const FORMATETC &formatetc) const override
Returns the mime type that will be created form the format specified in formatetc,...
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override
Returns true if the converter can convert to the mimeType from the available formats in pDataObj.
QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override
Returns true if the converter can convert from the mimeData to the format specified in formatetc.
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override
Convert the mimeData to the format specified in formatetc.
static QByteArray msgConversionError(const char *func, const char *format)
static const char x_qt_windows_mime[]
static bool canGetData(int cf, IDataObject *pDataObj)
static FORMATETC setCf(int cf)
static bool isCustomMimeType(const QString &mimeType)
static QByteArray getData(int cf, IDataObject *pDataObj, int lindex=-1)
static QString customMimeType(const QString &mimeType, int *lindex=nullptr)
static bool readDib(QBuffer &buffer, QImage &img)
static int getCf(const FORMATETC &formatetc)
static const char dibFormatC[]
static bool setData(const QByteArray &data, STGMEDIUM *pmedium)