source: trunk/src/gui/kernel/qmime_win.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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