| 1 | /****************************************************************************
|
|---|
| 2 | **
|
|---|
| 3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|---|
| 4 | ** Contact: Qt Software Information ([email protected])
|
|---|
| 5 | **
|
|---|
| 6 | ** Copyright (C) 2009 netlabs.org. OS/2 parts.
|
|---|
| 7 | **
|
|---|
| 8 | ** This file is part of the QtGui module of the Qt Toolkit.
|
|---|
| 9 | **
|
|---|
| 10 | ** $QT_BEGIN_LICENSE:LGPL$
|
|---|
| 11 | ** Commercial Usage
|
|---|
| 12 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
|---|
| 13 | ** accordance with the Qt Commercial License Agreement provided with the
|
|---|
| 14 | ** Software or, alternatively, in accordance with the terms contained in
|
|---|
| 15 | ** a written agreement between you and Nokia.
|
|---|
| 16 | **
|
|---|
| 17 | ** GNU Lesser General Public License Usage
|
|---|
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
|---|
| 19 | ** General Public License version 2.1 as published by the Free Software
|
|---|
| 20 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
|---|
| 21 | ** packaging of this file. Please review the following information to
|
|---|
| 22 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
|---|
| 23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|---|
| 24 | **
|
|---|
| 25 | ** In addition, as a special exception, Nokia gives you certain
|
|---|
| 26 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
|---|
| 27 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
|---|
| 28 | ** package.
|
|---|
| 29 | **
|
|---|
| 30 | ** GNU General Public License Usage
|
|---|
| 31 | ** Alternatively, this file may be used under the terms of the GNU
|
|---|
| 32 | ** General Public License version 3.0 as published by the Free Software
|
|---|
| 33 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
|---|
| 34 | ** packaging of this file. Please review the following information to
|
|---|
| 35 | ** ensure the GNU General Public License version 3.0 requirements will be
|
|---|
| 36 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
|---|
| 37 | **
|
|---|
| 38 | ** If you are unsure which license is appropriate for your use, please
|
|---|
| 39 | ** contact the sales department at [email protected].
|
|---|
| 40 | ** $QT_END_LICENSE$
|
|---|
| 41 | **
|
|---|
| 42 | ****************************************************************************/
|
|---|
| 43 |
|
|---|
| 44 | #include "qmime.h"
|
|---|
| 45 |
|
|---|
| 46 | #include "qimagereader.h"
|
|---|
| 47 | #include "qimagewriter.h"
|
|---|
| 48 | #include "qdatastream.h"
|
|---|
| 49 | #include "qbuffer.h"
|
|---|
| 50 | #include "qt_os2.h"
|
|---|
| 51 | #include "qapplication_p.h"
|
|---|
| 52 | #include "qtextcodec.h"
|
|---|
| 53 | #include "qregexp.h"
|
|---|
| 54 | #include "qalgorithms.h"
|
|---|
| 55 | #include "qmap.h"
|
|---|
| 56 | #include "qdnd_p.h"
|
|---|
| 57 | #include "qurl.h"
|
|---|
| 58 | #include "qvariant.h"
|
|---|
| 59 | #include "qtextdocument.h"
|
|---|
| 60 | #include "qdir.h"
|
|---|
| 61 |
|
|---|
| 62 | #define QMIME_DEBUG
|
|---|
| 63 |
|
|---|
| 64 | QT_BEGIN_NAMESPACE
|
|---|
| 65 |
|
|---|
| 66 | class QPMMimeList
|
|---|
| 67 | {
|
|---|
| 68 | public:
|
|---|
| 69 | QPMMimeList();
|
|---|
| 70 | ~QPMMimeList();
|
|---|
| 71 | void addMime(QPMMime *mime);
|
|---|
| 72 | void removeMime(QPMMime *mime);
|
|---|
| 73 | QList<QPMMime*> mimes();
|
|---|
| 74 |
|
|---|
| 75 | private:
|
|---|
| 76 | void init();
|
|---|
| 77 | bool initialized;
|
|---|
| 78 | QList<QPMMime*> list;
|
|---|
| 79 | };
|
|---|
| 80 |
|
|---|
| 81 | Q_GLOBAL_STATIC(QPMMimeList, theMimeList);
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 | /*!
|
|---|
| 85 | \class QPMMime
|
|---|
| 86 | \brief The QMPMime class maps open-standard MIME to OS/2 PM Clipboard
|
|---|
| 87 | formats.
|
|---|
| 88 | \ingroup io
|
|---|
| 89 | \ingroup draganddrop
|
|---|
| 90 | \ingroup misc
|
|---|
| 91 |
|
|---|
| 92 | Qt's drag-and-drop and clipboard facilities use the MIME standard.
|
|---|
| 93 | On X11, this maps trivially to the Xdnd protocol, but on OS/2
|
|---|
| 94 | although some applications use MIME types to describe clipboard
|
|---|
| 95 | formats, others use arbitrary non-standardized naming conventions,
|
|---|
| 96 | or unnamed built-in formats of the Presentation Manager.
|
|---|
| 97 |
|
|---|
| 98 | By instantiating subclasses of QPMMime that provide conversions between OS/2
|
|---|
| 99 | PM Clipboard and MIME formats, you can convert proprietary clipboard formats
|
|---|
| 100 | to MIME formats.
|
|---|
| 101 |
|
|---|
| 102 | Qt has predefined support for the following PM Clipboard formats (custom
|
|---|
| 103 | formats registered in the system atom table by name are given in double
|
|---|
| 104 | quotes):
|
|---|
| 105 |
|
|---|
| 106 | \table
|
|---|
| 107 | \header \o PM Format \o Equivalent MIME type
|
|---|
| 108 | \row \o \c CF_TEXT \o \c text/plain (system codepage,
|
|---|
| 109 | zero-terminated string)
|
|---|
| 110 | \row \o \c "text/unicode" \o \c text/plain (16-bit Unicode,
|
|---|
| 111 | zero-terminated string, Mozilla-compatible)
|
|---|
| 112 | \row \o \c "text/html" \o \c text/html (16-bit Unicode,
|
|---|
| 113 | zero-terminated string, Mozilla-compatible)
|
|---|
| 114 | \row \o \c CF_BITMAP \o \c{image/xyz}, where \c xyz is
|
|---|
| 115 | a \l{QImageWriter::supportedImageFormats()}{Qt image format}
|
|---|
| 116 | \row \o \c "x-mime:<mime>" \o data in the format corresponding to the given
|
|---|
| 117 | MIME type \c <mime>
|
|---|
| 118 | \endtable
|
|---|
| 119 |
|
|---|
| 120 | Note that all "x-mime:<mime>" formats use the CFI_POINTER storage type. That
|
|---|
| 121 | is, the clipboard contains a pointer to the memory block containing the MIME
|
|---|
| 122 | data in the corresponding format. The first 4 bytes of this memory block
|
|---|
| 123 | always contain the length of the subsequent MIME data array, in bytes.
|
|---|
| 124 |
|
|---|
| 125 | An example use of this class by the user application would be to map the
|
|---|
| 126 | PM Metafile clipboard format (\c CF_METAFILE) to and from the MIME type
|
|---|
| 127 | \c{image/x-metafile}. This conversion might simply be adding or removing a
|
|---|
| 128 | header, or even just passing on the data. See \l{Drag and Drop} for more
|
|---|
| 129 | information on choosing and definition MIME types.
|
|---|
| 130 | */
|
|---|
| 131 |
|
|---|
| 132 | /*!
|
|---|
| 133 | Constructs a new conversion object, adding it to the globally accessed
|
|---|
| 134 | list of available converters.
|
|---|
| 135 | */
|
|---|
| 136 | QPMMime::QPMMime()
|
|---|
| 137 | {
|
|---|
| 138 | theMimeList()->addMime(this);
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | /*!
|
|---|
| 142 | Destroys a conversion object, removing it from the global
|
|---|
| 143 | list of available converters.
|
|---|
| 144 | */
|
|---|
| 145 | QPMMime::~QPMMime()
|
|---|
| 146 | {
|
|---|
| 147 | theMimeList()->removeMime(this);
|
|---|
| 148 | }
|
|---|
| 149 |
|
|---|
| 150 | /*!
|
|---|
| 151 | Registers the MIME type \a mime, and returns an ID number
|
|---|
| 152 | identifying the format on OS/2. Intended to be used by QPMMime
|
|---|
| 153 | implementations for registering custom clipboard formats they use.
|
|---|
| 154 | */
|
|---|
| 155 | // static
|
|---|
| 156 | ULONG QPMMime::registerMimeType(const QString &mime)
|
|---|
| 157 | {
|
|---|
| 158 | ULONG cf = WinAddAtom(WinQuerySystemAtomTable(), mime.toLocal8Bit());
|
|---|
| 159 | if (!cf) {
|
|---|
| 160 | #ifndef QT_NO_DEBUG
|
|---|
| 161 | qWarning("QPMMime: WinAddAtom failed with %lX",
|
|---|
| 162 | WinGetLastError(NULLHANDLE));
|
|---|
| 163 | #endif
|
|---|
| 164 | return 0;
|
|---|
| 165 | }
|
|---|
| 166 |
|
|---|
| 167 | return cf;
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | /*!
|
|---|
| 171 | Allocates a block of shared memory of the given size and returns the address
|
|---|
| 172 | of this block. This memory block may be then filled with data and returned
|
|---|
| 173 | by convertFromMimeData() as the value of the CFI_POINTER type.
|
|---|
| 174 | */
|
|---|
| 175 | // static
|
|---|
| 176 | ULONG QPMMime::allocateMemory(size_t size)
|
|---|
| 177 | {
|
|---|
| 178 | if (size == 0)
|
|---|
| 179 | return 0;
|
|---|
| 180 |
|
|---|
| 181 | ULONG data = 0;
|
|---|
| 182 |
|
|---|
| 183 | // allocate giveable memory for the array
|
|---|
| 184 | APIRET arc = DosAllocSharedMem((PVOID *)&data, NULL, size,
|
|---|
| 185 | PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE);
|
|---|
| 186 | if (arc != NO_ERROR) {
|
|---|
| 187 | #ifndef QT_NO_DEBUG
|
|---|
| 188 | qWarning("QPMMime::allocateMemory: DosAllocSharedMem failed with %lu", arc);
|
|---|
| 189 | #endif
|
|---|
| 190 | return 0;
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | return data;
|
|---|
| 194 | }
|
|---|
| 195 |
|
|---|
| 196 | /*!
|
|---|
| 197 | Frees memory allocated by allocateMemory(). Normally, not used because the
|
|---|
| 198 | CFI_POINTER memory blocks are owned by the system after
|
|---|
| 199 | convertFromMimeData() returns.
|
|---|
| 200 | */
|
|---|
| 201 | // static
|
|---|
| 202 | void QPMMime::freeMemory(ULONG addr)
|
|---|
| 203 | {
|
|---|
| 204 | DosFreeMem((PVOID)addr);
|
|---|
| 205 | }
|
|---|
| 206 |
|
|---|
| 207 | /*!
|
|---|
| 208 | \fn QList<MimeCFPair> QPMMime::formatsForMimeData(const QMimeData *mimeData) const
|
|---|
| 209 |
|
|---|
| 210 | Returns a list of ULONG values representing the different OS/2 PM
|
|---|
| 211 | clipboard formats that can be provided for the \a mimeData, in order of
|
|---|
| 212 | precedence (the most suitable format goes first), or an empty list if
|
|---|
| 213 | neither of the mime types provided by \a mimeData is supported by this
|
|---|
| 214 | converter. Note that each item in the returned list is actually a pair
|
|---|
| 215 | consisting of the mime type name and the corresponding format identifier.
|
|---|
| 216 |
|
|---|
| 217 | All subclasses must reimplement this pure virtual function.
|
|---|
| 218 | */
|
|---|
| 219 |
|
|---|
| 220 | /*!
|
|---|
| 221 | \fn bool QPMMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
|
|---|
| 222 | ULONG &flags, ULONG *data) const
|
|---|
| 223 |
|
|---|
| 224 | Converts the \a mimeData to the specified \a format.
|
|---|
| 225 |
|
|---|
| 226 | If \a data is not NULL, a handle to the converted data should be then placed
|
|---|
| 227 | in a variable pointed to by \a data and with the necessary flags describing
|
|---|
| 228 | the handle returned in the \a flags variable.
|
|---|
| 229 |
|
|---|
| 230 | The following flags describing the data storage type are recognized:
|
|---|
| 231 |
|
|---|
| 232 | \table
|
|---|
| 233 | \row \o \c CFI_POINTER \o \a data is a pointer to a block of memory
|
|---|
| 234 | allocated with QPMMime::allocateMemory()
|
|---|
| 235 | \row \o \c CFI_HANDLE \o \a data is a handle to the appropriate
|
|---|
| 236 | PM resource
|
|---|
| 237 | \endtable
|
|---|
| 238 |
|
|---|
| 239 | If \a data is NULL then a delayed conversion is requested by the caller.
|
|---|
| 240 | The implementation should return the appropriate flags in the \a flags
|
|---|
| 241 | variable and may perform the real data conversion later when this method is
|
|---|
| 242 | called again with \a data being non-NULL.
|
|---|
| 243 |
|
|---|
| 244 | Return true if the conversion was successful.
|
|---|
| 245 |
|
|---|
| 246 | All subclasses must reimplement this pure virtual function.
|
|---|
| 247 | */
|
|---|
| 248 |
|
|---|
| 249 | /*!
|
|---|
| 250 | \fn QList<MimeCFPair> QPMMime::mimesForFormats(const QList<ULONG> &formats) const
|
|---|
| 251 |
|
|---|
| 252 | Returns a list of mime types that will be created form the specified \a list
|
|---|
| 253 | of \a formats, in order of precedence (the most suitable mime type comes
|
|---|
| 254 | first), or an empty list if neither of the \a formats is supported by this
|
|---|
| 255 | converter. Note that each item in the returned list is actually a pair
|
|---|
| 256 | consisting of the mime type name and the corresponding format identifier.
|
|---|
| 257 |
|
|---|
| 258 | All subclasses must reimplement this pure virtual function.
|
|---|
| 259 | */
|
|---|
| 260 |
|
|---|
| 261 | /*!
|
|---|
| 262 | \fn QVariant QPMMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
|
|---|
| 263 | const QString &mimeType,
|
|---|
| 264 | QVariant::Type preferredType) const
|
|---|
| 265 |
|
|---|
| 266 | Returns a QVariant containing the converted from the \a data in the
|
|---|
| 267 | specified \a format with the given \a flags to the requested \a mimeType. If
|
|---|
| 268 | possible the QVariant should be of the \a preferredType to avoid needless
|
|---|
| 269 | conversions.
|
|---|
| 270 |
|
|---|
| 271 | All subclasses must reimplement this pure virtual function.
|
|---|
| 272 | */
|
|---|
| 273 |
|
|---|
| 274 | // static
|
|---|
| 275 | QList<QPMMime::Match> QPMMime::allConvertersFromFormats(const QList<ULONG> &formats)
|
|---|
| 276 | {
|
|---|
| 277 | QList<Match> matches;
|
|---|
| 278 |
|
|---|
| 279 | QList<QPMMime*> mimes = theMimeList()->mimes();
|
|---|
| 280 | foreach(QPMMime *mime, mimes) {
|
|---|
| 281 | QList<MimeCFPair> fmts = mime->mimesForFormats(formats);
|
|---|
| 282 | int priority = 0;
|
|---|
| 283 | foreach (MimeCFPair fmt, fmts) {
|
|---|
| 284 | ++priority;
|
|---|
| 285 | QList<Match>::iterator it = matches.begin();
|
|---|
| 286 | for (; it != matches.end(); ++it) {
|
|---|
| 287 | Match &match = *it;
|
|---|
| 288 | if (match.mime == fmt.mime) {
|
|---|
| 289 | // replace if priority is higher, ignore otherwise
|
|---|
| 290 | if (priority < match.priority) {
|
|---|
| 291 | match.converter = mime;
|
|---|
| 292 | match.format = fmt.format;
|
|---|
| 293 | match.priority = priority;
|
|---|
| 294 | }
|
|---|
| 295 | break;
|
|---|
| 296 | }
|
|---|
| 297 | }
|
|---|
| 298 | if (it == matches.end()) {
|
|---|
| 299 | matches += Match(mime, fmt.mime, fmt.format, priority);
|
|---|
| 300 | }
|
|---|
| 301 | }
|
|---|
| 302 | }
|
|---|
| 303 |
|
|---|
| 304 | return matches;
|
|---|
| 305 | }
|
|---|
| 306 |
|
|---|
| 307 | // static
|
|---|
| 308 | QList<QPMMime::Match> QPMMime::allConvertersFromMimeData(const QMimeData *mimeData)
|
|---|
| 309 | {
|
|---|
| 310 | QList<Match> matches;
|
|---|
| 311 |
|
|---|
| 312 | QList<QPMMime*> mimes = theMimeList()->mimes();
|
|---|
| 313 | foreach(QPMMime *mime, mimes) {
|
|---|
| 314 | QList<MimeCFPair> fmts = mime->formatsForMimeData(mimeData);
|
|---|
| 315 | int priority = 0;
|
|---|
| 316 | foreach (MimeCFPair fmt, fmts) {
|
|---|
| 317 | ++priority;
|
|---|
| 318 | QList<Match>::iterator it = matches.begin();
|
|---|
| 319 | for (; it != matches.end(); ++it) {
|
|---|
| 320 | Match &match = *it;
|
|---|
| 321 | if (mime == mimes.last()) { // QPMMimeAnyMime?
|
|---|
| 322 | if (match.mime == fmt.mime){
|
|---|
| 323 | // we assume that specialized converters (that come
|
|---|
| 324 | // first) provide a more precise conversion than
|
|---|
| 325 | // QPMMimeAnyMime and don't let it get into the list in
|
|---|
| 326 | // order to avoid unnecessary duplicate representations
|
|---|
| 327 | break;
|
|---|
| 328 | }
|
|---|
| 329 | }
|
|---|
| 330 | if (match.format == fmt.format) {
|
|---|
| 331 | // replace if priority is higher, ignore otherwise
|
|---|
| 332 | if (priority < match.priority) {
|
|---|
| 333 | match.converter = mime;
|
|---|
| 334 | match.mime = fmt.mime;
|
|---|
| 335 | match.priority = priority;
|
|---|
| 336 | }
|
|---|
| 337 | break;
|
|---|
| 338 | }
|
|---|
| 339 | }
|
|---|
| 340 | if (it == matches.end()) {
|
|---|
| 341 | matches += Match(mime, fmt.mime, fmt.format, priority);
|
|---|
| 342 | }
|
|---|
| 343 | }
|
|---|
| 344 | }
|
|---|
| 345 |
|
|---|
| 346 | return matches;
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | QString QPMMime::formatName(ULONG format)
|
|---|
| 350 | {
|
|---|
| 351 | QString name;
|
|---|
| 352 | HATOMTBL tbl = WinQuerySystemAtomTable();
|
|---|
| 353 | if (tbl != NULLHANDLE) {
|
|---|
| 354 | ULONG len = WinQueryAtomLength(tbl, format);
|
|---|
| 355 | QByteArray atom(len, '\0');
|
|---|
| 356 | WinQueryAtomName(tbl, format, atom.data(), atom.size() + 1);
|
|---|
| 357 | name = QString::fromLocal8Bit(atom);
|
|---|
| 358 | }
|
|---|
| 359 | return name;
|
|---|
| 360 | }
|
|---|
| 361 |
|
|---|
| 362 | ////////////////////////////////////////////////////////////////////////////////
|
|---|
| 363 |
|
|---|
| 364 | class QPMMimeText : public QPMMime
|
|---|
| 365 | {
|
|---|
| 366 | public:
|
|---|
| 367 | QPMMimeText();
|
|---|
| 368 |
|
|---|
| 369 | // for converting from Qt
|
|---|
| 370 | QList<MimeCFPair> formatsForMimeData(const QMimeData *mimeData) const;
|
|---|
| 371 | bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
|
|---|
| 372 | ULONG &flags, ULONG *data) const;
|
|---|
| 373 |
|
|---|
| 374 | // for converting to Qt
|
|---|
| 375 | QList<MimeCFPair> mimesForFormats(const QList<ULONG> &formats) const;
|
|---|
| 376 | QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
|
|---|
| 377 | const QString &mimeType,
|
|---|
| 378 | QVariant::Type preferredType) const;
|
|---|
| 379 |
|
|---|
| 380 | const ULONG CF_TextUnicode;
|
|---|
| 381 | const ULONG CF_TextHtml;
|
|---|
| 382 | };
|
|---|
| 383 |
|
|---|
| 384 | QPMMimeText::QPMMimeText()
|
|---|
| 385 | // "text/unicode" is what Mozilla uses to for unicode
|
|---|
| 386 | : CF_TextUnicode (registerMimeType(QLatin1String("text/unicode")))
|
|---|
| 387 | // "text/html" is what Mozilla uses to for HTML
|
|---|
| 388 | , CF_TextHtml (registerMimeType(QLatin1String("text/html")))
|
|---|
| 389 | {
|
|---|
| 390 | }
|
|---|
| 391 |
|
|---|
| 392 | QList<QPMMime::MimeCFPair> QPMMimeText::formatsForMimeData(const QMimeData *mimeData) const
|
|---|
| 393 | {
|
|---|
| 394 | QList<MimeCFPair> fmts;
|
|---|
| 395 | // prefer HTML as it's reacher
|
|---|
| 396 | if (mimeData->hasHtml())
|
|---|
| 397 | fmts << MimeCFPair(QLatin1String("text/html"), CF_TextHtml);
|
|---|
| 398 | // prefer unicode over local8Bit
|
|---|
| 399 | if (mimeData->hasText())
|
|---|
| 400 | fmts << MimeCFPair(QLatin1String("text/plain"), CF_TextUnicode)
|
|---|
| 401 | << MimeCFPair(QLatin1String("text/plain"), CF_TEXT);
|
|---|
| 402 | return fmts;
|
|---|
| 403 | }
|
|---|
| 404 |
|
|---|
| 405 | // text/plain is defined as using CRLF, but so many programs don't,
|
|---|
| 406 | // and programmers just look for '\n' in strings.
|
|---|
| 407 | // OS/2 really needs CRLF, so we ensure it here.
|
|---|
| 408 | bool QPMMimeText::convertFromMimeData(const QMimeData *mimeData, ULONG format,
|
|---|
| 409 | ULONG &flags, ULONG *data) const
|
|---|
| 410 | {
|
|---|
| 411 | if (!mimeData->hasText())
|
|---|
| 412 | return false;
|
|---|
| 413 |
|
|---|
| 414 | flags = CFI_POINTER;
|
|---|
| 415 |
|
|---|
| 416 | if (data == NULL)
|
|---|
| 417 | return true; // delayed rendering, nothing to do
|
|---|
| 418 |
|
|---|
| 419 | QByteArray r;
|
|---|
| 420 |
|
|---|
| 421 | if (format == CF_TEXT) {
|
|---|
| 422 | QByteArray str = mimeData->text().toLocal8Bit();
|
|---|
| 423 | // Anticipate required space for CRLFs at 1/40
|
|---|
| 424 | int maxsize = str.size()+str.size()/40+1;
|
|---|
| 425 | r.fill('\0', maxsize);
|
|---|
| 426 | char *o = r.data();
|
|---|
| 427 | const char *d = str.data();
|
|---|
| 428 | const int s = str.size();
|
|---|
| 429 | bool cr = false;
|
|---|
| 430 | int j = 0;
|
|---|
| 431 | for (int i = 0; i < s; i++) {
|
|---|
| 432 | char c = d[i];
|
|---|
| 433 | if (c == '\r')
|
|---|
| 434 | cr = true;
|
|---|
| 435 | else {
|
|---|
| 436 | if (c == '\n') {
|
|---|
| 437 | if (!cr)
|
|---|
| 438 | o[j++] = '\r';
|
|---|
| 439 | }
|
|---|
| 440 | cr = false;
|
|---|
| 441 | }
|
|---|
| 442 | o[j++] = c;
|
|---|
| 443 | if (j+1 >= maxsize) {
|
|---|
| 444 | maxsize += maxsize/4;
|
|---|
| 445 | r.resize(maxsize);
|
|---|
| 446 | o = r.data();
|
|---|
| 447 | }
|
|---|
| 448 | }
|
|---|
| 449 | if (j < r.size())
|
|---|
| 450 | o[j] = '\0';
|
|---|
| 451 | } else if (format == CF_TextUnicode || CF_TextHtml) {
|
|---|
| 452 | QString str = mimeData->text();
|
|---|
| 453 | const QChar *u = str.unicode();
|
|---|
| 454 | QString res;
|
|---|
| 455 | const int s = str.length();
|
|---|
| 456 | int maxsize = s + s/40 + 3;
|
|---|
| 457 | res.resize(maxsize);
|
|---|
| 458 | int ri = 0;
|
|---|
| 459 | bool cr = false;
|
|---|
| 460 | for (int i = 0; i < s; ++i) {
|
|---|
| 461 | if (*u == QLatin1Char('\r'))
|
|---|
| 462 | cr = true;
|
|---|
| 463 | else {
|
|---|
| 464 | if (*u == QLatin1Char('\n') && !cr)
|
|---|
| 465 | res[ri++] = QLatin1Char('\r');
|
|---|
| 466 | cr = false;
|
|---|
| 467 | }
|
|---|
| 468 | res[ri++] = *u;
|
|---|
| 469 | if (ri+3 >= maxsize) {
|
|---|
| 470 | maxsize += maxsize/4;
|
|---|
| 471 | res.resize(maxsize);
|
|---|
| 472 | }
|
|---|
| 473 | ++u;
|
|---|
| 474 | }
|
|---|
| 475 | res.truncate(ri);
|
|---|
| 476 | const int byteLength = res.length()*2;
|
|---|
| 477 | r.fill('\0', byteLength + 2);
|
|---|
| 478 | memcpy(r.data(), res.unicode(), byteLength);
|
|---|
| 479 | r[byteLength] = 0;
|
|---|
| 480 | r[byteLength+1] = 0;
|
|---|
| 481 | } else{
|
|---|
| 482 | return false;
|
|---|
| 483 | }
|
|---|
| 484 |
|
|---|
| 485 | *data = QPMMime::allocateMemory(r.size());
|
|---|
| 486 | if (!*data)
|
|---|
| 487 | return false;
|
|---|
| 488 |
|
|---|
| 489 | memcpy((void *)*data, r.data(), r.size());
|
|---|
| 490 | return true;
|
|---|
| 491 | }
|
|---|
| 492 |
|
|---|
| 493 | QList<QPMMime::MimeCFPair> QPMMimeText::mimesForFormats(const QList<ULONG> &formats) const
|
|---|
| 494 | {
|
|---|
| 495 | QList<MimeCFPair> mimes;
|
|---|
| 496 | // prefer HTML as it's reacher
|
|---|
| 497 | if (formats.contains(CF_TextHtml))
|
|---|
| 498 | mimes << MimeCFPair(QLatin1String("text/html"), CF_TextHtml);
|
|---|
| 499 | // prefer unicode over local8Bit
|
|---|
| 500 | if (formats.contains(CF_TextUnicode))
|
|---|
| 501 | mimes << MimeCFPair(QLatin1String("text/plain"), CF_TextUnicode);
|
|---|
| 502 | if (formats.contains(CF_TEXT))
|
|---|
| 503 | mimes << MimeCFPair(QLatin1String("text/plain"), CF_TEXT);
|
|---|
| 504 | return mimes;
|
|---|
| 505 | }
|
|---|
| 506 |
|
|---|
| 507 | QVariant QPMMimeText::convertFromFormat(ULONG format, ULONG flags, ULONG data,
|
|---|
| 508 | const QString &mimeType,
|
|---|
| 509 | QVariant::Type preferredType) const
|
|---|
| 510 | {
|
|---|
| 511 | QVariant ret;
|
|---|
| 512 |
|
|---|
| 513 | if (!mimeType.startsWith("text/plain") &&
|
|---|
| 514 | !mimeType.startsWith("text/html"))
|
|---|
| 515 | return ret;
|
|---|
| 516 | if (!(flags & CFI_POINTER) || !data)
|
|---|
| 517 | return ret;
|
|---|
| 518 |
|
|---|
| 519 | QString str;
|
|---|
| 520 |
|
|---|
| 521 | if (format == CF_TEXT) {
|
|---|
| 522 | const char *d = (const char *)data;
|
|---|
| 523 | QByteArray r("");
|
|---|
| 524 | if (*d) {
|
|---|
| 525 | const int s = qstrlen(d);
|
|---|
| 526 | r.fill('\0', s);
|
|---|
| 527 | char *o = r.data();
|
|---|
| 528 | int j = 0;
|
|---|
| 529 | for (int i = 0; i < s; i++) {
|
|---|
| 530 | char c = d[i];
|
|---|
| 531 | if (c != '\r')
|
|---|
| 532 | o[j++] = c;
|
|---|
| 533 | }
|
|---|
| 534 | }
|
|---|
| 535 | str = QString::fromLocal8Bit(r);
|
|---|
| 536 | } else if (format == CF_TextUnicode || CF_TextHtml) {
|
|---|
| 537 | str = QString::fromUtf16((const unsigned short *)data);
|
|---|
| 538 | str.replace(QLatin1String("\r\n"), QLatin1String("\n"));
|
|---|
| 539 | }
|
|---|
| 540 |
|
|---|
| 541 | if (preferredType == QVariant::String)
|
|---|
| 542 | ret = str;
|
|---|
| 543 | else
|
|---|
| 544 | ret = str.toUtf8();
|
|---|
| 545 |
|
|---|
| 546 | return ret;
|
|---|
| 547 | }
|
|---|
| 548 |
|
|---|
| 549 | ////////////////////////////////////////////////////////////////////////////////
|
|---|
| 550 |
|
|---|
| 551 | class QPMMimeAnyMime : public QPMMime
|
|---|
| 552 | {
|
|---|
| 553 | public:
|
|---|
| 554 | QPMMimeAnyMime();
|
|---|
| 555 |
|
|---|
| 556 | // for converting from Qt
|
|---|
| 557 | QList<MimeCFPair> formatsForMimeData(const QMimeData *mimeData) const;
|
|---|
| 558 | bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
|
|---|
| 559 | ULONG &flags, ULONG *data) const;
|
|---|
| 560 |
|
|---|
| 561 | // for converting to Qt
|
|---|
| 562 | QList<MimeCFPair> mimesForFormats(const QList<ULONG> &formats) const;
|
|---|
| 563 | QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
|
|---|
| 564 | const QString &mimeType,
|
|---|
| 565 | QVariant::Type preferredType) const;
|
|---|
| 566 |
|
|---|
| 567 | private:
|
|---|
| 568 | ULONG registerMimeType(const QString &mime) const;
|
|---|
| 569 | QString registerFormat(ULONG format) const;
|
|---|
| 570 |
|
|---|
| 571 | mutable QMap<QString, ULONG> cfMap;
|
|---|
| 572 | mutable QMap<ULONG, QString> mimeMap;
|
|---|
| 573 |
|
|---|
| 574 | static QStringList ianaTypes;
|
|---|
| 575 | static QString mimePrefix;
|
|---|
| 576 | static QString customPrefix;
|
|---|
| 577 | };
|
|---|
| 578 |
|
|---|
| 579 | // static
|
|---|
| 580 | QStringList QPMMimeAnyMime::ianaTypes;
|
|---|
| 581 | QString QPMMimeAnyMime::mimePrefix;
|
|---|
| 582 | QString QPMMimeAnyMime::customPrefix;
|
|---|
| 583 |
|
|---|
| 584 | QPMMimeAnyMime::QPMMimeAnyMime()
|
|---|
| 585 | {
|
|---|
| 586 | //MIME Media-Types
|
|---|
| 587 | if (!ianaTypes.size()) {
|
|---|
| 588 | ianaTypes.append(QLatin1String("application/"));
|
|---|
| 589 | ianaTypes.append(QLatin1String("audio/"));
|
|---|
| 590 | ianaTypes.append(QLatin1String("example/"));
|
|---|
| 591 | ianaTypes.append(QLatin1String("image/"));
|
|---|
| 592 | ianaTypes.append(QLatin1String("message/"));
|
|---|
| 593 | ianaTypes.append(QLatin1String("model/"));
|
|---|
| 594 | ianaTypes.append(QLatin1String("multipart/"));
|
|---|
| 595 | ianaTypes.append(QLatin1String("text/"));
|
|---|
| 596 | ianaTypes.append(QLatin1String("video/"));
|
|---|
| 597 |
|
|---|
| 598 | mimePrefix = QLatin1String("x-mime:");
|
|---|
| 599 | customPrefix = QLatin1String("application/x-qt-pm-mime;value=\"");
|
|---|
| 600 | }
|
|---|
| 601 | }
|
|---|
| 602 |
|
|---|
| 603 | QList<QPMMime::MimeCFPair> QPMMimeAnyMime::formatsForMimeData(const QMimeData *mimeData) const
|
|---|
| 604 | {
|
|---|
| 605 | QList<MimeCFPair> fmts;
|
|---|
| 606 |
|
|---|
| 607 | QStringList mimes = QInternalMimeData::formatsHelper(mimeData);
|
|---|
| 608 | foreach (QString mime, mimes) {
|
|---|
| 609 | ULONG cf = cfMap.value(mime);
|
|---|
| 610 | if (!cf)
|
|---|
| 611 | cf = registerMimeType(mime);
|
|---|
| 612 | if (cf)
|
|---|
| 613 | fmts << MimeCFPair(mime, cf);
|
|---|
| 614 | }
|
|---|
| 615 |
|
|---|
| 616 | return fmts;
|
|---|
| 617 | }
|
|---|
| 618 |
|
|---|
| 619 | bool QPMMimeAnyMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
|
|---|
| 620 | ULONG &flags, ULONG *data) const
|
|---|
| 621 | {
|
|---|
| 622 | QString mime = mimeMap.value(format);
|
|---|
| 623 | if (mime.isNull())
|
|---|
| 624 | return false;
|
|---|
| 625 |
|
|---|
| 626 | flags = CFI_POINTER;
|
|---|
| 627 |
|
|---|
| 628 | if (data == NULL)
|
|---|
| 629 | return true; // delayed rendering, nothing to do
|
|---|
| 630 |
|
|---|
| 631 | QByteArray r = QInternalMimeData::renderDataHelper(mime, mimeData);
|
|---|
| 632 | if (r.isNull())
|
|---|
| 633 | return false;
|
|---|
| 634 |
|
|---|
| 635 | *data = QPMMime::allocateMemory(r.size() + sizeof(ULONG));
|
|---|
| 636 | if (!*data)
|
|---|
| 637 | return false;
|
|---|
| 638 |
|
|---|
| 639 | *((ULONG *)(*data)) = r.size();
|
|---|
| 640 | memcpy((void *)(*data + sizeof(ULONG)), r.data(), r.size());
|
|---|
| 641 | return true;
|
|---|
| 642 | }
|
|---|
| 643 |
|
|---|
| 644 | QList<QPMMime::MimeCFPair> QPMMimeAnyMime::mimesForFormats(const QList<ULONG> &formats) const
|
|---|
| 645 | {
|
|---|
| 646 | QList<MimeCFPair> mimes;
|
|---|
| 647 |
|
|---|
| 648 | foreach (ULONG format, formats) {
|
|---|
| 649 | QString mime = mimeMap.value(format);
|
|---|
| 650 | if (mime.isEmpty())
|
|---|
| 651 | mime = registerFormat(format);
|
|---|
| 652 | if (!mime.isEmpty())
|
|---|
| 653 | mimes << MimeCFPair(mime, format);
|
|---|
| 654 | }
|
|---|
| 655 |
|
|---|
| 656 | return mimes;
|
|---|
| 657 | }
|
|---|
| 658 |
|
|---|
| 659 | QVariant QPMMimeAnyMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
|
|---|
| 660 | const QString &mimeType,
|
|---|
| 661 | QVariant::Type preferredType) const
|
|---|
| 662 | {
|
|---|
| 663 | Q_UNUSED(preferredType);
|
|---|
| 664 |
|
|---|
| 665 | QVariant ret;
|
|---|
| 666 |
|
|---|
| 667 | if (cfMap.value(mimeType) != format)
|
|---|
| 668 | return ret;
|
|---|
| 669 |
|
|---|
| 670 | if (!(flags & CFI_POINTER) || !data)
|
|---|
| 671 | return ret;
|
|---|
| 672 |
|
|---|
| 673 | // get the real block size (always rounded to the page boundary (4K))
|
|---|
| 674 | ULONG sz = ~0, fl = 0, arc;
|
|---|
| 675 | arc = DosQueryMem((PVOID)data, &sz, &fl);
|
|---|
| 676 | if (arc != NO_ERROR) {
|
|---|
| 677 | #ifndef QT_NO_DEBUG
|
|---|
| 678 | qWarning("QPMMimeText::convertFromFormat: DosQueryMem failed with %lu", arc);
|
|---|
| 679 | #endif
|
|---|
| 680 | return ret;
|
|---|
| 681 | }
|
|---|
| 682 | ULONG size = *((ULONG *)data);
|
|---|
| 683 | if (!size || size + sizeof(ULONG) > sz)
|
|---|
| 684 | return ret;
|
|---|
| 685 |
|
|---|
| 686 | // it should be enough to return the data and let QMimeData do the rest.
|
|---|
| 687 | ret = QByteArray((const char *)(data + sizeof(ULONG)), size);
|
|---|
| 688 | return ret;
|
|---|
| 689 | }
|
|---|
| 690 |
|
|---|
| 691 | ULONG QPMMimeAnyMime::registerMimeType(const QString &mime) const
|
|---|
| 692 | {
|
|---|
| 693 | if (mime.isEmpty())
|
|---|
| 694 | return 0;
|
|---|
| 695 |
|
|---|
| 696 | QString mimeToReg = mime;
|
|---|
| 697 |
|
|---|
| 698 | bool ianaType = false;
|
|---|
| 699 | foreach(QString prefix, ianaTypes) {
|
|---|
| 700 | if (mime.startsWith(prefix)) {
|
|---|
| 701 | ianaType = true;
|
|---|
| 702 | break;
|
|---|
| 703 | }
|
|---|
| 704 | }
|
|---|
| 705 | if (!ianaType) {
|
|---|
| 706 | // prepend the non-standard type with the prefix that makes it comply
|
|---|
| 707 | // with the standard
|
|---|
| 708 | mimeToReg = customPrefix + mime + QChar('\"');
|
|---|
| 709 | }
|
|---|
| 710 |
|
|---|
| 711 | mimeToReg = mimePrefix + mimeToReg;
|
|---|
| 712 | ULONG cf = QPMMime::registerMimeType(mimeToReg);
|
|---|
| 713 | if (cf) {
|
|---|
| 714 | cfMap[mime] = cf;
|
|---|
| 715 | mimeMap[cf] = mime;
|
|---|
| 716 | }
|
|---|
| 717 | return cf;
|
|---|
| 718 | }
|
|---|
| 719 |
|
|---|
| 720 | QString QPMMimeAnyMime::registerFormat(ULONG format) const
|
|---|
| 721 | {
|
|---|
| 722 | QString mime;
|
|---|
| 723 |
|
|---|
| 724 | if (!format)
|
|---|
| 725 | return mime;
|
|---|
| 726 |
|
|---|
| 727 | QString atomStr = formatName(format);
|
|---|
| 728 | if (atomStr.startsWith(mimePrefix)) {
|
|---|
| 729 | // the format represents the mime type we can recognize
|
|---|
| 730 | mime = atomStr.mid(mimePrefix.size());
|
|---|
| 731 | if (!mime.isEmpty()) {
|
|---|
| 732 | cfMap[mime] = format;
|
|---|
| 733 | mimeMap[format] = mime;
|
|---|
| 734 | }
|
|---|
| 735 | }
|
|---|
| 736 | return mime;
|
|---|
| 737 | }
|
|---|
| 738 |
|
|---|
| 739 | ////////////////////////////////////////////////////////////////////////////////
|
|---|
| 740 |
|
|---|
| 741 | QPMMimeList::QPMMimeList()
|
|---|
| 742 | : initialized(false)
|
|---|
| 743 | {
|
|---|
| 744 | }
|
|---|
| 745 |
|
|---|
| 746 | QPMMimeList::~QPMMimeList()
|
|---|
| 747 | {
|
|---|
| 748 | while (list.size())
|
|---|
| 749 | delete list.first();
|
|---|
| 750 | }
|
|---|
| 751 |
|
|---|
| 752 |
|
|---|
| 753 | void QPMMimeList::init()
|
|---|
| 754 | {
|
|---|
| 755 | if (!initialized) {
|
|---|
| 756 | initialized = true;
|
|---|
| 757 | new QPMMimeAnyMime; // must be the first (used as a fallback)
|
|---|
| 758 | new QPMMimeText;
|
|---|
| 759 | }
|
|---|
| 760 | }
|
|---|
| 761 |
|
|---|
| 762 | void QPMMimeList::addMime(QPMMime *mime)
|
|---|
| 763 | {
|
|---|
| 764 | init();
|
|---|
| 765 | list.prepend(mime);
|
|---|
| 766 | }
|
|---|
| 767 |
|
|---|
| 768 | void QPMMimeList::removeMime(QPMMime *mime)
|
|---|
| 769 | {
|
|---|
| 770 | init();
|
|---|
| 771 | list.removeAll(mime);
|
|---|
| 772 | }
|
|---|
| 773 |
|
|---|
| 774 | QList<QPMMime*> QPMMimeList::mimes()
|
|---|
| 775 | {
|
|---|
| 776 | init();
|
|---|
| 777 | return list;
|
|---|
| 778 | }
|
|---|
| 779 |
|
|---|
| 780 | QT_END_NAMESPACE
|
|---|