/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtGui module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qmime.h" | |
#include "qimagereader.h" | |
#include "qimagewriter.h" | |
#include "qdatastream.h" | |
#include "qbuffer.h" | |
#include "qt_windows.h" | |
#include "qapplication_p.h" | |
#include "qtextcodec.h" | |
#include "qregexp.h" | |
#include "qalgorithms.h" | |
#include "qmap.h" | |
#include "qdnd_p.h" | |
#include <shlobj.h> | |
#include "qurl.h" | |
#include "qvariant.h" | |
#include "qtextdocument.h" | |
#include "qdir.h" | |
#if defined(Q_OS_WINCE) | |
#include "qguifunctions_wince.h" | |
#endif | |
QT_BEGIN_NAMESPACE | |
#ifndef QT_NO_IMAGEFORMAT_BMP | |
#ifndef CF_DIBV5 | |
#define CF_DIBV5 17 | |
#endif | |
/* The MSVC compilers allows multi-byte characters, that has the behavior of | |
* that each character gets shifted into position. 0x73524742 below is for MSVC | |
* equivalent to doing 'sRGB', but this does of course not work | |
* on conformant C++ compilers. */ | |
#define BMP_LCS_sRGB 0x73524742 | |
#define BMP_LCS_GM_IMAGES 0x00000004L | |
struct _CIEXYZ { | |
long ciexyzX, ciexyzY, ciexyzZ; | |
}; | |
struct _CIEXYZTRIPLE { | |
_CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue; | |
}; | |
struct BMP_BITMAPV5HEADER { | |
DWORD bV5Size; | |
LONG bV5Width; | |
LONG bV5Height; | |
WORD bV5Planes; | |
WORD bV5BitCount; | |
DWORD bV5Compression; | |
DWORD bV5SizeImage; | |
LONG bV5XPelsPerMeter; | |
LONG bV5YPelsPerMeter; | |
DWORD bV5ClrUsed; | |
DWORD bV5ClrImportant; | |
DWORD bV5RedMask; | |
DWORD bV5GreenMask; | |
DWORD bV5BlueMask; | |
DWORD bV5AlphaMask; | |
DWORD bV5CSType; | |
_CIEXYZTRIPLE bV5Endpoints; | |
DWORD bV5GammaRed; | |
DWORD bV5GammaGreen; | |
DWORD bV5GammaBlue; | |
DWORD bV5Intent; | |
DWORD bV5ProfileData; | |
DWORD bV5ProfileSize; | |
DWORD bV5Reserved; | |
}; | |
static const int BMP_BITFIELDS = 3; | |
extern bool qt_read_dib(QDataStream&, QImage&); // qimage.cpp | |
extern bool qt_write_dib(QDataStream&, QImage); // qimage.cpp | |
static bool qt_write_dibv5(QDataStream &s, QImage image); | |
static bool qt_read_dibv5(QDataStream &s, QImage &image); | |
#endif | |
//#define QMIME_DEBUG | |
// helpers for using global memory | |
static int getCf(const FORMATETC &formatetc) | |
{ | |
return formatetc.cfFormat; | |
} | |
static FORMATETC setCf(int cf) | |
{ | |
FORMATETC formatetc; | |
formatetc.cfFormat = cf; | |
formatetc.dwAspect = DVASPECT_CONTENT; | |
formatetc.lindex = -1; | |
formatetc.ptd = NULL; | |
formatetc.tymed = TYMED_HGLOBAL; | |
return formatetc; | |
} | |
static bool setData(const QByteArray &data, STGMEDIUM *pmedium) | |
{ | |
HGLOBAL hData = GlobalAlloc(0, data.size()); | |
if (!hData) | |
return false; | |
void *out = GlobalLock(hData); | |
memcpy(out, data.data(), data.size()); | |
GlobalUnlock(hData); | |
pmedium->tymed = TYMED_HGLOBAL; | |
pmedium->hGlobal = hData; | |
pmedium->pUnkForRelease = 0; | |
return true; | |
} | |
static QByteArray getData(int cf, IDataObject *pDataObj) | |
{ | |
QByteArray data; | |
FORMATETC formatetc = setCf(cf); | |
STGMEDIUM s; | |
if (pDataObj->GetData(&formatetc, &s) == S_OK) { | |
DWORD * val = (DWORD*)GlobalLock(s.hGlobal); | |
data = QByteArray::fromRawData((char*)val, GlobalSize(s.hGlobal)); | |
data.detach(); | |
GlobalUnlock(s.hGlobal); | |
ReleaseStgMedium(&s); | |
} else { | |
//Try reading IStream data | |
formatetc.tymed = TYMED_ISTREAM; | |
if (pDataObj->GetData(&formatetc, &s) == S_OK) { | |
char szBuffer[4096]; | |
ULONG actualRead = 0; | |
LARGE_INTEGER pos = {{0, 0}}; | |
//Move to front (can fail depending on the data model implemented) | |
HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, NULL); | |
while(SUCCEEDED(hr)){ | |
hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead); | |
if (SUCCEEDED(hr) && actualRead > 0) { | |
data += QByteArray::fromRawData(szBuffer, actualRead); | |
} | |
if (actualRead != sizeof(szBuffer)) | |
break; | |
} | |
data.detach(); | |
ReleaseStgMedium(&s); | |
} | |
} | |
return data; | |
} | |
static bool canGetData(int cf, IDataObject * pDataObj) | |
{ | |
FORMATETC formatetc = setCf(cf); | |
if (pDataObj->QueryGetData(&formatetc) != S_OK){ | |
formatetc.tymed = TYMED_ISTREAM; | |
return pDataObj->QueryGetData(&formatetc) == S_OK; | |
} | |
return true; | |
} | |
class QWindowsMimeList | |
{ | |
public: | |
QWindowsMimeList(); | |
~QWindowsMimeList(); | |
void addWindowsMime(QWindowsMime * mime); | |
void removeWindowsMime(QWindowsMime * mime); | |
QList<QWindowsMime*> windowsMimes(); | |
private: | |
void init(); | |
bool initialized; | |
QList<QWindowsMime*> mimes; | |
}; | |
Q_GLOBAL_STATIC(QWindowsMimeList, theMimeList); | |
/*! | |
\class QWindowsMime | |
\brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. | |
\ingroup draganddrop | |
Qt's drag-and-drop and clipboard facilities use the MIME standard. | |
On X11, this maps trivially to the Xdnd protocol, but on Windows | |
although some applications use MIME types to describe clipboard | |
formats, others use arbitrary non-standardized naming conventions, | |
or unnamed built-in formats of Windows. | |
By instantiating subclasses of QWindowsMime that provide conversions | |
between Windows Clipboard and MIME formats, you can convert | |
proprietary clipboard formats to MIME formats. | |
Qt has predefined support for the following Windows Clipboard formats: | |
\table | |
\header \o Windows Format \o Equivalent MIME type | |
\row \o \c CF_UNICODETEXT \o \c text/plain | |
\row \o \c CF_TEXT \o \c text/plain | |
\row \o \c CF_DIB \o \c{image/xyz}, where \c xyz is | |
a \l{QImageWriter::supportedImageFormats()}{Qt image format} | |
\row \o \c CF_HDROP \o \c text/uri-list | |
\row \o \c CF_INETURL \o \c text/uri-list | |
\row \o \c CF_HTML \o \c text/html | |
\endtable | |
An example use of this class would be to map the Windows Metafile | |
clipboard format (\c CF_METAFILEPICT) to and from the MIME type | |
\c{image/x-wmf}. This conversion might simply be adding or removing | |
a header, or even just passing on the data. See \l{Drag and Drop} | |
for more information on choosing and definition MIME types. | |
You can check if a MIME type is convertible using canConvertFromMime() and | |
can perform conversions with convertToMime() and convertFromMime(). | |
*/ | |
/*! | |
Constructs a new conversion object, adding it to the globally accessed | |
list of available converters. | |
*/ | |
QWindowsMime::QWindowsMime() | |
{ | |
theMimeList()->addWindowsMime(this); | |
} | |
/*! | |
Destroys a conversion object, removing it from the global | |
list of available converters. | |
*/ | |
QWindowsMime::~QWindowsMime() | |
{ | |
theMimeList()->removeWindowsMime(this); | |
} | |
/*! | |
Registers the MIME type \a mime, and returns an ID number | |
identifying the format on Windows. | |
*/ | |
int QWindowsMime::registerMimeType(const QString &mime) | |
{ | |
int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); | |
if (!f) | |
qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format"); | |
return f; | |
} | |
/*! | |
\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
Returns true if the converter can convert from the \a mimeData to | |
the format specified in \a formatetc. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
/*! | |
\fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
Returns true if the converter can convert to the \a mimeType from | |
the available formats in \a pDataObj. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
/*! | |
\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const | |
Returns the mime type that will be created form the format specified | |
in \a formatetc, or an empty string if this converter does not support | |
\a formatetc. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
/*! | |
\fn QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
Returns a QVector of FORMATETC structures representing the different windows clipboard | |
formats that can be provided for the \a mimeType from the \a mimeData. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
/*! | |
\fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj, | |
QVariant::Type preferredType) const | |
Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. | |
If possible the QVariant should be of the \a preferredType to avoid needless conversions. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
/*! | |
\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const | |
Convert the \a mimeData to the format specified in \a formatetc. | |
The converted data should then be placed in \a pmedium structure. | |
Return true if the conversion was successful. | |
All subclasses must reimplement this pure virtual function. | |
*/ | |
QWindowsMime *QWindowsMime::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) | |
{ | |
QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); | |
for (int i=mimes.size()-1; i>=0; --i) { | |
if (mimes.at(i)->canConvertFromMime(formatetc, mimeData)) | |
return mimes.at(i); | |
} | |
return 0; | |
} | |
QWindowsMime *QWindowsMime::converterToMime(const QString &mimeType, IDataObject *pDataObj) | |
{ | |
QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); | |
for (int i=mimes.size()-1; i>=0; --i) { | |
if (mimes.at(i)->canConvertToMime(mimeType, pDataObj)) | |
return mimes.at(i); | |
} | |
return 0; | |
} | |
QVector<FORMATETC> QWindowsMime::allFormatsForMime(const QMimeData *mimeData) | |
{ | |
QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); | |
QVector<FORMATETC> formatics; | |
formatics.reserve(20); | |
#ifndef QT_NO_DRAGANDDROP | |
QStringList formats = QInternalMimeData::formatsHelper(mimeData); | |
for (int f=0; f<formats.size(); ++f) { | |
for (int i=mimes.size()-1; i>=0; --i) | |
formatics += mimes.at(i)->formatsForMime(formats.at(f), mimeData); | |
} | |
#else | |
Q_UNUSED(mimeData); | |
#endif //QT_NO_DRAGANDDROP | |
return formatics; | |
} | |
QStringList QWindowsMime::allMimesForFormats(IDataObject *pDataObj) | |
{ | |
QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); | |
QStringList formats; | |
LPENUMFORMATETC FAR fmtenum; | |
HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum); | |
if (hr == NOERROR) { | |
FORMATETC fmtetc; | |
while (S_OK == fmtenum->Next(1, &fmtetc, 0)) { | |
#if defined(QMIME_DEBUG) && !defined(Q_OS_WINCE) | |
qDebug("QWindowsMime::allMimesForFormats()"); | |
wchar_t buf[256] = {0}; | |
GetClipboardFormatName(fmtetc.cfFormat, buf, 255); | |
qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(buf)); | |
#endif | |
for (int i=mimes.size()-1; i>=0; --i) { | |
QString format = mimes.at(i)->mimeForFormat(fmtetc); | |
if (!format.isEmpty() && !formats.contains(format)) { | |
formats += format; | |
} | |
} | |
// as documented in MSDN to avoid possible memleak | |
if (fmtetc.ptd) | |
CoTaskMemFree(fmtetc.ptd); | |
} | |
fmtenum->Release(); | |
} | |
return formats; | |
} | |
class QWindowsMimeText : public QWindowsMime | |
{ | |
public: | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
}; | |
bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
int cf = getCf(formatetc); | |
return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText(); | |
} | |
/* | |
text/plain is defined as using CRLF, but so many programs don't, | |
and programmers just look for '\n' in strings. | |
Windows really needs CRLF, so we ensure it here. | |
*/ | |
bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const | |
{ | |
if (canConvertFromMime(formatetc, mimeData)) { | |
QByteArray data; | |
int cf = getCf(formatetc); | |
if (cf == CF_TEXT) { | |
data = mimeData->text().toLocal8Bit(); | |
// Anticipate required space for CRLFs at 1/40 | |
int maxsize=data.size()+data.size()/40+3; | |
QByteArray r(maxsize, '\0'); | |
char* o = r.data(); | |
const char* d = data.data(); | |
const int s = data.size(); | |
bool cr=false; | |
int j=0; | |
for (int i=0; i<s; i++) { | |
char c = d[i]; | |
if (c=='\r') | |
cr=true; | |
else { | |
if (c=='\n') { | |
if (!cr) | |
o[j++]='\r'; | |
} | |
cr=false; | |
} | |
o[j++]=c; | |
if (j+3 >= maxsize) { | |
maxsize += maxsize/4; | |
r.resize(maxsize); | |
o = r.data(); | |
} | |
} | |
o[j]=0; | |
return setData(r, pmedium); | |
} else if (cf == CF_UNICODETEXT) { | |
QString str = mimeData->text(); | |
const QChar *u = str.unicode(); | |
QString res; | |
const int s = str.length(); | |
int maxsize = s + s/40 + 3; | |
res.resize(maxsize); | |
int ri = 0; | |
bool cr = false; | |
for (int i=0; i < s; ++i) { | |
if (*u == QLatin1Char('\r')) | |
cr = true; | |
else { | |
if (*u == QLatin1Char('\n') && !cr) | |
res[ri++] = QLatin1Char('\r'); | |
cr = false; | |
} | |
res[ri++] = *u; | |
if (ri+3 >= maxsize) { | |
maxsize += maxsize/4; | |
res.resize(maxsize); | |
} | |
++u; | |
} | |
res.truncate(ri); | |
const int byteLength = res.length() * sizeof(ushort); | |
QByteArray r(byteLength + 2, '\0'); | |
memcpy(r.data(), res.unicode(), byteLength); | |
r[byteLength] = 0; | |
r[byteLength+1] = 0; | |
return setData(r, pmedium); | |
} | |
} | |
return false; | |
} | |
bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
return mimeType.startsWith(QLatin1String("text/plain")) | |
&& (canGetData(CF_UNICODETEXT, pDataObj) | |
|| canGetData(CF_TEXT, pDataObj)); | |
} | |
QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
int cf = getCf(formatetc); | |
if (cf == CF_UNICODETEXT || cf == CF_TEXT) | |
return QLatin1String("text/plain"); | |
return QString(); | |
} | |
QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
{ | |
QVector<FORMATETC> formatics; | |
if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) { | |
formatics += setCf(CF_UNICODETEXT); | |
formatics += setCf(CF_TEXT); | |
} | |
return formatics; | |
} | |
QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const | |
{ | |
QVariant ret; | |
if (canConvertToMime(mime, pDataObj)) { | |
QString str; | |
QByteArray data = getData(CF_UNICODETEXT, pDataObj); | |
if (!data.isEmpty()) { | |
str = QString::fromWCharArray((const wchar_t *)data.data()); | |
str.replace(QLatin1String("\r\n"), QLatin1String("\n")); | |
} else { | |
data = getData(CF_TEXT, pDataObj); | |
if (!data.isEmpty()) { | |
const char* d = data.data(); | |
const int s = qstrlen(d); | |
QByteArray r(data.size()+1, '\0'); | |
char* o = r.data(); | |
int j=0; | |
for (int i=0; i<s; i++) { | |
char c = d[i]; | |
if (c!='\r') | |
o[j++]=c; | |
} | |
o[j]=0; | |
str = QString::fromLocal8Bit(r); | |
} | |
} | |
if (preferredType == QVariant::String) | |
ret = str; | |
else | |
ret = str.toUtf8(); | |
} | |
return ret; | |
} | |
class QWindowsMimeURI : public QWindowsMime | |
{ | |
public: | |
QWindowsMimeURI(); | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
private: | |
int CF_INETURL_W; // wide char version | |
int CF_INETURL; | |
}; | |
QWindowsMimeURI::QWindowsMimeURI() | |
{ | |
CF_INETURL_W = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocatorW")); | |
CF_INETURL = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocator")); | |
} | |
bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
if (getCf(formatetc) == CF_HDROP) { | |
QList<QUrl> urls = mimeData->urls(); | |
for (int i=0; i<urls.size(); i++) { | |
if (!urls.at(i).toLocalFile().isEmpty()) | |
return true; | |
} | |
} | |
return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QLatin1String("text/uri-list")); | |
} | |
bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const | |
{ | |
if (canConvertFromMime(formatetc, mimeData)) { | |
if (getCf(formatetc) == CF_HDROP) { | |
QList<QUrl> urls = mimeData->urls(); | |
QStringList fileNames; | |
int size = sizeof(DROPFILES)+2; | |
for (int i=0; i<urls.size(); i++) { | |
QString fn = QDir::toNativeSeparators(urls.at(i).toLocalFile()); | |
if (!fn.isEmpty()) { | |
size += sizeof(ushort) * (fn.length() + 1); | |
fileNames.append(fn); | |
} | |
} | |
QByteArray result(size, '\0'); | |
DROPFILES* d = (DROPFILES*)result.data(); | |
d->pFiles = sizeof(DROPFILES); | |
GetCursorPos(&d->pt); // try | |
d->fNC = true; | |
char* files = ((char*)d) + d->pFiles; | |
d->fWide = true; | |
wchar_t* f = (wchar_t*)files; | |
for (int i=0; i<fileNames.size(); i++) { | |
int l = fileNames.at(i).length(); | |
memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort)); | |
f += l; | |
*f++ = 0; | |
} | |
*f = 0; | |
return setData(result, pmedium); | |
} else if (getCf(formatetc) == CF_INETURL_W) { | |
QList<QUrl> urls = mimeData->urls(); | |
QByteArray result; | |
if (!urls.isEmpty()) { | |
QString url = urls.at(0).toString(); | |
result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort)); | |
} | |
result.append('\0'); | |
result.append('\0'); | |
return setData(result, pmedium); | |
} else if (getCf(formatetc) == CF_INETURL) { | |
QList<QUrl> urls = mimeData->urls(); | |
QByteArray result; | |
if (!urls.isEmpty()) | |
result = urls.at(0).toString().toLocal8Bit(); | |
return setData(result, pmedium); | |
} | |
} | |
return false; | |
} | |
bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
return mimeType == QLatin1String("text/uri-list") | |
&& (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj)); | |
} | |
QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
QString format; | |
if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) | |
format = QLatin1String("text/uri-list"); | |
return format; | |
} | |
QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
{ | |
QVector<FORMATETC> formatics; | |
if (mimeType == QLatin1String("text/uri-list")) { | |
if (canConvertFromMime(setCf(CF_HDROP), mimeData)) | |
formatics += setCf(CF_HDROP); | |
if (canConvertFromMime(setCf(CF_INETURL_W), mimeData)) | |
formatics += setCf(CF_INETURL_W); | |
if (canConvertFromMime(setCf(CF_INETURL), mimeData)) | |
formatics += setCf(CF_INETURL); | |
} | |
return formatics; | |
} | |
QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const | |
{ | |
if (mimeType == QLatin1String("text/uri-list")) { | |
if (canGetData(CF_HDROP, pDataObj)) { | |
QByteArray texturi; | |
QList<QVariant> urls; | |
QByteArray data = getData(CF_HDROP, pDataObj); | |
if (data.isEmpty()) | |
return QVariant(); | |
LPDROPFILES hdrop = (LPDROPFILES)data.data(); | |
if (hdrop->fWide) { | |
const wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles); | |
int i = 0; | |
while (filesw[i]) { | |
QString fileurl = QString::fromWCharArray(filesw + i); | |
urls += QUrl::fromLocalFile(fileurl); | |
i += fileurl.length()+1; | |
} | |
} else { | |
const char* files = (const char *)data.data() + hdrop->pFiles; | |
int i=0; | |
while (files[i]) { | |
urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i)); | |
i += int(strlen(files+i))+1; | |
} | |
} | |
if (preferredType == QVariant::Url && urls.size() == 1) | |
return urls.at(0); | |
else if (!urls.isEmpty()) | |
return urls; | |
} else if (canGetData(CF_INETURL_W, pDataObj)) { | |
QByteArray data = getData(CF_INETURL_W, pDataObj); | |
if (data.isEmpty()) | |
return QVariant(); | |
return QUrl(QString::fromWCharArray((const wchar_t *)data.constData())); | |
} else if (canGetData(CF_INETURL, pDataObj)) { | |
QByteArray data = getData(CF_INETURL, pDataObj); | |
if (data.isEmpty()) | |
return QVariant(); | |
return QUrl(QString::fromLocal8Bit(data.constData())); | |
} | |
} | |
return QVariant(); | |
} | |
class QWindowsMimeHtml : public QWindowsMime | |
{ | |
public: | |
QWindowsMimeHtml(); | |
// for converting from Qt | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
// for converting to Qt | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
private: | |
int CF_HTML; | |
}; | |
QWindowsMimeHtml::QWindowsMimeHtml() | |
{ | |
CF_HTML = QWindowsMime::registerMimeType(QLatin1String("HTML Format")); | |
} | |
QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
{ | |
QVector<FORMATETC> formatetcs; | |
if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty())) | |
formatetcs += setCf(CF_HTML); | |
return formatetcs; | |
} | |
QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
if (getCf(formatetc) == CF_HTML) | |
return QLatin1String("text/html"); | |
return QString(); | |
} | |
bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj); | |
} | |
bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty()); | |
} | |
/* | |
The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions | |
in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag | |
Version: 1.0 | |
StartHTML:xxxxxxxxxx | |
EndHTML:xxxxxxxxxx | |
StartFragment:xxxxxxxxxx | |
EndFragment:xxxxxxxxxx | |
...html... | |
*/ | |
QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const | |
{ | |
Q_UNUSED(preferredType); | |
QVariant result; | |
if (canConvertToMime(mime, pDataObj)) { | |
QByteArray html = getData(CF_HTML, pDataObj); | |
#ifdef QMIME_DEBUG | |
qDebug("QWindowsMimeHtml::convertToMime"); | |
qDebug("raw :"); | |
qDebug(html); | |
#endif | |
int start = html.indexOf("StartFragment:"); | |
int end = html.indexOf("EndFragment:"); | |
if (start != -1) { | |
int startOffset = start + 14; | |
int i = startOffset; | |
while (html.at(i) != '\r' && html.at(i) != '\n') | |
++i; | |
QByteArray bytecount = html.mid(startOffset, i - startOffset); | |
start = bytecount.toInt(); | |
} | |
if (end != -1) { | |
int endOffset = end + 12; | |
int i = endOffset ; | |
while (html.at(i) != '\r' && html.at(i) != '\n') | |
++i; | |
QByteArray bytecount = html.mid(endOffset , i - endOffset); | |
end = bytecount.toInt(); | |
} | |
if (end > start && start > 0) { | |
html = "<!--StartFragment-->" + html.mid(start, end - start); | |
html += "<!--EndFragment-->"; | |
html.replace('\r', ""); | |
result = QString::fromUtf8(html); | |
} | |
} | |
return result; | |
} | |
bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const | |
{ | |
if (canConvertFromMime(formatetc, mimeData)) { | |
QByteArray data = mimeData->html().toUtf8(); | |
QByteArray result = | |
"Version:1.0\r\n" // 0-12 | |
"StartHTML:0000000105\r\n" // 13-35 | |
"EndHTML:0000000000\r\n" // 36-55 | |
"StartFragment:0000000000\r\n" // 58-86 | |
"EndFragment:0000000000\r\n\r\n"; // 87-105 | |
if (data.indexOf("<!--StartFragment-->") == -1) | |
result += "<!--StartFragment-->"; | |
result += data; | |
if (data.indexOf("<!--EndFragment-->") == -1) | |
result += "<!--EndFragment-->"; | |
// set the correct number for EndHTML | |
QByteArray pos = QString::number(result.size()).toLatin1(); | |
memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length()); | |
// set correct numbers for StartFragment and EndFragment | |
pos = QString::number(result.indexOf("<!--StartFragment-->") + 20).toLatin1(); | |
memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length()); | |
pos = QString::number(result.indexOf("<!--EndFragment-->")).toLatin1(); | |
memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length()); | |
return setData(result, pmedium); | |
} | |
return false; | |
} | |
#ifndef QT_NO_IMAGEFORMAT_BMP | |
class QWindowsMimeImage : public QWindowsMime | |
{ | |
public: | |
QWindowsMimeImage(); | |
// for converting from Qt | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
// for converting to Qt | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
private: | |
bool hasOriginalDIBV5(IDataObject *pDataObj) const; | |
UINT CF_PNG; | |
}; | |
QWindowsMimeImage::QWindowsMimeImage() | |
{ | |
CF_PNG = RegisterClipboardFormat(L"PNG"); | |
} | |
QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
{ | |
QVector<FORMATETC> formatetcs; | |
if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) { | |
//add DIBV5 if image has alpha channel | |
QImage image = qvariant_cast<QImage>(mimeData->imageData()); | |
if (!image.isNull() && image.hasAlphaChannel()) | |
formatetcs += setCf(CF_DIBV5); | |
formatetcs += setCf(CF_DIB); | |
} | |
return formatetcs; | |
} | |
QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
int cf = getCf(formatetc); | |
if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) | |
return QLatin1String("application/x-qt-image"); | |
return QString(); | |
} | |
bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
if ((mimeType == QLatin1String("application/x-qt-image")) && | |
(canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj))) | |
return true; | |
return false; | |
} | |
bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
int cf = getCf(formatetc); | |
if (mimeData->hasImage()) { | |
if (cf == CF_DIB) | |
return true; | |
else if (cf == CF_DIBV5) { | |
//support DIBV5 conversion only if the image has alpha channel | |
QImage image = qvariant_cast<QImage>(mimeData->imageData()); | |
if (!image.isNull() && image.hasAlphaChannel()) | |
return true; | |
} | |
} | |
return false; | |
} | |
bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const | |
{ | |
int cf = getCf(formatetc); | |
if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) { | |
QImage img = qvariant_cast<QImage>(mimeData->imageData()); | |
if (img.isNull()) | |
return false; | |
QByteArray ba; | |
QDataStream s(&ba, QIODevice::WriteOnly); | |
s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### | |
if (cf == CF_DIB) { | |
if (img.format() > QImage::Format_ARGB32) | |
img = img.convertToFormat(QImage::Format_RGB32); | |
if (qt_write_dib(s, img)) | |
return setData(ba, pmedium); | |
} else { | |
if (qt_write_dibv5(s, img)) | |
return setData(ba, pmedium); | |
} | |
} | |
return false; | |
} | |
bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const | |
{ | |
bool isSynthesized = true; | |
IEnumFORMATETC *pEnum =NULL; | |
HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum); | |
if (res == S_OK && pEnum) { | |
FORMATETC fc; | |
while ((res = pEnum->Next(1, &fc, 0)) == S_OK) { | |
if (fc.ptd) | |
CoTaskMemFree(fc.ptd); | |
if (fc.cfFormat == CF_DIB) | |
break; | |
else if (fc.cfFormat == CF_DIBV5) { | |
isSynthesized = false; | |
break; | |
} | |
} | |
pEnum->Release(); | |
} | |
return !isSynthesized; | |
} | |
QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const | |
{ | |
Q_UNUSED(preferredType); | |
QVariant result; | |
if (mimeType != QLatin1String("application/x-qt-image")) | |
return result; | |
//Try to convert from a format which has more data | |
//DIBV5, use only if its is not synthesized | |
if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) { | |
QImage img; | |
QByteArray data = getData(CF_DIBV5, pDataObj); | |
QDataStream s(&data, QIODevice::ReadOnly); | |
s.setByteOrder(QDataStream::LittleEndian); | |
if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5 | |
return img; | |
} | |
} | |
//PNG, MS Office place this (undocumented) | |
if (canGetData(CF_PNG, pDataObj)) { | |
QImage img; | |
QByteArray data = getData(CF_PNG, pDataObj); | |
if (img.loadFromData(data, "PNG")) { | |
return img; | |
} | |
} | |
//Fallback to DIB | |
if (canGetData(CF_DIB, pDataObj)) { | |
QImage img; | |
QByteArray data = getData(CF_DIB, pDataObj); | |
QDataStream s(&data, QIODevice::ReadOnly); | |
s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### | |
if (qt_read_dib(s, img)) { // ##### encaps "-14" | |
return img; | |
} | |
} | |
// Failed | |
return result; | |
} | |
#endif | |
class QBuiltInMimes : public QWindowsMime | |
{ | |
public: | |
QBuiltInMimes(); | |
// for converting from Qt | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
// for converting to Qt | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
private: | |
QMap<int, QString> outFormats; | |
QMap<int, QString> inFormats; | |
}; | |
QBuiltInMimes::QBuiltInMimes() | |
: QWindowsMime() | |
{ | |
outFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color")); | |
inFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color")); | |
} | |
bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
// really check | |
return formatetc.tymed & TYMED_HGLOBAL | |
&& outFormats.contains(formatetc.cfFormat) | |
&& mimeData->formats().contains(outFormats.value(formatetc.cfFormat)); | |
} | |
bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const | |
{ | |
if (canConvertFromMime(formatetc, mimeData)) { | |
QByteArray data; | |
if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) { | |
// text/html is in wide chars on windows (compatible with mozillia) | |
QString html = mimeData->html(); | |
// same code as in the text converter up above | |
const QChar *u = html.unicode(); | |
QString res; | |
const int s = html.length(); | |
int maxsize = s + s/40 + 3; | |
res.resize(maxsize); | |
int ri = 0; | |
bool cr = false; | |
for (int i=0; i < s; ++i) { | |
if (*u == QLatin1Char('\r')) | |
cr = true; | |
else { | |
if (*u == QLatin1Char('\n') && !cr) | |
res[ri++] = QLatin1Char('\r'); | |
cr = false; | |
} | |
res[ri++] = *u; | |
if (ri+3 >= maxsize) { | |
maxsize += maxsize/4; | |
res.resize(maxsize); | |
} | |
++u; | |
} | |
res.truncate(ri); | |
const int byteLength = res.length() * sizeof(ushort); | |
QByteArray r(byteLength + 2, '\0'); | |
memcpy(r.data(), res.unicode(), byteLength); | |
r[byteLength] = 0; | |
r[byteLength+1] = 0; | |
data = r; | |
} else { | |
#ifndef QT_NO_DRAGANDDROP | |
data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData); | |
#endif //QT_NO_DRAGANDDROP | |
} | |
return setData(data, pmedium); | |
} | |
return false; | |
} | |
QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const | |
{ | |
QVector<FORMATETC> formatetcs; | |
if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType)) | |
formatetcs += setCf(outFormats.key(mimeType)); | |
return formatetcs; | |
} | |
bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
return (!inFormats.keys(mimeType).isEmpty()) | |
&& canGetData(inFormats.key(mimeType), pDataObj); | |
} | |
QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const | |
{ | |
QVariant val; | |
if (canConvertToMime(mimeType, pDataObj)) { | |
QByteArray data = getData(inFormats.key(mimeType), pDataObj); | |
if (!data.isEmpty()) { | |
#ifdef QMIME_DEBUG | |
qDebug("QBuiltInMimes::convertToMime()"); | |
#endif | |
if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) { | |
// text/html is in wide chars on windows (compatible with Mozilla) | |
val = QString::fromWCharArray((const wchar_t *)data.data()); | |
} else { | |
val = data; // it should be enough to return the data and let QMimeData do the rest. | |
} | |
} | |
} | |
return val; | |
} | |
QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
return inFormats.value(getCf(formatetc)); | |
} | |
class QLastResortMimes : public QWindowsMime | |
{ | |
public: | |
QLastResortMimes(); | |
// for converting from Qt | |
bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; | |
bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; | |
QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; | |
// for converting to Qt | |
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; | |
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; | |
QString mimeForFormat(const FORMATETC &formatetc) const; | |
private: | |
QMap<int, QString> formats; | |
static QStringList ianaTypes; | |
static QStringList excludeList; | |
}; | |
QStringList QLastResortMimes::ianaTypes; | |
QStringList QLastResortMimes::excludeList; | |
QLastResortMimes::QLastResortMimes() | |
{ | |
//MIME Media-Types | |
if (!ianaTypes.size()) { | |
ianaTypes.append(QLatin1String("application/")); | |
ianaTypes.append(QLatin1String("audio/")); | |
ianaTypes.append(QLatin1String("example/")); | |
ianaTypes.append(QLatin1String("image/")); | |
ianaTypes.append(QLatin1String("message/")); | |
ianaTypes.append(QLatin1String("model/")); | |
ianaTypes.append(QLatin1String("multipart/")); | |
ianaTypes.append(QLatin1String("text/")); | |
ianaTypes.append(QLatin1String("video/")); | |
} | |
//Types handled by other classes | |
if (!excludeList.size()) { | |
excludeList.append(QLatin1String("HTML Format")); | |
excludeList.append(QLatin1String("UniformResourceLocator")); | |
excludeList.append(QLatin1String("text/html")); | |
excludeList.append(QLatin1String("text/plain")); | |
excludeList.append(QLatin1String("text/uri-list")); | |
excludeList.append(QLatin1String("application/x-qt-image")); | |
excludeList.append(QLatin1String("application/x-color")); | |
} | |
} | |
bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const | |
{ | |
// really check | |
#ifndef QT_NO_DRAGANDDROP | |
return formatetc.tymed & TYMED_HGLOBAL | |
&& (formats.contains(formatetc.cfFormat) | |
&& QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData)); | |
#else | |
Q_UNUSED(mimeData); | |
Q_UNUSED(formatetc); | |
return formatetc.tymed & TYMED_HGLOBAL | |
&& formats.contains(formatetc.cfFormat); | |
#endif //QT_NO_DRAGANDDROP | |
} | |
bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const | |
{ | |
#ifndef QT_NO_DRAGANDDROP | |
return canConvertFromMime(formatetc, mimeData) | |
&& setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium); | |
#else | |
Q_UNUSED(mimeData); | |
Q_UNUSED(formatetc); | |
Q_UNUSED(pmedium); | |
return false; | |
#endif //QT_NO_DRAGANDDROP | |
} | |
QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const | |
{ | |
QVector<FORMATETC> formatetcs; | |
if (!formats.keys(mimeType).isEmpty()) { | |
formatetcs += setCf(formats.key(mimeType)); | |
} else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){ | |
// register any other available formats | |
int cf = QWindowsMime::registerMimeType(mimeType); | |
QLastResortMimes *that = const_cast<QLastResortMimes *>(this); | |
that->formats.insert(cf, mimeType); | |
formatetcs += setCf(cf); | |
} | |
return formatetcs; | |
} | |
static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\""; | |
static bool isCustomMimeType(const QString &mimeType) | |
{ | |
return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); | |
} | |
static QString customMimeType(const QString &mimeType) | |
{ | |
int len = sizeof(x_qt_windows_mime) - 1; | |
int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len; | |
return mimeType.mid(len, n); | |
} | |
bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const | |
{ | |
if (isCustomMimeType(mimeType)) { | |
QString clipFormat = customMimeType(mimeType); | |
int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); | |
return canGetData(cf, pDataObj); | |
} else if (formats.keys(mimeType).isEmpty()) { | |
// if it is not in there then register it an see if we can get it | |
int cf = QWindowsMime::registerMimeType(mimeType); | |
return canGetData(cf, pDataObj); | |
} else { | |
return canGetData(formats.key(mimeType), pDataObj); | |
} | |
return false; | |
} | |
QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const | |
{ | |
Q_UNUSED(preferredType); | |
QVariant val; | |
if (canConvertToMime(mimeType, pDataObj)) { | |
QByteArray data; | |
if (isCustomMimeType(mimeType)) { | |
QString clipFormat = customMimeType(mimeType); | |
int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); | |
data = getData(cf, pDataObj); | |
} else if (formats.keys(mimeType).isEmpty()) { | |
int cf = QWindowsMime::registerMimeType(mimeType); | |
data = getData(cf, pDataObj); | |
} else { | |
data = getData(formats.key(mimeType), pDataObj); | |
} | |
if (!data.isEmpty()) | |
val = data; // it should be enough to return the data and let QMimeData do the rest. | |
} | |
return val; | |
} | |
QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const | |
{ | |
QString format = formats.value(getCf(formatetc)); | |
if (!format.isEmpty()) | |
return format; | |
wchar_t buffer[256]; | |
int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); | |
if (len) { | |
QString clipFormat = QString::fromWCharArray(buffer, len); | |
#ifndef QT_NO_DRAGANDDROP | |
if (QInternalMimeData::canReadData(clipFormat)) | |
format = clipFormat; | |
else if((formatetc.cfFormat >= 0xC000)){ | |
//create the mime as custom. not registered. | |
if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) { | |
//check if this is a mime type | |
bool ianaType = false; | |
int sz = ianaTypes.size(); | |
for (int i = 0; i < sz; i++) { | |
if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) { | |
ianaType = true; | |
break; | |
} | |
} | |
if (!ianaType) | |
format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"'); | |
else | |
format = clipFormat; | |
} | |
} | |
#endif //QT_NO_DRAGANDDROP | |
} | |
return format; | |
} | |
QWindowsMimeList::QWindowsMimeList() | |
: initialized(false) | |
{ | |
} | |
QWindowsMimeList::~QWindowsMimeList() | |
{ | |
while (mimes.size()) | |
delete mimes.first(); | |
} | |
void QWindowsMimeList::init() | |
{ | |
if (!initialized) { | |
initialized = true; | |
#ifndef QT_NO_IMAGEFORMAT_BMP | |
new QWindowsMimeImage; | |
#endif | |
new QLastResortMimes; | |
new QWindowsMimeText; | |
new QWindowsMimeURI; | |
new QWindowsMimeHtml; | |
new QBuiltInMimes; | |
} | |
} | |
void QWindowsMimeList::addWindowsMime(QWindowsMime * mime) | |
{ | |
init(); | |
mimes.append(mime); | |
} | |
void QWindowsMimeList::removeWindowsMime(QWindowsMime * mime) | |
{ | |
init(); | |
mimes.removeAll(mime); | |
} | |
QList<QWindowsMime*> QWindowsMimeList::windowsMimes() | |
{ | |
init(); | |
return mimes; | |
} | |
#ifndef QT_NO_IMAGEFORMAT_BMP | |
static bool qt_write_dibv5(QDataStream &s, QImage image) | |
{ | |
QIODevice* d = s.device(); | |
if (!d->isWritable()) | |
return false; | |
//depth will be always 32 | |
int bpl_bmp = image.width()*4; | |
BMP_BITMAPV5HEADER bi ={0}; | |
bi.bV5Size = sizeof(BMP_BITMAPV5HEADER); | |
bi.bV5Width = image.width(); | |
bi.bV5Height = image.height(); | |
bi.bV5Planes = 1; | |
bi.bV5BitCount = 32; | |
bi.bV5Compression = BI_BITFIELDS; | |
bi.bV5SizeImage = bpl_bmp*image.height(); | |
bi.bV5XPelsPerMeter = 0; | |
bi.bV5YPelsPerMeter = 0; | |
bi.bV5ClrUsed = 0; | |
bi.bV5ClrImportant = 0; | |
bi.bV5BlueMask = 0x000000ff; | |
bi.bV5GreenMask = 0x0000ff00; | |
bi.bV5RedMask = 0x00ff0000; | |
bi.bV5AlphaMask = 0xff000000; | |
bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB | |
bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES | |
d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size); | |
if (s.status() != QDataStream::Ok) | |
return false; | |
DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff}; | |
d->write(reinterpret_cast<const char*>(colorSpace), sizeof(colorSpace)); | |
if (s.status() != QDataStream::Ok) | |
return false; | |
if (image.format() != QImage::Format_ARGB32) | |
image = image.convertToFormat(QImage::Format_ARGB32); | |
uchar *buf = new uchar[bpl_bmp]; | |
uchar *b; | |
memset(buf, 0, bpl_bmp); | |
for (int y=image.height()-1; y>=0; y--) { | |
// write the image bits | |
QRgb *p = (QRgb *)image.scanLine(y); | |
QRgb *end = p + image.width(); | |
b = buf; | |
while (p < end) { | |
int alpha = qAlpha(*p); | |
if (alpha) { | |
*b++ = qBlue(*p); | |
*b++ = qGreen(*p); | |
*b++ = qRed(*p); | |
} else { | |
//white for fully transparent pixels. | |
*b++ = 0xff; | |
*b++ = 0xff; | |
*b++ = 0xff; | |
} | |
*b++ = alpha; | |
p++; | |
} | |
d->write((char*)buf, bpl_bmp); | |
if (s.status() != QDataStream::Ok) { | |
delete[] buf; | |
return false; | |
} | |
} | |
delete[] buf; | |
return true; | |
} | |
static int calc_shift(int mask) | |
{ | |
int result = 0; | |
while (!(mask & 1)) { | |
result++; | |
mask >>= 1; | |
} | |
return result; | |
} | |
//Supports only 32 bit DIBV5 | |
static bool qt_read_dibv5(QDataStream &s, QImage &image) | |
{ | |
BMP_BITMAPV5HEADER bi; | |
QIODevice* d = s.device(); | |
if (d->atEnd()) | |
return false; | |
d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header | |
if (s.status() != QDataStream::Ok) | |
return false; | |
int nbits = bi.bV5BitCount; | |
int comp = bi.bV5Compression; | |
if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS) | |
return false; //Unsupported DIBV5 format | |
int w = bi.bV5Width, h = bi.bV5Height; | |
int red_mask = bi.bV5RedMask; | |
int green_mask = bi.bV5GreenMask; | |
int blue_mask = bi.bV5BlueMask; | |
int alpha_mask = bi.bV5AlphaMask; | |
int red_shift = 0; | |
int green_shift = 0; | |
int blue_shift = 0; | |
int alpha_shift = 0; | |
QImage::Format format = QImage::Format_ARGB32; | |
if (bi.bV5Height < 0) | |
h = -h; // support images with negative height | |
if (image.size() != QSize(w, h) || image.format() != format) { | |
image = QImage(w, h, format); | |
if (image.isNull()) // could not create image | |
return false; | |
} | |
image.setDotsPerMeterX(bi.bV5XPelsPerMeter); | |
image.setDotsPerMeterY(bi.bV5YPelsPerMeter); | |
// read color table | |
DWORD colorSpace[3]; | |
if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace)) | |
return false; | |
red_shift = calc_shift(red_mask); | |
green_shift = calc_shift(green_mask); | |
blue_shift = calc_shift(blue_mask); | |
if (alpha_mask) { | |
alpha_shift = calc_shift(alpha_mask); | |
} | |
int bpl = image.bytesPerLine(); | |
uchar *data = image.bits(); | |
register QRgb *p; | |
QRgb *end; | |
uchar *buf24 = new uchar[bpl]; | |
int bpl24 = ((w*nbits+31)/32)*4; | |
uchar *b; | |
unsigned int c; | |
while (--h >= 0) { | |
p = (QRgb *)(data + h*bpl); | |
end = p + w; | |
if (d->read((char *)buf24,bpl24) != bpl24) | |
break; | |
b = buf24; | |
while (p < end) { | |
c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24; | |
*p++ = qRgba(((c & red_mask) >> red_shift) , | |
((c & green_mask) >> green_shift), | |
((c & blue_mask) >> blue_shift), | |
((c & alpha_mask) >> alpha_shift)); | |
b += 4; | |
} | |
} | |
delete[] buf24; | |
if (bi.bV5Height < 0) { | |
// Flip the image | |
uchar *buf = new uchar[bpl]; | |
h = -bi.bV5Height; | |
for (int y = 0; y < h/2; ++y) { | |
memcpy(buf, data + y*bpl, bpl); | |
memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl); | |
memcpy(data + (h-y-1)*bpl, buf, bpl); | |
} | |
delete [] buf; | |
} | |
return true; | |
} | |
#endif | |
QT_END_NAMESPACE |