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

Last change on this file since 147 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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