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

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 51.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
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 QString url = urls.at(0).toString();
644 result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort));
645 result.append('\0');
646 result.append('\0');
647 return setData(result, pmedium);
648 } else if (getCf(formatetc) == CF_INETURL) {
649 QList<QUrl> urls = mimeData->urls();
650 QByteArray result = urls.at(0).toString().toLocal8Bit();
651 return setData(result, pmedium);
652 }
653 }
654
655 return false;
656}
657
658bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
659{
660 return mimeType == QLatin1String("text/uri-list")
661 && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj));
662}
663
664QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
665{
666 QString format;
667 if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
668 format = QLatin1String("text/uri-list");
669 return format;
670}
671
672QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
673{
674 QVector<FORMATETC> formatics;
675 if (mimeType == QLatin1String("text/uri-list")) {
676 if (canConvertFromMime(setCf(CF_HDROP), mimeData))
677 formatics += setCf(CF_HDROP);
678 if (canConvertFromMime(setCf(CF_INETURL_W), mimeData))
679 formatics += setCf(CF_INETURL_W);
680 if (canConvertFromMime(setCf(CF_INETURL), mimeData))
681 formatics += setCf(CF_INETURL);
682 }
683 return formatics;
684}
685
686QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const
687{
688 if (mimeType == QLatin1String("text/uri-list")) {
689 if (canGetData(CF_HDROP, pDataObj)) {
690 QByteArray texturi;
691 QList<QVariant> urls;
692
693 QByteArray data = getData(CF_HDROP, pDataObj);
694 if (data.isEmpty())
695 return QVariant();
696
697 LPDROPFILES hdrop = (LPDROPFILES)data.data();
698 if (hdrop->fWide) {
699 const wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles);
700 int i = 0;
701 while (filesw[i]) {
702 QString fileurl = QString::fromWCharArray(filesw + i);
703 urls += QUrl::fromLocalFile(fileurl);
704 i += fileurl.length()+1;
705 }
706 } else {
707 const char* files = (const char *)data.data() + hdrop->pFiles;
708 int i=0;
709 while (files[i]) {
710 urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i));
711 i += int(strlen(files+i))+1;
712 }
713 }
714
715 if (preferredType == QVariant::Url && urls.size() == 1)
716 return urls.at(0);
717 else if (!urls.isEmpty())
718 return urls;
719 } else if (canGetData(CF_INETURL_W, pDataObj)) {
720 QByteArray data = getData(CF_INETURL_W, pDataObj);
721 if (data.isEmpty())
722 return QVariant();
723 return QUrl(QString::fromWCharArray((const wchar_t *)data.constData()));
724 } else if (canGetData(CF_INETURL, pDataObj)) {
725 QByteArray data = getData(CF_INETURL, pDataObj);
726 if (data.isEmpty())
727 return QVariant();
728 return QUrl(QString::fromLocal8Bit(data.constData()));
729 }
730 }
731 return QVariant();
732}
733
734class QWindowsMimeHtml : public QWindowsMime
735{
736public:
737 QWindowsMimeHtml();
738
739 // for converting from Qt
740 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
741 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
742 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
743
744 // for converting to Qt
745 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
746 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
747 QString mimeForFormat(const FORMATETC &formatetc) const;
748
749private:
750 int CF_HTML;
751};
752
753QWindowsMimeHtml::QWindowsMimeHtml()
754{
755 CF_HTML = QWindowsMime::registerMimeType(QLatin1String("HTML Format"));
756}
757
758QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
759{
760 QVector<FORMATETC> formatetcs;
761 if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty()))
762 formatetcs += setCf(CF_HTML);
763 return formatetcs;
764}
765
766QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
767{
768 if (getCf(formatetc) == CF_HTML)
769 return QLatin1String("text/html");
770 return QString();
771}
772
773bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
774{
775 return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj);
776}
777
778
779bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
780{
781 return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty());
782}
783
784/*
785The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions
786in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag
787
788 Version: 1.0
789 StartHTML:xxxxxxxxxx
790 EndHTML:xxxxxxxxxx
791 StartFragment:xxxxxxxxxx
792 EndFragment:xxxxxxxxxx
793 ...html...
794
795*/
796QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const
797{
798 Q_UNUSED(preferredType);
799 QVariant result;
800 if (canConvertToMime(mime, pDataObj)) {
801 QByteArray html = getData(CF_HTML, pDataObj);
802#ifdef QMIME_DEBUG
803 qDebug("QWindowsMimeHtml::convertToMime");
804 qDebug("raw :");
805 qDebug(html);
806#endif
807 int start = html.indexOf("StartFragment:");
808 int end = html.indexOf("EndFragment:");
809
810 if (start != -1) {
811 int startOffset = start + 14;
812 int i = startOffset;
813 while (html.at(i) != '\r' && html.at(i) != '\n')
814 ++i;
815 QByteArray bytecount = html.mid(startOffset, i - startOffset);
816 start = bytecount.toInt();
817 }
818
819 if (end != -1) {
820 int endOffset = end + 12;
821 int i = endOffset ;
822 while (html.at(i) != '\r' && html.at(i) != '\n')
823 ++i;
824 QByteArray bytecount = html.mid(endOffset , i - endOffset);
825 end = bytecount.toInt();
826 }
827
828 if (end > start && start > 0) {
829 html = "<!--StartFragment-->" + html.mid(start, end - start);
830 html += "<!--EndFragment-->";
831 html.replace('\r', "");
832 result = QString::fromUtf8(html);
833 }
834 }
835 return result;
836}
837
838bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
839{
840 if (canConvertFromMime(formatetc, mimeData)) {
841 QByteArray data = mimeData->html().toUtf8();
842 QByteArray result =
843 "Version:1.0\r\n" // 0-12
844 "StartHTML:0000000105\r\n" // 13-35
845 "EndHTML:0000000000\r\n" // 36-55
846 "StartFragment:0000000000\r\n" // 58-86
847 "EndFragment:0000000000\r\n\r\n"; // 87-105
848
849 if (data.indexOf("<!--StartFragment-->") == -1)
850 result += "<!--StartFragment-->";
851 result += data;
852 if (data.indexOf("<!--EndFragment-->") == -1)
853 result += "<!--EndFragment-->";
854
855 // set the correct number for EndHTML
856 QByteArray pos = QString::number(result.size()).toLatin1();
857 memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length());
858
859 // set correct numbers for StartFragment and EndFragment
860 pos = QString::number(result.indexOf("<!--StartFragment-->") + 20).toLatin1();
861 memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length());
862 pos = QString::number(result.indexOf("<!--EndFragment-->")).toLatin1();
863 memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length());
864
865 return setData(result, pmedium);
866 }
867 return false;
868}
869
870
871#ifndef QT_NO_IMAGEFORMAT_BMP
872class QWindowsMimeImage : public QWindowsMime
873{
874public:
875 QWindowsMimeImage();
876 // for converting from Qt
877 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
878 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
879 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
880
881 // for converting to Qt
882 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
883 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
884 QString mimeForFormat(const FORMATETC &formatetc) const;
885private:
886 bool hasOriginalDIBV5(IDataObject *pDataObj) const;
887 UINT CF_PNG;
888};
889
890QWindowsMimeImage::QWindowsMimeImage()
891{
892 CF_PNG = RegisterClipboardFormat(L"PNG");
893}
894
895QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
896{
897 QVector<FORMATETC> formatetcs;
898 if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) {
899 //add DIBV5 if image has alpha channel
900 QImage image = qvariant_cast<QImage>(mimeData->imageData());
901 if (!image.isNull() && image.hasAlphaChannel())
902 formatetcs += setCf(CF_DIBV5);
903 formatetcs += setCf(CF_DIB);
904 }
905 return formatetcs;
906}
907
908QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
909{
910 int cf = getCf(formatetc);
911 if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG))
912 return QLatin1String("application/x-qt-image");
913 return QString();
914}
915
916bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
917{
918 if ((mimeType == QLatin1String("application/x-qt-image")) &&
919 (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj)))
920 return true;
921 return false;
922}
923
924bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
925{
926 int cf = getCf(formatetc);
927 if (mimeData->hasImage()) {
928 if (cf == CF_DIB)
929 return true;
930 else if (cf == CF_DIBV5) {
931 //support DIBV5 conversion only if the image has alpha channel
932 QImage image = qvariant_cast<QImage>(mimeData->imageData());
933 if (!image.isNull() && image.hasAlphaChannel())
934 return true;
935 }
936 }
937 return false;
938}
939
940bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
941{
942 int cf = getCf(formatetc);
943 if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) {
944 QImage img = qvariant_cast<QImage>(mimeData->imageData());
945 if (img.isNull())
946 return false;
947 QByteArray ba;
948 QDataStream s(&ba, QIODevice::WriteOnly);
949 s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
950 if (cf == CF_DIB) {
951 if (qt_write_dib(s, img))
952 return setData(ba, pmedium);
953 } else {
954 if (qt_write_dibv5(s, img))
955 return setData(ba, pmedium);
956 }
957 }
958 return false;
959}
960
961bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
962{
963 bool isSynthesized = true;
964 IEnumFORMATETC *pEnum =NULL;
965 HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
966 if (res == S_OK && pEnum) {
967 FORMATETC fc;
968 while ((res = pEnum->Next(1, &fc, 0)) == S_OK) {
969 if (fc.ptd)
970 CoTaskMemFree(fc.ptd);
971 if (fc.cfFormat == CF_DIB)
972 break;
973 else if (fc.cfFormat == CF_DIBV5) {
974 isSynthesized = false;
975 break;
976 }
977 }
978 pEnum->Release();
979 }
980 return !isSynthesized;
981}
982
983QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
984{
985 Q_UNUSED(preferredType);
986 QVariant result;
987 if (mimeType != QLatin1String("application/x-qt-image"))
988 return result;
989 //Try to convert from a format which has more data
990 //DIBV5, use only if its is not synthesized
991 if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) {
992 QImage img;
993 QByteArray data = getData(CF_DIBV5, pDataObj);
994 QDataStream s(&data, QIODevice::ReadOnly);
995 s.setByteOrder(QDataStream::LittleEndian);
996 if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5
997 return img;
998 }
999 }
1000 //PNG, MS Office place this (undocumented)
1001 if (canGetData(CF_PNG, pDataObj)) {
1002 QImage img;
1003 QByteArray data = getData(CF_PNG, pDataObj);
1004 if (img.loadFromData(data, "PNG")) {
1005 return img;
1006 }
1007 }
1008 //Fallback to DIB
1009 if (canGetData(CF_DIB, pDataObj)) {
1010 QImage img;
1011 QByteArray data = getData(CF_DIB, pDataObj);
1012 QDataStream s(&data, QIODevice::ReadOnly);
1013 s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
1014 if (qt_read_dib(s, img)) { // ##### encaps "-14"
1015 return img;
1016 }
1017 }
1018 // Failed
1019 return result;
1020}
1021#endif
1022
1023class QBuiltInMimes : public QWindowsMime
1024{
1025public:
1026 QBuiltInMimes();
1027
1028 // for converting from Qt
1029 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
1030 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
1031 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
1032
1033 // for converting to Qt
1034 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
1035 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
1036 QString mimeForFormat(const FORMATETC &formatetc) const;
1037
1038private:
1039 QMap<int, QString> outFormats;
1040 QMap<int, QString> inFormats;
1041};
1042
1043QBuiltInMimes::QBuiltInMimes()
1044: QWindowsMime()
1045{
1046 outFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
1047 inFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color"));
1048}
1049
1050bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1051{
1052 // really check
1053 return formatetc.tymed & TYMED_HGLOBAL
1054 && outFormats.contains(formatetc.cfFormat)
1055 && mimeData->formats().contains(outFormats.value(formatetc.cfFormat));
1056}
1057
1058bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1059{
1060 if (canConvertFromMime(formatetc, mimeData)) {
1061 QByteArray data;
1062 if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) {
1063 // text/html is in wide chars on windows (compatible with mozillia)
1064 QString html = mimeData->html();
1065 // same code as in the text converter up above
1066 const QChar *u = html.unicode();
1067 QString res;
1068 const int s = html.length();
1069 int maxsize = s + s/40 + 3;
1070 res.resize(maxsize);
1071 int ri = 0;
1072 bool cr = false;
1073 for (int i=0; i < s; ++i) {
1074 if (*u == QLatin1Char('\r'))
1075 cr = true;
1076 else {
1077 if (*u == QLatin1Char('\n') && !cr)
1078 res[ri++] = QLatin1Char('\r');
1079 cr = false;
1080 }
1081 res[ri++] = *u;
1082 if (ri+3 >= maxsize) {
1083 maxsize += maxsize/4;
1084 res.resize(maxsize);
1085 }
1086 ++u;
1087 }
1088 res.truncate(ri);
1089 const int byteLength = res.length() * sizeof(ushort);
1090 QByteArray r(byteLength + 2, '\0');
1091 memcpy(r.data(), res.unicode(), byteLength);
1092 r[byteLength] = 0;
1093 r[byteLength+1] = 0;
1094 data = r;
1095 } else {
1096#ifndef QT_NO_DRAGANDDROP
1097 data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData);
1098#endif //QT_NO_DRAGANDDROP
1099 }
1100 return setData(data, pmedium);
1101 }
1102 return false;
1103}
1104
1105QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
1106{
1107 QVector<FORMATETC> formatetcs;
1108 if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType))
1109 formatetcs += setCf(outFormats.key(mimeType));
1110 return formatetcs;
1111}
1112
1113bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1114{
1115 return (!inFormats.keys(mimeType).isEmpty())
1116 && canGetData(inFormats.key(mimeType), pDataObj);
1117}
1118
1119QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
1120{
1121 QVariant val;
1122 if (canConvertToMime(mimeType, pDataObj)) {
1123 QByteArray data = getData(inFormats.key(mimeType), pDataObj);
1124 if (!data.isEmpty()) {
1125#ifdef QMIME_DEBUG
1126 qDebug("QBuiltInMimes::convertToMime()");
1127#endif
1128 if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) {
1129 // text/html is in wide chars on windows (compatible with Mozilla)
1130 val = QString::fromWCharArray((const wchar_t *)data.data());
1131 } else {
1132 val = data; // it should be enough to return the data and let QMimeData do the rest.
1133 }
1134 }
1135 }
1136 return val;
1137}
1138
1139QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
1140{
1141 return inFormats.value(getCf(formatetc));
1142}
1143
1144
1145class QLastResortMimes : public QWindowsMime
1146{
1147public:
1148
1149 QLastResortMimes();
1150 // for converting from Qt
1151 bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
1152 bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const;
1153 QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const;
1154
1155 // for converting to Qt
1156 bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const;
1157 QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const;
1158 QString mimeForFormat(const FORMATETC &formatetc) const;
1159
1160private:
1161 QMap<int, QString> formats;
1162 static QStringList ianaTypes;
1163 static QStringList excludeList;
1164};
1165
1166QStringList QLastResortMimes::ianaTypes;
1167QStringList QLastResortMimes::excludeList;
1168
1169QLastResortMimes::QLastResortMimes()
1170{
1171 //MIME Media-Types
1172 if (!ianaTypes.size()) {
1173 ianaTypes.append(QLatin1String("application/"));
1174 ianaTypes.append(QLatin1String("audio/"));
1175 ianaTypes.append(QLatin1String("example/"));
1176 ianaTypes.append(QLatin1String("image/"));
1177 ianaTypes.append(QLatin1String("message/"));
1178 ianaTypes.append(QLatin1String("model/"));
1179 ianaTypes.append(QLatin1String("multipart/"));
1180 ianaTypes.append(QLatin1String("text/"));
1181 ianaTypes.append(QLatin1String("video/"));
1182 }
1183 //Types handled by other classes
1184 if (!excludeList.size()) {
1185 excludeList.append(QLatin1String("HTML Format"));
1186 excludeList.append(QLatin1String("UniformResourceLocator"));
1187 excludeList.append(QLatin1String("text/html"));
1188 excludeList.append(QLatin1String("text/plain"));
1189 excludeList.append(QLatin1String("text/uri-list"));
1190 excludeList.append(QLatin1String("application/x-qt-image"));
1191 excludeList.append(QLatin1String("application/x-color"));
1192 }
1193}
1194
1195bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
1196{
1197 // really check
1198#ifndef QT_NO_DRAGANDDROP
1199 return formatetc.tymed & TYMED_HGLOBAL
1200 && (formats.contains(formatetc.cfFormat)
1201 && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData));
1202#else
1203 Q_UNUSED(mimeData);
1204 Q_UNUSED(formatetc);
1205 return formatetc.tymed & TYMED_HGLOBAL
1206 && formats.contains(formatetc.cfFormat);
1207#endif //QT_NO_DRAGANDDROP
1208}
1209
1210bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
1211{
1212#ifndef QT_NO_DRAGANDDROP
1213 return canConvertFromMime(formatetc, mimeData)
1214 && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium);
1215#else
1216 Q_UNUSED(mimeData);
1217 Q_UNUSED(formatetc);
1218 Q_UNUSED(pmedium);
1219 return false;
1220#endif //QT_NO_DRAGANDDROP
1221}
1222
1223QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
1224{
1225 QVector<FORMATETC> formatetcs;
1226 if (!formats.keys(mimeType).isEmpty()) {
1227 formatetcs += setCf(formats.key(mimeType));
1228 } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){
1229 // register any other available formats
1230 int cf = QWindowsMime::registerMimeType(mimeType);
1231 QLastResortMimes *that = const_cast<QLastResortMimes *>(this);
1232 that->formats.insert(cf, mimeType);
1233 formatetcs += setCf(cf);
1234 }
1235 return formatetcs;
1236}
1237static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\"";
1238
1239static bool isCustomMimeType(const QString &mimeType)
1240{
1241 return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive);
1242}
1243
1244static QString customMimeType(const QString &mimeType)
1245{
1246 int len = sizeof(x_qt_windows_mime) - 1;
1247 int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len;
1248 return mimeType.mid(len, n);
1249}
1250
1251bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
1252{
1253 if (isCustomMimeType(mimeType)) {
1254 QString clipFormat = customMimeType(mimeType);
1255 int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
1256 return canGetData(cf, pDataObj);
1257 } else if (formats.keys(mimeType).isEmpty()) {
1258 // if it is not in there then register it an see if we can get it
1259 int cf = QWindowsMime::registerMimeType(mimeType);
1260 return canGetData(cf, pDataObj);
1261 } else {
1262 return canGetData(formats.key(mimeType), pDataObj);
1263 }
1264 return false;
1265}
1266
1267QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
1268{
1269 Q_UNUSED(preferredType);
1270 QVariant val;
1271 if (canConvertToMime(mimeType, pDataObj)) {
1272 QByteArray data;
1273 if (isCustomMimeType(mimeType)) {
1274 QString clipFormat = customMimeType(mimeType);
1275 int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
1276 data = getData(cf, pDataObj);
1277 } else if (formats.keys(mimeType).isEmpty()) {
1278 int cf = QWindowsMime::registerMimeType(mimeType);
1279 data = getData(cf, pDataObj);
1280 } else {
1281 data = getData(formats.key(mimeType), pDataObj);
1282 }
1283 if (!data.isEmpty())
1284 val = data; // it should be enough to return the data and let QMimeData do the rest.
1285 }
1286 return val;
1287}
1288
1289QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
1290{
1291 QString format = formats.value(getCf(formatetc));
1292 if (!format.isEmpty())
1293 return format;
1294
1295 wchar_t buffer[256];
1296 int len = GetClipboardFormatName(getCf(formatetc), buffer, 256);
1297
1298 if (len) {
1299 QString clipFormat = QString::fromWCharArray(buffer, len);
1300#ifndef QT_NO_DRAGANDDROP
1301 if (QInternalMimeData::canReadData(clipFormat))
1302 format = clipFormat;
1303 else if((formatetc.cfFormat >= 0xC000)){
1304 //create the mime as custom. not registered.
1305 if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) {
1306 //check if this is a mime type
1307 bool ianaType = false;
1308 int sz = ianaTypes.size();
1309 for (int i = 0; i < sz; i++) {
1310 if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) {
1311 ianaType = true;
1312 break;
1313 }
1314 }
1315 if (!ianaType)
1316 format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"');
1317 else
1318 format = clipFormat;
1319 }
1320 }
1321#endif //QT_NO_DRAGANDDROP
1322 }
1323
1324 return format;
1325}
1326
1327QWindowsMimeList::QWindowsMimeList()
1328 : initialized(false)
1329{
1330}
1331
1332QWindowsMimeList::~QWindowsMimeList()
1333{
1334 while (mimes.size())
1335 delete mimes.first();
1336}
1337
1338
1339void QWindowsMimeList::init()
1340{
1341 if (!initialized) {
1342 initialized = true;
1343#ifndef QT_NO_IMAGEFORMAT_BMP
1344 new QWindowsMimeImage;
1345#endif
1346 new QLastResortMimes;
1347 new QWindowsMimeText;
1348 new QWindowsMimeURI;
1349
1350 new QWindowsMimeHtml;
1351 new QBuiltInMimes;
1352 }
1353}
1354
1355void QWindowsMimeList::addWindowsMime(QWindowsMime * mime)
1356{
1357 init();
1358 mimes.append(mime);
1359}
1360
1361void QWindowsMimeList::removeWindowsMime(QWindowsMime * mime)
1362{
1363 init();
1364 mimes.removeAll(mime);
1365}
1366
1367QList<QWindowsMime*> QWindowsMimeList::windowsMimes()
1368{
1369 init();
1370 return mimes;
1371}
1372
1373#ifndef QT_NO_IMAGEFORMAT_BMP
1374static bool qt_write_dibv5(QDataStream &s, QImage image)
1375{
1376 QIODevice* d = s.device();
1377 if (!d->isWritable())
1378 return false;
1379
1380 //depth will be always 32
1381 int bpl_bmp = image.width()*4;
1382
1383 BMP_BITMAPV5HEADER bi ={0};
1384 bi.bV5Size = sizeof(BMP_BITMAPV5HEADER);
1385 bi.bV5Width = image.width();
1386 bi.bV5Height = image.height();
1387 bi.bV5Planes = 1;
1388 bi.bV5BitCount = 32;
1389 bi.bV5Compression = BI_BITFIELDS;
1390 bi.bV5SizeImage = bpl_bmp*image.height();
1391 bi.bV5XPelsPerMeter = 0;
1392 bi.bV5YPelsPerMeter = 0;
1393 bi.bV5ClrUsed = 0;
1394 bi.bV5ClrImportant = 0;
1395 bi.bV5BlueMask = 0x000000ff;
1396 bi.bV5GreenMask = 0x0000ff00;
1397 bi.bV5RedMask = 0x00ff0000;
1398 bi.bV5AlphaMask = 0xff000000;
1399 bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB
1400 bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES
1401
1402 d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size);
1403 if (s.status() != QDataStream::Ok)
1404 return false;
1405
1406 DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff};
1407 d->write(reinterpret_cast<const char*>(colorSpace), sizeof(colorSpace));
1408 if (s.status() != QDataStream::Ok)
1409 return false;
1410
1411 if (image.format() != QImage::Format_ARGB32)
1412 image = image.convertToFormat(QImage::Format_ARGB32);
1413
1414 uchar *buf = new uchar[bpl_bmp];
1415 uchar *b;
1416
1417 memset(buf, 0, bpl_bmp);
1418 for (int y=image.height()-1; y>=0; y--) {
1419 // write the image bits
1420 QRgb *p = (QRgb *)image.scanLine(y);
1421 QRgb *end = p + image.width();
1422 b = buf;
1423 while (p < end) {
1424 int alpha = qAlpha(*p);
1425 if (alpha) {
1426 *b++ = qBlue(*p);
1427 *b++ = qGreen(*p);
1428 *b++ = qRed(*p);
1429 } else {
1430 //white for fully transparent pixels.
1431 *b++ = 0xff;
1432 *b++ = 0xff;
1433 *b++ = 0xff;
1434 }
1435 *b++ = alpha;
1436 p++;
1437 }
1438 d->write((char*)buf, bpl_bmp);
1439 if (s.status() != QDataStream::Ok) {
1440 delete[] buf;
1441 return false;
1442 }
1443 }
1444 delete[] buf;
1445 return true;
1446}
1447
1448static int calc_shift(int mask)
1449{
1450 int result = 0;
1451 while (!(mask & 1)) {
1452 result++;
1453 mask >>= 1;
1454 }
1455 return result;
1456}
1457
1458//Supports only 32 bit DIBV5
1459static bool qt_read_dibv5(QDataStream &s, QImage &image)
1460{
1461 BMP_BITMAPV5HEADER bi;
1462 QIODevice* d = s.device();
1463 if (d->atEnd())
1464 return false;
1465
1466 d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header
1467 if (s.status() != QDataStream::Ok)
1468 return false;
1469
1470 int nbits = bi.bV5BitCount;
1471 int comp = bi.bV5Compression;
1472 if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS)
1473 return false; //Unsupported DIBV5 format
1474
1475 int w = bi.bV5Width, h = bi.bV5Height;
1476 int red_mask = bi.bV5RedMask;
1477 int green_mask = bi.bV5GreenMask;
1478 int blue_mask = bi.bV5BlueMask;
1479 int alpha_mask = bi.bV5AlphaMask;
1480 int red_shift = 0;
1481 int green_shift = 0;
1482 int blue_shift = 0;
1483 int alpha_shift = 0;
1484 QImage::Format format = QImage::Format_ARGB32;
1485
1486 if (bi.bV5Height < 0)
1487 h = -h; // support images with negative height
1488 if (image.size() != QSize(w, h) || image.format() != format) {
1489 image = QImage(w, h, format);
1490 if (image.isNull()) // could not create image
1491 return false;
1492 }
1493 image.setDotsPerMeterX(bi.bV5XPelsPerMeter);
1494 image.setDotsPerMeterY(bi.bV5YPelsPerMeter);
1495 // read color table
1496 DWORD colorSpace[3];
1497 if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace))
1498 return false;
1499
1500 red_shift = calc_shift(red_mask);
1501 green_shift = calc_shift(green_mask);
1502 blue_shift = calc_shift(blue_mask);
1503 if (alpha_mask) {
1504 alpha_shift = calc_shift(alpha_mask);
1505 }
1506
1507 int bpl = image.bytesPerLine();
1508 uchar *data = image.bits();
1509 register QRgb *p;
1510 QRgb *end;
1511 uchar *buf24 = new uchar[bpl];
1512 int bpl24 = ((w*nbits+31)/32)*4;
1513 uchar *b;
1514 unsigned int c;
1515
1516 while (--h >= 0) {
1517 p = (QRgb *)(data + h*bpl);
1518 end = p + w;
1519 if (d->read((char *)buf24,bpl24) != bpl24)
1520 break;
1521 b = buf24;
1522 while (p < end) {
1523 c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24;
1524 *p++ = qRgba(((c & red_mask) >> red_shift) ,
1525 ((c & green_mask) >> green_shift),
1526 ((c & blue_mask) >> blue_shift),
1527 ((c & alpha_mask) >> alpha_shift));
1528 b += 4;
1529 }
1530 }
1531 delete[] buf24;
1532
1533 if (bi.bV5Height < 0) {
1534 // Flip the image
1535 uchar *buf = new uchar[bpl];
1536 h = -bi.bV5Height;
1537 for (int y = 0; y < h/2; ++y) {
1538 memcpy(buf, data + y*bpl, bpl);
1539 memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl);
1540 memcpy(data + (h-y-1)*bpl, buf, bpl);
1541 }
1542 delete [] buf;
1543 }
1544
1545 return true;
1546}
1547
1548#endif
1549
1550QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.