Index: /trunk/src/gui/kernel/qmime.h
===================================================================
--- /trunk/src/gui/kernel/qmime.h	(revision 336)
+++ /trunk/src/gui/kernel/qmime.h	(revision 337)
@@ -135,8 +135,12 @@
                                        QVariant::Type preferredType) const = 0;
 
+protected:
+
     static ULONG registerMimeType(const QString &mime);
 
     static ULONG allocateMemory(size_t size);
     static void freeMemory(ULONG addr);
+
+    static QString formatName(ULONG format);
 
 private:
Index: /trunk/src/gui/kernel/qmime_pm.cpp
===================================================================
--- /trunk/src/gui/kernel/qmime_pm.cpp	(revision 336)
+++ /trunk/src/gui/kernel/qmime_pm.cpp	(revision 337)
@@ -100,15 +100,27 @@
     to MIME formats.
 
-    Qt has predefined support for the following PM Clipboard formats:
+    Qt has predefined support for the following PM Clipboard formats (custom
+    formats registered in the system atom table by name are given in double
+    quotes):
 
     \table
     \header \o PM Format \o Equivalent MIME type
-    \row \o \c CF_TEXT        \o \c text/plain
-    \row \o \c CF_BITMAP      \o \c{image/xyz}, where \c xyz is
-                                 a \l{QImageWriter::supportedImageFormats()}{Qt image format}
+    \row \o \c CF_TEXT          \o \c text/plain (system codepage,
+                                   zero-terminated string)
+    \row \o \c "text/unicode"   \o \c text/plain (16-bit Unicode, Mozilla-compatible,
+                                   zero-terminated string)
+    \row \o \c CF_BITMAP        \o \c{image/xyz}, where \c xyz is
+                                    a \l{QImageWriter::supportedImageFormats()}{Qt image format}
+    \row \o \c "x-mime:<mime>"  \o data in the format corresponding to the given
+                                   MIME type \c <mime>
     \endtable
 
-    An example use of this class would be to map the PM Metafile
-    clipboard format (\c CF_METAFILE) to and from the MIME type
+    Note that all "x-mime:<mime>" formats use the CFI_POINTER storage type. That
+    is, the clipboard contains a pointer to the memory block containing the MIME
+    data in the corresponding format. The first 4 bytes of this memory block
+    always contain the length of the subsequent MIME data array, in bytes.
+
+    An example use of this class by the user application would be to map the
+    PM Metafile clipboard format (\c CF_METAFILE) to and from the MIME type
     \c{image/x-metafile}. This conversion might simply be adding or removing a
     header, or even just passing on the data. See \l{Drag and Drop} for more
@@ -167,6 +179,6 @@
     ULONG data = 0;
 
-    // allocate giveable memory for the array + dword for its size
-    APIRET arc = DosAllocSharedMem((PVOID *)&data, NULL, size + 4,
+    // allocate giveable memory for the array
+    APIRET arc = DosAllocSharedMem((PVOID *)&data, NULL, size,
                                    PAG_WRITE  | PAG_COMMIT | OBJ_GIVEABLE);
     if (arc != NO_ERROR) {
@@ -177,21 +189,4 @@
     }
 
-    /// @todo I think we don't need it any more
-#if 0
-    // get the size rounded to the page boundary (4K)
-    ULONG sz = ~0, flags = 0;
-    arc = DosQueryMem((PVOID)(data + size - 1), &sz, &flags);
-    if (arc != NO_ERROR) {
-#ifndef QT_NO_DEBUG
-        qWarning("QPMMime::allocateMemory: DosQueryMem failed with %lu", arc);
-#endif
-        DosFreeMem((PVOID)data);
-        return 0;
-    }
-    sz += (size - 1);
-    // store the exact size to the last dword of the last page
-    *(((ULONG *)(data + sz)) - 1) = size;
-#endif
-
     return data;
 }
@@ -226,9 +221,9 @@
     Converts the \a mimeData to the specified \a format.
 
-    If \a data is not NULL, a handle to the converted data should then be placed
+    If \a data is not NULL, a handle to the converted data should be then placed
     in a variable pointed to by \a data and with the necessary flags describing
     the handle returned in the \a flags variable.
 
-    The following flags describing the data type are recognized:
+    The following flags describing the data storage type are recognized:
 
     \table
@@ -280,6 +275,6 @@
 
     QList<QPMMime*> mimes = theMimeList()->mimes();
