db86deb91cf56361963bef9e8334fac52b1e13cc
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qmacmime.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qmacmime.h"
43 #include "qcocoahelpers.h"
44 #include "qmacclipboard.h"
45
46 #include "qdebug.h"
47 #include "qpixmap.h"
48 #include "qimagewriter.h"
49 #include "qimagereader.h"
50 #include "qdatastream.h"
51 #include "qbuffer.h"
52 #include "qdatetime.h"
53 #include "qguiapplication.h"
54 #include "qtextcodec.h"
55 #include "qregexp.h"
56 #include "qurl.h"
57 #include "qmap.h"
58
59 #include <Cocoa/Cocoa.h>
60
61 QT_BEGIN_NAMESPACE
62
63 extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp
64
65 typedef QList<QMacPasteboardMime*> MimeList;
66 Q_GLOBAL_STATIC(MimeList, globalMimeList)
67
68 static void cleanup_mimes()
69 {
70     MimeList *mimes = globalMimeList();
71     while (!mimes->isEmpty())
72         delete mimes->takeFirst();
73 }
74
75 Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
76
77 /*!
78     \fn void qRegisterDraggedTypes(const QStringList &types)
79     \relates QMacPasteboardMime
80
81     Registers the given \a types as custom pasteboard types.
82
83     This function should be called to enable the Drag and Drop events
84     for custom pasteboard types on Cocoa implementations. This is required
85     in addition to a QMacPasteboardMime subclass implementation. By default
86     drag and drop is enabled for all standard pasteboard types.
87
88    \sa QMacPasteboardMime
89 */
90 Q_WIDGETS_EXPORT void qRegisterDraggedTypes(const QStringList &types)
91 {
92     (*globalDraggedTypesList()) += types;
93 }
94
95 const QStringList& qEnabledDraggedTypes()
96 {
97     return (*globalDraggedTypesList());
98 }
99
100
101 /*****************************************************************************
102   QDnD debug facilities
103  *****************************************************************************/
104 //#define DEBUG_MIME_MAPS
105
106 //functions
107 extern QString qt_mac_from_pascal_string(const Str255);  //qglobal.cpp
108 extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1);  //qglobal.cpp
109
110 ScrapFlavorType qt_mac_mime_type = 'CUTE';
111 CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker");
112
113 /*!
114   \class QMacPasteboardMime
115   \brief The QMacPasteboardMime class converts between a MIME type and a
116   \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform
117   Type Identifier (UTI)} format.
118   \since 4.2
119
120   \ingroup draganddrop
121   \inmodule QtWidgets
122
123   Qt's drag and drop and clipboard facilities use the MIME
124   standard. On X11, this maps trivially to the Xdnd protocol. On
125   Mac, although some applications use MIME to describe clipboard
126   contents, it is more common to use Apple's UTI format.
127
128   QMacPasteboardMime's role is to bridge the gap between MIME and UTI;
129   By subclasses this class, one can extend Qt's drag and drop
130   and clipboard handling to convert to and from unsupported, or proprietary, UTI formats.
131
132   A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation.
133
134   Qt has predefined support for the following UTIs:
135   \list
136     \i public.utf8-plain-text - converts to "text/plain"
137     \i public.utf16-plain-text - converts to "text/plain"
138     \i public.html - converts to "text/html"
139     \i public.url - converts to "text/uri-list"
140     \i public.file-url - converts to "text/uri-list"
141     \i public.tiff - converts to "application/x-qt-image"
142     \i public.vcard - converts to "text/plain"
143     \i com.apple.traditional-mac-plain-text - converts to "text/plain"
144     \i com.apple.pict - converts to "application/x-qt-image"
145   \endlist
146
147   When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to
148   find an instance that can convert to, or from, a specific MIME type. It will do this by calling
149   canConvert() on each instance, starting with (and choosing) the last created instance first.
150   The actual conversions will be done by using convertToMime() and convertFromMime().
151
152   \note The API uses the term "flavor" in some cases. This is for backwards
153   compatibility reasons, and should now be understood as UTIs.
154 */
155
156 /*! \enum QMacPasteboardMime::QMacPasteboardMimeType
157     \internal
158 */
159
160 /*!
161   Constructs a new conversion object of type \a t, adding it to the
162   globally accessed list of available convertors.
163 */
164 QMacPasteboardMime::QMacPasteboardMime(char t) : type(t)
165 {
166     globalMimeList()->append(this);
167 }
168
169 /*!
170   Destroys a conversion object, removing it from the global
171   list of available convertors.
172 */
173 QMacPasteboardMime::~QMacPasteboardMime()
174 {
175     if (!QGuiApplication::closingDown())
176         globalMimeList()->removeAll(this);
177 }
178
179 class QMacPasteboardMimeAny : public QMacPasteboardMime {
180 private:
181
182 public:
183     QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
184     }
185     ~QMacPasteboardMimeAny() {
186     }
187     QString convertorName();
188
189     QString flavorFor(const QString &mime);
190     QString mimeFor(QString flav);
191     bool canConvert(const QString &mime, QString flav);
192     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
193     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
194 };
195
196 QString QMacPasteboardMimeAny::convertorName()
197 {
198     return QLatin1String("Any-Mime");
199 }
200
201 QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
202 {
203     // do not handle the mime type name in the drag pasteboard
204     if (mime == QLatin1String("application/x-qt-mime-type-name"))
205         return QString();
206     QString ret = QLatin1String("com.trolltech.anymime.") + mime;
207     return ret.replace(QLatin1Char('/'), QLatin1String("--"));
208 }
209
210 QString QMacPasteboardMimeAny::mimeFor(QString flav)
211 {
212     const QString any_prefix = QLatin1String("com.trolltech.anymime.");
213     if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
214         return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/"));
215     return QString();
216 }
217
218 bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
219 {
220     return mimeFor(flav) == mime;
221 }
222
223 QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
224 {
225     if (data.count() > 1)
226         qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
227     QVariant ret;
228     if (mime == QLatin1String("text/plain"))
229         ret = QString::fromUtf8(data.first());
230     else
231         ret = data.first();
232     return ret;
233 }
234
235 QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
236 {
237     QList<QByteArray> ret;
238     if (mime == QLatin1String("text/plain"))
239         ret.append(data.toString().toUtf8());
240     else
241         ret.append(data.toByteArray());
242     return ret;
243 }
244
245 class QMacPasteboardMimeTypeName : public QMacPasteboardMime {
246 private:
247
248 public:
249     QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
250     }
251     ~QMacPasteboardMimeTypeName() {
252     }
253     QString convertorName();
254
255     QString flavorFor(const QString &mime);
256     QString mimeFor(QString flav);
257     bool canConvert(const QString &mime, QString flav);
258     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
259     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
260 };
261
262 QString QMacPasteboardMimeTypeName::convertorName()
263 {
264     return QLatin1String("Qt-Mime-Type");
265 }
266
267 QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
268 {
269     if (mime == QLatin1String("application/x-qt-mime-type-name"))
270         return QLatin1String("com.trolltech.qt.MimeTypeName");
271     return QString();
272 }
273
274 QString QMacPasteboardMimeTypeName::mimeFor(QString)
275 {
276     return QString();
277 }
278
279 bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
280 {
281     return false;
282 }
283
284 QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
285 {
286     QVariant ret;
287     return ret;
288 }
289
290 QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
291 {
292     QList<QByteArray> ret;
293     ret.append(QString("x-qt-mime-type-name").toUtf8());
294     return ret;
295 }
296
297 class QMacPasteboardMimePlainText : public QMacPasteboardMime {
298 public:
299     QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { }
300     QString convertorName();
301
302     QString flavorFor(const QString &mime);
303     QString mimeFor(QString flav);
304     bool canConvert(const QString &mime, QString flav);
305     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
306     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
307 };
308
309 QString QMacPasteboardMimePlainText::convertorName()
310 {
311     return QLatin1String("PlainText");
312 }
313
314 QString QMacPasteboardMimePlainText::flavorFor(const QString &mime)
315 {
316     if (mime == QLatin1String("text/plain"))
317         return QLatin1String("com.apple.traditional-mac-plain-text");
318     return QString();
319 }
320
321 QString QMacPasteboardMimePlainText::mimeFor(QString flav)
322 {
323     if (flav == QLatin1String("com.apple.traditional-mac-plain-text"))
324         return QLatin1String("text/plain");
325     return QString();
326 }
327
328 bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav)
329 {
330     return flavorFor(mime) == flav;
331 }
332
333 QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
334 {
335     if (data.count() > 1)
336         qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data");
337     const QByteArray &firstData = data.first();
338     QVariant ret;
339     if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) {
340         QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
341                                              reinterpret_cast<const UInt8 *>(firstData.constData()),
342                                              firstData.size(), CFStringGetSystemEncoding(), false));
343         ret = QString(str);
344     } else {
345         qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
346     }
347     return ret;
348 }
349
350 QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor)
351 {
352     QList<QByteArray> ret;
353     QString string = data.toString();
354     if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text")))
355         ret.append(string.toLatin1());
356     return ret;
357 }
358
359 class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime {
360 public:
361     QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { }
362     QString convertorName();
363
364     QString flavorFor(const QString &mime);
365     QString mimeFor(QString flav);
366     bool canConvert(const QString &mime, QString flav);
367     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
368     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
369 };
370
371 QString QMacPasteboardMimeUnicodeText::convertorName()
372 {
373     return QLatin1String("UnicodeText");
374 }
375
376 QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
377 {
378     if (mime == QLatin1String("text/plain"))
379         return QLatin1String("public.utf16-plain-text");
380     int i = mime.indexOf(QLatin1String("charset="));
381     if (i >= 0) {
382         QString cs(mime.mid(i+8).toLower());
383         i = cs.indexOf(QLatin1Char(';'));
384         if (i>=0)
385             cs = cs.left(i);
386         if (cs == QLatin1String("system"))
387             return QLatin1String("public.utf8-plain-text");
388         else if (cs == QLatin1String("iso-10646-ucs-2")
389                  || cs == QLatin1String("utf16"))
390             return QLatin1String("public.utf16-plain-text");
391     }
392     return QString();
393 }
394
395 QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
396 {
397     if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text"))
398         return QLatin1String("text/plain");
399     return QString();
400 }
401
402 bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
403 {
404     return flavorFor(mime) == flav;
405 }
406
407 QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
408 {
409     if (data.count() > 1)
410         qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data");
411     const QByteArray &firstData = data.first();
412     // I can only handle two types (system and unicode) so deal with them that way
413     QVariant ret;
414     if (flavor == QLatin1String("public.utf8-plain-text")) {
415         QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
416                                              reinterpret_cast<const UInt8 *>(firstData.constData()),
417                                              firstData.size(), CFStringGetSystemEncoding(), false));
418         ret = QString(str);
419     } else if (flavor == QLatin1String("public.utf16-plain-text")) {
420         ret = QString(reinterpret_cast<const QChar *>(firstData.constData()),
421                       firstData.size() / sizeof(QChar));
422     } else {
423         qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
424     }
425     return ret;
426 }
427
428 QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
429 {
430     QList<QByteArray> ret;
431     QString string = data.toString();
432     if (flavor == QLatin1String("public.utf8-plain-text"))
433         ret.append(string.toUtf8());
434     else if (flavor == QLatin1String("public.utf16-plain-text"))
435         ret.append(QByteArray((char*)string.utf16(), string.length()*2));
436     return ret;
437 }
438
439 class QMacPasteboardMimeHTMLText : public QMacPasteboardMime {
440 public:
441     QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { }
442     QString convertorName();
443
444     QString flavorFor(const QString &mime);
445     QString mimeFor(QString flav);
446     bool canConvert(const QString &mime, QString flav);
447     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
448     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
449 };
450
451 QString QMacPasteboardMimeHTMLText::convertorName()
452 {
453     return QLatin1String("HTML");
454 }
455
456 QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
457 {
458     if (mime == QLatin1String("text/html"))
459         return QLatin1String("public.html");
460     return QString();
461 }
462
463 QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
464 {
465     if (flav == QLatin1String("public.html"))
466         return QLatin1String("text/html");
467     return QString();
468 }
469
470 bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
471 {
472     return flavorFor(mime) == flav;
473 }
474
475 QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
476 {
477     if (!canConvert(mimeType, flavor))
478         return QVariant();
479     if (data.count() > 1)
480         qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
481     return data.first();
482 }
483
484 QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
485 {
486     QList<QByteArray> ret;
487     if (!canConvert(mime, flavor))
488         return ret;
489     ret.append(data.toByteArray());
490     return ret;
491 }
492
493 class QMacPasteboardMimeTiff : public QMacPasteboardMime {
494 public:
495     QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { }
496     QString convertorName();
497
498     QString flavorFor(const QString &mime);
499     QString mimeFor(QString flav);
500     bool canConvert(const QString &mime, QString flav);
501     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
502     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
503 };
504
505 QString QMacPasteboardMimeTiff::convertorName()
506 {
507     return QLatin1String("Tiff");
508 }
509
510 QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
511 {
512     if (mime.startsWith(QLatin1String("application/x-qt-image")))
513         return QLatin1String("public.tiff");
514     return QString();
515 }
516
517 QString QMacPasteboardMimeTiff::mimeFor(QString flav)
518 {
519     if (flav == QLatin1String("public.tiff"))
520         return QLatin1String("application/x-qt-image");
521     return QString();
522 }
523
524 bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
525 {
526     return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
527 }
528
529 QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
530 {
531     if (data.count() > 1)
532         qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
533     QVariant ret;
534     if (!canConvert(mime, flav))
535         return ret;
536     const QByteArray &a = data.first();
537     QCFType<CGImageRef> image;
538     QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0,
539                                                 reinterpret_cast<const UInt8 *>(a.constData()),
540                                                 a.size(), kCFAllocatorNull);
541     QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
542     image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
543
544     // ### TODO (msorvig) QPixmap conversion
545     //if (image != 0)
546     //    ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage());
547     return ret;
548 }
549
550 QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
551 {
552     QList<QByteArray> ret;
553     if (!canConvert(mime, flav))
554         return ret;
555
556     QImage img = qvariant_cast<QImage>(variant);
557     QCFType<CGImageRef> cgimage = qt_mac_image_to_cgimage(img);
558
559     QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
560     QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
561     if (imageDestination != 0) {
562         CFTypeRef keys[2];
563         QCFType<CFTypeRef> values[2];
564         QCFType<CFDictionaryRef> options;
565         keys[0] = kCGImagePropertyPixelWidth;
566         keys[1] = kCGImagePropertyPixelHeight;
567         int width = img.width();
568         int height = img.height();
569         values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
570         values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
571         options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys),
572                                      reinterpret_cast<const void **>(values), 2,
573                                      &kCFTypeDictionaryKeyCallBacks,
574                                      &kCFTypeDictionaryValueCallBacks);
575         CGImageDestinationAddImage(imageDestination, cgimage, options);
576         CGImageDestinationFinalize(imageDestination);
577     }
578     QByteArray ar(CFDataGetLength(data), 0);
579     CFDataGetBytes(data,
580             CFRangeMake(0, ar.size()),
581             reinterpret_cast<UInt8 *>(ar.data()));
582     ret.append(ar);
583     return ret;
584 }
585
586
587 class QMacPasteboardMimeFileUri : public QMacPasteboardMime {
588 public:
589     QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { }
590     QString convertorName();
591
592     QString flavorFor(const QString &mime);
593     QString mimeFor(QString flav);
594     bool canConvert(const QString &mime, QString flav);
595     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
596     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
597 };
598
599 QString QMacPasteboardMimeFileUri::convertorName()
600 {
601     return QLatin1String("FileURL");
602 }
603
604 QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
605 {
606     if (mime == QLatin1String("text/uri-list"))
607         return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
608     return QString();
609 }
610
611 QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
612 {
613     if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)))
614         return QLatin1String("text/uri-list");
615     return QString();
616 }
617
618 bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
619 {
620     return mime == QLatin1String("text/uri-list")
621             && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
622 }
623
624 QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
625 {
626     if (!canConvert(mime, flav))
627         return QVariant();
628     QList<QVariant> ret;
629     for (int i = 0; i < data.size(); ++i) {
630         QUrl url = QUrl::fromEncoded(data.at(i));
631         if (url.host().toLower() == QLatin1String("localhost"))
632             url.setHost(QString());
633         url.setPath(url.path().normalized(QString::NormalizationForm_C));
634         ret.append(url);
635     }
636     return QVariant(ret);
637 }
638
639 QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
640 {
641     QList<QByteArray> ret;
642     if (!canConvert(mime, flav))
643         return ret;
644     QList<QVariant> urls = data.toList();
645     for (int i = 0; i < urls.size(); ++i) {
646         QUrl url = urls.at(i).toUrl();
647         if (url.scheme().isEmpty())
648             url.setScheme(QLatin1String("file"));
649         if (url.scheme().toLower() == QLatin1String("file")) {
650             if (url.host().isEmpty())
651                 url.setHost(QLatin1String("localhost"));
652             url.setPath(url.path().normalized(QString::NormalizationForm_D));
653         }
654         ret.append(url.toEncoded());
655     }
656     return ret;
657 }
658
659 class QMacPasteboardMimeUrl : public QMacPasteboardMime {
660 public:
661     QMacPasteboardMimeUrl() : QMacPasteboardMime(MIME_ALL) { }
662     QString convertorName();
663
664     QString flavorFor(const QString &mime);
665     QString mimeFor(QString flav);
666     bool canConvert(const QString &mime, QString flav);
667     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
668     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
669 };
670
671 QString QMacPasteboardMimeUrl::convertorName()
672 {
673     return QLatin1String("URL");
674 }
675
676 QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
677 {
678     if (mime.startsWith(QLatin1String("text/uri-list")))
679         return QLatin1String("public.url");
680     return QString();
681 }
682
683 QString QMacPasteboardMimeUrl::mimeFor(QString flav)
684 {
685     if (flav == QLatin1String("public.url"))
686         return QLatin1String("text/uri-list");
687     return QString();
688 }
689
690 bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
691 {
692     return flav == QLatin1String("public.url")
693             && mime == QLatin1String("text/uri-list");
694 }
695
696 QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
697 {
698     if (!canConvert(mime, flav))
699         return QVariant();
700
701     QList<QVariant> ret;
702     for (int i=0; i<data.size(); ++i) {
703         QUrl url = QUrl::fromEncoded(data.at(i));
704         if (url.host().toLower() == QLatin1String("localhost"))
705             url.setHost(QString());
706         url.setPath(url.path().normalized(QString::NormalizationForm_C));
707         ret.append(url);
708     }
709     return QVariant(ret);
710 }
711
712 QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav)
713 {
714     QList<QByteArray> ret;
715     if (!canConvert(mime, flav))
716         return ret;
717
718     QList<QVariant> urls = data.toList();
719     for (int i=0; i<urls.size(); ++i) {
720         QUrl url = urls.at(i).toUrl();
721         if (url.scheme().isEmpty())
722             url.setScheme(QLatin1String("file"));
723         if (url.scheme().toLower() == QLatin1String("file")) {
724             if (url.host().isEmpty())
725                 url.setHost(QLatin1String("localhost"));
726             url.setPath(url.path().normalized(QString::NormalizationForm_D));
727         }
728         ret.append(url.toEncoded());
729     }
730     return ret;
731 }
732
733 class QMacPasteboardMimeVCard : public QMacPasteboardMime
734 {
735 public:
736     QMacPasteboardMimeVCard() : QMacPasteboardMime(MIME_ALL){ }
737     QString convertorName();
738
739     QString flavorFor(const QString &mime);
740     QString mimeFor(QString flav);
741     bool canConvert(const QString &mime, QString flav);
742     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
743     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
744 };
745
746 QString QMacPasteboardMimeVCard::convertorName()
747 {
748     return QString("VCard");
749 }
750
751 bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
752 {
753     return mimeFor(flav) == mime;
754 }
755
756 QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
757 {
758     if (mime.startsWith(QLatin1String("text/plain")))
759         return QLatin1String("public.vcard");
760     return QString();
761 }
762
763 QString QMacPasteboardMimeVCard::mimeFor(QString flav)
764 {
765     if (flav == QLatin1String("public.vcard"))
766         return QLatin1String("text/plain");
767     return QString();
768 }
769
770 QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
771 {
772     QByteArray cards;
773     if (mime == QLatin1String("text/plain")) {
774         for (int i=0; i<data.size(); ++i)
775             cards += data[i];
776     }
777     return QVariant(cards);
778 }
779
780 QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString)
781 {
782     QList<QByteArray> ret;
783     if (mime == QLatin1String("text/plain"))
784         ret.append(data.toString().toUtf8());
785     return ret;
786 }
787
788
789 /*!
790   \internal
791
792   This is an internal function.
793 */
794 void QMacPasteboardMime::initialize()
795 {
796     if (globalMimeList()->isEmpty()) {
797         qAddPostRoutine(cleanup_mimes);
798
799         //standard types that we wrap
800         new QMacPasteboardMimeTiff;
801         new QMacPasteboardMimeUnicodeText;
802         new QMacPasteboardMimePlainText;
803         new QMacPasteboardMimeHTMLText;
804         new QMacPasteboardMimeFileUri;
805         new QMacPasteboardMimeUrl;
806         new QMacPasteboardMimeTypeName;
807         new QMacPasteboardMimeVCard;
808         //make sure our "non-standard" types are always last! --Sam
809         new QMacPasteboardMimeAny;
810     }
811 }
812
813 /*!
814   Returns the most-recently created QMacPasteboardMime of type \a t that can convert
815   between the \a mime and \a flav formats.  Returns 0 if no such convertor
816   exists.
817 */
818 QMacPasteboardMime*
819 QMacPasteboardMime::convertor(uchar t, const QString &mime, QString flav)
820 {
821     MimeList *mimes = globalMimeList();
822     for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
823 #ifdef DEBUG_MIME_MAPS
824         qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]",
825                (*it)->convertorName().toLatin1().constData(),
826                (*it)->type & t, mime.toLatin1().constData(),
827                flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
828                (*it)->canConvert(mime,flav));
829         for (int i = 0; i < (*it)->countFlavors(); ++i) {
830             int f = (*it)->flavor(i);
831             qDebug("  %d) %d[%c%c%c%c] [%s]", i, f,
832                    (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF,
833                    (*it)->convertorName().toLatin1().constData());
834         }
835 #endif
836         if (((*it)->type & t) && (*it)->canConvert(mime, flav))
837             return (*it);
838     }
839     return 0;
840 }
841 /*!
842   Returns a MIME type of type \a t for \a flav, or 0 if none exists.
843 */
844 QString QMacPasteboardMime::flavorToMime(uchar t, QString flav)
845 {
846     MimeList *mimes = globalMimeList();
847     for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
848 #ifdef DEBUG_MIME_MAPS
849         qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]",
850                (*it)->convertorName().toLatin1().constData(),
851                (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF,
852                (*it)->mimeFor(flav).toLatin1().constData());
853
854 #endif
855         if ((*it)->type & t) {
856             QString mimeType = (*it)->mimeFor(flav);
857             if (!mimeType.isNull())
858                 return mimeType;
859         }
860     }
861     return QString();
862 }
863
864 /*!
865   Returns a list of all currently defined QMacPasteboardMime objects of type \a t.
866 */
867 QList<QMacPasteboardMime*> QMacPasteboardMime::all(uchar t)
868 {
869     MimeList ret;
870     MimeList *mimes = globalMimeList();
871     for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) {
872         if ((*it)->type & t)
873             ret.append((*it));
874     }
875     return ret;
876 }
877
878
879 /*!
880   \fn QString QMacPasteboardMime::convertorName()
881
882   Returns a name for the convertor.
883
884   All subclasses must reimplement this pure virtual function.
885 */
886
887 /*!
888   \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav)
889
890   Returns true if the convertor can convert (both ways) between
891   \a mime and \a flav; otherwise returns false.
892
893   All subclasses must reimplement this pure virtual function.
894 */
895
896 /*!
897   \fn QString QMacPasteboardMime::mimeFor(QString flav)
898
899   Returns the MIME UTI used for Mac flavor \a flav, or 0 if this
900   convertor does not support \a flav.
901
902   All subclasses must reimplement this pure virtual function.
903 */
904
905 /*!
906   \fn QString QMacPasteboardMime::flavorFor(const QString &mime)
907
908   Returns the Mac UTI used for MIME type \a mime, or 0 if this
909   convertor does not support \a mime.
910
911   All subclasses must reimplement this pure virtual function.
912 */
913
914 /*!
915     \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
916
917     Returns \a data converted from Mac UTI \a flav to MIME type \a
918     mime.
919
920     Note that Mac flavors must all be self-terminating. The input \a
921     data may contain trailing data.
922
923     All subclasses must reimplement this pure virtual function.
924 */
925
926 /*!
927   \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav)
928
929   Returns \a data converted from MIME type \a mime
930     to Mac UTI \a flav.
931
932   Note that Mac flavors must all be self-terminating.  The return
933   value may contain trailing data.
934
935   All subclasses must reimplement this pure virtual function.
936 */
937
938 QT_END_NAMESPACE