Implement cocoa clipboard support.
[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 Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList)
68
69 /*!
70     \fn void qRegisterDraggedTypes(const QStringList &types)
71     \relates QMacPasteboardMime
72
73     Registers the given \a types as custom pasteboard types.
74
75     This function should be called to enable the Drag and Drop events
76     for custom pasteboard types on Cocoa implementations. This is required
77     in addition to a QMacPasteboardMime subclass implementation. By default
78     drag and drop is enabled for all standard pasteboard types.
79
80    \sa QMacPasteboardMime
81 */
82 Q_WIDGETS_EXPORT void qRegisterDraggedTypes(const QStringList &types)
83 {
84     (*globalDraggedTypesList()) += types;
85 }
86
87 const QStringList& qEnabledDraggedTypes()
88 {
89     return (*globalDraggedTypesList());
90 }
91
92
93 /*****************************************************************************
94   QDnD debug facilities
95  *****************************************************************************/
96 //#define DEBUG_MIME_MAPS
97
98 //functions
99 extern QString qt_mac_from_pascal_string(const Str255);  //qglobal.cpp
100 extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1);  //qglobal.cpp
101
102 ScrapFlavorType qt_mac_mime_type = 'CUTE';
103 CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker");
104
105 /*!
106   \class QMacPasteboardMime
107   \brief The QMacPasteboardMime class converts between a MIME type and a
108   \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform
109   Type Identifier (UTI)} format.
110   \since 4.2
111
112   \ingroup draganddrop
113   \inmodule QtWidgets
114
115   Qt's drag and drop and clipboard facilities use the MIME
116   standard. On X11, this maps trivially to the Xdnd protocol. On
117   Mac, although some applications use MIME to describe clipboard
118   contents, it is more common to use Apple's UTI format.
119
120   QMacPasteboardMime's role is to bridge the gap between MIME and UTI;
121   By subclasses this class, one can extend Qt's drag and drop
122   and clipboard handling to convert to and from unsupported, or proprietary, UTI formats.
123
124   A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation.
125
126   Qt has predefined support for the following UTIs:
127   \list
128     \i public.utf8-plain-text - converts to "text/plain"
129     \i public.utf16-plain-text - converts to "text/plain"
130     \i public.html - converts to "text/html"
131     \i public.url - converts to "text/uri-list"
132     \i public.file-url - converts to "text/uri-list"
133     \i public.tiff - converts to "application/x-qt-image"
134     \i public.vcard - converts to "text/plain"
135     \i com.apple.traditional-mac-plain-text - converts to "text/plain"
136     \i com.apple.pict - converts to "application/x-qt-image"
137   \endlist
138
139   When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to
140   find an instance that can convert to, or from, a specific MIME type. It will do this by calling
141   canConvert() on each instance, starting with (and choosing) the last created instance first.
142   The actual conversions will be done by using convertToMime() and convertFromMime().
143
144   \note The API uses the term "flavor" in some cases. This is for backwards
145   compatibility reasons, and should now be understood as UTIs.
146 */
147
148 /*! \enum QMacPasteboardMime::QMacPasteboardMimeType
149     \internal
150 */
151
152 /*!
153   Constructs a new conversion object of type \a t, adding it to the
154   globally accessed list of available convertors.
155 */
156 QMacPasteboardMime::QMacPasteboardMime(char t) : type(t)
157 {
158     globalMimeList()->append(this);
159 }
160
161 /*!
162   Destroys a conversion object, removing it from the global
163   list of available convertors.
164 */
165 QMacPasteboardMime::~QMacPasteboardMime()
166 {
167     if (!QGuiApplication::closingDown())
168         globalMimeList()->removeAll(this);
169 }
170
171 class QMacPasteboardMimeAny : public QMacPasteboardMime {
172 private:
173
174 public:
175     QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
176     }
177     ~QMacPasteboardMimeAny() {
178     }
179     QString convertorName();
180
181     QString flavorFor(const QString &mime);
182     QString mimeFor(QString flav);
183     bool canConvert(const QString &mime, QString flav);
184     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
185     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
186 };
187
188 QString QMacPasteboardMimeAny::convertorName()
189 {
190     return QLatin1String("Any-Mime");
191 }
192
193 QString QMacPasteboardMimeAny::flavorFor(const QString &mime)
194 {
195     // do not handle the mime type name in the drag pasteboard
196     if (mime == QLatin1String("application/x-qt-mime-type-name"))
197         return QString();
198     QString ret = QLatin1String("com.trolltech.anymime.") + mime;
199     return ret.replace(QLatin1Char('/'), QLatin1String("--"));
200 }
201
202 QString QMacPasteboardMimeAny::mimeFor(QString flav)
203 {
204     const QString any_prefix = QLatin1String("com.trolltech.anymime.");
205     if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix))
206         return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/"));
207     return QString();
208 }
209
210 bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav)
211 {
212     return mimeFor(flav) == mime;
213 }
214
215 QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString)
216 {
217     if (data.count() > 1)
218         qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data");
219     QVariant ret;
220     if (mime == QLatin1String("text/plain"))
221         ret = QString::fromUtf8(data.first());
222     else
223         ret = data.first();
224     return ret;
225 }
226
227 QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString)
228 {
229     QList<QByteArray> ret;
230     if (mime == QLatin1String("text/plain"))
231         ret.append(data.toString().toUtf8());
232     else
233         ret.append(data.toByteArray());
234     return ret;
235 }
236
237 class QMacPasteboardMimeTypeName : public QMacPasteboardMime {
238 private:
239
240 public:
241     QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) {
242     }
243     ~QMacPasteboardMimeTypeName() {
244     }
245     QString convertorName();
246
247     QString flavorFor(const QString &mime);
248     QString mimeFor(QString flav);
249     bool canConvert(const QString &mime, QString flav);
250     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
251     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
252 };
253
254 QString QMacPasteboardMimeTypeName::convertorName()
255 {
256     return QLatin1String("Qt-Mime-Type");
257 }
258
259 QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime)
260 {
261     if (mime == QLatin1String("application/x-qt-mime-type-name"))
262         return QLatin1String("com.trolltech.qt.MimeTypeName");
263     return QString();
264 }
265
266 QString QMacPasteboardMimeTypeName::mimeFor(QString)
267 {
268     return QString();
269 }
270
271 bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString)
272 {
273     return false;
274 }
275
276 QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString)
277 {
278     QVariant ret;
279     return ret;
280 }
281
282 QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString)
283 {
284     QList<QByteArray> ret;
285     ret.append(QString("x-qt-mime-type-name").toUtf8());
286     return ret;
287 }
288
289 class QMacPasteboardMimePlainText : public QMacPasteboardMime {
290 public:
291     QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { }
292     QString convertorName();
293
294     QString flavorFor(const QString &mime);
295     QString mimeFor(QString flav);
296     bool canConvert(const QString &mime, QString flav);
297     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
298     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
299 };
300
301 QString QMacPasteboardMimePlainText::convertorName()
302 {
303     return QLatin1String("PlainText");
304 }
305
306 QString QMacPasteboardMimePlainText::flavorFor(const QString &mime)
307 {
308     if (mime == QLatin1String("text/plain"))
309         return QLatin1String("com.apple.traditional-mac-plain-text");
310     return QString();
311 }
312
313 QString QMacPasteboardMimePlainText::mimeFor(QString flav)
314 {
315     if (flav == QLatin1String("com.apple.traditional-mac-plain-text"))
316         return QLatin1String("text/plain");
317     return QString();
318 }
319
320 bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav)
321 {
322     return flavorFor(mime) == flav;
323 }
324
325 QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
326 {
327     if (data.count() > 1)
328         qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data");
329     const QByteArray &firstData = data.first();
330     QVariant ret;
331     if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) {
332         QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
333                                              reinterpret_cast<const UInt8 *>(firstData.constData()),
334                                              firstData.size(), CFStringGetSystemEncoding(), false));
335         ret = QString(str);
336     } else {
337         qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
338     }
339     return ret;
340 }
341
342 QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor)
343 {
344     QList<QByteArray> ret;
345     QString string = data.toString();
346     if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text")))
347         ret.append(string.toLatin1());
348     return ret;
349 }
350
351 class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime {
352 public:
353     QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { }
354     QString convertorName();
355
356     QString flavorFor(const QString &mime);
357     QString mimeFor(QString flav);
358     bool canConvert(const QString &mime, QString flav);
359     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
360     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
361 };
362
363 QString QMacPasteboardMimeUnicodeText::convertorName()
364 {
365     return QLatin1String("UnicodeText");
366 }
367
368 QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime)
369 {
370     if (mime == QLatin1String("text/plain"))
371         return QLatin1String("public.utf16-plain-text");
372     int i = mime.indexOf(QLatin1String("charset="));
373     if (i >= 0) {
374         QString cs(mime.mid(i+8).toLower());
375         i = cs.indexOf(QLatin1Char(';'));
376         if (i>=0)
377             cs = cs.left(i);
378         if (cs == QLatin1String("system"))
379             return QLatin1String("public.utf8-plain-text");
380         else if (cs == QLatin1String("iso-10646-ucs-2")
381                  || cs == QLatin1String("utf16"))
382             return QLatin1String("public.utf16-plain-text");
383     }
384     return QString();
385 }
386
387 QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav)
388 {
389     if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text"))
390         return QLatin1String("text/plain");
391     return QString();
392 }
393
394 bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav)
395 {
396     return flavorFor(mime) == flav;
397 }
398
399 QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor)
400 {
401     if (data.count() > 1)
402         qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data");
403     const QByteArray &firstData = data.first();
404     // I can only handle two types (system and unicode) so deal with them that way
405     QVariant ret;
406     if (flavor == QLatin1String("public.utf8-plain-text")) {
407         QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault,
408                                              reinterpret_cast<const UInt8 *>(firstData.constData()),
409                                              firstData.size(), CFStringGetSystemEncoding(), false));
410         ret = QString(str);
411     } else if (flavor == QLatin1String("public.utf16-plain-text")) {
412         ret = QString(reinterpret_cast<const QChar *>(firstData.constData()),
413                       firstData.size() / sizeof(QChar));
414     } else {
415         qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype));
416     }
417     return ret;
418 }
419
420 QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor)
421 {
422     QList<QByteArray> ret;
423     QString string = data.toString();
424     if (flavor == QLatin1String("public.utf8-plain-text"))
425         ret.append(string.toUtf8());
426     else if (flavor == QLatin1String("public.utf16-plain-text"))
427         ret.append(QByteArray((char*)string.utf16(), string.length()*2));
428     return ret;
429 }
430
431 class QMacPasteboardMimeHTMLText : public QMacPasteboardMime {
432 public:
433     QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { }
434     QString convertorName();
435
436     QString flavorFor(const QString &mime);
437     QString mimeFor(QString flav);
438     bool canConvert(const QString &mime, QString flav);
439     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
440     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
441 };
442
443 QString QMacPasteboardMimeHTMLText::convertorName()
444 {
445     return QLatin1String("HTML");
446 }
447
448 QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime)
449 {
450     if (mime == QLatin1String("text/html"))
451         return QLatin1String("public.html");
452     return QString();
453 }
454
455 QString QMacPasteboardMimeHTMLText::mimeFor(QString flav)
456 {
457     if (flav == QLatin1String("public.html"))
458         return QLatin1String("text/html");
459     return QString();
460 }
461
462 bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav)
463 {
464     return flavorFor(mime) == flav;
465 }
466
467 QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor)
468 {
469     if (!canConvert(mimeType, flavor))
470         return QVariant();
471     if (data.count() > 1)
472         qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data");
473     return data.first();
474 }
475
476 QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor)
477 {
478     QList<QByteArray> ret;
479     if (!canConvert(mime, flavor))
480         return ret;
481     ret.append(data.toByteArray());
482     return ret;
483 }
484
485 class QMacPasteboardMimeTiff : public QMacPasteboardMime {
486 public:
487     QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { }
488     QString convertorName();
489
490     QString flavorFor(const QString &mime);
491     QString mimeFor(QString flav);
492     bool canConvert(const QString &mime, QString flav);
493     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
494     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
495 };
496
497 QString QMacPasteboardMimeTiff::convertorName()
498 {
499     return QLatin1String("Tiff");
500 }
501
502 QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
503 {
504     if (mime.startsWith(QLatin1String("application/x-qt-image")))
505         return QLatin1String("public.tiff");
506     return QString();
507 }
508
509 QString QMacPasteboardMimeTiff::mimeFor(QString flav)
510 {
511     if (flav == QLatin1String("public.tiff"))
512         return QLatin1String("application/x-qt-image");
513     return QString();
514 }
515
516 bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
517 {
518     return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
519 }
520
521 QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
522 {
523     if (data.count() > 1)
524         qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
525     QVariant ret;
526     if (!canConvert(mime, flav))
527         return ret;
528     const QByteArray &a = data.first();
529     QCFType<CGImageRef> image;
530     QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0,
531                                                 reinterpret_cast<const UInt8 *>(a.constData()),
532                                                 a.size(), kCFAllocatorNull);
533     QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
534     image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
535
536     // ### TODO (msorvig) QPixmap conversion
537     //if (image != 0)
538     //    ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage());
539     return ret;
540 }
541
542 QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
543 {
544     QList<QByteArray> ret;
545     if (!canConvert(mime, flav))
546         return ret;
547
548     QImage img = qvariant_cast<QImage>(variant);
549     QCFType<CGImageRef> cgimage = qt_mac_image_to_cgimage(img);
550
551     QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
552     QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
553     if (imageDestination != 0) {
554         CFTypeRef keys[2];
555         QCFType<CFTypeRef> values[2];
556         QCFType<CFDictionaryRef> options;
557         keys[0] = kCGImagePropertyPixelWidth;
558         keys[1] = kCGImagePropertyPixelHeight;
559         int width = img.width();
560         int height = img.height();
561         values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
562         values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
563         options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys),
564                                      reinterpret_cast<const void **>(values), 2,
565                                      &kCFTypeDictionaryKeyCallBacks,
566                                      &kCFTypeDictionaryValueCallBacks);
567         CGImageDestinationAddImage(imageDestination, cgimage, options);
568         CGImageDestinationFinalize(imageDestination);
569     }
570     QByteArray ar(CFDataGetLength(data), 0);
571     CFDataGetBytes(data,
572             CFRangeMake(0, ar.size()),
573             reinterpret_cast<UInt8 *>(ar.data()));
574     ret.append(ar);
575     return ret;
576 }
577
578
579 class QMacPasteboardMimeFileUri : public QMacPasteboardMime {
580 public:
581     QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { }
582     QString convertorName();
583
584     QString flavorFor(const QString &mime);
585     QString mimeFor(QString flav);
586     bool canConvert(const QString &mime, QString flav);
587     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
588     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
589 };
590
591 QString QMacPasteboardMimeFileUri::convertorName()
592 {
593     return QLatin1String("FileURL");
594 }
595
596 QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime)
597 {
598     if (mime == QLatin1String("text/uri-list"))
599         return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
600     return QString();
601 }
602
603 QString QMacPasteboardMimeFileUri::mimeFor(QString flav)
604 {
605     if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)))
606         return QLatin1String("text/uri-list");
607     return QString();
608 }
609
610 bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav)
611 {
612     return mime == QLatin1String("text/uri-list")
613             && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0));
614 }
615
616 QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
617 {
618     if (!canConvert(mime, flav))
619         return QVariant();
620     QList<QVariant> ret;
621     for (int i = 0; i < data.size(); ++i) {
622         QUrl url = QUrl::fromEncoded(data.at(i));
623         if (url.host().toLower() == QLatin1String("localhost"))
624             url.setHost(QString());
625         url.setPath(url.path().normalized(QString::NormalizationForm_C));
626         ret.append(url);
627     }
628     return QVariant(ret);
629 }
630
631 QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav)
632 {
633     QList<QByteArray> ret;
634     if (!canConvert(mime, flav))
635         return ret;
636     QList<QVariant> urls = data.toList();
637     for (int i = 0; i < urls.size(); ++i) {
638         QUrl url = urls.at(i).toUrl();
639         if (url.scheme().isEmpty())
640             url.setScheme(QLatin1String("file"));
641         if (url.scheme().toLower() == QLatin1String("file")) {
642             if (url.host().isEmpty())
643                 url.setHost(QLatin1String("localhost"));
644             url.setPath(url.path().normalized(QString::NormalizationForm_D));
645         }
646         ret.append(url.toEncoded());
647     }
648     return ret;
649 }
650
651 class QMacPasteboardMimeUrl : public QMacPasteboardMime {
652 public:
653     QMacPasteboardMimeUrl() : QMacPasteboardMime(MIME_ALL) { }
654     QString convertorName();
655
656     QString flavorFor(const QString &mime);
657     QString mimeFor(QString flav);
658     bool canConvert(const QString &mime, QString flav);
659     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
660     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
661 };
662
663 QString QMacPasteboardMimeUrl::convertorName()
664 {
665     return QLatin1String("URL");
666 }
667
668 QString QMacPasteboardMimeUrl::flavorFor(const QString &mime)
669 {
670     if (mime.startsWith(QLatin1String("text/uri-list")))
671         return QLatin1String("public.url");
672     return QString();
673 }
674
675 QString QMacPasteboardMimeUrl::mimeFor(QString flav)
676 {
677     if (flav == QLatin1String("public.url"))
678         return QLatin1String("text/uri-list");
679     return QString();
680 }
681
682 bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav)
683 {
684     return flav == QLatin1String("public.url")
685             && mime == QLatin1String("text/uri-list");
686 }
687
688 QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
689 {
690     if (!canConvert(mime, flav))
691         return QVariant();
692
693     QList<QVariant> ret;
694     for (int i=0; i<data.size(); ++i) {
695         QUrl url = QUrl::fromEncoded(data.at(i));
696         if (url.host().toLower() == QLatin1String("localhost"))
697             url.setHost(QString());
698         url.setPath(url.path().normalized(QString::NormalizationForm_C));
699         ret.append(url);
700     }
701     return QVariant(ret);
702 }
703
704 QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav)
705 {
706     QList<QByteArray> ret;
707     if (!canConvert(mime, flav))
708         return ret;
709
710     QList<QVariant> urls = data.toList();
711     for (int i=0; i<urls.size(); ++i) {
712         QUrl url = urls.at(i).toUrl();
713         if (url.scheme().isEmpty())
714             url.setScheme(QLatin1String("file"));
715         if (url.scheme().toLower() == QLatin1String("file")) {
716             if (url.host().isEmpty())
717                 url.setHost(QLatin1String("localhost"));
718             url.setPath(url.path().normalized(QString::NormalizationForm_D));
719         }
720         ret.append(url.toEncoded());
721     }
722     return ret;
723 }
724
725 class QMacPasteboardMimeVCard : public QMacPasteboardMime
726 {
727 public:
728     QMacPasteboardMimeVCard() : QMacPasteboardMime(MIME_ALL){ }
729     QString convertorName();
730
731     QString flavorFor(const QString &mime);
732     QString mimeFor(QString flav);
733     bool canConvert(const QString &mime, QString flav);
734     QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
735     QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
736 };
737
738 QString QMacPasteboardMimeVCard::convertorName()
739 {
740     return QString("VCard");
741 }
742
743 bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav)
744 {
745     return mimeFor(flav) == mime;
746 }
747
748 QString QMacPasteboardMimeVCard::flavorFor(const QString &mime)
749 {
750     if (mime.startsWith(QLatin1String("text/plain")))
751         return QLatin1String("public.vcard");
752     return QString();
753 }
754
755 QString QMacPasteboardMimeVCard::mimeFor(QString flav)
756 {
757     if (flav == QLatin1String("public.vcard"))
758         return QLatin1String("text/plain");
759     return QString();
760 }
761
762 QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString)
763 {
764     QByteArray cards;
765     if (mime == QLatin1String("text/plain")) {
766         for (int i=0; i<data.size(); ++i)
767             cards += data[i];
768     }
769     return QVariant(cards);
770 }
771
772 QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString)
773 {
774     QList<QByteArray> ret;
775     if (mime == QLatin1String("text/plain"))
776         ret.append(data.toString().toUtf8());
777     return ret;
778 }
779
780
781 /*!
782   \internal
783
784   This is an internal function.
785 */
786 void QMacPasteboardMime::initializeMimeTypes()
787 {
788     if (globalMimeList()->isEmpty()) {
789         //standard types that we wrap
790         new QMacPasteboardMimeTiff;
791         new QMacPasteboardMimeUnicodeText;
792         new QMacPasteboardMimePlainText;
793         new QMacPasteboardMimeHTMLText;
794         new QMacPasteboardMimeFileUri;
795         new QMacPasteboardMimeUrl;
796         new QMacPasteboardMimeTypeName;
797         new QMacPasteboardMimeVCard;
798         //make sure our "non-standard" types are always last! --Sam
799         new QMacPasteboardMimeAny;
800     }
801 }
802
803 /*!
804   \internal
805 */
806 void QMacPasteboardMime::destroyMimeTypes()
807 {
808     MimeList *mimes = globalMimeList();
809     while (!mimes->isEmpty())
810         delete mimes->takeFirst();
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