Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwindowsmime.h"
41#include "qwindowscontext.h"
42
43#include <QtGui/private/qinternalmimedata_p.h>
44#include <QtCore/qbytearraymatcher.h>
45#include <QtCore/qtextcodec.h>
46#include <QtCore/qmap.h>
47#include <QtCore/qurl.h>
48#include <QtCore/qdir.h>
49#include <QtCore/qdebug.h>
50#include <QtCore/qbuffer.h>
51#include <QtGui/qimagereader.h>
52#include <QtGui/qimagewriter.h>
53
54#include <shlobj.h>
55#include <algorithm>
56
57QT_BEGIN_NAMESPACE
58
59/* The MSVC compilers allows multi-byte characters, that has the behavior of
60 * that each character gets shifted into position. 0x73524742 below is for MSVC
61 * equivalent to doing 'sRGB', but this does of course not work
62 * on conformant C++ compilers. */
63#define BMP_LCS_sRGB 0x73524742
64#define BMP_LCS_GM_IMAGES 0x00000004L
65
66struct _CIEXYZ {
67 long ciexyzX, ciexyzY, ciexyzZ;
68};
69
70struct _CIEXYZTRIPLE {
71 _CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue;
72};
73
74struct BMP_BITMAPV5HEADER {
75 DWORD bV5Size;
76 LONG bV5Width;
77 LONG bV5Height;
78 WORD bV5Planes;
79 WORD bV5BitCount;
80 DWORD bV5Compression;
81 DWORD bV5SizeImage;
82 LONG bV5XPelsPerMeter;
83 LONG bV5YPelsPerMeter;
84 DWORD bV5ClrUsed;
85 DWORD bV5ClrImportant;
86 DWORD bV5RedMask;
87 DWORD bV5GreenMask;
88 DWORD bV5BlueMask;
89 DWORD bV5AlphaMask;
90 DWORD bV5CSType;
91 _CIEXYZTRIPLE bV5Endpoints;
92 DWORD bV5GammaRed;
93 DWORD bV5GammaGreen;
94 DWORD bV5GammaBlue;
95 DWORD bV5Intent;
96 DWORD bV5ProfileData;
97 DWORD bV5ProfileSize;
98 DWORD bV5Reserved;
99};
100static const int BMP_BITFIELDS = 3;
101
102static const char dibFormatC[] = "dib";
103
104static inline QByteArray msgConversionError(const char *func, const char *format)
105{
106 QByteArray msg = func;
107 msg += ": Unable to convert DIB image. The image converter plugin for '";
108 msg += format;
109 msg += "' is not available. Available formats: ";
110 const QList<QByteArray> &formats = QImageReader::supportedImageFormats();
111 for (const QByteArray &af : formats) {
112 msg += af;
113 msg += ' ';
114 }
115 return msg;
116}
117
118static inline QImage readDib(QByteArray data)
119{
120 QBuffer buffer(&data);
121 buffer.open(QIODevice::ReadOnly);
122 QImageReader reader(&buffer, dibFormatC);
123 if (!reader.canRead()) {
124 qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
125 return QImage();
126 }
127 return reader.read();
128}
129
130static QByteArray writeDib(const QImage &img)
131{
132 QByteArray ba;
133 QBuffer buffer(&ba);
134 buffer.open(QIODevice::ReadWrite);
135 QImageWriter writer(&buffer, dibFormatC);
136 if (!writer.canWrite()) {
137 qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData());
138 return ba;
139 }
140 if (!writer.write(img))
141 ba.clear();
142 return ba;
143}
144
145static bool qt_write_dibv5(QDataStream &s, QImage image)
146{
147 QIODevice* d = s.device();
148 if (!d->isWritable())
149 return false;
150
151 //depth will be always 32
152 int bpl_bmp = image.width()*4;
153
154 BMP_BITMAPV5HEADER bi;
155 ZeroMemory(&bi, sizeof(bi));
156 bi.bV5Size = sizeof(BMP_BITMAPV5HEADER);
157 bi.bV5Width = image.width();
158 bi.bV5Height = image.height();
159 bi.bV5Planes = 1;
160 bi.bV5BitCount = 32;
161 bi.bV5Compression = BI_BITFIELDS;
162 bi.bV5SizeImage = DWORD(bpl_bmp * image.height());
163 bi.bV5XPelsPerMeter = 0;
164 bi.bV5YPelsPerMeter = 0;
165 bi.bV5ClrUsed = 0;
166 bi.bV5ClrImportant = 0;
167 bi.bV5BlueMask = 0x000000ff;
168 bi.bV5GreenMask = 0x0000ff00;
169 bi.bV5RedMask = 0x00ff0000;
170 bi.bV5AlphaMask = 0xff000000;
171 bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB
172 bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES
173
174 d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size);
175 if (s.status() != QDataStream::Ok)
176 return false;
177
178 if (image.format() != QImage::Format_ARGB32)
179 image = image.convertToFormat(QImage::Format_ARGB32);
180
181 auto *buf = new uchar[bpl_bmp];
182
183 memset(buf, 0, size_t(bpl_bmp));
184 for (int y=image.height()-1; y>=0; y--) {
185 // write the image bits
186 const QRgb *p = reinterpret_cast<const QRgb *>(image.constScanLine(y));
187 const QRgb *end = p + image.width();
188 uchar *b = buf;
189 while (p < end) {
190 int alpha = qAlpha(*p);
191 if (alpha) {
192 *b++ = uchar(qBlue(*p));
193 *b++ = uchar(qGreen(*p));
194 *b++ = uchar(qRed(*p));
195 } else {
196 //white for fully transparent pixels.
197 *b++ = 0xff;
198 *b++ = 0xff;
199 *b++ = 0xff;
200 }
201 *b++ = uchar(alpha);
202 p++;
203 }
204 d->write(reinterpret_cast<const char *>(buf), bpl_bmp);
205 if (s.status() != QDataStream::Ok) {
206 delete[] buf;
207 return false;
208 }
209 }
210 delete[] buf;
211 return true;
212}
213
214static int calc_shift(int mask)
215{
216 int result = 0;
217 while (!(mask & 1)) {
218 result++;
219 mask >>= 1;
220 }
221 return result;
222}
223
224//Supports only 32 bit DIBV5
225static bool qt_read_dibv5(QDataStream &s, QImage &image)
226{
227 BMP_BITMAPV5HEADER bi;
228 QIODevice* d = s.device();
229 if (d->atEnd())
230 return false;
231
232 d->read(reinterpret_cast<char *>(&bi), sizeof(bi)); // read BITMAPV5HEADER header
233 if (s.status() != QDataStream::Ok)
234 return false;
235
236 const int nbits = bi.bV5BitCount;
237 if (nbits != 32 || bi.bV5Planes != 1 || bi.bV5Compression != BMP_BITFIELDS)
238 return false; //Unsupported DIBV5 format
239
240 const int w = bi.bV5Width;
241 int h = bi.bV5Height;
242 const int red_mask = int(bi.bV5RedMask);
243 const int green_mask = int(bi.bV5GreenMask);
244 const int blue_mask = int(bi.bV5BlueMask);
245 const int alpha_mask = int(bi.bV5AlphaMask);
246
247 const QImage::Format format = QImage::Format_ARGB32;
248
249 if (bi.bV5Height < 0)
250 h = -h; // support images with negative height
251 if (image.size() != QSize(w, h) || image.format() != format) {
252 image = QImage(w, h, format);
253 if (image.isNull()) // could not create image
254 return false;
255 }
256 image.setDotsPerMeterX(bi.bV5XPelsPerMeter);
257 image.setDotsPerMeterY(bi.bV5YPelsPerMeter);
258
259 const int red_shift = calc_shift(red_mask);
260 const int green_shift = calc_shift(green_mask);
261 const int blue_shift = calc_shift(blue_mask);
262 const int alpha_shift = alpha_mask ? calc_shift(alpha_mask) : 0u;
263
264 const int bpl = image.bytesPerLine();
265 uchar *data = image.bits();
266
267 auto *buf24 = new uchar[bpl];
268 const int bpl24 = ((w * nbits + 31) / 32) * 4;
269
270 while (--h >= 0) {
271 QRgb *p = reinterpret_cast<QRgb *>(data + h * bpl);
272 QRgb *end = p + w;
273 if (d->read(reinterpret_cast<char *>(buf24), bpl24) != bpl24)
274 break;
275 const uchar *b = buf24;
276 while (p < end) {
277 const int c = *b | (*(b + 1)) << 8 | (*(b + 2)) << 16 | (*(b + 3)) << 24;
278 *p++ = qRgba(((c & red_mask) >> red_shift) ,
279 ((c & green_mask) >> green_shift),
280 ((c & blue_mask) >> blue_shift),
281 ((c & alpha_mask) >> alpha_shift));
282 b += 4;
283 }
284 }
285 delete[] buf24;
286
287 if (bi.bV5Height < 0) {
288 // Flip the image
289 auto *buf = new uchar[bpl];
290 h = -bi.bV5Height;
291 for (int y = 0; y < h/2; ++y) {
292 memcpy(buf, data + y * bpl, size_t(bpl));
293 memcpy(data + y*bpl, data + (h - y -1) * bpl, size_t(bpl));
294 memcpy(data + (h - y -1 ) * bpl, buf, size_t(bpl));
295 }
296 delete [] buf;
297 }
298
299 return true;
300}
301
302// helpers for using global memory
303
304static int getCf(const FORMATETC &formatetc)
305{
306 return formatetc.cfFormat;
307}
308
309static FORMATETC setCf(int cf)
310{
311 FORMATETC formatetc;
312 formatetc.cfFormat = CLIPFORMAT(cf);
313 formatetc.dwAspect = DVASPECT_CONTENT;
314 formatetc.lindex = -1;
315 formatetc.ptd = nullptr;
316 formatetc.tymed = TYMED_HGLOBAL;
317 return formatetc;
318}
319
320static bool setData(const QByteArray &data, STGMEDIUM *pmedium)
321{
322 HGLOBAL hData = GlobalAlloc(0, SIZE_T(data.size()));
323 if (!hData)
324 return false;
325
326 void *out = GlobalLock(hData);
327 memcpy(out, data.data(), size_t(data.size()));
328 GlobalUnlock(hData);
329 pmedium->tymed = TYMED_HGLOBAL;
330 pmedium->hGlobal = hData;
331 pmedium->pUnkForRelease = nullptr;
332 return true;
333}
334
335static QByteArray getData(int cf, IDataObject *pDataObj, int lindex = -1)
336{
337 QByteArray data;
338 FORMATETC formatetc = setCf(cf);
339 formatetc.lindex = lindex;
340 STGMEDIUM s;
341 if (pDataObj->GetData(&formatetc, &s) == S_OK) {
342 const void *val = GlobalLock(s.hGlobal);
343 data = QByteArray::fromRawData(reinterpret_cast<const char *>(val), int(GlobalSize(s.hGlobal)));
344 data.detach();
345 GlobalUnlock(s.hGlobal);
346 ReleaseStgMedium(&s);
347 } else {
348 //Try reading IStream data
349 formatetc.tymed = TYMED_ISTREAM;
350 if (pDataObj->GetData(&formatetc, &s) == S_OK) {
351 char szBuffer[4096];
352 ULONG actualRead = 0;
353 LARGE_INTEGER pos = {{0, 0}};
354 //Move to front (can fail depending on the data model implemented)
355 HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, nullptr);
356 while(SUCCEEDED(hr)){
357 hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead);
358 if (SUCCEEDED(hr) && actualRead > 0) {
359 data += QByteArray::fromRawData(szBuffer, int(actualRead));
360 }
361 if (actualRead != sizeof(szBuffer))
362 break;
363 }
364 data.detach();
365 ReleaseStgMedium(&s);
366 }
367 }
368 return data;
369}
370
371static bool canGetData(int cf, IDataObject * pDataObj)
372{
373 FORMATETC formatetc = setCf(cf);
374 if (pDataObj->QueryGetData(&formatetc) != S_OK){
375 formatetc.tymed = TYMED_ISTREAM;
376 return pDataObj->QueryGetData(&formatetc) == S_OK;
377 }
378 return true;
379}
380
381#ifndef QT_NO_DEBUG_STREAM
382QDebug operator<<(QDebug d, const FORMATETC &tc)
383{
384 QDebugStateSaver saver(d);
385 d.nospace();
386 d << "FORMATETC(cfFormat=" << tc.cfFormat << ' ';
387 switch (tc.cfFormat) {
388 case CF_TEXT:
389 d << "CF_TEXT";
390 break;
391 case CF_BITMAP:
392 d << "CF_BITMAP";
393 break;
394 case CF_TIFF:
395 d << "CF_TIFF";
396 break;
397 case CF_OEMTEXT:
398 d << "CF_OEMTEXT";
399 break;
400 case CF_DIB:
401 d << "CF_DIB";
402 break;
403 case CF_DIBV5:
404 d << "CF_DIBV5";
405 break;
406 case CF_UNICODETEXT:
407 d << "CF_UNICODETEXT";
408 break;
409 case CF_ENHMETAFILE:
410 d << "CF_ENHMETAFILE";
411 break;
412 default:
413 d << QWindowsMimeConverter::clipboardFormatName(tc.cfFormat);
414 break;
415 }
416 d << ", dwAspect=" << tc.dwAspect << ", lindex=" << tc.lindex
417 << ", tymed=" << tc.tymed << ", ptd=" << tc.ptd << ')';
418 return d;
419}
420
421QDebug operator<<(QDebug d, IDataObject *dataObj)
422{
423 QDebugStateSaver saver(d);
424 d.nospace();
425 d.noquote();
426 d << "IDataObject(";
427 if (dataObj) { // Output formats contained in IDataObject.
428 IEnumFORMATETC *enumFormatEtc;
429 if (SUCCEEDED(dataObj->EnumFormatEtc(DATADIR_GET, &enumFormatEtc)) && enumFormatEtc) {
430 FORMATETC formatEtc[1];
431 ULONG fetched;
432 if (SUCCEEDED(enumFormatEtc->Reset())) {
433 while (SUCCEEDED(enumFormatEtc->Next(1, formatEtc, &fetched)) && fetched)
434 d << formatEtc[0] << ',';
435 enumFormatEtc->Release();
436 }
437 }
438 } else {
439 d << '0';
440 }
441 d << ')';
442 return d;
443}
444#endif // !QT_NO_DEBUG_STREAM
445
446/*!
447 \class QWindowsMime
448 \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats.
449 \internal
450 \ingroup qt-lighthouse-win
451
452 Qt's drag-and-drop and clipboard facilities use the MIME standard.
453 On X11, this maps trivially to the Xdnd protocol, but on Windows
454 although some applications use MIME types to describe clipboard
455 formats, others use arbitrary non-standardized naming conventions,
456 or unnamed built-in formats of Windows.
457
458 By instantiating subclasses of QWindowsMime that provide conversions
459 between Windows Clipboard and MIME formats, you can convert
460 proprietary clipboard formats to MIME formats.
461
462 Qt has predefined support for the following Windows Clipboard formats:
463
464 \table
465 \header \li Windows Format \li Equivalent MIME type
466 \row \li \c CF_UNICODETEXT \li \c text/plain
467 \row \li \c CF_TEXT \li \c text/plain
468 \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is
469 a \l{QImageWriter::supportedImageFormats()}{Qt image format}
470 \row \li \c CF_HDROP \li \c text/uri-list
471 \row \li \c CF_INETURL \li \c text/uri-list
472 \row \li \c CF_HTML \li \c text/html
473 \endtable
474
475 An example use of this class would be to map the Windows Metafile
476 clipboard format (\c CF_METAFILEPICT) to and from the MIME type
477 \c{image/x-wmf}. This conversion might simply be adding or removing
478 a header, or even just passing on the data. See \l{Drag and Drop}
479 for more information on choosing and definition MIME types.
480
481 You can check if a MIME type is convertible using canConvertFromMime() and
482 can perform conversions with convertToMime() and convertFromMime().
483
484 \sa QWindowsMimeConverter
485*/
486
487/*!
488Constructs a new conversion object, adding it to the globally accessed
489list of available converters.
490*/
491QWindowsMime::QWindowsMime() = default;
492
493/*!
494Destroys a conversion object, removing it from the global
495list of available converters.
496*/
497QWindowsMime::~QWindowsMime() = default;
498
499/*!
500 Registers the MIME type \a mime, and returns an ID number
501 identifying the format on Windows.
502*/
503int QWindowsMime::registerMimeType(const QString &mime)
504{
505 const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16()));
506 if (!f)
507 qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format");
508
509 return int(f);
510}
511
512/*!
513\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
514
515 Returns \c true if the converter can convert from the \a mimeData to
516 the format specified in \a formatetc.
517
518 All subclasses must reimplement this pure virtual function.
519*/
520
521/*!
522 \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
523
524 Returns \c true if the converter can convert to the \a mimeType from
525 the available formats in \a pDataObj.
526
527 All subclasses must reimplement this pure virtual function.
528*/
529
530/*!
531\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const
532
533 Returns the mime type that will be created form the format specified
534 in \a formatetc, or an empty string if this converter does not support
535 \a formatetc.
536
537 All subclasses must reimplement this pure virtual function.
538*/
539
540/*!
541\fn QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
542
543 Returns a QVector of FORMATETC structures representing the different windows clipboard
544 formats that can be provided for the \a mimeType from the \a mimeData.
545
546 All subclasses must reimplement this pure virtual function.
547*/
548
549/*!
550 \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj,
551 QVariant::Type preferredType) const
552
553 Returns a QVariant containing the converted data for \a mimeType from \a pDataObj.
554 If possible the QVariant should be of the \a preferredType to avoid needless conversions.
555
556 All subclasses must reimplement this pure virtual function.
557*/
558
559/*!
560\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
561
562 Convert the \a mimeData to the format specified in \a formatetc.
563 The converted data should then be placed in \a pmedium structure.
564
565 Return true if the conversion was successful.
566
567 All subclasses must reimplement this pure virtual function.
568*/
569
570class QWindowsMimeText : public QWindowsMime
571{
572public:
573 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
574 QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const override;
575 QString mimeForFormat(const FORMATETC &formatetc) const override;
576 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
577 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override;
578 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
579};
580
581bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
582{
583 int cf = getCf(formatetc);
584 return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText();
585}
586
587/*
588text/plain is defined as using CRLF, but so many programs don't,
589and programmers just look for '\n' in strings.
590Windows really needs CRLF, so we ensure it here.
591*/
592bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
593{
594 if (canConvertFromMime(formatetc, mimeData)) {
595 QByteArray data;
596 int cf = getCf(formatetc);
597 if (cf == CF_TEXT) {
598 data = mimeData->text().toLocal8Bit();
599 // Anticipate required space for CRLFs at 1/40
600 int maxsize=data.size()+data.size()/40+3;
601 QByteArray r(maxsize, '\0');
602 char* o = r.data();
603 const char* d = data.data();
604 const int s = data.size();
605 bool cr=false;
606 int j=0;
607 for (int i=0; i<s; i++) {
608 char c = d[i];
609 if (c=='\r')
610 cr=true;
611 else {
612 if (c=='\n') {
613 if (!cr)
614 o[j++]='\r';
615 }
616 cr=false;
617 }
618 o[j++]=c;
619 if (j+3 >= maxsize) {
620 maxsize += maxsize/4;
621 r.resize(maxsize);
622 o = r.data();
623 }
624 }
625 o[j]=0;
626 return setData(r, pmedium);
627 }
628 if (cf == CF_UNICODETEXT) {
629 QString str = mimeData->text();
630 const QChar *u = str.unicode();
631 QString res;
632 const int s = str.length();
633 int maxsize = s + s/40 + 3;
634 res.resize(maxsize);
635 int ri = 0;
636 bool cr = false;
637 for (int i=0; i < s; ++i) {
638 if (*u == QLatin1Char('\r'))
639 cr = true;
640 else {
641 if (*u == QLatin1Char('\n') && !cr)
642 res[ri++] = QLatin1Char('\r');
643 cr = false;
644 }
645 res[ri++] = *u;
646 if (ri+3 >= maxsize) {
647 maxsize += maxsize/4;
648 res.resize(maxsize);
649 }
650 ++u;
651 }
652 res.truncate(ri);
653 const int byteLength = res.length() * int(sizeof(ushort));
654 QByteArray r(byteLength + 2, '\0');
655 memcpy(r.data(), res.unicode(), size_t(byteLength));
656 r[byteLength] = 0;
657 r[byteLength+1] = 0;
658 return setData(r, pmedium);
659 }
660 }
661 return false;
662}
663
664bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
665{
666 return mimeType.startsWith(QLatin1String("text/plain"))
667 && (canGetData(CF_UNICODETEXT, pDataObj)
668 || canGetData(CF_TEXT, pDataObj));
669}
670
671QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const
672{
673 int cf = getCf(formatetc);
674 if (cf == CF_UNICODETEXT || cf == CF_TEXT)
675 return QStringLiteral("text/plain");
676 return QString();
677}
678
679
680QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
681{
682 QVector<FORMATETC> formatics;
683 if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) {
684 formatics += setCf(CF_UNICODETEXT);
685 formatics += setCf(CF_TEXT);
686 }
687 return formatics;
688}
689
690QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
691{
692 QVariant ret;
693
694 if (canConvertToMime(mime, pDataObj)) {
695 QString str;
696 QByteArray data = getData(CF_UNICODETEXT, pDataObj);
697 if (!data.isEmpty()) {
698 str = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()));
699 str.replace(QLatin1String("\r\n"), QLatin1String("\n"));
700 } else {
701 data = getData(CF_TEXT, pDataObj);
702 if (!data.isEmpty()) {
703 const char* d = data.data();
704 const unsigned s = qstrlen(d);
705 QByteArray r(data.size()+1, '\0');
706 char* o = r.data();
707 int j=0;
708 for (unsigned i = 0; i < s; ++i) {
709 char c = d[i];
710 if (c!='\r')
711 o[j++]=c;
712 }
713 o[j]=0;
714 str = QString::fromLocal8Bit(r);
715 }
716 }
717 if (preferredType == QVariant::String)
718 ret = str;
719 else
720 ret = std::move(str).toUtf8();
721 }
722 qCDebug(lcQpaMime) << __FUNCTION__ << ret;
723 return ret;
724}
725
726class QWindowsMimeURI : public QWindowsMime
727{
728public:
729 QWindowsMimeURI();
730 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
731 QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const override;
732 QString mimeForFormat(const FORMATETC &formatetc) const override;
733 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
734 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const override;
735 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
736private:
737 int CF_INETURL_W; // wide char version
738 int CF_INETURL;
739};
740
741QWindowsMimeURI::QWindowsMimeURI()
742{
743 CF_INETURL_W = QWindowsMime::registerMimeType(QStringLiteral("UniformResourceLocatorW"));
744 CF_INETURL = QWindowsMime::registerMimeType(QStringLiteral("UniformResourceLocator"));
745}
746
747bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
748{
749 if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) {
750 const QList<QUrl> urls = mimeData->urls();
751 for (const QUrl &url : urls) {
752 if (url.isLocalFile())
753 return true;
754 }
755 }
756 return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls();
757}
758
759bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const
760{
761 if (canConvertFromMime(formatetc, mimeData)) {
762 if (getCf(formatetc) == CF_HDROP) {
763 const QList<QUrl> &urls = mimeData->urls();
764 QStringList fileNames;
765 int size = sizeof(DROPFILES)+2;
766 for (const QUrl &url : urls) {
767 const QString fn = QDir::toNativeSeparators(url.toLocalFile());
768 if (!fn.isEmpty()) {
769 size += sizeof(ushort) * size_t(fn.length() + 1);
770 fileNames.append(fn);
771 }
772 }
773
774 QByteArray result(size, '\0');
775 auto* d = reinterpret_cast<DROPFILES *>(result.data());
776 d->pFiles = sizeof(DROPFILES);
777 GetCursorPos(&d->pt); // try
778 d->fNC = true;
779 char *files = (reinterpret_cast<char*>(d)) + d->pFiles;
780
781 d->fWide = true;
782 auto *f = reinterpret_cast<wchar_t *>(files);
783 for (int i=0; i<fileNames.size(); i++) {
784 const auto l = size_t(fileNames.at(i).length());
785 memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort));
786 f += l;
787 *f++ = 0;
788 }
789 *f = 0;
790
791 return setData(result, pmedium);
792 }
793 if (getCf(formatetc) == CF_INETURL_W) {
794 QList<QUrl> urls = mimeData->urls();
795 QByteArray result;
796 if (!urls.isEmpty()) {
797 QString url = urls.at(0).toString();
798 result = QByteArray(reinterpret_cast<const char *>(url.utf16()),
799 url.length() * int(sizeof(ushort)));
800 }
801 result.append('\0');
802 result.append('\0');
803 return setData(result, pmedium);
804 }
805 if (getCf(formatetc) == CF_INETURL) {
806 QList<QUrl> urls = mimeData->urls();
807 QByteArray result;
808 if (!urls.isEmpty())
809 result = urls.at(0).toString().toLocal8Bit();
810 return setData(result, pmedium);
811 }
812 }
813
814 return false;
815}
816
817bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
818{
819 return mimeType == QLatin1String("text/uri-list")
820 && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
821}
822
823QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
824{
825 QString format;
826 if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
827 format = QStringLiteral("text/uri-list");
828 return format;
829}
830
831QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
832{
833 QVector<FORMATETC> formatics;
834 if (mimeType == QLatin1String("text/uri-list")) {
835 if (canConvertFromMime(setCf(CF_HDROP), mimeData))
836 formatics += setCf(CF_HDROP);
837 if (canConvertFromMime(setCf(CF_INETURL_W), mimeData))
838 formatics += setCf(CF_INETURL_W);
839 if (canConvertFromMime(setCf(CF_INETURL), mimeData))
840 formatics += setCf(CF_INETURL);
841 }
842 return formatics;
843}
844
845QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
846{
847 if (mimeType == QLatin1String("text/uri-list")) {
848 if (canGetData(CF_HDROP, pDataObj)) {
849 QList<QVariant> urls;
850
851 QByteArray data = getData(CF_HDROP, pDataObj);
852 if (data.isEmpty())
853 return QVariant();
854
855 const auto *hdrop = reinterpret_cast<const DROPFILES *>(data.constData());
856 if (hdrop->fWide) {
857 const auto *filesw = reinterpret_cast<const wchar_t *>(data.constData() + hdrop->pFiles);
858 int i = 0;
859 while (filesw[i]) {
860 QString fileurl = QString::fromWCharArray(filesw + i);
861 urls += QUrl::fromLocalFile(fileurl);
862 i += fileurl.length()+1;
863 }
864 } else {
865 const char* files = reinterpret_cast<const char *>(data.constData() + hdrop->pFiles);
866 int i=0;
867 while (files[i]) {
868 urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
869 i += int(strlen(files+i))+1;
870 }
871 }
872
873 if (preferredType == QVariant::Url && urls.size() == 1)
874 return urls.at(0);
875 if (!urls.isEmpty())
876 return urls;
877 } else if (canGetData(CF_INETURL_W, pDataObj)) {
878 QByteArray data = getData(CF_INETURL_W, pDataObj);
879 if (data.isEmpty())
880 return QVariant();
881 return QUrl(QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData())));
882 } else if (canGetData(CF_INETURL, pDataObj)) {
883 QByteArray data = getData(CF_INETURL, pDataObj);
884 if (data.isEmpty())
885 return QVariant();
886 return QUrl(QString::fromLocal8Bit(data.constData()));
887 }
888 }
889 return QVariant();
890}
891
892class QWindowsMimeHtml : public QWindowsMime
893{
894public:
895 QWindowsMimeHtml();
896
897 // for converting from Qt
898 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
899 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
900 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
901
902 // for converting to Qt
903 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
904 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override;
905 QString mimeForFormat(const FORMATETC &formatetc) const override;
906
907private:
908 int CF_HTML;
909};
910
911QWindowsMimeHtml::QWindowsMimeHtml()
912{
913 CF_HTML = QWindowsMime::registerMimeType(QStringLiteral("HTML Format"));
914}
915
916QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
917{
918 QVector<FORMATETC> formatetcs;
919 if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty()))
920 formatetcs += setCf(CF_HTML);
921 return formatetcs;
922}
923
924QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
925{
926 if (getCf(formatetc) == CF_HTML)
927 return QStringLiteral("text/html");
928 return QString();
929}
930
931bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
932{
933 return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj);
934}
935
936
937bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
938{
939 return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
940}
941
942/*
943The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions
944in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag
945
946 Version: 1.0
947 StartHTML:xxxxxxxxxx
948 EndHTML:xxxxxxxxxx
949 StartFragment:xxxxxxxxxx
950 EndFragment:xxxxxxxxxx
951 ...html...
952
953*/
954QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const
955{
956 Q_UNUSED(preferredType);
957 QVariant result;
958 if (canConvertToMime(mime, pDataObj)) {
959 QByteArray html = getData(CF_HTML, pDataObj);
960 static Q_RELAXED_CONSTEXPR auto startMatcher = qMakeStaticByteArrayMatcher("StartHTML:");
961 static Q_RELAXED_CONSTEXPR auto endMatcher = qMakeStaticByteArrayMatcher("EndHTML:");
962 qCDebug(lcQpaMime) << __FUNCTION__ << "raw:" << html;
963 int start = startMatcher.indexIn(html);
964 int end = endMatcher.indexIn(html);
965
966 if (start != -1) {
967 int startOffset = start + 10;
968 int i = startOffset;
969 while (html.at(i) != '\r' && html.at(i) != '\n')
970 ++i;
971 QByteArray bytecount = html.mid(startOffset, i - startOffset);
972 start = bytecount.toInt();
973 }
974
975 if (end != -1) {
976 int endOffset = end + 8;
977 int i = endOffset ;
978 while (html.at(i) != '\r' && html.at(i) != '\n')
979 ++i;
980 QByteArray bytecount = html.mid(endOffset , i - endOffset);
981 end = bytecount.toInt();
982 }
983
984 if (end > start && start > 0) {
985 html = html.mid(start, end - start);
986 html.replace('\r', "");
987 result = QString::fromUtf8(html);
988 }
989 }
990 return result;
991}
992
993bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
994{
995 if (canConvertFromMime(formatetc, mimeData)) {
996 QByteArray data = mimeData->html().toUtf8();
997 QByteArray result =
998 "Version:1.0\r\n" // 0-12
999 "StartHTML:0000000107\r\n" // 13-35
1000 "EndHTML:0000000000\r\n" // 36-55
1001 "StartFragment:0000000000\r\n" // 56-81
1002 "EndFragment:0000000000\r\n\r\n"; // 82-107
1003
1004 static Q_RELAXED_CONSTEXPR auto startFragmentMatcher = qMakeStaticByteArrayMatcher("<!--StartFragment-->");
1005 static Q_RELAXED_CONSTEXPR auto endFragmentMatcher = qMakeStaticByteArrayMatcher("<!--EndFragment-->");
1006
1007 if (startFragmentMatcher.indexIn(data) == -1)
1008 result += "<!--StartFragment-->";
1009 result += data;
1010 if (endFragmentMatcher.indexIn(data) == -1)
1011 result += "<!--EndFragment-->";
1012
1013 // set the correct number for EndHTML
1014 QByteArray pos = QByteArray::number(result.size());
1015 memcpy(reinterpret_cast<char *>(result.data() + 53 - pos.length()), pos.constData(), size_t(pos.length()));
1016
1017 // set correct numbers for StartFragment and EndFragment
1018 pos = QByteArray::number(startFragmentMatcher.indexIn(result) + 20);
1019 memcpy(reinterpret_cast<char *>(result.data() + 79 - pos.length()), pos.constData(), size_t(pos.length()));
1020 pos = QByteArray::number(endFragmentMatcher.indexIn(result));
1021 memcpy(reinterpret_cast<char *>(result.data() + 103 - pos.length()), pos.constData(), size_t(pos.length()));
1022
1023 return setData(result, pmedium);
1024 }
1025 return false;
1026}
1027
1028
1029#ifndef QT_NO_IMAGEFORMAT_BMP
1030class QWindowsMimeImage : public QWindowsMime
1031{
1032public:
1033 QWindowsMimeImage();
1034 // for converting from Qt
1035 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
1036 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
1037 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
1038
1039 // for converting to Qt
1040 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
1041 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override;
1042 QString mimeForFormat(const FORMATETC &formatetc) const override;
1043private:
1044 bool hasOriginalDIBV5(IDataObject *pDataObj) const;
1045 UINT CF_PNG;
1046};
1047
1048QWindowsMimeImage::QWindowsMimeImage()
1049{
1050 CF_PNG = RegisterClipboardFormat(L"PNG");
1051}
1052
1053QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
1054{
1055 QVector<FORMATETC> formatetcs;
1056 if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) {
1057 //add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656).
1058 auto image = qvariant_cast<QImage>(mimeData->imageData());
1059 if (!image.isNull() && image.hasAlphaChannel())
1060 formatetcs += setCf(CF_DIBV5);
1061 formatetcs += setCf(CF_DIB);
1062 }
1063 if (!formatetcs.isEmpty())
1064 qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs;
1065 return formatetcs;
1066}
1067
1068QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
1069{
1070 int cf = getCf(formatetc);
1071 if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG))
1072 return QStringLiteral("application/x-qt-image");
1073 return QString();
1074}
1075
1076bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1077{
1078 return mimeType == QLatin1String("application/x-qt-image")
1079 && (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj));
1080}
1081
1082bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1083{
1084 int cf = getCf(formatetc);
1085 if (!mimeData->hasImage())
1086 return false;
1087 const QImage image = qvariant_cast<QImage>(mimeData->imageData());
1088 if (image.isNull())
1089 return false;
1090 // QTBUG-64322: Use PNG only for transparent images as otherwise MS PowerPoint
1091 // cannot handle it.
1092 return cf == CF_DIBV5 || cf == CF_DIB
1093 || (cf == int(CF_PNG) && image.hasAlphaChannel());
1094}
1095
1096bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1097{
1098 int cf = getCf(formatetc);
1099 if ((cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) && mimeData->hasImage()) {
1100 auto img = qvariant_cast<QImage>(mimeData->imageData());
1101 if (img.isNull())
1102 return false;
1103 QByteArray ba;
1104 if (cf == CF_DIB) {
1105 if (img.format() > QImage::Format_ARGB32)
1106 img = img.convertToFormat(QImage::Format_RGB32);
1107 const QByteArray ba = writeDib(img);
1108 if (!ba.isEmpty())
1109 return setData(ba, pmedium);
1110 } else if (cf == int(CF_PNG)) {
1111 QBuffer buffer(&ba);
1112 const bool written = buffer.open(QIODevice::WriteOnly) && img.save(&buffer, "PNG");
1113 buffer.close();
1114 if (written)
1115 return setData(ba, pmedium);
1116 } else {
1117 QDataStream s(&ba, QIODevice::WriteOnly);
1118 s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
1119 if (qt_write_dibv5(s, img))
1120 return setData(ba, pmedium);
1121 }
1122 }
1123 return false;
1124}
1125
1126bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
1127{
1128 bool isSynthesized = true;
1129 IEnumFORMATETC *pEnum = nullptr;
1130 HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
1131 if (res == S_OK && pEnum) {
1132 FORMATETC fc;
1133 while ((res = pEnum->Next(1, &fc, nullptr)) == S_OK) {
1134 if (fc.ptd)
1135 CoTaskMemFree(fc.ptd);
1136 if (fc.cfFormat == CF_DIB)
1137 break;
1138 if (fc.cfFormat == CF_DIBV5) {
1139 isSynthesized = false;
1140 break;
1141 }
1142 }
1143 pEnum->Release();
1144 }
1145 return !isSynthesized;
1146}
1147
1148QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
1149{
1150 Q_UNUSED(preferredType);
1151 QVariant result;
1152 if (mimeType != QLatin1String("application/x-qt-image"))
1153 return result;
1154 //Try to convert from a format which has more data
1155 //DIBV5, use only if its is not synthesized
1156 if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) {
1157 QImage img;
1158 QByteArray data = getData(CF_DIBV5, pDataObj);
1159 QDataStream s(&data, QIODevice::ReadOnly);
1160 s.setByteOrder(QDataStream::LittleEndian);
1161 if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5
1162 return img;
1163 }
1164 }
1165 //PNG, MS Office place this (undocumented)
1166 if (canGetData(CF_PNG, pDataObj)) {
1167 QImage img;
1168 QByteArray data = getData(CF_PNG, pDataObj);
1169 if (img.loadFromData(data, "PNG")) {
1170 return img;
1171 }
1172 }
1173 //Fallback to DIB
1174 if (canGetData(CF_DIB, pDataObj)) {
1175 const QImage img = readDib(getData(CF_DIB, pDataObj));
1176 if (!img.isNull())
1177 return img;
1178 }
1179 // Failed
1180 return result;
1181}
1182#endif
1183
1184class QBuiltInMimes : public QWindowsMime
1185{
1186public:
1187 QBuiltInMimes();
1188
1189 // for converting from Qt
1190 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
1191 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
1192 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
1193
1194 // for converting to Qt
1195 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
1196 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override;
1197 QString mimeForFormat(const FORMATETC &formatetc) const override;
1198
1199private:
1200 QMap<int, QString> outFormats;
1201 QMap<int, QString> inFormats;
1202};
1203
1204QBuiltInMimes::QBuiltInMimes()
1205: QWindowsMime()
1206{
1207 outFormats.insert(QWindowsMime::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color"));
1208 inFormats.insert(QWindowsMime::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color"));
1209}
1210
1211bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1212{
1213 // really check
1214 return formatetc.tymed & TYMED_HGLOBAL
1215 && outFormats.contains(formatetc.cfFormat)
1216 && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
1217}
1218
1219bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1220{
1221 if (canConvertFromMime(formatetc, mimeData)) {
1222 QByteArray data;
1223 if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) {
1224 // text/html is in wide chars on windows (compatible with mozillia)
1225 QString html = mimeData->html();
1226 // same code as in the text converter up above
1227 const QChar *u = html.unicode();
1228 QString res;
1229 const int s = html.length();
1230 int maxsize = s + s/40 + 3;
1231 res.resize(maxsize);
1232 int ri = 0;
1233 bool cr = false;
1234 for (int i=0; i < s; ++i) {
1235 if (*u == QLatin1Char('\r'))
1236 cr = true;
1237 else {
1238 if (*u == QLatin1Char('\n') && !cr)
1239 res[ri++] = QLatin1Char('\r');
1240 cr = false;
1241 }
1242 res[ri++] = *u;
1243 if (ri+3 >= maxsize) {
1244 maxsize += maxsize/4;
1245 res.resize(maxsize);
1246 }
1247 ++u;
1248 }
1249 res.truncate(ri);
1250 const int byteLength = res.length() * int(sizeof(ushort));
1251 QByteArray r(byteLength + 2, '\0');
1252 memcpy(r.data(), res.unicode(), size_t(byteLength));
1253 r[byteLength] = 0;
1254 r[byteLength+1] = 0;
1255 data = r;
1256 } else {
1257#if QT_CONFIG(draganddrop)
1258 data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
1259#endif // QT_CONFIG(draganddrop)
1260 }
1261 return setData(data, pmedium);
1262 }
1263 return false;
1264}
1265
1266QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
1267{
1268 QVector<FORMATETC> formatetcs;
1269 const auto mit = std::find(outFormats.cbegin(), outFormats.cend(), mimeType);
1270 if (mit != outFormats.cend() && mimeData->formats().contains(mimeType))
1271 formatetcs += setCf(mit.key());
1272 return formatetcs;
1273}
1274
1275bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1276{
1277 const auto mit = std::find(inFormats.cbegin(), inFormats.cend(), mimeType);
1278 return mit != inFormats.cend() && canGetData(mit.key(), pDataObj);
1279}
1280
1281QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
1282{
1283 QVariant val;
1284 if (canConvertToMime(mimeType, pDataObj)) {
1285 QByteArray data = getData(inFormats.key(mimeType), pDataObj);
1286 if (!data.isEmpty()) {
1287 qCDebug(lcQpaMime) << __FUNCTION__;
1288 if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) {
1289 // text/html is in wide chars on windows (compatible with Mozilla)
1290 val = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()));
1291 } else {
1292 val = data; // it should be enough to return the data and let QMimeData do the rest.
1293 }
1294 }
1295 }
1296 return val;
1297}
1298
1299QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
1300{
1301 return inFormats.value(getCf(formatetc));
1302}
1303
1304
1305class QLastResortMimes : public QWindowsMime
1306{
1307public:
1308
1309 QLastResortMimes();
1310 // for converting from Qt
1311 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const override;
1312 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const override;
1313 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const override;
1314
1315 // for converting to Qt
1316 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
1317 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const override;
1318 QString mimeForFormat(const FORMATETC &formatetc) const override;
1319
1320private:
1321 mutable QMap<int, QString> formats;
1322 static QStringList ianaTypes;
1323 static QStringList excludeList;
1324};
1325
1326QStringList QLastResortMimes::ianaTypes;
1327QStringList QLastResortMimes::excludeList;
1328
1329QLastResortMimes::QLastResortMimes()
1330{
1331 //MIME Media-Types
1332 if (ianaTypes.isEmpty()) {
1333 ianaTypes.append(QStringLiteral("application/"));
1334 ianaTypes.append(QStringLiteral("audio/"));
1335 ianaTypes.append(QStringLiteral("example/"));
1336 ianaTypes.append(QStringLiteral("image/"));
1337 ianaTypes.append(QStringLiteral("message/"));
1338 ianaTypes.append(QStringLiteral("model/"));
1339 ianaTypes.append(QStringLiteral("multipart/"));
1340 ianaTypes.append(QStringLiteral("text/"));
1341 ianaTypes.append(QStringLiteral("video/"));
1342 }
1343 //Types handled by other classes
1344 if (excludeList.isEmpty()) {
1345 excludeList.append(QStringLiteral("HTML Format"));
1346 excludeList.append(QStringLiteral("UniformResourceLocator"));
1347 excludeList.append(QStringLiteral("text/html"));
1348 excludeList.append(QStringLiteral("text/plain"));
1349 excludeList.append(QStringLiteral("text/uri-list"));
1350 excludeList.append(QStringLiteral("application/x-qt-image"));
1351 excludeList.append(QStringLiteral("application/x-color"));
1352 }
1353}
1354
1355bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1356{
1357 // really check
1358#if QT_CONFIG(draganddrop)
1359 return formatetc.tymed & TYMED_HGLOBAL
1360 && (formats.contains(formatetc.cfFormat)
1361 && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
1362#else
1363 Q_UNUSED(mimeData);
1364 Q_UNUSED(formatetc);
1365 return formatetc.tymed & TYMED_HGLOBAL
1366 && formats.contains(formatetc.cfFormat);
1367#endif // QT_CONFIG(draganddrop)
1368}
1369
1370bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1371{
1372#if QT_CONFIG(draganddrop)
1373 return canConvertFromMime(formatetc, mimeData)
1374 && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
1375#else
1376 Q_UNUSED(mimeData);
1377 Q_UNUSED(formatetc);
1378 Q_UNUSED(pmedium);
1379 return false;
1380#endif // QT_CONFIG(draganddrop)
1381}
1382
1383QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
1384{
1385 QVector<FORMATETC> formatetcs;
1386 auto mit = std::find(formats.begin(), formats.end(), mimeType);
1387 // register any other available formats
1388 if (mit == formats.end() && !excludeList.contains(mimeType, Qt::CaseInsensitive))
1389 mit = formats.insert(QWindowsMime::registerMimeType(mimeType), mimeType);
1390 if (mit != formats.end())
1391 formatetcs += setCf(mit.key());
1392
1393 if (!formatetcs.isEmpty())
1394 qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs;
1395 return formatetcs;
1396}
1397static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\"";
1398
1399static bool isCustomMimeType(const QString &mimeType)
1400{
1401 return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive);
1402}
1403
1404static QString customMimeType(const QString &mimeType, int *lindex = nullptr)
1405{
1406 int len = sizeof(x_qt_windows_mime) - 1;
1407 int n = mimeType.lastIndexOf(QLatin1Char('\"')) - len;
1408 QString ret = mimeType.mid(len, n);
1409
1410 const int beginPos = mimeType.indexOf(QLatin1String(";index="));
1411 if (beginPos > -1) {
1412 const int endPos = mimeType.indexOf(QLatin1Char(';'), beginPos + 1);
1413 const int indexStartPos = beginPos + 7;
1414 if (lindex)
1415 *lindex = mimeType.midRef(indexStartPos, endPos == -1 ? endPos : endPos - indexStartPos).toInt();
1416 } else {
1417 if (lindex)
1418 *lindex = -1;
1419 }
1420 return ret;
1421}
1422
1423bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1424{
1425 if (isCustomMimeType(mimeType)) {
1426 // MSDN documentation for QueryGetData says only -1 is supported, so ignore lindex here.
1427 QString clipFormat = customMimeType(mimeType);
1428 const UINT cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
1429 return canGetData(int(cf), pDataObj);
1430 }
1431 // if it is not in there then register it and see if we can get it
1432 const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
1433 const int cf = mit != formats.cend() ? mit.key() : QWindowsMime::registerMimeType(mimeType);
1434 return canGetData(cf, pDataObj);
1435}
1436
1437QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
1438{
1439 Q_UNUSED(preferredType);
1440 QVariant val;
1441 if (canConvertToMime(mimeType, pDataObj)) {
1442 QByteArray data;
1443 if (isCustomMimeType(mimeType)) {
1444 int lindex;
1445 QString clipFormat = customMimeType(mimeType, &lindex);
1446 const UINT cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
1447 data = getData(int(cf), pDataObj, lindex);
1448 } else {
1449 const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
1450 const int cf = mit != formats.cend() ? mit.key() : QWindowsMime::registerMimeType(mimeType);
1451 data = getData(cf, pDataObj);
1452 }
1453 if (!data.isEmpty())
1454 val = data; // it should be enough to return the data and let QMimeData do the rest.
1455 }
1456 return val;
1457}
1458
1459QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
1460{
1461 QString format = formats.value(getCf(formatetc));
1462 if (!format.isEmpty())
1463 return format;
1464
1465 const QString clipFormat = QWindowsMimeConverter::clipboardFormatName(getCf(formatetc));
1466 if (!clipFormat.isEmpty()) {
1467#if QT_CONFIG(draganddrop)
1468 if (QInternalMimeData::canReadData(clipFormat))
1469 format = clipFormat;
1470 else if((formatetc.cfFormat >= 0xC000)){
1471 //create the mime as custom. not registered.
1472 if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
1473 //check if this is a mime type
1474 bool ianaType = false;
1475 int sz = ianaTypes.size();
1476 for (int i = 0; i < sz; i++) {
1477 if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) {
1478 ianaType = true;
1479 break;
1480 }
1481 }
1482 if (!ianaType)
1483 format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"');
1484 else
1485 format = clipFormat;
1486 }
1487 }
1488#endif // QT_CONFIG(draganddrop)
1489 }
1490
1491 return format;
1492}
1493
1494/*!
1495 \class QWindowsMimeConverter
1496 \brief Manages the list of QWindowsMime instances.
1497 \internal
1498 \ingroup qt-lighthouse-win
1499 \sa QWindowsMime
1500*/
1501
1502QWindowsMimeConverter::QWindowsMimeConverter() = default;
1503
1504QWindowsMimeConverter::~QWindowsMimeConverter()
1505{
1506 qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount);
1507}
1508
1509QWindowsMime * QWindowsMimeConverter::converterToMime(const QString &mimeType, IDataObject *pDataObj) const
1510{
1511 ensureInitialized();
1512 for (int i = m_mimes.size()-1; i >= 0; --i) {
1513 if (m_mimes.at(i)->canConvertToMime(mimeType, pDataObj))
1514 return m_mimes.at(i);
1515 }
1516 return nullptr;
1517}
1518
1519QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) const
1520{
1521 qCDebug(lcQpaMime) << "QWindowsMime::allMimesForFormats()";
1522 ensureInitialized();
1523 QStringList formats;
1524 LPENUMFORMATETC FAR fmtenum;
1525 HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum);
1526
1527 if (hr == NOERROR) {
1528 FORMATETC fmtetc;
1529 while (S_OK == fmtenum->Next(1, &fmtetc, nullptr)) {
1530 for (int i= m_mimes.size() - 1; i >= 0; --i) {
1531 QString format = m_mimes.at(i)->mimeForFormat(fmtetc);
1532 if (!format.isEmpty() && !formats.contains(format)) {
1533 formats += format;
1534 if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled())
1535 qCDebug(lcQpaMime) << __FUNCTION__ << fmtetc << format;
1536 }
1537 }
1538 // as documented in MSDN to avoid possible memleak
1539 if (fmtetc.ptd)
1540 CoTaskMemFree(fmtetc.ptd);
1541 }
1542 fmtenum->Release();
1543 }
1544 qCDebug(lcQpaMime) << pDataObj << formats;
1545 return formats;
1546}
1547
1548QWindowsMime * QWindowsMimeConverter::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1549{
1550 ensureInitialized();
1551 qCDebug(lcQpaMime) << __FUNCTION__ << formatetc;
1552 for (int i = m_mimes.size()-1; i >= 0; --i) {
1553 if (m_mimes.at(i)->canConvertFromMime(formatetc, mimeData))
1554 return m_mimes.at(i);
1555 }
1556 return nullptr;
1557}
1558
1559QVector<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeData) const
1560{
1561 ensureInitialized();
1562 QVector<FORMATETC> formatics;
1563#if !QT_CONFIG(draganddrop)
1564 Q_UNUSED(mimeData);
1565#else
1566 formatics.reserve(20);
1567 const QStringList formats = QInternalMimeData::formatsHelper(mimeData);
1568 for (int f = 0; f < formats.size(); ++f) {
1569 for (int i = m_mimes.size() - 1; i >= 0; --i)
1570 formatics += m_mimes.at(i)->formatsForMime(formats.at(f), mimeData);
1571 }
1572#endif // QT_CONFIG(draganddrop)
1573 return formatics;
1574}
1575
1576void QWindowsMimeConverter::ensureInitialized() const
1577{
1578 if (m_mimes.isEmpty()) {
1579 m_mimes
1580#ifndef QT_NO_IMAGEFORMAT_BMP
1581 << new QWindowsMimeImage
1582#endif //QT_NO_IMAGEFORMAT_BMP
1583 << new QLastResortMimes
1584 << new QWindowsMimeText << new QWindowsMimeURI
1585 << new QWindowsMimeHtml << new QBuiltInMimes;
1586 m_internalMimeCount = m_mimes.size();
1587 }
1588}
1589
1590QString QWindowsMimeConverter::clipboardFormatName(int cf)
1591{
1592 wchar_t buf[256] = {0};
1593 return GetClipboardFormatName(UINT(cf), buf, 255)
1594 ? QString::fromWCharArray(buf) : QString();
1595}
1596
1597QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes,
1598 IDataObject *pDataObj,
1599 QVariant::Type preferredType,
1600 QString *formatIn /* = 0 */) const
1601{
1602 for (const QString &format : mimeTypes) {
1603 if (const QWindowsMime *converter = converterToMime(format, pDataObj)) {
1604 if (converter->canConvertToMime(format, pDataObj)) {
1605 const QVariant dataV = converter->convertToMime(format, pDataObj, preferredType);
1606 if (dataV.isValid()) {
1607 qCDebug(lcQpaMime) << __FUNCTION__ << mimeTypes << "\nFormat: "
1608 << format << pDataObj << " returns " << dataV;
1609 if (formatIn)
1610 *formatIn = format;
1611 return dataV;
1612 }
1613 }
1614 }
1615 }
1616 qCDebug(lcQpaMime) << __FUNCTION__ << "fails" << mimeTypes << pDataObj << preferredType;
1617 return QVariant();
1618}
1619
1620void QWindowsMimeConverter::registerMime(QWindowsMime *mime)
1621{
1622 ensureInitialized();
1623 m_mimes.append(mime);
1624}
1625
1626QT_END_NAMESPACE
1627

Warning: That file was not part of the compilation database. It may have many parsing errors.