Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsmimeregistry.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
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>
17
18#include <shlobj.h>
19#include <algorithm>
20
21QT_BEGIN_NAMESPACE
22
23using namespace Qt::StringLiterals;
24
25/* The MSVC compilers allows multi-byte characters, that has the behavior of
26 * that each character gets shifted into position. 0x73524742 below is for MSVC
27 * equivalent to doing 'sRGB', but this does of course not work
28 * on conformant C++ compilers. */
29#define BMP_LCS_sRGB 0x73524742
30#define BMP_LCS_GM_IMAGES 0x00000004L
31
32struct _CIEXYZ {
34};
35
39
66
67static const char dibFormatC[] = "dib";
68
69static inline QByteArray msgConversionError(const char *func, const char *format)
70{
71 QByteArray msg = func;
72 msg += ": Unable to convert DIB image. The image converter plugin for '";
73 msg += format;
74 msg += "' is not available. Available formats: ";
75 const auto &formats = QImageReader::supportedImageFormats();
76 for (const QByteArray &af : formats) {
77 msg += af;
78 msg += ' ';
79 }
80 return msg;
81}
82
83static inline bool readDib(QBuffer &buffer, QImage &img)
84{
85 QImageReader reader(&buffer, dibFormatC);
86 if (!reader.canRead()) {
87 qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
88 return false;
89 }
90 img = reader.read();
91 return true;
92}
93
94#if QT_CONFIG(imageformat_bmp)
95static QByteArray writeDib(const QImage &img)
96{
97 QByteArray ba;
98 QBuffer buffer(&ba);
99 buffer.open(QIODevice::ReadWrite);
100 QImageWriter writer(&buffer, dibFormatC);
101 if (!writer.canWrite()) {
102 qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
103 return ba;
104 }
105 if (!writer.write(img))
106 ba.clear();
107 return ba;
108}
109
110static bool qt_write_dibv5(QDataStream &s, QImage image)
111{
112 QIODevice* d = s.device();
113 if (!d->isWritable())
114 return false;
115
116 //depth will be always 32
117 qsizetype bpl_bmp = qsizetype(image.width()) * 4;
118 qsizetype size = bpl_bmp * image.height();
119 if (qsizetype(DWORD(size)) != size)
120 return false;
121
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();
127 bi.bV5Planes = 1;
128 bi.bV5BitCount = 32;
129 bi.bV5Compression = BI_BITFIELDS;
130 bi.bV5SizeImage = DWORD(bpl_bmp * image.height());
131 bi.bV5XPelsPerMeter = 0;
132 bi.bV5YPelsPerMeter = 0;
133 bi.bV5ClrUsed = 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; //LCS_sRGB
140 bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES
141
142 d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size);
143 if (s.status() != QDataStream::Ok)
144 return false;
145
146 d->write(reinterpret_cast<const char *>(&bi.bV5RedMask), sizeof(bi.bV5RedMask));
147 if (s.status() != QDataStream::Ok)
148 return false;
149
150 d->write(reinterpret_cast<const char *>(&bi.bV5GreenMask), sizeof(bi.bV5GreenMask));
151 if (s.status() != QDataStream::Ok)
152 return false;
153
154 d->write(reinterpret_cast<const char *>(&bi.bV5BlueMask), sizeof(bi.bV5BlueMask));
155 if (s.status() != QDataStream::Ok)
156 return false;
157
158 if (image.format() != QImage::Format_ARGB32)
159 image = std::move(image).convertToFormat(QImage::Format_ARGB32);
160
161 auto *buf = new uchar[bpl_bmp];
162
163 memset(buf, 0, size_t(bpl_bmp));
164 for (int y=image.height()-1; y>=0; y--) {
165 // write the image bits
166 const QRgb *p = reinterpret_cast<const QRgb *>(image.constScanLine(y));
167 const QRgb *end = p + image.width();
168 uchar *b = buf;
169 while (p < end) {
170 int alpha = qAlpha(*p);
171 if (alpha) {
172 *b++ = uchar(qBlue(*p));
173 *b++ = uchar(qGreen(*p));
174 *b++ = uchar(qRed(*p));
175 } else {
176 //white for fully transparent pixels.
177 *b++ = 0xff;
178 *b++ = 0xff;
179 *b++ = 0xff;
180 }
181 *b++ = uchar(alpha);
182 p++;
183 }
184 d->write(reinterpret_cast<const char *>(buf), bpl_bmp);
185 if (s.status() != QDataStream::Ok) {
186 delete[] buf;
187 return false;
188 }
189 }
190 delete[] buf;
191 return true;
192}
193#endif //QT_CONFIG(imageformat_bmp)
194
195// helpers for using global memory
196
197static int getCf(const FORMATETC &formatetc)
198{
199 return formatetc.cfFormat;
200}
201
202static FORMATETC setCf(int cf)
203{
204 FORMATETC formatetc;
205 formatetc.cfFormat = CLIPFORMAT(cf);
206 formatetc.dwAspect = DVASPECT_CONTENT;
207 formatetc.lindex = -1;
208 formatetc.ptd = nullptr;
209 formatetc.tymed = TYMED_HGLOBAL;
210 return formatetc;
211}
212
213static bool setData(const QByteArray &data, STGMEDIUM *pmedium)
214{
215 HGLOBAL hData = GlobalAlloc(0, SIZE_T(data.size()));
216 if (!hData)
217 return false;
218
219 void *out = GlobalLock(hData);
220 memcpy(out, data.data(), size_t(data.size()));
221 GlobalUnlock(hData);
222 pmedium->tymed = TYMED_HGLOBAL;
223 pmedium->hGlobal = hData;
224 pmedium->pUnkForRelease = nullptr;
225 return true;
226}
227
228static QByteArray getData(int cf, IDataObject *pDataObj, int lindex = -1)
229{
230 QByteArray data;
231 FORMATETC formatetc = setCf(cf);
232 formatetc.lindex = lindex;
233 STGMEDIUM s;
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)));
237 data.detach();
238 GlobalUnlock(s.hGlobal);
239 ReleaseStgMedium(&s);
240 } else {
241 //Try reading IStream data
242 formatetc.tymed = TYMED_ISTREAM;
243 if (pDataObj->GetData(&formatetc, &s) == S_OK) {
244 char szBuffer[4096];
245 ULONG actualRead = 0;
246 LARGE_INTEGER pos = {{0, 0}};
247 //Move to front (can fail depending on the data model implemented)
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));
253 }
254 if (actualRead != sizeof(szBuffer))
255 break;
256 }
257 data.detach();
258 ReleaseStgMedium(&s);
259 }
260 }
261 return data;
262}
263
264static bool canGetData(int cf, IDataObject * pDataObj)
265{
266 FORMATETC formatetc = setCf(cf);
267 if (pDataObj->QueryGetData(&formatetc) != S_OK){
268 formatetc.tymed = TYMED_ISTREAM;
269 return pDataObj->QueryGetData(&formatetc) == S_OK;
270 }
271 return true;
272}
273
274#ifndef QT_NO_DEBUG_STREAM
275QDebug operator<<(QDebug d, const FORMATETC &tc)
276{
277 QDebugStateSaver saver(d);
278 d.nospace();
279 d << "FORMATETC(cfFormat=" << tc.cfFormat << ' ';
280 switch (tc.cfFormat) {
281 case CF_TEXT:
282 d << "CF_TEXT";
283 break;
284 case CF_BITMAP:
285 d << "CF_BITMAP";
286 break;
287 case CF_TIFF:
288 d << "CF_TIFF";
289 break;
290 case CF_OEMTEXT:
291 d << "CF_OEMTEXT";
292 break;
293 case CF_DIB:
294 d << "CF_DIB";
295 break;
296 case CF_DIBV5:
297 d << "CF_DIBV5";
298 break;
299 case CF_UNICODETEXT:
300 d << "CF_UNICODETEXT";
301 break;
302 case CF_ENHMETAFILE:
303 d << "CF_ENHMETAFILE";
304 break;
305 default:
306 d << QWindowsMimeRegistry::clipboardFormatName(tc.cfFormat);
307 break;
308 }
309 d << ", dwAspect=" << tc.dwAspect << ", lindex=" << tc.lindex
310 << ", tymed=" << tc.tymed << ", ptd=" << tc.ptd << ')';
311 return d;
312}
313
314QDebug operator<<(QDebug d, IDataObject *dataObj)
315{
316 QDebugStateSaver saver(d);
317 d.nospace();
318 d.noquote();
319 d << "IDataObject(";
320 if (dataObj) { // Output formats contained in IDataObject.
321 IEnumFORMATETC *enumFormatEtc;
322 if (SUCCEEDED(dataObj->EnumFormatEtc(DATADIR_GET, &enumFormatEtc)) && enumFormatEtc) {
323 FORMATETC formatEtc[1];
324 ULONG fetched;
325 if (SUCCEEDED(enumFormatEtc->Reset())) {
326 while (SUCCEEDED(enumFormatEtc->Next(1, formatEtc, &fetched)) && fetched)
327 d << formatEtc[0] << ',';
328 enumFormatEtc->Release();
329 }
330 }
331 } else {
332 d << "0x0";
333 }
334 d << ')';
335 return d;
336}
337#endif // !QT_NO_DEBUG_STREAM
338
340{
341public:
342 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
343 QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override;
344 QString mimeForFormat(const FORMATETC &formatetc) const override;
345 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
346 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override;
347 QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
348};
349
350bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
351{
352 int cf = getCf(formatetc);
353 return (cf == CF_UNICODETEXT || (cf == CF_TEXT && GetACP() != CP_UTF8)) && mimeData->hasText();
354}
355
356/*
357text/plain is defined as using CRLF, but so many programs don't,
358and programmers just look for '\n' in strings.
359Windows really needs CRLF, so we ensure it here.
360*/
361bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
362{
363 if (canConvertFromMime(formatetc, mimeData)) {
364 QByteArray data;
365 int cf = getCf(formatetc);
366 if (cf == CF_TEXT) {
367 data = mimeData->text().toLocal8Bit();
368 // Anticipate required space for CRLFs at 1/40
369 int maxsize=data.size()+data.size()/40+3;
370 QByteArray r(maxsize, '\0');
371 char* o = r.data();
372 const char* d = data.data();
373 const int s = data.size();
374 bool cr=false;
375 int j=0;
376 for (int i=0; i<s; i++) {
377 char c = d[i];
378 if (c=='\r')
379 cr=true;
380 else {
381 if (c=='\n') {
382 if (!cr)
383 o[j++]='\r';
384 }
385 cr=false;
386 }
387 o[j++]=c;
388 if (j+3 >= maxsize) {
389 maxsize += maxsize/4;
390 r.resize(maxsize);
391 o = r.data();
392 }
393 }
394 o[j]=0;
395 return setData(r, pmedium);
396 }
397 if (cf == CF_UNICODETEXT) {
398 QString str = mimeData->text();
399 const QChar *u = str.unicode();
400 QString res;
401 const int s = str.length();
402 int maxsize = s + s/40 + 3;
403 res.resize(maxsize);
404 int ri = 0;
405 bool cr = false;
406 for (int i=0; i < s; ++i) {
407 if (*u == u'\r')
408 cr = true;
409 else {
410 if (*u == u'\n' && !cr)
411 res[ri++] = u'\r';
412 cr = false;
413 }
414 res[ri++] = *u;
415 if (ri+3 >= maxsize) {
416 maxsize += maxsize/4;
417 res.resize(maxsize);
418 }
419 ++u;
420 }
421 res.truncate(ri);
422 const int byteLength = res.length() * int(sizeof(ushort));
423 QByteArray r(byteLength + 2, '\0');
424 memcpy(r.data(), res.unicode(), size_t(byteLength));
425 r[byteLength] = 0;
426 r[byteLength+1] = 0;
427 return setData(r, pmedium);
428 }
429 }
430 return false;
431}
432
433bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
434{
435 return mimeType.startsWith(u"text/plain")
436 && (canGetData(CF_UNICODETEXT, pDataObj)
437 || canGetData(CF_TEXT, pDataObj));
438}
439
440QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const
441{
442 int cf = getCf(formatetc);
443 if (cf == CF_UNICODETEXT || cf == CF_TEXT)
444 return u"text/plain"_s;
445 return QString();
446}
447
448
449QList<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
450{
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);
456 }
457 return formatics;
458}
459
460QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const
461{
462 QVariant ret;
463
464 if (canConvertToMime(mime, pDataObj)) {
465 QString str;
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);
470 } else {
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');
476 char* o = r.data();
477 int j=0;
478 for (unsigned i = 0; i < s; ++i) {
479 char c = d[i];
480 if (c!='\r')
481 o[j++]=c;
482 }
483 o[j]=0;
484 str = QString::fromLocal8Bit(r);
485 }
486 }
487 if (preferredType.id() == QMetaType::QString)
488 ret = str;
489 else
490 ret = std::move(str).toUtf8();
491 }
492 qCDebug(lcQpaMime) << __FUNCTION__ << ret;
493 return ret;
494}
495
497{
498public:
500 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
501 QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QMetaType preferredType) const override;
502 QString mimeForFormat(const FORMATETC &formatetc) const override;
503 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
504 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override;
505 QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
506private:
507 int CF_INETURL_W; // wide char version
508 int CF_INETURL;
509};
510
512{
513 CF_INETURL_W = registerMimeType(u"UniformResourceLocatorW"_s);
514 CF_INETURL = registerMimeType(u"UniformResourceLocator"_s);
515}
516
517bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
518{
519 if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) {
520 const auto urls = mimeData->urls();
521 for (const QUrl &url : urls) {
522 if (url.isLocalFile())
523 return true;
524 }
525 }
526 return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls();
527}
528
529bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
530{
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());
538 if (!fn.isEmpty()) {
539 size += sizeof(ushort) * size_t(fn.length() + 1);
540 fileNames.append(fn);
541 }
542 }
543
544 QByteArray result(int(size), '\0');
545 auto* d = reinterpret_cast<DROPFILES *>(result.data());
546 d->pFiles = sizeof(DROPFILES);
547 GetCursorPos(&d->pt); // try
548 d->fNC = true;
549 char *files = (reinterpret_cast<char*>(d)) + d->pFiles;
550
551 d->fWide = true;
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));
556 f += l;
557 *f++ = 0;
558 }
559 *f = 0;
560
561 return setData(result, pmedium);
562 }
563 if (getCf(formatetc) == CF_INETURL_W) {
564 const auto urls = mimeData->urls();
565 QByteArray result;
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)));
570 }
571 result.append('\0');
572 result.append('\0');
573 return setData(result, pmedium);
574 }
575 if (getCf(formatetc) == CF_INETURL) {
576 const auto urls = mimeData->urls();
577 QByteArray result;
578 if (!urls.isEmpty())
579 result = urls.at(0).toString().toLocal8Bit();
580 return setData(result, pmedium);
581 }
582 }
583
584 return false;
585}
586
587bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
588{
589 return mimeType == u"text/uri-list"
590 && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
591}
592
593QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
594{
595 QString format;
596 if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
597 format = u"text/uri-list"_s;
598 return format;
599}
600
601QList<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
602{
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);
611 }
612 return formatics;
613}
614
615QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QMetaType preferredType) const
616{
617 if (mimeType == u"text/uri-list") {
618 if (canGetData(CF_HDROP, pDataObj)) {
619 QList<QVariant> urls;
620
621 QByteArray data = getData(CF_HDROP, pDataObj);
622 if (data.isEmpty())
623 return QVariant();
624
625 const auto *hdrop = reinterpret_cast<const DROPFILES *>(data.constData());
626 if (hdrop->fWide) {
627 const auto *filesw = reinterpret_cast<const wchar_t *>(data.constData() + hdrop->pFiles);
628 int i = 0;
629 while (filesw[i]) {
630 QString fileurl = QString::fromWCharArray(filesw + i);
631 urls += QUrl::fromLocalFile(fileurl);
632 i += fileurl.length()+1;
633 }
634 } else {
635 const char* files = reinterpret_cast<const char *>(data.constData() + hdrop->pFiles);
636 int i=0;
637 while (files[i]) {
638 urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
639 i += int(strlen(files+i))+1;
640 }
641 }
642
643 if (preferredType.id() == QMetaType::QUrl && urls.size() == 1)
644 return urls.at(0);
645 if (!urls.isEmpty())
646 return urls;
647 } else if (canGetData(CF_INETURL_W, pDataObj)) {
648 QByteArray data = getData(CF_INETURL_W, pDataObj);
649 if (data.isEmpty())
650 return QVariant();
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);
654 if (data.isEmpty())
655 return QVariant();
656 return QUrl(QString::fromLocal8Bit(data.constData()));
657 }
658 }
659 return QVariant();
660}
661
663{
664public:
666
667 // for converting from Qt
668 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
669 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
670 QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
671
672 // for converting to Qt
673 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
674 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override;
675 QString mimeForFormat(const FORMATETC &formatetc) const override;
676
677private:
678 int CF_HTML;
679};
680
682{
683 CF_HTML = registerMimeType(u"HTML Format"_s);
684}
685
686QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
687{
688 QList<FORMATETC> formatetcs;
689 if (mimeType == u"text/html" && (!mimeData->html().isEmpty()))
690 formatetcs += setCf(CF_HTML);
691 return formatetcs;
692}
693
694QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
695{
696 if (getCf(formatetc) == CF_HTML)
697 return u"text/html"_s;
698 return QString();
699}
700
701bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
702{
703 return mimeType == u"text/html" && canGetData(CF_HTML, pDataObj);
704}
705
706
707bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
708{
709 return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
710}
711
712/*
713The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions
714in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag
715
716 Version: 1.0
717 StartHTML:xxxxxxxxxx
718 EndHTML:xxxxxxxxxx
719 StartFragment:xxxxxxxxxx
720 EndFragment:xxxxxxxxxx
721 ...html...
722
723*/
724QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const
725{
726 Q_UNUSED(preferredType);
727 QVariant result;
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);
735
736 if (start != -1) {
737 int startOffset = start + 10;
738 int i = startOffset;
739 while (html.at(i) != '\r' && html.at(i) != '\n')
740 ++i;
741 QByteArray bytecount = html.mid(startOffset, i - startOffset);
742 start = bytecount.toInt();
743 }
744
745 if (end != -1) {
746 int endOffset = end + 8;
747 int i = endOffset ;
748 while (html.at(i) != '\r' && html.at(i) != '\n')
749 ++i;
750 QByteArray bytecount = html.mid(endOffset , i - endOffset);
751 end = bytecount.toInt();
752 }
753
754 if (end > start && start > 0) {
755 html = html.mid(start, end - start);
756 html.replace('\r', "");
757 result = QString::fromUtf8(html);
758 }
759 }
760 return result;
761}
762
763bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
764{
765 if (canConvertFromMime(formatetc, mimeData)) {
766 QByteArray data = mimeData->html().toUtf8();
767 QByteArray result =
768 "Version:1.0\r\n" // 0-12
769 "StartHTML:0000000107\r\n" // 13-35
770 "EndHTML:0000000000\r\n" // 36-55
771 "StartFragment:0000000000\r\n" // 56-81
772 "EndFragment:0000000000\r\n\r\n"; // 82-107
773
774 static constexpr auto startFragmentMatcher = qMakeStaticByteArrayMatcher("<!--StartFragment-->");
775 static constexpr auto endFragmentMatcher = qMakeStaticByteArrayMatcher("<!--EndFragment-->");
776
777 if (startFragmentMatcher.indexIn(data) == -1)
778 result += "<!--StartFragment-->";
779 result += data;
780 if (endFragmentMatcher.indexIn(data) == -1)
781 result += "<!--EndFragment-->";
782
783 // set the correct number for EndHTML
784 QByteArray pos = QByteArray::number(result.size());
785 memcpy(reinterpret_cast<char *>(result.data() + 53 - pos.length()), pos.constData(), size_t(pos.length()));
786
787 // set correct numbers for StartFragment and EndFragment
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()));
792
793 return setData(result, pmedium);
794 }
795 return false;
796}
797
798
799#if QT_CONFIG(imageformat_bmp)
800class QWindowsMimeImage : public QWindowsMimeConverter
801{
802public:
803 QWindowsMimeImage();
804 // for converting from Qt
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;
808
809 // for converting to Qt
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;
813private:
814 bool hasOriginalDIBV5(IDataObject *pDataObj) const;
815 UINT CF_PNG;
816};
817
818QWindowsMimeImage::QWindowsMimeImage()
819{
820 CF_PNG = RegisterClipboardFormat(L"PNG");
821}
822
823QList<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
824{
825 QList<FORMATETC> formatetcs;
826 if (mimeData->hasImage() && mimeType == u"application/x-qt-image") {
827 //add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656).
828 auto image = qvariant_cast<QImage>(mimeData->imageData());
829 if (!image.isNull() && image.hasAlphaChannel())
830 formatetcs += setCf(CF_DIBV5);
831 formatetcs += setCf(CF_DIB);
832 }
833 if (!formatetcs.isEmpty())
834 qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs;
835 return formatetcs;
836}
837
838QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
839{
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;
843 return QString();
844}
845
846bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
847{
848 return mimeType == u"application/x-qt-image"
849 && (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj));
850}
851
852bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
853{
854 int cf = getCf(formatetc);
855 if (!mimeData->hasImage())
856 return false;
857 const QImage image = qvariant_cast<QImage>(mimeData->imageData());
858 if (image.isNull())
859 return false;
860 // QTBUG-64322: Use PNG only for transparent images as otherwise MS PowerPoint
861 // cannot handle it.
862 return cf == CF_DIBV5 || cf == CF_DIB
863 || (cf == int(CF_PNG) && image.hasAlphaChannel());
864}
865
866bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
867{
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());
871 if (img.isNull())
872 return false;
873 QByteArray ba;
874 if (cf == CF_DIB) {
875 if (img.format() > QImage::Format_ARGB32)
876 img = std::move(img).convertToFormat(QImage::Format_RGB32);
877 const QByteArray ba = writeDib(img);
878 if (!ba.isEmpty())
879 return setData(ba, pmedium);
880 } else if (cf == int(CF_PNG)) {
881 QBuffer buffer(&ba);
882 const bool written = buffer.open(QIODevice::WriteOnly) && img.save(&buffer, "PNG");
883 buffer.close();
884 if (written)
885 return setData(ba, pmedium);
886 } else {
887 QDataStream s(&ba, QIODevice::WriteOnly);
888 s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
889 if (qt_write_dibv5(s, std::move(img)))
890 return setData(ba, pmedium);
891 }
892 }
893 return false;
894}
895
896bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
897{
898 bool isSynthesized = true;
899 IEnumFORMATETC *pEnum = nullptr;
900 HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
901 if (res == S_OK && pEnum) {
902 FORMATETC fc;
903 while ((res = pEnum->Next(1, &fc, nullptr)) == S_OK) {
904 if (fc.ptd)
905 CoTaskMemFree(fc.ptd);
906 if (fc.cfFormat == CF_DIB)
907 break;
908 if (fc.cfFormat == CF_DIBV5) {
909 isSynthesized = false;
910 break;
911 }
912 }
913 pEnum->Release();
914 }
915 return !isSynthesized;
916}
917
918QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const
919{
920 Q_UNUSED(preferredType);
921 QVariant result;
922 if (mimeType != u"application/x-qt-image")
923 return result;
924 // Try to convert from DIBV5 as it is the most widespread format that supports transparency,
925 // but avoid synthesizing it, as that typically loses transparency, e.g. from Office
926 const bool canGetDibV5 = canGetData(CF_DIBV5, pDataObj);
927 const bool hasOrigDibV5 = canGetDibV5 ? hasOriginalDIBV5(pDataObj) : false;
928 qCDebug(lcQpaMime) << "canGetDibV5:" << canGetDibV5 << "hasOrigDibV5:" << hasOrigDibV5;
929 // PNG, MS Office place this (undocumented)
930 if (!hasOrigDibV5 && canGetData(CF_PNG, pDataObj)) {
931 qCDebug(lcQpaMime) << "Decoding PNG";
932 QImage img;
933 QByteArray data = getData(CF_PNG, pDataObj);
934 if (img.loadFromData(data, "PNG")) {
935 return img;
936 }
937 }
938
939 if (canGetDibV5 || canGetData(CF_DIB, pDataObj)) {
940 qCDebug(lcQpaMime) << "Decoding DIB";
941 QImage img;
942 QByteArray data = getData(canGetDibV5 ? CF_DIBV5 : CF_DIB, pDataObj);
943 QBuffer buffer(&data);
944 if (readDib(buffer, img))
945 return img;
946 }
947 // Failed
948 return result;
949}
950#endif //QT_CONFIG(imageformat_bmp)
951
953{
954public:
956
957 // for converting from Qt
958 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
959 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
960 QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
961
962 // for converting to Qt
963 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
964 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override;
965 QString mimeForFormat(const FORMATETC &formatetc) const override;
966
967private:
968 QMap<int, QString> outFormats;
969 QMap<int, QString> inFormats;
970};
971
974{
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);
977}
978
979bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
980{
981 // really check
982 return formatetc.tymed & TYMED_HGLOBAL
983 && outFormats.contains(formatetc.cfFormat)
984 && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
985}
986
987bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
988{
989 if (canConvertFromMime(formatetc, mimeData)) {
990 QByteArray data;
991 if (outFormats.value(getCf(formatetc)) == u"text/html") {
992 // text/html is in wide chars on windows (compatible with mozillia)
993 QString html = mimeData->html();
994 // same code as in the text converter up above
995 const QChar *u = html.unicode();
996 QString res;
997 const int s = html.length();
998 int maxsize = s + s/40 + 3;
999 res.resize(maxsize);
1000 int ri = 0;
1001 bool cr = false;
1002 for (int i=0; i < s; ++i) {
1003 if (*u == u'\r')
1004 cr = true;
1005 else {
1006 if (*u == u'\n' && !cr)
1007 res[ri++] = u'\r';
1008 cr = false;
1009 }
1010 res[ri++] = *u;
1011 if (ri+3 >= maxsize) {
1012 maxsize += maxsize/4;
1013 res.resize(maxsize);
1014 }
1015 ++u;
1016 }
1017 res.truncate(ri);
1018 const int byteLength = res.length() * int(sizeof(ushort));
1019 QByteArray r(byteLength + 2, '\0');
1020 memcpy(r.data(), res.unicode(), size_t(byteLength));
1021 r[byteLength] = 0;
1022 r[byteLength+1] = 0;
1023 data = r;
1024 } else {
1025#if QT_CONFIG(draganddrop)
1026 data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
1027#endif // QT_CONFIG(draganddrop)
1028 }
1029 return setData(data, pmedium);
1030 }
1031 return false;
1032}
1033
1034QList<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
1035{
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());
1040 return formatetcs;
1041}
1042
1043bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1044{
1045 const auto mit = std::find(inFormats.cbegin(), inFormats.cend(), mimeType);
1046 return mit != inFormats.cend() && canGetData(mit.key(), pDataObj);
1047}
1048
1049QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const
1050{
1051 QVariant val;
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)) {
1057 // text/html is in wide chars on windows (compatible with Mozilla)
1058 val = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()));
1059 } else {
1060 val = data; // it should be enough to return the data and let QMimeData do the rest.
1061 }
1062 }
1063 }
1064 return val;
1065}
1066
1067QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
1068{
1069 return inFormats.value(getCf(formatetc));
1070}
1071
1072
1074{
1075public:
1076
1078 // for converting from Qt
1079 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
1080 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
1081 QList<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
1082
1083 // for converting to Qt
1084 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
1085 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override;
1086 QString mimeForFormat(const FORMATETC &formatetc) const override;
1087
1088private:
1089 mutable QMap<int, QString> formats;
1090 static QStringList ianaTypes;
1091 static QStringList excludeList;
1092};
1093
1094QStringList QLastResortMimes::ianaTypes;
1095QStringList QLastResortMimes::excludeList;
1096
1098{
1099 //MIME Media-Types
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);
1110 }
1111 //Types handled by other classes
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);
1120 }
1121}
1122
1123bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1124{
1125 // really check
1126#if QT_CONFIG(draganddrop)
1127 return formatetc.tymed & TYMED_HGLOBAL
1128 && (formats.contains(formatetc.cfFormat)
1129 && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
1130#else
1131 Q_UNUSED(mimeData);
1132 Q_UNUSED(formatetc);
1133 return formatetc.tymed & TYMED_HGLOBAL
1134 && formats.contains(formatetc.cfFormat);
1135#endif // QT_CONFIG(draganddrop)
1136}
1137
1138bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1139{
1140#if QT_CONFIG(draganddrop)
1141 return canConvertFromMime(formatetc, mimeData)
1142 && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
1143#else
1144 Q_UNUSED(mimeData);
1145 Q_UNUSED(formatetc);
1146 Q_UNUSED(pmedium);
1147 return false;
1148#endif // QT_CONFIG(draganddrop)
1149}
1150
1151QList<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
1152{
1153 QList<FORMATETC> formatetcs;
1154 auto mit = std::find(formats.begin(), formats.end(), mimeType);
1155 // register any other available formats
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());
1160
1161 if (!formatetcs.isEmpty())
1162 qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs;
1163 return formatetcs;
1164}
1165static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\"";
1166
1167static bool isCustomMimeType(const QString &mimeType)
1168{
1169 return mimeType.startsWith(QLatin1StringView(x_qt_windows_mime), Qt::CaseInsensitive);
1170}
1171
1172static QString customMimeType(const QString &mimeType, int *lindex = nullptr)
1173{
1174 int len = sizeof(x_qt_windows_mime) - 1;
1175 int n = mimeType.lastIndexOf(u'\"') - len;
1176 QString ret = mimeType.mid(len, n);
1177
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;
1182 if (lindex)
1183 *lindex = QStringView{mimeType}.mid(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt();
1184 } else {
1185 if (lindex)
1186 *lindex = -1;
1187 }
1188 return ret;
1189}
1190
1191bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1192{
1193 if (isCustomMimeType(mimeType)) {
1194 // MSDN documentation for QueryGetData says only -1 is supported, so ignore lindex here.
1195 QString clipFormat = customMimeType(mimeType);
1196 const UINT cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
1197 return canGetData(int(cf), pDataObj);
1198 }
1199 // if it is not in there then register it and see if we can get it
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);
1203}
1204
1205QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const
1206{
1207 Q_UNUSED(preferredType);
1208 QVariant val;
1209 if (canConvertToMime(mimeType, pDataObj)) {
1210 QByteArray data;
1211 if (isCustomMimeType(mimeType)) {
1212 int lindex;
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);
1216 } else {
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);
1220 }
1221 if (!data.isEmpty())
1222 val = data; // it should be enough to return the data and let QMimeData do the rest.
1223 }
1224 return val;
1225}
1226
1227QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
1228{
1229 QString format = formats.value(getCf(formatetc));
1230 if (!format.isEmpty())
1231 return format;
1232
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)){
1239 //create the mime as custom. not registered.
1240 if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
1241 //check if this is a mime type
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)) {
1246 ianaType = true;
1247 break;
1248 }
1249 }
1250 if (!ianaType)
1251 format = QLatin1StringView(x_qt_windows_mime) + clipFormat + u'"';
1252 else
1253 format = clipFormat;
1254 }
1255 }
1256#endif // QT_CONFIG(draganddrop)
1257 }
1258
1259 return format;
1260}
1261
1262/*!
1263 \class QWindowsMimeRegistry
1264 \brief Manages the list of QWindowsMimeConverter instances.
1265 \internal
1266 \sa QWindowsMimeConverter
1267*/
1268
1270
1272{
1273 qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount);
1274}
1275
1276QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterToMime(const QString &mimeType, IDataObject *pDataObj) const
1277{
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);
1282 }
1283 return nullptr;
1284}
1285
1287{
1288 qCDebug(lcQpaMime) << "QWindowsMimeConverter::allMimesForFormats()";
1289 ensureInitialized();
1290 QStringList formats;
1291 LPENUMFORMATETC FAR fmtenum;
1292 HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum);
1293
1294 if (hr == NOERROR) {
1295 FORMATETC fmtetc;
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)) {
1300 formats += format;
1301 if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled())
1302 qCDebug(lcQpaMime) << __FUNCTION__ << fmtetc << format;
1303 }
1304 }
1305 // as documented in MSDN to avoid possible memleak
1306 if (fmtetc.ptd)
1307 CoTaskMemFree(fmtetc.ptd);
1308 }
1309 fmtenum->Release();
1310 }
1311 qCDebug(lcQpaMime) << pDataObj << formats;
1312 return formats;
1313}
1314
1315QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1316{
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);
1322 }
1323 return nullptr;
1324}
1325
1326QList<FORMATETC> QWindowsMimeRegistry::allFormatsForMime(const QMimeData *mimeData) const
1327{
1328 ensureInitialized();
1329 QList<FORMATETC> formatics;
1330#if !QT_CONFIG(draganddrop)
1331 Q_UNUSED(mimeData);
1332#else
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);
1338 }
1339#endif // QT_CONFIG(draganddrop)
1340 return formatics;
1341}
1342
1343void QWindowsMimeRegistry::ensureInitialized() const
1344{
1345 if (m_internalMimeCount == 0) {
1346 m_internalMimeCount = -1; // prevent reentrancy when types register themselves
1347#if QT_CONFIG(imageformat_bmp)
1348 (void)new QWindowsMimeImage;
1349#endif //QT_CONFIG(imageformat_bmp)
1350 (void)new QLastResortMimes;
1351 (void)new QWindowsMimeText;
1352 (void)new QWindowsMimeURI;
1353 (void)new QWindowsMimeHtml;
1354 (void)new QBuiltInMimes;
1355 m_internalMimeCount = m_mimes.size();
1356 Q_ASSERT(m_internalMimeCount > 0);
1357 }
1358}
1359
1360QString QWindowsMimeRegistry::clipboardFormatName(int cf)
1361{
1362 wchar_t buf[256] = {0};
1363 return GetClipboardFormatName(UINT(cf), buf, 255)
1364 ? QString::fromWCharArray(buf) : QString();
1365}
1366
1367QVariant QWindowsMimeRegistry::convertToMime(const QStringList &mimeTypes,
1368 IDataObject *pDataObj,
1369 QMetaType preferredType,
1370 QString *formatIn /* = 0 */) const
1371{
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;
1379 if (formatIn)
1380 *formatIn = format;
1381 return dataV;
1382 }
1383 }
1384 }
1385 }
1386 qCDebug(lcQpaMime) << __FUNCTION__ << "fails" << mimeTypes << pDataObj << preferredType.id();
1387 return QVariant();
1388}
1389
1390void QWindowsMimeRegistry::registerMime(QWindowsMimeConverter *mime)
1391{
1392 ensureInitialized();
1393 m_mimes.append(mime);
1394}
1395
1396/*!
1397 Registers the MIME type \a mime, and returns an ID number
1398 identifying the format on Windows.
1399
1400 A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be
1401 registered as the clipboard format for \c WindowsType.
1402*/
1403int QWindowsMimeRegistry::registerMimeType(const QString &mime)
1404{
1405 const QString mimeType = isCustomMimeType(mime) ? customMimeType(mime) : mime;
1406 const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mimeType.utf16()));
1407 if (!f) {
1408 qErrnoWarning("QWindowsMimeRegistry::registerMimeType: Failed to register clipboard format "
1409 "for %s", qPrintable(mime));
1410 }
1411
1412 return int(f);
1413}
1414
1415QT_END_NAMESPACE
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)