-    for (int i = mimes.size()-1; i >= 0; --i) {
-        QList<MimeCFPair> fmts = mimes[i]->mimesForFormats(formats);
+    foreach(QPMMime *mime, mimes) {
+        QList<MimeCFPair> fmts = mime->mimesForFormats(formats);
         int priority = 0;
         foreach (MimeCFPair fmt, fmts) {
@@ -291,5 +286,5 @@
                     // replace if priority is higher, ignore otherwise
                     if (priority < match.priority) {
-                        match.converter = mimes[i];
+                        match.converter = mime;
                         match.format = fmt.second;
                         match.priority = priority;
@@ -299,5 +294,5 @@
             }
             if (it == matches.end()) {
-                matches += Match(mimes[i], fmt.first, fmt.second, priority);
+                matches += Match(mime, fmt.first, fmt.second, priority);
             }
         }
@@ -313,6 +308,6 @@
 
     QList<QPMMime*> mimes = theMimeList()->mimes();
-    for (int i = mimes.size()-1; i >= 0; --i) {
-        QList<ULONG> cfs = mimes[i]->formatsForMimeData(mimeData);
+    foreach(QPMMime *mime, mimes) {
+        QList<ULONG> cfs = mime->formatsForMimeData(mimeData);
         int priority = 0;
         foreach (ULONG cf, cfs) {
@@ -324,5 +319,5 @@
                     // replace if priority is higher, ignore otherwise
                     if (priority < match.priority) {
-                        match.converter = mimes[i];
+                        match.converter = mime;
                         match.priority = priority;
                     }
@@ -331,5 +326,5 @@
             }
             if (it == matches.end()) {
-                matches += Match(mimes[i], cf, priority);
+                matches += Match(mime, cf, priority);
             }
         }
@@ -337,4 +332,17 @@
 
     return matches;
+}
+
+QString QPMMime::formatName(ULONG format)
+{
+    QString name;
+    HATOMTBL tbl = WinQuerySystemAtomTable();
+    if (tbl != NULLHANDLE) {
+        ULONG len = WinQueryAtomLength(tbl, format);
+        QByteArray atom(len, '\0');
+        WinQueryAtomName(tbl, format, atom.data(), atom.size() + 1);
+        name = QString::fromLocal8Bit(atom);
+    }
+    return name;
 }
 
@@ -346,8 +354,10 @@
     QPMMimeText();
 
+    // for converting from Qt
     QList<ULONG> formatsForMimeData(const QMimeData *mimeData) const;
     bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
                              ULONG &flags, ULONG *data) const;
 
+    // for converting to Qt
     QList<MimeCFPair> mimesForFormats(const QList<ULONG> &formats) const;
     QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
@@ -519,4 +529,194 @@
 ////////////////////////////////////////////////////////////////////////////////
 
+class QPMMimeAnyMime : public QPMMime
+{
+public:
+    QPMMimeAnyMime();
+
+    // for converting from Qt
+    QList<ULONG> formatsForMimeData(const QMimeData *mimeData) const;
+    bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
+                             ULONG &flags, ULONG *data) const;
+
+    // for converting to Qt
+    QList<MimeCFPair> mimesForFormats(const QList<ULONG> &formats) const;
+    QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
+                               const QString &mimeType,
+                               QVariant::Type preferredType) const;
+
+private:
+    ULONG registerMimeType(const QString &mime) const;
+    QString registerFormat(ULONG format) const;
+
+    mutable QMap<QString, ULONG> cfMap;
+    mutable QMap<ULONG, QString> mimeMap;
+
+    static QStringList ianaTypes;
+    static QString mimePrefix;
+    static QString customPrefix;
+};
+
+// static
+QStringList QPMMimeAnyMime::ianaTypes;
+QString QPMMimeAnyMime::mimePrefix;
+QString QPMMimeAnyMime::customPrefix;
+
+QPMMimeAnyMime::QPMMimeAnyMime()
+{
+    //MIME Media-Types
+    if (!ianaTypes.size()) {
+        ianaTypes.append(QLatin1String("application/"));
+        ianaTypes.append(QLatin1String("audio/"));
+        ianaTypes.append(QLatin1String("example/"));
+        ianaTypes.append(QLatin1String("image/"));
+        ianaTypes.append(QLatin1String("message/"));
+        ianaTypes.append(QLatin1String("model/"));
+        ianaTypes.append(QLatin1String("multipart/"));
+        ianaTypes.append(QLatin1String("text/"));
+        ianaTypes.append(QLatin1String("video/"));
+
+        mimePrefix = QLatin1String("x-mime:");
+        customPrefix = QLatin1String("application/x-qt-pm-mime;value=\"");
+    }
+}
+
+QList<ULONG> QPMMimeAnyMime::formatsForMimeData(const QMimeData *mimeData) const
+{
+    QList<ULONG> cfs;
+
+    QStringList mimes = QInternalMimeData::formatsHelper(mimeData);
+    foreach (QString mime, mimes) {
+        ULONG cf = cfMap.value(mime);
+        if (!cf)
+            cf = registerMimeType(mime);
+        if (cf)
+            cfs << cf;
+    }
+
+    return cfs;
+}
+
+bool QPMMimeAnyMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
+                                         ULONG &flags, ULONG *data) const
+{
+    QString mime = mimeMap.value(format);
+    if (mime.isNull())
+        return false;
+
+    flags = CFI_POINTER;
+
+    if (data == NULL)
+        return true; // delayed rendering, nothing to do
+
+    QByteArray r = QInternalMimeData::renderDataHelper(mime, mimeData);
+    if (r.isNull())
+        return false;
+
+    *data = QPMMime::allocateMemory(r.size() + sizeof(ULONG));
+    if (!*data)
+        return false;
+
+    *((ULONG *)(*data)) = r.size();
+    memcpy((void *)(*data + sizeof(ULONG)), r.data(), r.size());
+    return true;
+}
+
+QList<QPMMime::MimeCFPair> QPMMimeAnyMime::mimesForFormats(const QList<ULONG> &formats) const
+{
+    QList<MimeCFPair> mimes;
+
+    foreach (ULONG format, formats) {
+        QString mime = mimeMap.value(format);
+        if (mime.isEmpty())
+            mime = registerFormat(format);
+        if (!mime.isEmpty())
+            mimes << qMakePair(mime, format);
+    }
+
+    return mimes;
+}
+
+QVariant QPMMimeAnyMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
+                                           const QString &mimeType,
+                                           QVariant::Type preferredType) const
+{
+    Q_UNUSED(preferredType);
+
+    QVariant ret;
+
+    if (cfMap.value(mimeType) != format)
+        return ret;
+
+    if (!(flags & CFI_POINTER) || !data)
+        return ret;
+
+    // get the real block size (always rounded to the page boundary (4K))
+    ULONG sz = ~0, fl = 0, arc;
+    arc = DosQueryMem((PVOID)data, &sz, &fl);
+    if (arc != NO_ERROR) {
+#ifndef QT_NO_DEBUG
+        qWarning("QPMMimeText::convertFromFormat: DosQueryMem failed with %lu", arc);
+#endif
+        return ret;
+    }
+    ULONG size = *((ULONG *)data);
+    if (!size || size + sizeof(ULONG) > sz)
+        return ret;
+
+    // it should be enough to return the data and let QMimeData do the rest.
+    ret = QByteArray((const char *)(data + sizeof(ULONG)), size);
+    return ret;
+}
+
+ULONG QPMMimeAnyMime::registerMimeType(const QString &mime) const
+{
+    if (mime.isEmpty())
+        return 0;
+
+    QString mimeToReg = mime;
+
+    bool ianaType = false;
+    foreach(QString prefix, ianaTypes) {
+        if (mime.startsWith(prefix)) {
+            ianaType = true;
+            break;
+        }
+    }
+    if (!ianaType) {
+        // prepend the non-standard type with the prefix that makes it comply
+        // with the standard
+        mimeToReg = customPrefix + mime + QChar('\"');
+    }
+
+    mimeToReg = mimePrefix + mimeToReg;
+    ULONG cf = QPMMime::registerMimeType(mimeToReg);
+    if (cf) {
+        cfMap[mime] = cf;
+        mimeMap[cf] = mime;
+    }
+    return cf;
+}
+
+QString QPMMimeAnyMime::registerFormat(ULONG format) const
+{
+    QString mime;
+
+    if (!format)
+        return mime;
+
+    QString atomStr = formatName(format);
+    if (atomStr.startsWith(mimePrefix)) {
+        // the format represents the mime type we can recognize
+        mime = atomStr.mid(mimePrefix.size());
+        if (!mime.isEmpty()) {
+            cfMap[mime] = format;
+            mimeMap[format] = mime;
+        }
+    }
+    return mime;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 QPMMimeList::QPMMimeList()
     : initialized(false)
@@ -535,4 +735,5 @@
     if (!initialized) {
         initialized = true;
+        new QPMMimeAnyMime; // must be the first (used as a fallback)
         new QPMMimeText;
     }
@@ -542,5 +743,5 @@
 {
     init();
-    list.append(mime);
+    list.prepend(mime);
 }
 
