src/gui/kernel/qdnd_mac.mm
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/kernel/qdnd_mac.mm	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,749 @@
+/****************************************************************************
+**
+** 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 "qapplication.h"
+#ifndef QT_NO_DRAGANDDROP
+#include "qbitmap.h"
+#include "qcursor.h"
+#include "qevent.h"
+#include "qpainter.h"
+#include "qurl.h"
+#include "qwidget.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include <stdlib.h>
+#include <string.h>
+#ifndef QT_NO_ACCESSIBILITY
+# include "qaccessible.h"
+#endif
+
+#include <private/qapplication_p.h>
+#include <private/qwidget_p.h>
+#include <private/qdnd_p.h>
+#include <private/qt_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+QMacDndAnswerRecord qt_mac_dnd_answer_rec; 
+
+/*****************************************************************************
+  QDnD debug facilities
+ *****************************************************************************/
+//#define DEBUG_DRAG_EVENTS
+//#define DEBUG_DRAG_PROMISES
+
+/*****************************************************************************
+  QDnD globals
+ *****************************************************************************/
+bool qt_mac_in_drag = false;
+#ifndef QT_MAC_USE_COCOA
+static DragReference qt_mac_current_dragRef = 0;
+#endif
+
+/*****************************************************************************
+  Externals
+ *****************************************************************************/
+extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp
+extern uint qGlobalPostedEventsCount(); //qapplication.cpp
+extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
+/*****************************************************************************
+  QDnD utility functions
+ *****************************************************************************/
+
+//default pixmap
+static const int default_pm_hotx = -2;
+static const int default_pm_hoty = -16;
+#ifndef QT_MAC_USE_COCOA
+static const char * const default_pm[] = {
+    "13 9 3 1",
+    ".      c None",
+    "       c #000000",
+    "X      c #FFFFFF",
+    "X X X X X X X",
+    " X X X X X X ",
+    "X ......... X",
+    " X.........X ",
+    "X ......... X",
+    " X.........X ",
+    "X ......... X",
+    " X X X X X X ",
+    "X X X X X X X",
+};
+#endif
+
+//action management
+#ifdef DEBUG_DRAG_EVENTS
+# define MAP_MAC_ENUM(x) x, #x
+#else
+# define MAP_MAC_ENUM(x) x
+#endif
+struct mac_enum_mapper
+{
+    int mac_code;
+    int qt_code;
+#ifdef DEBUG_DRAG_EVENTS
+    char *qt_desc;
+#endif
+};
+#ifndef QT_MAC_USE_COCOA
+static mac_enum_mapper dnd_action_symbols[] = {
+    { kDragActionAlias, MAP_MAC_ENUM(Qt::LinkAction) },
+    { kDragActionMove, MAP_MAC_ENUM(Qt::MoveAction) },
+    { kDragActionGeneric, MAP_MAC_ENUM(Qt::CopyAction) },
+    { kDragActionCopy, MAP_MAC_ENUM(Qt::CopyAction) },
+    { 0, MAP_MAC_ENUM(0) }
+};
+static DragActions qt_mac_dnd_map_qt_actions(Qt::DropActions qActions)
+{
+    DragActions ret = kDragActionNothing;
+    for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+        if(qActions & dnd_action_symbols[i].qt_code)
+            ret |= dnd_action_symbols[i].mac_code;
+    }
+    return ret;
+}
+static Qt::DropActions qt_mac_dnd_map_mac_actions(DragActions macActions)
+{
+#ifdef DEBUG_DRAG_EVENTS
+    qDebug("Converting DND ActionList: 0x%lx", macActions);
+#endif
+    Qt::DropActions ret = Qt::IgnoreAction;
+    for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+#ifdef DEBUG_DRAG_EVENTS
+        qDebug(" %d) [%s] : %s", i, dnd_action_symbols[i].qt_desc,
+                (macActions & dnd_action_symbols[i].mac_code) ? "true" : "false");
+#endif
+        if(macActions & dnd_action_symbols[i].mac_code)
+            ret |= Qt::DropAction(dnd_action_symbols[i].qt_code);
+    }
+    return ret;
+}
+static Qt::DropAction qt_mac_dnd_map_mac_default_action(DragActions macActions)
+{
+    static Qt::DropAction preferred_actions[] = { Qt::CopyAction, Qt::LinkAction, //in order
+        Qt::MoveAction, Qt::IgnoreAction };
+    Qt::DropAction ret = Qt::IgnoreAction;
+    const Qt::DropActions qtActions = qt_mac_dnd_map_mac_actions(macActions);
+    for(int i = 0; preferred_actions[i] != Qt::IgnoreAction; ++i) {
+        if(qtActions & preferred_actions[i]) {
+            ret = preferred_actions[i];
+            break;
+        }
+    }
+#ifdef DEBUG_DRAG_EVENTS
+    for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
+        if(dnd_action_symbols[i].qt_code == ret) {
+            qDebug("Got default action: %s [0x%lx]", dnd_action_symbols[i].qt_desc, macActions);
+            break;
+        }
+    }
+#endif
+    return ret;
+}
+static void qt_mac_dnd_update_action(DragReference dragRef) {
+    SInt16 modifiers;
+    GetDragModifiers(dragRef, &modifiers, 0, 0);
+    qt_mac_send_modifiers_changed(modifiers, qApp);
+
+    Qt::DropAction qtAction = Qt::IgnoreAction;
+    {
+        DragActions macAllowed = kDragActionNothing;
+        GetDragDropAction(dragRef, &macAllowed);
+        Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(macAllowed);
+        qtAction = QDragManager::self()->defaultAction(qtAllowed, QApplication::keyboardModifiers());
+#if 1
+        if(!(qtAllowed & qtAction))
+            qtAction = qt_mac_dnd_map_mac_default_action(macAllowed);
+#endif
+    }
+    DragActions macAction = qt_mac_dnd_map_qt_actions(qtAction);
+
+    DragActions currentActions;
+    GetDragDropAction(dragRef, &currentActions);
+    if(currentActions != macAction) {
+        SetDragDropAction(dragRef, macAction);
+        QDragManager::self()->emitActionChanged(qtAction);
+    }
+}
+#endif // !QT_MAC_USE_COCOA
+
+/*****************************************************************************
+  DnD functions
+ *****************************************************************************/
+bool QDropData::hasFormat_sys(const QString &mime) const
+{
+#ifndef QT_MAC_USE_COCOA
+    OSPasteboardRef board;
+    if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return false;
+    }
+    return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mime);
+#else
+    Q_UNUSED(mime);
+    return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+QVariant QDropData::retrieveData_sys(const QString &mime, QVariant::Type type) const
+{
+#ifndef QT_MAC_USE_COCOA
+    OSPasteboardRef board;
+    if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return QVariant();
+    }
+    return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mime, type);
+#else
+    Q_UNUSED(mime);
+    Q_UNUSED(type);
+    return QVariant();
+#endif // !QT_MAC_USE_COCOA
+}
+
+QStringList QDropData::formats_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+    OSPasteboardRef board;
+    if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return QStringList();
+    }
+    return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+#else
+    return QStringList();
+#endif
+}
+
+void QDragManager::timerEvent(QTimerEvent*)
+{
+}
+
+bool QDragManager::eventFilter(QObject *, QEvent *)
+{
+    return false;
+}
+
+void QDragManager::updateCursor()
+{
+}
+
+void QDragManager::cancel(bool)
+{
+    if(object) {
+        beingCancelled = true;
+        object = 0;
+    }
+#ifndef QT_NO_ACCESSIBILITY
+    QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
+#endif
+}
+
+void QDragManager::move(const QPoint &)
+{
+}
+
+void QDragManager::drop()
+{
+}
+
+/**
+    If a drop action is already set on the carbon event
+    (from e.g. an earlier enter event), we insert the same
+    action on the new Qt event that has yet to be sendt.
+*/
+static inline bool qt_mac_set_existing_drop_action(const DragRef &dragRef, QDropEvent &event)
+{
+#ifndef QT_MAC_USE_COCOA
+    DragActions currentAction = kDragActionNothing;        
+    OSStatus err = GetDragDropAction(dragRef, &currentAction);
+    if (err == noErr && currentAction != kDragActionNothing) {
+        // This looks a bit evil, but we only ever set one action, so it's OK.
+        event.setDropAction(Qt::DropAction(int(qt_mac_dnd_map_mac_actions(currentAction))));
+        return true;
+    }
+#else
+    Q_UNUSED(dragRef);
+    Q_UNUSED(event);
+#endif
+    return false;
+}
+
+/**
+    If an answer rect has been set on the event (after being sent
+    to the global event processor), we store that rect so we can
+    check if the mouse is in the same area upon next drag move event. 
+*/
+void qt_mac_copy_answer_rect(const QDragMoveEvent &event)
+{
+    if (!event.answerRect().isEmpty()) {
+        qt_mac_dnd_answer_rec.rect = event.answerRect();
+        qt_mac_dnd_answer_rec.buttons = event.mouseButtons();
+        qt_mac_dnd_answer_rec.modifiers = event.keyboardModifiers();
+        qt_mac_dnd_answer_rec.lastAction = event.dropAction();
+    }    
+}
+
+bool qt_mac_mouse_inside_answer_rect(QPoint mouse)
+{
+    if (!qt_mac_dnd_answer_rec.rect.isEmpty()
+            && qt_mac_dnd_answer_rec.rect.contains(mouse)
+            && QApplication::mouseButtons() == qt_mac_dnd_answer_rec.buttons
+            && QApplication::keyboardModifiers() == qt_mac_dnd_answer_rec.modifiers)
+        return true;
+    else
+        return false;
+}
+
+bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef)
+{
+#ifndef QT_MAC_USE_COCOA
+    Q_Q(QWidget);
+    qt_mac_current_dragRef = dragRef;
+    if (kind != kEventControlDragLeave)
+        qt_mac_dnd_update_action(dragRef);
+
+    Point mouse;
+    GetDragMouse(dragRef, &mouse, 0L);
+    if(!mouse.h && !mouse.v)
+        GetGlobalMouse(&mouse);
+
+    if(QApplicationPrivate::modalState()) {
+        for(QWidget *modal = q; modal; modal = modal->parentWidget()) {
+            if(modal->isWindow()) {
+                if(modal != QApplication::activeModalWidget())
+                    return noErr;
+                break;
+            }
+        }
+    }
+
+    //lookup the possible actions
+    DragActions allowed = kDragActionNothing;
+    GetDragAllowableActions(dragRef, &allowed);
+    Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(allowed);
+
+    //lookup the source dragAccepted 
+    QMimeData *dropdata = QDragManager::self()->dropData;
+    if(QDragManager::self()->source())
+        dropdata = QDragManager::self()->dragPrivate()->data;
+
+    // 'interrestedInDrag' should end up being 'true' if a later drop
+    // will be accepted by the widget for the current mouse position 
+    bool interrestedInDrag = true;
+
+    //Dispatch events
+    if (kind == kEventControlDragWithin) {
+        if (qt_mac_mouse_inside_answer_rect(q->mapFromGlobal(QPoint(mouse.h, mouse.v))))
+            return qt_mac_dnd_answer_rec.lastAction == Qt::IgnoreAction;
+        else
+            qt_mac_dnd_answer_rec.clear();
+        
+        QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+                QApplication::mouseButtons(), QApplication::keyboardModifiers());
+                
+        // Accept the event by default if a
+        // drag action exists on the carbon event
+        if (qt_mac_set_existing_drop_action(dragRef, qDMEvent))
+            qDMEvent.accept();
+            
+        QApplication::sendEvent(q, &qDMEvent);
+        if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+            interrestedInDrag = false;
+
+        qt_mac_copy_answer_rect(qDMEvent);
+        SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+        
+    } else if (kind == kEventControlDragEnter) {
+        qt_mac_dnd_answer_rec.clear();
+        
+        QDragEnterEvent qDEEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+                QApplication::mouseButtons(), QApplication::keyboardModifiers());
+        qt_mac_set_existing_drop_action(dragRef, qDEEvent);                         
+        QApplication::sendEvent(q, &qDEEvent);
+        SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDEEvent.dropAction()));
+        
+        if (!qDEEvent.isAccepted())
+            // The widget is simply not interested in this
+            // drag. So tell carbon this by returning 'false'. We will then
+            // not receive any further move, drop or leave events for this widget.
+            return false;
+        else {
+            // Documentation states that a drag move event is sent immediately after
+            // a drag enter event. So we do that. This will honor widgets overriding
+            // 'dragMoveEvent' only, and not 'dragEnterEvent' 
+            QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+                    QApplication::mouseButtons(), QApplication::keyboardModifiers());
+            qDMEvent.accept(); // accept by default, since enter event was accepted.
+            qDMEvent.setDropAction(qDEEvent.dropAction());         
+            QApplication::sendEvent(q, &qDMEvent);
+            if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
+                interrestedInDrag = false;
+            
+            qt_mac_copy_answer_rect(qDMEvent);           
+            SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
+        }
+        
+    } else if(kind == kEventControlDragLeave) {
+        QDragLeaveEvent de;
+        QApplication::sendEvent(q, &de);
+    } else if(kind == kEventControlDragReceive) {
+        QDropEvent de(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
+                      QApplication::mouseButtons(), QApplication::keyboardModifiers());
+        if(QDragManager::self()->object)
+            QDragManager::self()->dragPrivate()->target = q;
+        QApplication::sendEvent(q, &de);
+        if(!de.isAccepted()) {
+            interrestedInDrag = false;
+            SetDragDropAction(dragRef, kDragActionNothing);
+        } else {
+            if(QDragManager::self()->object)
+                QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
+            SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(de.dropAction()));
+        }
+    }
+
+#ifdef DEBUG_DRAG_EVENTS
+    {
+        const char *desc = 0;
+        switch(kind) {
+        case kEventControlDragWithin: desc = "DragMove"; break;
+        case kEventControlDragEnter: desc = "DragEnter"; break;
+        case kEventControlDragLeave: desc = "DragLeave"; break;
+        case kEventControlDragReceive: desc = "DragDrop"; break;
+        }
+        if(desc) {
+            QPoint pos(q->mapFromGlobal(QPoint(mouse.h, mouse.v)));
+            qDebug("Sending <%s>(%d, %d) event to %p(%s::%s) [%d] (%p)",
+                   desc, pos.x(), pos.y(), q, q->metaObject()->className(),
+                   q->objectName().toLocal8Bit().constData(), ret, dragRef);
+        }
+    }
+#endif
+
+    //set the cursor
+    bool found_cursor = false;
+    if(kind == kEventControlDragWithin || kind == kEventControlDragEnter) {
+        ThemeCursor cursor = kThemeNotAllowedCursor;
+        found_cursor = true;
+        if (interrestedInDrag) {
+            DragActions action = kDragActionNothing;
+            GetDragDropAction(dragRef, &action);
+            switch(qt_mac_dnd_map_mac_default_action(action)) {
+            case Qt::IgnoreAction:
+                cursor = kThemeNotAllowedCursor;
+                break;
+            case Qt::MoveAction:
+                cursor = kThemeArrowCursor;
+                break;
+            case Qt::CopyAction:
+                cursor = kThemeCopyArrowCursor;
+                break;
+            case Qt::LinkAction:
+                cursor = kThemeAliasArrowCursor;
+                break;
+            default:
+                cursor = kThemeNotAllowedCursor;
+                break;
+            }
+        }
+        SetThemeCursor(cursor);
+    }
+    if(found_cursor) {
+        qt_mac_set_cursor(0, QPoint()); //just use our's
+    } else {
+        QCursor cursor(Qt::ArrowCursor);
+        if(qApp && qApp->overrideCursor()) {
+            cursor = *qApp->overrideCursor();
+        } else if(q) {
+            for(QWidget *p = q; p; p = p->parentWidget()) {
+                if(p->isEnabled() && p->testAttribute(Qt::WA_SetCursor)) {
+                    cursor = p->cursor();
+                    break;
+                }
+            }
+        }
+        qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v));
+    }
+
+    //idle things
+    if(qGlobalPostedEventsCount()) {
+        QApplication::sendPostedEvents();
+        QApplication::flush();
+    }
+    
+    // If this was not a drop, tell carbon that we will be interresed in receiving more
+    // events for the current drag. We do that by returning true. 
+    if (kind == kEventControlDragReceive)
+        return interrestedInDrag;
+    else
+        return true;
+#else
+    Q_UNUSED(kind);
+    Q_UNUSED(dragRef);
+    return false;
+#endif // !QT_MAC_USE_COCOA
+}
+
+#ifndef QT_MAC_USE_COCOA
+Qt::DropAction QDragManager::drag(QDrag *o)
+{
+  
+    if(qt_mac_in_drag) {     //just make sure..
+        qWarning("Qt: Internal error: WH0A, unexpected condition reached");
+        return Qt::IgnoreAction;
+    }
+    if(object == o)
+        return Qt::IgnoreAction;
+    /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
+       so we just bail early to prevent it */
+    if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
+        return Qt::IgnoreAction;
+
+    if(object) {
+        dragPrivate()->source->removeEventFilter(this);
+        cancel();
+        beingCancelled = false;
+    }
+
+    object = o;
+    dragPrivate()->target = 0;
+
+#ifndef QT_NO_ACCESSIBILITY
+    QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
+#endif
+
+    //setup the data
+    QMacPasteboard dragBoard(QMacPasteboardMime::MIME_DND);
+    dragBoard.setMimeData(dragPrivate()->data);
+
+    //create the drag
+    OSErr result;
+    DragRef dragRef;
+    if((result = NewDragWithPasteboard(dragBoard.pasteBoard(), &dragRef)))
+        return Qt::IgnoreAction;
+    //setup the actions
+    DragActions possibleActions = qt_mac_dnd_map_qt_actions(dragPrivate()->possible_actions);
+    SetDragAllowableActions(dragRef, //local
+                            possibleActions,
+                            true);
+    SetDragAllowableActions(dragRef, //remote (same as local)
+                            possibleActions,
+                            false);
+
+
+    QPoint hotspot;
+    QPixmap pix = dragPrivate()->pixmap;
+    if(pix.isNull()) {
+        if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
+            //get the string
+            QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
+                                                : dragPrivate()->data->urls().first().toString();
+            if(s.length() > 26)
+                s = s.left(23) + QChar(0x2026);
+            if(!s.isEmpty()) {
+                //draw it
+                QFont f(qApp->font());
+                f.setPointSize(12);
+                QFontMetrics fm(f);
+                const int width = fm.width(s);
+                const int height = fm.height();
+                if(width > 0 && height > 0) {
+                    QPixmap tmp(width, height);
+                    QPainter p(&tmp);
+                    p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
+                    p.setPen(Qt::color1);
+                    p.setFont(f);
+                    p.drawText(0, fm.ascent(), s);
+                    p.end();
+                    //save it
+                    pix = tmp;
+                    hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
+                }
+            }
+        } else {
+            pix = QPixmap(default_pm);
+            hotspot = QPoint(default_pm_hotx, default_pm_hoty);
+        }
+    } else {
+        hotspot = dragPrivate()->hotspot;
+    }
+
+    //so we must fake an event
+    EventRecord fakeEvent;
+    GetGlobalMouse(&(fakeEvent.where));
+    fakeEvent.message = 0;
+    fakeEvent.what = mouseDown;
+    fakeEvent.when = EventTimeToTicks(GetCurrentEventTime());
+    fakeEvent.modifiers = GetCurrentKeyModifiers();
+    if(GetCurrentEventButtonState() & 2)
+        fakeEvent.modifiers |= controlKey;
+
+    //find the hotspot in relation to the pixmap
+    Point boundsPoint;
+    boundsPoint.h = fakeEvent.where.h - hotspot.x();
+    boundsPoint.v = fakeEvent.where.v - hotspot.y();
+    Rect boundsRect;
+    SetRect(&boundsRect, boundsPoint.h, boundsPoint.v, boundsPoint.h + pix.width(), boundsPoint.v + pix.height());
+
+    //set the drag image
+    QRegion dragRegion(boundsPoint.h, boundsPoint.v, pix.width(), pix.height()), pixRegion;
+    if(!pix.isNull()) {
+        HIPoint hipoint = { -hotspot.x(), -hotspot.y() };
+        CGImageRef img = (CGImageRef)pix.macCGHandle();
+        SetDragImageWithCGImage(dragRef, img, &hipoint, 0);
+        CGImageRelease(img);
+    }
+
+    SetDragItemBounds(dragRef, (ItemReference)1 , &boundsRect);
+    { //do the drag
+        qt_mac_in_drag = true;
+        qt_mac_dnd_update_action(dragRef);
+        result = TrackDrag(dragRef, &fakeEvent, QMacSmartQuickDrawRegion(dragRegion.toQDRgn()));
+        qt_mac_in_drag = false;
+    }
+    object = 0;
+    if(result == noErr) {
+        // Check if the receiver points us to
+        // a file location. If so, we need to do
+        // the file copy/move ourselves:
+        QCFType<CFURLRef> pasteLocation = 0;
+        PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
+        if (pasteLocation){
+            Qt::DropAction action = o->d_func()->defaultDropAction;
+            if (action == Qt::IgnoreAction){
+                if (o->d_func()->possible_actions & Qt::MoveAction)
+                    action = Qt::MoveAction;
+                else if (o->d_func()->possible_actions & Qt::CopyAction)
+                    action = Qt::CopyAction;
+                
+            }
+            bool atleastOne = false;
+            QList<QUrl> urls = o->mimeData()->urls();
+            for (int i = 0; i < urls.size(); ++i){
+                QUrl fromUrl = urls.at(i);
+                QString filename = QFileInfo(fromUrl.path()).fileName();
+                QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
+                if (action == Qt::MoveAction){
+                    if (QFile::rename(fromUrl.path(), toUrl.path()))
+                        atleastOne = true;
+                } else if (action == Qt::CopyAction){
+                    if (QFile::copy(fromUrl.path(), toUrl.path()))
+                        atleastOne = true;
+                }
+            }
+            if (atleastOne){
+                DisposeDrag(dragRef);
+                return action;
+            }
+        }
+
+        DragActions ret = kDragActionNothing;
+        GetDragDropAction(dragRef, &ret);
+        DisposeDrag(dragRef); //cleanup
+        return qt_mac_dnd_map_mac_default_action(ret);
+    }
+    DisposeDrag(dragRef); //cleanup
+    return Qt::IgnoreAction;
+}
+#endif
+
+void QDragManager::updatePixmap()
+{
+}
+
+QCocoaDropData::QCocoaDropData(CFStringRef pasteboard)
+    : QInternalMimeData()
+{
+    NSString* pasteboardName = (NSString*)pasteboard;
+    [pasteboardName retain];
+    dropPasteboard = pasteboard;
+}
+
+QCocoaDropData::~QCocoaDropData()
+{
+    NSString* pasteboardName = (NSString*)dropPasteboard;
+    [pasteboardName release];
+}
+
+QStringList QCocoaDropData::formats_sys() const
+{
+    QStringList formats; 
+    OSPasteboardRef board;
+    if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return formats;
+    }
+    formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
+    return formats;
+}
+
+QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
+{
+    QVariant data;
+    OSPasteboardRef board;
+    if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return data;
+    }
+    data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type);
+    CFRelease(board);
+    return data;
+}
+
+bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const
+{
+    bool has = false;
+    OSPasteboardRef board;
+    if (PasteboardCreate(dropPasteboard, &board) != noErr) {
+        qDebug("DnD: Cannot get PasteBoard!");
+        return has;
+    }
+    has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType);
+    CFRelease(board);
+    return has;
+}
+
+#endif // QT_NO_DRAGANDDROP
+QT_END_NAMESPACE