Index: /trunk/src/gui/kernel/qdnd_pm.cpp
===================================================================
--- /trunk/src/gui/kernel/qdnd_pm.cpp	(revision 443)
+++ /trunk/src/gui/kernel/qdnd_pm.cpp	(revision 444)
@@ -57,5 +57,5 @@
 #include "qt_os2.h"
 
-#define QDND_DEBUG // in pair with qmime_pm.cpp
+//#define QDND_DEBUG // in pair with qmime_pm.cpp
 
 #ifdef QDND_DEBUG
@@ -89,5 +89,6 @@
     bool hasFormat_sys(const QString &mimeType);
     QStringList formats_sys();
-    QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type);
+    QVariant retrieveData_sys(const QString &mimeType,
+                              QVariant::Type preferredType);
 
 private:
@@ -209,5 +210,5 @@
 
 QVariant QPMDragData::retrieveData_sys(const QString &mimeType,
-                                       QVariant::Type type)
+                                       QVariant::Type preferredType)
 {
     QVariant result;
@@ -222,5 +223,5 @@
     foreach (QPMMime::DropWorker *w, workers) {
         if (w->hasFormat(mimeType)) {
-            result = w->retrieveData(mimeType, type);
+            result = w->retrieveData(mimeType, preferredType);
             break;
         }
@@ -270,15 +271,14 @@
 MRESULT qt_dispatchDragAndDrop(QWidget *widget, const QMSG &qmsg)
 {
-    // @todo maybe delete last*Op variables
-
-    static HWND lastDragOverHwnd = 0; // last target window
+    static QWidget *lastDragOverWidget = 0; // last target widget
     static USHORT lastDragOverOp = 0; // last DM_DRAGOVER operation
 
     static USHORT supportedOps = 0; // operations supported by all items
+    static bool sourceAllowsOp = false; // does source allow requested operation
 
     static USHORT lastDropReply = DOR_NEVERDROP; // last reply to DM_DRAGOVER
     static USHORT lastOpRequest = DO_UNKNOWN; // last op requested in DM_DRAGOVER
 
-    static Qt::DropAction lastProposedAction = Qt::IgnoreAction; // last proposed action
+    static Qt::DropAction lastProposedAction = Qt::CopyAction; // last proposed action
     static QRect lastAnswerRect; // last accepted rectangle from the target
     // @todo use lastAnswerRect to compress DM_DRAGOVER events
@@ -289,4 +289,6 @@
 
     BOOL ok = FALSE;
+
+    QDragManager *manager = QDragManager::self();
 
     switch (qmsg.msg) {
@@ -294,29 +296,4 @@
             DEBUG(("DM_DRAGOVER: hwnd %lX di %p x %d y %d", qmsg.hwnd, qmsg.mp1,
                    SHORT1FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)));
-
-            bool first = lastDragOverHwnd != qmsg.hwnd;
-            if (first) {
-                // the first DM_DRAGOVER message
-                lastDragOverHwnd = qmsg.hwnd;
-                lastDropReply = DOR_NEVERDROP;
-                lastOpRequest = DO_UNKNOWN;
-                lastProposedAction = Qt::IgnoreAction;
-                lastAnswerRect = QRect();
-                supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
-                // ensure drag data is reset (just in case of a wrong msg flow...)
-                dragData.reset();
-            }
-
-            Q_ASSERT(first || widget->acceptDrops());
-            if (!widget->acceptDrops()) {
-                if (!first) {
-                    // Odin32 apps are dramatically bogus, they continue to send
-                    // DM_DRAGOVER even if we reply DOR_NEVERDROP. Simulate
-                    // DM_DRAGLEAVE
-                    lastDragOverHwnd = 0;
-                    dragData.reset();
-                }
-                return MRFROM2SHORT(DOR_NEVERDROP, 0);
-            }
 
             DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
@@ -325,4 +302,50 @@
             if (!ok || !info)
                 return MRFROM2SHORT(DOR_NEVERDROP, 0);
+
+            // flip y coordinate
+            QPoint pnt(info->xDrop, info->yDrop);
+            pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
+            pnt = widget->mapFromGlobal(pnt);
+
+            QWidget *dragOverWidget = widget->childAt(pnt);
+            if (!dragOverWidget)
+                dragOverWidget = widget;
+
+            bool first = lastDragOverWidget != dragOverWidget;
+            if (first) {
+                // the first DM_DRAGOVER message
+                if (lastDragOverWidget != 0) {
+                    // send drag leave to the old widget
+                    dragData.reset();
+                    QPointer<QWidget> dragOverWidgetGuard(dragOverWidget);
+                    QDragLeaveEvent dle;
+                    QApplication::sendEvent(lastDragOverWidget, &dle);
+                    if (!dragOverWidgetGuard) {
+                        dragOverWidget = widget->childAt(pnt);
+                        if (!dragOverWidget)
+                            dragOverWidget = widget;
+                    }
+                }
+                lastDragOverWidget = dragOverWidget;
+                lastDragOverOp = 0;
+                supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
+                sourceAllowsOp = false;
+                lastDropReply = DOR_NEVERDROP;
+                lastOpRequest = DO_UNKNOWN;
+                lastProposedAction = manager->defaultAction(toQDragDropActions(supportedOps),
+                                                            Qt::NoModifier);
+                lastAnswerRect = QRect();
+                // ensure drag data is reset (just in case of a wrong msg flow...)
+                dragData.reset();
+            }
+
+            if (!dragOverWidget->acceptDrops()) {
+                // We don't reply with DOR_NEVERDROP here because in this
+                // case PM will stop sending DM_DRAGOVER to the given HWND but
+                // we may have another non-native child (that has the same HWND)
+                // at a different position that accepts drops
+                DrgFreeDraginfo(info);
+                return MRFROM2SHORT(DOR_NODROP, 0);
+            }
 
             USHORT dropReply = DOR_DROP;
@@ -352,6 +375,4 @@
             if (dropReply != DOR_NEVERDROP) {
 
-                bool sourceAllowsOp = false;
-
                 if (first || lastDragOverOp != info->usOperation) {
                     // the current drop operation was changed by a modifier key
@@ -373,10 +394,4 @@
                 // the drop. Other platforms seem to do the same.
 
-                // flip y coordinate
-                QPoint pnt(info->xDrop, info->yDrop);
-                pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
-                pnt = widget->mapFromGlobal(pnt);
-
-                QDragManager *manager = QDragManager::self();
                 QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
 
@@ -388,10 +403,11 @@
                                         QApplication::mouseButtons(),
                                         QApplication::keyboardModifiers());
-                    QApplication::sendEvent(widget, &dee);
+                    QApplication::sendEvent(dragOverWidget, &dee);
                     // if not allowed or not accepted, always reply DOR_NODROP
-                    // to have DM_DRAGOVER delivered to us again in any case
+                    // to have DM_DRAGOVER delivered to us again for a new test
                     if (sourceAllowsOp && dee.isAccepted()) {
                         dropReply = DOR_DROP;
                         action = dee.dropAction();
+                        lastProposedAction = dee.proposedAction();
                         lastAnswerRect = dee.answerRect();
                     } else {
@@ -410,8 +426,10 @@
                     dme.setDropAction(action);
                     dme.accept();
-                    QApplication::sendEvent(widget, &dme);
+                    QApplication::sendEvent(dragOverWidget, &dme);
                     if (sourceAllowsOp && dme.isAccepted()) {
                         dropReply = DOR_DROP;
                         action = dme.dropAction();
+                        lastProposedAction = dme.proposedAction();
+                        lastAnswerRect = dme.answerRect();
                     } else {
                         dropReply = DOR_NODROP;
@@ -431,16 +449,14 @@
 
             // Odin32 apps produce incorrect message flow, ignore
-            Q_ASSERT(lastDragOverHwnd != 0);
-            if (lastDragOverHwnd == 0)
+            Q_ASSERT(lastDragOverWidget != 0);
+            if (lastDragOverWidget == 0)
                 return 0;
 
-            lastDragOverHwnd = 0;
+            QDragLeaveEvent de;
+            QApplication::sendEvent(lastDragOverWidget, &de);
+
+            lastDragOverWidget = 0;
             dragData.reset();
 
-            if (!widget->acceptDrops())
-                return 0;
-
-            QDragLeaveEvent de;
-            QApplication::sendEvent(widget, &de);
             return 0;
         }
@@ -449,21 +465,17 @@
 
             // Odin32 apps produce incorrect message flow, ignore
-            Q_ASSERT(lastDragOverHwnd != 0);
-            if (lastDragOverHwnd == 0)
+            Q_ASSERT(lastDragOverWidget != 0);
+            if (lastDragOverWidget == 0)
                 return 0;
 
             // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
             // DOR_NODROP, simulate DM_DRAGLEAVE
-            Q_ASSERT(lastDropReply == DM_DROP);
-            if (lastDropReply != DM_DROP) {
-                WinSendMsg(qmsg.hwnd, DM_DRAGLEAVE, 0, 0);
+            Q_ASSERT(lastDropReply == DOR_DROP);
+            if (lastDropReply != DOR_DROP) {
+                QMSG qmsg2 = qmsg;
+                qmsg2.msg = DM_DRAGLEAVE;
+                qt_dispatchDragAndDrop(widget, qmsg2);
                 return 0;
             }
-
-            lastDragOverHwnd = 0;
-
-            Q_ASSERT(widget->acceptDrops());
-            if (!widget->acceptDrops())
-                return 0;
 
             DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
@@ -471,7 +483,5 @@
             Q_ASSERT(ok && info);
             if (!ok || !info)
-                return MRFROM2SHORT(DOR_NEVERDROP, 0);
-
-            Q_ASSERT(lastOpRequest == info->usOperation);
+                return 0;
 
             // flip y coordinate
@@ -480,4 +490,13 @@
             pnt = widget->mapFromGlobal(pnt);
 
+            Q_ASSERT(lastOpRequest == info->usOperation);
+
+            Q_ASSERT(lastDragOverWidget->acceptDrops());
+            if (!lastDragOverWidget->acceptDrops()) {
+                DrgDeleteDraginfoStrHandles(info);
+                DrgFreeDraginfo(info);
+                return 0;
+            }
+
             QDragManager *manager = QDragManager::self();
             QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
@@ -492,9 +511,10 @@
                 de.setDropAction(action);
 
-            QApplication::sendEvent(widget, &de);
+            QApplication::sendEvent(lastDragOverWidget, &de);
 
             if (lastDropReply == DOR_DROP)
                 de.accept();
 
+            lastDragOverWidget = 0;
             dragData.reset(de.isAccepted());
 
Index: /trunk/src/gui/kernel/qmime.h
===================================================================
--- /trunk/src/gui/kernel/qmime.h	(revision 443)
+++ /trunk/src/gui/kernel/qmime.h	(revision 444)
@@ -148,10 +148,10 @@
         virtual HWND hwnd() const = 0;
         // methods implemented if isExclusive() == TRUE and itemCount() == 0
-        virtual DRAGINFO *createDragInfo(const char *name, USHORT supportedOps)
-                                        { return NULL; }
+        virtual DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps)
+                                        { return 0; }
         // methods implemented if itemCount() >= 0
         virtual QByteArray composeFormatString() { return QByteArray(); }
         virtual bool prepare(const char *drm, const char *drf, DRAGITEM *item,
-                             ULONG itemIndex) { return FALSE; }
+                             ULONG itemIndex) { return false; }
         virtual void defaultFileType(const char *&type, const char *&ext) {};
 
@@ -185,5 +185,5 @@
         {
         public:
-            virtual const QString &format(const char *drf) const = 0;
+            virtual QString format(const char *drf) const = 0;
             virtual bool provide(const char *drf, const QByteArray &allData,
                                  ULONG itemIndex, QByteArray &itemData) = 0;
@@ -217,5 +217,6 @@
         virtual bool hasFormat(const QString &mimeType) const = 0;
         virtual QStringList formats() const = 0;
-        virtual QVariant retrieveData(const QString &mimeType, QVariant::Type type) const = 0;
+        virtual QVariant retrieveData(const QString &mimeType,
+                                      QVariant::Type preferredType) const = 0;
 
     private:
@@ -236,5 +237,6 @@
         bool hasFormat(const QString &mimeType) const;
         QStringList formats() const;
-        QVariant retrieveData(const QString &mimeType, QVariant::Type type) const;
+        QVariant retrieveData(const QString &mimeType,
+                              QVariant::Type preferredType) const;
 
         // QPMObjectWindow interface
Index: /trunk/src/gui/kernel/qmime_pm.cpp
===================================================================
--- /trunk/src/gui/kernel/qmime_pm.cpp	(revision 443)
+++ /trunk/src/gui/kernel/qmime_pm.cpp	(revision 444)
@@ -60,5 +60,5 @@
 #include "qdir.h"
 
-#define QDND_DEBUG // in pair with qdnd_pm.cpp
+//#define QDND_DEBUG // in pair with qdnd_pm.cpp
 
 #ifdef QDND_DEBUG
@@ -769,5 +769,5 @@
 }
 
-// @todo
+// @todo remove
 //int QPMMime::DefaultDropWorker::formatCount() const
 //{
@@ -814,9 +814,9 @@
 
 QVariant QPMMime::DefaultDropWorker::retrieveData(const QString &mimeType,
-                                                  QVariant::Type type) const
-{
-    Q_UNUSED(type);
-
-    DEBUG(() << "DefaultDropWorker::retrieveData(" << mimeType << ")");
+                                                  QVariant::Type preferredType) const
+{
+    Q_UNUSED(preferredType);
+
+    DEBUG(() << "DefaultDropWorker::retrieveData: mimeType" << mimeType);
 
     QVariant ret;
@@ -1000,4 +1000,5 @@
                 bool ok = file.remove();
                 Q_ASSERT((ok = ok));
+                Q_UNUSED(ok);
             } else {
                 Q_ASSERT(xfer->hstrRenderToName);
@@ -1088,6 +1089,6 @@
             if (d->sending_DM_RENDER)
             {
+#ifndef QT_NO_DEBUG
                 DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
-#ifndef QT_NO_DEBUG
                 qWarning("Drag item 0x%08lX sent DM_RENDERCOMPLETE w/o first "
                          "replying to DM_RENDER!\n"
@@ -1420,4 +1421,6 @@
     All subclasses must reimplement this pure virtual function.
 */
+
+// @todo add DnD interfaces docs
 
 // static
@@ -1788,5 +1791,5 @@
 QPMMime::DefaultDragWorker *QPMMime::defaultExclDragWorker()
 {
-    static DefaultDragWorker defExclDragWorker(false /* exclusive */);
+    static DefaultDragWorker defExclDragWorker(true /* exclusive */);
     return &defExclDragWorker;
 }
@@ -1821,6 +1824,66 @@
                                QVariant::Type preferredType) const;
 
+#if !defined(QT_NO_DRAGANDDROP)
+
+    // Direct Manipulation (DND) converter interface
+    DragWorker *dragWorkerFor(const QString &mimeType, QMimeData *mimeData);
+    DropWorker *dropWorkerFor(DRAGINFO *info);
+
+    class NativeFileDrag : public DragWorker, public QPMObjectWindow
+    {
+    public:
+        // DragWorker interface
+        bool cleanup(bool isCancelled) { return true; } // always disallow Move
+        bool isExclusive() const { return true; }
+        ULONG itemCount() const { return 0; } // super exclusive
+        HWND hwnd() const { return QPMObjectWindow::hwnd(); }
+        DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps);
+        // QPMObjectWindow interface (dummy implementation, we don't need to interact)
+        MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2) { return 0; }
+    };
+
+    class NativeFileDrop : public DropWorker
+    {
+    public:
+        // DropWorker interface
+        bool isExclusive() const { return true; }
+        bool hasFormat(const QString &mimeType) const;
+        QStringList formats() const;
+        QVariant retrieveData(const QString &mimeType,
+                              QVariant::Type preferredType) const;
+    };
+
+    class TextDragProvider : public DefaultDragWorker::Provider
+    {
+    public:
+        TextDragProvider() : exclusive(false) {}
+        bool exclusive;
+        // Provider interface
+        QString format(const char *drf) const;
+        bool provide(const char *drf, const QByteArray &allData,
+                     ULONG itemIndex, QByteArray &itemData);
+        void fileType(const char *drf, const char *&type, const char *&ext);
+    };
+
+    class TextDropProvider : public DefaultDropWorker::Provider
+    {
+    public:
+        // Provider interface
+        const char *drf(const QString &mimeType) const;
+        bool provide(const QString &format, ULONG itemIndex,
+                     const QByteArray &itemData, QByteArray &allData);
+    };
+
+#endif // !QT_NO_DRAGANDDROP
+
     const ULONG CF_TextUnicode;
     const ULONG CF_TextHtml;
+
+#if !defined(QT_NO_DRAGANDDROP)
+    NativeFileDrag nativeFileDrag;
+    NativeFileDrop nativeFileDrop;
+    TextDragProvider textDragProvider;
+    TextDropProvider textDropProvider;
+#endif // !QT_NO_DRAGANDDROP
 };
 
@@ -1999,4 +2062,326 @@
 }
 
+#if !defined(QT_NO_DRAGANDDROP)
+
+DRAGINFO *QPMMimeText::NativeFileDrag::createDragInfo(const QString &targetName,
+                                                      USHORT supportedOps)
+{
+    Q_ASSERT(source());
+    if (!source())
+        return 0;
+
+    // obtain the list of files
+    QList<QUrl> list;
+    if (source()->hasUrls())
+        list = source()->urls();
+    ULONG itemCnt = list.count();
+    Q_ASSERT(itemCnt);
+    if (!itemCnt)
+        return 0;
+
+    DEBUG(() << "QPMMimeText::NativeFileDrag: itemCnt" << itemCnt);
+
+    DRAGINFO *info = DrgAllocDraginfo(itemCnt);
+    Q_ASSERT(info);
+    if (!info)
+        return 0;
+
+    bool ok = true;
+    QList<QUrl>::iterator it = list.begin();
+    for (ULONG i = 0; i < itemCnt; ++i, ++it) {
+        DRAGITEM *item = DrgQueryDragitemPtr(info, i);
+        Q_ASSERT(item);
+        if (!item) {
+            ok = false;
+            break;
+        }
+
+        QByteArray fileName = QFile::encodeName(QDir::convertSeparators(it->toLocalFile()));
+
+        int sep = fileName.lastIndexOf('\\');
+        Q_ASSERT(sep > 0 && sep < fileName.length() - 1);
+        if (sep <= 0 || sep >= fileName.length() - 1) {
+            ok = false;
+            break;
+        }
+
+        item->hstrSourceName = DrgAddStrHandle(fileName.data() + sep + 1);
+        fileName.truncate(sep + 1);
+        item->hstrContainerName = DrgAddStrHandle(fileName);
+
+        DEBUG(() << "QPMMimeText::NativeFileDrag: item" << i
+                 << "dir" << queryHSTR(item->hstrContainerName)
+                 << "name" << queryHSTR(item->hstrSourceName));
+
+        item->hwndItem = hwnd();
+        item->ulItemID = 0;
+        item->hstrType = DrgAddStrHandle(DRT_UNKNOWN);
+        item->hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_UNKNOWN>");
+        item->hstrTargetName = 0;
+        item->cxOffset = 0;
+        item->cyOffset = 0;
+        item->fsControl = 0;
+        item->fsSupportedOps = supportedOps;
+    }
+
+    if (!ok) {
+        DrgFreeDraginfo(info);
+        info = 0;
+    }
+
+    return info;
+}
+
+bool QPMMimeText::NativeFileDrop::hasFormat(const QString &mimeType) const
+{
+    return mimeType == QLatin1String("text/uri-list");
+}
+
+QStringList QPMMimeText::NativeFileDrop::formats() const
+{
+    QStringList mimes;
+    mimes << QLatin1String("text/uri-list");
+    return mimes;
+}
+
+QVariant QPMMimeText::NativeFileDrop::retrieveData(const QString &mimeType,
+                                                   QVariant::Type preferredType) const
+{
+    QVariant result;
+
+    Q_ASSERT(info());
+    if (!info())
+        return result;
+
+    ULONG itemCount = DrgQueryDragitemCount(info());
+    Q_ASSERT(itemCount);
+    if (!itemCount)
+        return result;
+
+    // sanity check
+    if (mimeType != QLatin1String("text/uri-list"))
+        return result;
+
+    QList<QVariant> urls;
+
+    for (ULONG i = 0; i < itemCount; ++i) {
+        DRAGITEM *item = DrgQueryDragitemPtr(info(), i);
+        Q_ASSERT(item);
+        QByteArray fullName;
+        if (!item || !canTargetRenderAsOS2File(item, &fullName))
+            return result;
+        QString fn = QFile::decodeName(fullName);
+        urls += QUrl::fromLocalFile(fn).toString();
+    }
+
+    if (preferredType == QVariant::Url && urls.size() == 1)
+        result = urls.at(0);
+    else if (!urls.isEmpty())
+        result = urls;
+
+    return result;
+}
+
+QString QPMMimeText::TextDragProvider::format(const char *drf) const
+{
+    QString result;
+
+    if (qstrcmp(drf, "DRF_TEXT") == 0) {
+        if (exclusive)
+            result = QLatin1String("text/uri-list");
+        else
+            result = QLatin1String("text/plain");
+    }
+    return result;
+}
+
+bool QPMMimeText::TextDragProvider::provide(const char *drf,
+                                            const QByteArray &allData,
+                                            ULONG itemIndex,
+                                            QByteArray &itemData)
+{
+    if (qstrcmp(drf, "DRF_TEXT") == 0) {
+        if (exclusive) {
+            // locate the required item
+            int dataSize = allData.size();
+            if (!dataSize)
+                return false;
+            int begin = 0, end = 0, next = 0;
+            do {
+                begin = next;
+                end = allData.indexOf('\r', begin);
+                if (end >= 0) {
+                    next = end + 1;
+                    if (next < dataSize && allData[next] == '\n')
+                        ++next;
+                } else {
+                    end = allData.indexOf('\n', begin);
+                    if (end >= 0)
+                        next = end + 1;
+                }
+            } while (itemIndex-- && end >= 0 && next < dataSize);
+            int urlLen = end - begin;
+            if (urlLen <= 0)
+                return false;
+            QUrl url = QUrl(QString::fromUtf8(allData.data() + begin, urlLen));
+            if (!url.isValid())
+                return false;
+            itemData = url.toEncoded();
+        } else {
+            itemData = allData;
+        }
+        return true;
+    }
+    return false;
+}
+
+void QPMMimeText::TextDragProvider::fileType(const char *drf, const char *&type,
+                                             const char *&ext)
+{
+    if (qstrcmp(drf, "DRF_TEXT") == 0) {
+        if (exclusive) {
+            type = "UniformResourceLocator";
+            // no extension for URLs
+        } else {
+            type = DRT_TEXT;
+            ext = "txt";
+        }
+    }
+};
+
+const char *QPMMimeText::TextDropProvider::drf(const QString &mimeType) const
+{
+    // sanity check
+    if (mimeType == QLatin1String("text/plain") ||
+        mimeType == QLatin1String("text/uri-list"))
+        return "DRF_TEXT";
+    return 0;
+}
+
+bool QPMMimeText::TextDropProvider::provide(const QString &mimeType,
+                                            ULONG itemIndex,
+                                            const QByteArray &itemData,
+                                            QByteArray &allData)
+{
+    if (mimeType == QLatin1String("text/plain")) {
+        allData = itemData;
+        return true;
+    }
+
+    if (mimeType == QLatin1String("text/uri-list")) {
+        QUrl url = QUrl::fromEncoded(itemData);
+        if (!url.isValid())
+            return false;
+        // append the URL to the list
+        allData += url.toString().toUtf8();
+        allData += "\r\n";
+        return true;
+    }
+
+    return false;
+}
+
+QPMMime::DragWorker *QPMMimeText::dragWorkerFor(const QString &mimeType,
+                                                QMimeData *mimeData)
+{
+    if (mimeType == QLatin1String("text/plain")) {
+        // add a cooperative provider
+        textDragProvider.exclusive = false;
+        DefaultDragWorker *defWorker = defaultCoopDragWorker();
+        defWorker->addProvider("DRF_TEXT", &textDragProvider);
+        return defWorker;
+    }
+
+    if (mimeType == QLatin1String("text/uri-list")) {
+        // see what kind of items text/uri-list represents
+        QList<QUrl> urls = mimeData->urls();
+        int fileCnt = 0;
+        foreach (const QUrl &url, urls) {
+            if (url.scheme() == QLatin1String("file"))
+                ++fileCnt;
+        }
+        if (fileCnt && fileCnt == urls.count()) {
+            // all items are local files, return an exclusive file drag worker
+            return &nativeFileDrag;
+        }
+        if (urls.count() && !fileCnt) {
+            // all items are non-files, add an exclusive provider for the
+            // specified item count
+            textDragProvider.exclusive = true;
+            DefaultDragWorker *defWorker = defaultExclDragWorker();
+            bool ok = defWorker->addProvider("DRF_TEXT", &textDragProvider,
+                                             urls.count());
+            return ok ? defWorker : 0;
+        }
+        // if items are mixed, we return NULL to fallback to QPMMimeAnyMime
+    }
+
+    return 0;
+}
+
+QPMMime::DropWorker *QPMMimeText::dropWorkerFor(DRAGINFO *info)
+{
+    ULONG itemCount = DrgQueryDragitemCount(info);
+    Q_ASSERT(itemCount);
+    if (!itemCount)
+        return 0;
+
+    if (itemCount == 1) {
+        DRAGITEM *item = DrgQueryDragitemPtr(info, 0);
+        Q_ASSERT(item);
+        if (!item)
+            return 0;
+        // proceed only if the target cannot render DRM_OS2FILE on its own
+        // and if the item type is not "UniformResourceLocator" (which will be
+        // processed below)
+        if (!canTargetRenderAsOS2File(item) &&
+            !DrgVerifyType(item, "UniformResourceLocator")) {
+            DefaultDropWorker *defWorker = defaultDropWorker();
+            // check that we support one of DRMs and the format is DRF_TEXT
+            if (defWorker->canRender(item, "DRF_TEXT")) {
+                // add a cooperative provider (can coexist with others)
+                defWorker->addProvider(QLatin1String("text/plain"),
+                                        &textDropProvider);
+                return defWorker;
+            }
+            return 0;
+        }
+    }
+
+    // Either the target can render DRM_OS2FILE on its own (so it's a valid
+    // file/directory name), or it's an "UniformResourceLocator", or there is
+    // more than one drag item. Check that all items are of either one type
+    // or another. If so, we can represent them as 'text/uri-list'.
+    bool allAreFiles = true;
+    bool allAreURLs = true;
+    DefaultDropWorker *defWorker = defaultDropWorker();
+    for (ULONG i = 0; i < itemCount; ++i) {
+        DRAGITEM *item = DrgQueryDragitemPtr(info, i);
+        Q_ASSERT(item);
+        if (!item)
+            return 0;
+        if (allAreFiles)
+            allAreFiles &= canTargetRenderAsOS2File(item);
+        if (allAreURLs)
+            allAreURLs &= DrgVerifyType(item, "UniformResourceLocator") &&
+                          defWorker->canRender(item, "DRF_TEXT");
+        if (!allAreFiles && !allAreURLs)
+            return 0;
+    }
+
+    if (allAreFiles) {
+        // return an exclusive drop worker
+        return &nativeFileDrop;
+    }
+
+    // add an exclusive provider (can neither coexist with other workers
+    // or providers)
+    bool ok = defWorker->addExclusiveProvider(QLatin1String("text/uri-list"),
+                                              &textDropProvider);
+    return ok ? defWorker : 0;
+}
+
+#endif // !QT_NO_DRAGANDDROP
+
 ////////////////////////////////////////////////////////////////////////////////
 
