src/gui/kernel/qdnd.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/kernel/qdnd.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,697 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qbitmap.h"
+#include "qdrag.h"
+#include "qpixmap.h"
+#include "qevent.h"
+#include "qfile.h"
+#include "qtextcodec.h"
+#include "qapplication.h"
+#include "qpoint.h"
+#include "qwidget.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qregexp.h"
+#include "qdir.h"
+#include "qdnd_p.h"
+#include "qimagereader.h"
+#include "qimagewriter.h"
+#include "qdebug.h"
+#include <ctype.h>
+
+#ifndef QT_NO_DRAGANDDROP
+
+QT_BEGIN_NAMESPACE
+
+// These pixmaps approximate the images in the Windows User Interface Guidelines.
+
+// XPM
+
+static const char * const move_xpm[] = {
+"11 20 3 1",
+".        c None",
+#if defined(Q_WS_WIN)
+"a        c #000000",
+"X        c #FFFFFF", // Windows cursor is traditionally white
+#else
+"a        c #FFFFFF",
+"X        c #000000", // X11 cursor is traditionally black
+#endif
+"aa.........",
+"aXa........",
+"aXXa.......",
+"aXXXa......",
+"aXXXXa.....",
+"aXXXXXa....",
+"aXXXXXXa...",
+"aXXXXXXXa..",
+"aXXXXXXXXa.",
+"aXXXXXXXXXa",
+"aXXXXXXaaaa",
+"aXXXaXXa...",
+"aXXaaXXa...",
+"aXa..aXXa..",
+"aa...aXXa..",
+"a.....aXXa.",
+"......aXXa.",
+".......aXXa",
+".......aXXa",
+"........aa."};
+
+#ifdef Q_WS_WIN
+/* XPM */
+static const char * const ignore_xpm[] = {
+"24 30 3 1",
+".        c None",
+"a        c #000000",
+"X        c #FFFFFF",
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa.....XXXX.....",
+".......aXXa..XXaaaaXX...",
+".......aXXa.XaaaaaaaaX..",
+"........aa.XaaaXXXXaaaX.",
+"...........XaaaaX..XaaX.",
+"..........XaaXaaaX..XaaX",
+"..........XaaXXaaaX.XaaX",
+"..........XaaX.XaaaXXaaX",
+"..........XaaX..XaaaXaaX",
+"...........XaaX..XaaaaX.",
+"...........XaaaXXXXaaaX.",
+"............XaaaaaaaaX..",
+".............XXaaaaXX...",
+"...............XXXX....."};
+#endif
+
+/* XPM */
+static const char * const copy_xpm[] = {
+"24 30 3 1",
+".        c None",
+"a        c #000000",
+"X        c #FFFFFF",
+#if defined(Q_WS_WIN) // Windows cursor is traditionally white
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa..............",
+".......aXXa.............",
+".......aXXa.............",
+"........aa...aaaaaaaaaaa",
+#else
+"XX......................",
+"XaX.....................",
+"XaaX....................",
+"XaaaX...................",
+"XaaaaX..................",
+"XaaaaaX.................",
+"XaaaaaaX................",
+"XaaaaaaaX...............",
+"XaaaaaaaaX..............",
+"XaaaaaaaaaX.............",
+"XaaaaaaXXXX.............",
+"XaaaXaaX................",
+"XaaXXaaX................",
+"XaX..XaaX...............",
+"XX...XaaX...............",
+"X.....XaaX..............",
+"......XaaX..............",
+".......XaaX.............",
+".......XaaX.............",
+"........XX...aaaaaaaaaaa",
+#endif
+".............aXXXXXXXXXa",
+".............aXXXXXXXXXa",
+".............aXXXXaXXXXa",
+".............aXXXXaXXXXa",
+".............aXXaaaaaXXa",
+".............aXXXXaXXXXa",
+".............aXXXXaXXXXa",
+".............aXXXXXXXXXa",
+".............aXXXXXXXXXa",
+".............aaaaaaaaaaa"};
+
+/* XPM */
+static const char * const link_xpm[] = {
+"24 30 3 1",
+".        c None",
+"a        c #000000",
+"X        c #FFFFFF",
+#if defined(Q_WS_WIN) // Windows cursor is traditionally white
+"aa......................",
+"aXa.....................",
+"aXXa....................",
+"aXXXa...................",
+"aXXXXa..................",
+"aXXXXXa.................",
+"aXXXXXXa................",
+"aXXXXXXXa...............",
+"aXXXXXXXXa..............",
+"aXXXXXXXXXa.............",
+"aXXXXXXaaaa.............",
+"aXXXaXXa................",
+"aXXaaXXa................",
+"aXa..aXXa...............",
+"aa...aXXa...............",
+"a.....aXXa..............",
+"......aXXa..............",
+".......aXXa.............",
+".......aXXa.............",
+"........aa...aaaaaaaaaaa",
+#else
+"XX......................",
+"XaX.....................",
+"XaaX....................",
+"XaaaX...................",
+"XaaaaX..................",
+"XaaaaaX.................",
+"XaaaaaaX................",
+"XaaaaaaaX...............",
+"XaaaaaaaaX..............",
+"XaaaaaaaaaX.............",
+"XaaaaaaXXXX.............",
+"XaaaXaaX................",
+"XaaXXaaX................",
+"XaX..XaaX...............",
+"XX...XaaX...............",
+"X.....XaaX..............",
+"......XaaX..............",
+".......XaaX.............",
+".......XaaX.............",
+"........XX...aaaaaaaaaaa",
+#endif
+".............aXXXXXXXXXa",
+".............aXXXaaaaXXa",
+".............aXXXXaaaXXa",
+".............aXXXaaaaXXa",
+".............aXXaaaXaXXa",
+".............aXXaaXXXXXa",
+".............aXXaXXXXXXa",
+".............aXXXaXXXXXa",
+".............aXXXXXXXXXa",
+".............aaaaaaaaaaa"};
+
+#ifndef QT_NO_DRAGANDDROP
+
+//#define QDND_DEBUG
+
+#ifdef QDND_DEBUG
+QString dragActionsToString(Qt::DropActions actions)
+{
+    QString str;
+    if (actions == Qt::IgnoreAction) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += "IgnoreAction";
+    }
+    if (actions & Qt::LinkAction) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += "LinkAction";
+    }
+    if (actions & Qt::CopyAction) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += "CopyAction";
+    }
+    if (actions & Qt::MoveAction) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += "MoveAction";
+    }
+    if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += "TargetMoveAction";
+    }
+    return str;
+}
+
+QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies)
+{
+    QString str;
+    if (moderfies & Qt::ControlModifier) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += Qt::ControlModifier;
+    }
+    if (moderfies & Qt::AltModifier) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += Qt::AltModifier;
+    }
+    if (moderfies & Qt::ShiftModifier) {
+        if (!str.isEmpty())
+            str += " | ";
+        str += Qt::ShiftModifier;
+    }
+    return str;
+}
+#endif
+
+
+// the universe's only drag manager
+QDragManager *QDragManager::instance = 0;
+
+
+QDragManager::QDragManager()
+    : QObject(qApp)
+{
+    Q_ASSERT(!instance);
+
+#ifdef Q_WS_WIN
+    n_cursor = 4;
+#else
+    n_cursor = 3;
+#endif
+
+#ifdef Q_WS_QWS
+    currentActionForOverrideCursor = Qt::IgnoreAction;
+#endif
+    pm_cursor = new QPixmap[n_cursor];
+    pm_cursor[0] = QPixmap((const char **)move_xpm);
+    pm_cursor[1] = QPixmap((const char **)copy_xpm);
+    pm_cursor[2] = QPixmap((const char **)link_xpm);
+#ifdef Q_WS_WIN
+    pm_cursor[3] = QPixmap((const char **)ignore_xpm);
+#endif
+    object = 0;
+    beingCancelled = false;
+    restoreCursor = false;
+    willDrop = false;
+    eventLoop = 0;
+    dropData = new QDropData();
+    currentDropTarget = 0;
+#ifdef Q_WS_X11
+    xdndMimeTransferedPixmapIndex = 0;
+#endif
+}
+
+
+QDragManager::~QDragManager()
+{
+#ifndef QT_NO_CURSOR
+    if (restoreCursor)
+        QApplication::restoreOverrideCursor();
+#endif
+    instance = 0;
+    delete [] pm_cursor;
+    delete dropData;
+}
+
+QDragManager *QDragManager::self()
+{
+    if (!instance && !QApplication::closingDown())
+        instance = new QDragManager;
+    return instance;
+}
+
+QPixmap QDragManager::dragCursor(Qt::DropAction action) const
+{
+    QDragPrivate * d = dragPrivate();
+    if (d && d->customCursors.contains(action))
+        return d->customCursors[action];
+    else if (action == Qt::MoveAction)
+        return pm_cursor[0];
+    else if (action == Qt::CopyAction)
+        return pm_cursor[1];
+    else if (action == Qt::LinkAction)
+        return pm_cursor[2];
+#ifdef Q_WS_WIN
+    else if (action == Qt::IgnoreAction)
+        return pm_cursor[3];
+#endif
+    return QPixmap();
+}
+
+bool QDragManager::hasCustomDragCursors() const
+{
+    QDragPrivate * d = dragPrivate();
+    return d && !d->customCursors.isEmpty();
+}
+
+Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions,
+                                           Qt::KeyboardModifiers modifiers) const
+{
+#ifdef QDND_DEBUG
+    qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)");
+    qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1());
+#endif
+
+    QDragPrivate *d = dragPrivate();
+    Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction;
+
+    if (defaultAction == Qt::IgnoreAction) {
+        //This means that the drag was initiated by QDrag::start and we need to
+        //preserve the old behavior
+#ifdef Q_WS_MAC
+        defaultAction = Qt::MoveAction;
+#else
+        defaultAction = Qt::CopyAction;
+#endif
+    }
+
+#ifdef Q_WS_MAC
+    if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
+        defaultAction = Qt::LinkAction;
+    else if (modifiers & Qt::AltModifier)
+        defaultAction = Qt::CopyAction;
+    else if (modifiers & Qt::ControlModifier)
+        defaultAction = Qt::MoveAction;
+#else
+    if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier)
+        defaultAction = Qt::LinkAction;
+    else if (modifiers & Qt::ControlModifier)
+        defaultAction = Qt::CopyAction;
+    else if (modifiers & Qt::ShiftModifier)
+        defaultAction = Qt::MoveAction;
+    else if (modifiers & Qt::AltModifier)
+        defaultAction = Qt::LinkAction;
+#endif
+
+    // if the object is set take the list of possibles from it
+    if (object)
+        possibleActions = object->d_func()->possible_actions;
+
+#ifdef QDND_DEBUG
+    qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
+#endif
+
+    // Check if the action determined is allowed
+    if (!(possibleActions & defaultAction)) {
+        if (possibleActions & Qt::CopyAction)
+            defaultAction = Qt::CopyAction;
+        else if (possibleActions & Qt::MoveAction)
+            defaultAction = Qt::MoveAction;
+        else if (possibleActions & Qt::LinkAction)
+            defaultAction = Qt::LinkAction;
+        else
+            defaultAction = Qt::IgnoreAction;
+    }
+
+#ifdef QDND_DEBUG
+    qDebug("default action : %s", dragActionsToString(defaultAction).latin1());
+#endif
+
+    return defaultAction;
+}
+
+void QDragManager::setCurrentTarget(QWidget *target, bool dropped)
+{
+    if (currentDropTarget == target)
+        return;
+
+    currentDropTarget = target;
+    if (!dropped && object) {
+        object->d_func()->target = target;
+        emit object->targetChanged(target);
+    }
+
+}
+
+QWidget *QDragManager::currentTarget()
+{
+    return currentDropTarget;
+}
+
+#endif
+
+QDropData::QDropData()
+    : QInternalMimeData()
+{
+}
+
+QDropData::~QDropData()
+{
+}
+#endif // QT_NO_DRAGANDDROP
+
+#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD))
+
+static QStringList imageReadMimeFormats()
+{
+    QStringList formats;
+    QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
+    for (int i = 0; i < imageFormats.size(); ++i) {
+        QString format = QLatin1String("image/");
+        format += QString::fromLatin1(imageFormats.at(i).toLower());
+        formats.append(format);
+    }
+
+    //put png at the front because it is best
+    int pngIndex = formats.indexOf(QLatin1String("image/png"));
+    if (pngIndex != -1 && pngIndex != 0)
+        formats.move(pngIndex, 0);
+
+    return formats;
+}
+
+
+static QStringList imageWriteMimeFormats()
+{
+    QStringList formats;
+    QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
+    for (int i = 0; i < imageFormats.size(); ++i) {
+        QString format = QLatin1String("image/");
+        format += QString::fromLatin1(imageFormats.at(i).toLower());
+        formats.append(format);
+    }
+
+    //put png at the front because it is best
+    int pngIndex = formats.indexOf(QLatin1String("image/png"));
+    if (pngIndex != -1 && pngIndex != 0)
+        formats.move(pngIndex, 0);
+
+    return formats;
+}
+
+QInternalMimeData::QInternalMimeData()
+    : QMimeData()
+{
+}
+
+QInternalMimeData::~QInternalMimeData()
+{
+}
+
+bool QInternalMimeData::hasFormat(const QString &mimeType) const
+{
+    bool foundFormat = hasFormat_sys(mimeType);
+    if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) {
+        QStringList imageFormats = imageReadMimeFormats();
+        for (int i = 0; i < imageFormats.size(); ++i) {
+            if ((foundFormat = hasFormat_sys(imageFormats.at(i))))
+                break;
+        }
+    }
+    return foundFormat;
+}
+
+QStringList QInternalMimeData::formats() const
+{
+    QStringList realFormats = formats_sys();
+    if (!realFormats.contains(QLatin1String("application/x-qt-image"))) {
+        QStringList imageFormats = imageReadMimeFormats();
+        for (int i = 0; i < imageFormats.size(); ++i) {
+            if (realFormats.contains(imageFormats.at(i))) {
+                realFormats += QLatin1String("application/x-qt-image");
+                break;
+            }
+        }
+    }
+    return realFormats;
+}
+
+QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
+{
+    QVariant data = retrieveData_sys(mimeType, type);
+    if (mimeType == QLatin1String("application/x-qt-image")) {
+        if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) {
+            // try to find an image
+            QStringList imageFormats = imageReadMimeFormats();
+            for (int i = 0; i < imageFormats.size(); ++i) {
+                data = retrieveData_sys(imageFormats.at(i), type);
+                if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty()))
+                    continue;
+                break;
+            }
+        }
+        // we wanted some image type, but all we got was a byte array. Convert it to an image.
+        if (data.type() == QVariant::ByteArray
+            && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap))
+            data = QImage::fromData(data.toByteArray());
+
+    } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) {
+        QColor c;
+        QByteArray ba = data.toByteArray();
+        if (ba.size() == 8) {
+            ushort * colBuf = (ushort *)ba.data();
+            c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF),
+                      qreal(colBuf[1]) / qreal(0xFFFF),
+                      qreal(colBuf[2]) / qreal(0xFFFF),
+                      qreal(colBuf[3]) / qreal(0xFFFF));
+            data = c;
+        } else {
+            qWarning("Qt: Invalid color format");
+        }
+    } else if (data.type() != type && data.type() == QVariant::ByteArray) {
+        // try to use mime data's internal conversion stuf.
+        QInternalMimeData *that = const_cast<QInternalMimeData *>(this);
+        that->setData(mimeType, data.toByteArray());
+        data = QMimeData::retrieveData(mimeType, type);
+        that->clear();
+    }
+    return data;
+}
+
+bool QInternalMimeData::canReadData(const QString &mimeType)
+{
+    return imageReadMimeFormats().contains(mimeType);
+}
+
+// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core.
+QStringList QInternalMimeData::formatsHelper(const QMimeData *data)
+{
+    QStringList realFormats = data->formats();
+    if (realFormats.contains(QLatin1String("application/x-qt-image"))) {
+        // add all supported image formats
+        QStringList imageFormats = imageWriteMimeFormats();
+        for (int i = 0; i < imageFormats.size(); ++i) {
+            if (!realFormats.contains(imageFormats.at(i)))
+                realFormats.append(imageFormats.at(i));
+        }
+    }
+    return realFormats;
+}
+
+bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data)
+{
+
+    bool foundFormat = data->hasFormat(mimeType);
+    if (!foundFormat) {
+        if (mimeType == QLatin1String("application/x-qt-image")) {
+            // check all supported image formats
+            QStringList imageFormats = imageWriteMimeFormats();
+            for (int i = 0; i < imageFormats.size(); ++i) {
+                if ((foundFormat = data->hasFormat(imageFormats.at(i))))
+                    break;
+            }
+        } else if (mimeType.startsWith(QLatin1String("image/"))) {
+            return data->hasImage() && imageWriteMimeFormats().contains(mimeType);
+        }
+    }
+    return foundFormat;
+}
+
+QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data)
+{
+    QByteArray ba;
+    if (mimeType == QLatin1String("application/x-color")) {
+        /* QMimeData can only provide colors as QColor or the name
+           of a color as a QByteArray or a QString. So we need to do
+           the conversion to application/x-color here.
+           The application/x-color format is :
+           type: application/x-color
+           format: 16
+           data[0]: red
+           data[1]: green
+           data[2]: blue
+           data[3]: opacity
+        */
+        ba.resize(8);
+        ushort * colBuf = (ushort *)ba.data();
+        QColor c = qvariant_cast<QColor>(data->colorData());
+        colBuf[0] = ushort(c.redF() * 0xFFFF);
+        colBuf[1] = ushort(c.greenF() * 0xFFFF);
+        colBuf[2] = ushort(c.blueF() * 0xFFFF);
+        colBuf[3] = ushort(c.alphaF() * 0xFFFF);
+    } else {
+        ba = data->data(mimeType);
+        if (ba.isEmpty()) {
+            if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) {
+                QImage image = qvariant_cast<QImage>(data->imageData());
+                QBuffer buf(&ba);
+                buf.open(QBuffer::WriteOnly);
+                // would there not be PNG ??
+                image.save(&buf, "PNG");
+            } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) {
+                QImage image = qvariant_cast<QImage>(data->imageData());
+                QBuffer buf(&ba);
+                buf.open(QBuffer::WriteOnly);
+                image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
+            }
+        }
+    }
+    return ba;
+}
+
+#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD
+
+QT_END_NAMESPACE