VirtualBox

Ignore:
Timestamp:
Jan 29, 2014 11:12:44 AM (11 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
91898
Message:

DnD: First working implementation for Windows guest->host support; still work in progress. As for now only pure text data can be dragged over.

Location:
trunk/src/VBox/Frontends/VirtualBox
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk

    r50191 r50265  
    55
    66#
    7 # Copyright (C) 2006-2013 Oracle Corporation
     7# Copyright (C) 2006-2014 Oracle Corporation
    88#
    99# This file is part of VirtualBox Open Source Edition (OSE), as
     
    176176        ./src/net
    177177endif
    178        
     178
    179179VirtualBox_INCS = \
    180180        $(VBOX_GUI_INC_DIRS) \
     
    390390        src/widgets/VBoxOSTypeSelectorButton.h \
    391391        src/widgets/UINameAndSystemEditor.h \
    392     src/widgets/UIWarningPane.h \
     392        src/widgets/UIWarningPane.h \
    393393        src/widgets/UIFilmContainer.h \
    394394        src/widgets/graphics/UIGraphicsButton.h \
     
    433433
    434434ifdef VBOX_GUI_WITH_NETWORK_MANAGER
    435 VirtualBox_QT_MOCHDRS += \
     435 VirtualBox_QT_MOCHDRS += \
    436436        src/net/UINetworkManager.h \
    437437        src/net/UINetworkManagerDialog.h \
     
    448448        src/settings/global/UIGlobalSettingsProxy.h \
    449449        src/settings/global/UIGlobalSettingsUpdate.h
     450endif
     451
     452ifdef VBOX_WITH_DRAG_AND_DROP
     453 ifdef VBOX_WITH_DRAG_AND_DROP_GH
     454  VirtualBox_QT_MOCHDRS += \
     455        src/runtime/UIDnDMIMEData.h
     456 endif
    450457endif
    451458
     
    477484
    478485ifdef VBOX_GUI_WITH_NETWORK_MANAGER
    479 VirtualBox_QT_MOCSRCS += \
     486 VirtualBox_QT_MOCSRCS += \
    480487        src/net/UINetworkReply.cpp \
    481488        src/net/UIUpdateManager.cpp
     489endif
     490
     491ifdef VBOX_WITH_DRAG_AND_DROP
     492 ifdef VBOX_WITH_DRAG_AND_DROP_GH
     493  VirtualBox_QT_MOCSRCS += \
     494        src/runtime/UIDnDMIMEData.cpp
     495 endif
    482496endif
    483497
     
    763777 VirtualBox_QT_MOCSRCS += \
    764778        src/runtime/UIDnDHandler.cpp
     779 ifdef VBOX_WITH_DRAG_AND_DROP_GH
     780  VirtualBox_SOURCES += \
     781        src/runtime/UIDnDMIMEData.cpp
     782  VirtualBox_QT_MOCSRCS += \
     783        src/runtime/UIDnDMIMEData.cpp
     784 endif
    765785endif
    766786
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp

    r49891 r50265  
    77
    88/*
    9  * Copyright (C) 2011-2013 Oracle Corporation
     9 * Copyright (C) 2011-2014 Oracle Corporation
    1010 *
    1111 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2121#include <QApplication>
    2222#include <QKeyEvent>
    23 #include <QMimeData>
    2423#include <QStringList>
    2524#include <QTimer>
     
    3332/* GUI includes: */
    3433#include "UIDnDHandler.h"
     34#ifdef VBOX_WITH_DRAG_AND_DROP_GH
     35# include "UIDnDMIMEData.h"
     36#endif
    3537#include "UIMessageCenter.h"
    3638
     
    4042#include "CGuest.h"
    4143
    42 UIDnDHandler *UIDnDHandler::m_pInstance = 0;
    43 
    44 UIDnDHandler::UIDnDHandler()
     44UIDnDHandler *UIDnDHandler::m_pInstance = NULL;
     45
     46UIDnDHandler::UIDnDHandler(void)
    4547{
    4648}
     
    5052 */
    5153
    52 Qt::DropAction UIDnDHandler::dragHGEnter(CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget * /* pParent = 0 */)
     54Qt::DropAction UIDnDHandler::dragHGEnter(CGuest &guest, ulong screenId, int x, int y,
     55                                         Qt::DropAction proposedAction, Qt::DropActions possibleActions,
     56                                         const QMimeData *pMimeData, QWidget * /* pParent = NULL */)
    5357{
    5458    LogFlowFunc(("screenId=%RU32, x=%d, y=%d, action=%ld\n",
     
    6670}
    6771
    68 Qt::DropAction UIDnDHandler::dragHGMove(CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget * /* pParent = 0 */)
     72Qt::DropAction UIDnDHandler::dragHGMove(CGuest &guest, ulong screenId, int x, int y,
     73                                        Qt::DropAction proposedAction, Qt::DropActions possibleActions,
     74                                        const QMimeData *pMimeData, QWidget * /* pParent = NULL */)
    6975{
    7076#ifdef DEBUG_andy
     
    8591}
    8692
    87 Qt::DropAction UIDnDHandler::dragHGDrop(CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent /* = 0 */)
     93Qt::DropAction UIDnDHandler::dragHGDrop(CGuest &guest, ulong screenId, int x, int y,
     94                                        Qt::DropAction proposedAction, Qt::DropActions possibleActions,
     95                                        const QMimeData *pMimeData, QWidget *pParent /* = NULL */)
    8896{
    8997    LogFlowFunc(("screenId=%RU32, x=%d, y=%d, action=%ld\n",
     
    132140}
    133141
    134 void UIDnDHandler::dragHGLeave(CGuest &guest, ulong screenId, QWidget * /* pParent = 0 */)
     142void UIDnDHandler::dragHGLeave(CGuest &guest, ulong screenId, QWidget * /* pParent = NULL */)
    135143{
    136144    LogFlowFunc(("screenId=%RU32\n", screenId));
     
    138146}
    139147
    140 /*
    141  * Guest -> Host
    142  */
    143 
    144 class UIDnDMimeData: public QMimeData
    145 {
    146     Q_OBJECT;
    147 
    148     enum State
    149     {
    150         Dragging,
    151         Dropped,
    152         Finished,
    153         Canceled
    154     };
    155 
    156 public:
    157     UIDnDMimeData(CSession &session, QStringList formats, Qt::DropAction defAction, Qt::DropActions actions, QWidget *pParent)
    158       : m_pParent(pParent)
    159       , m_session(session)
    160       , m_formats(formats)
    161       , m_defAction(defAction)
    162       , m_actions(actions)
    163       , m_fState(Dragging)
    164     {
    165         /*
    166          * This is unbelievable hacky, but I didn't find another way. Stupid
    167          * Qt QDrag interface is so less verbose, that we in principle know
    168          * nothing about what happens when the user drag something around. It
    169          * is possible that the target request data (s. retrieveData) while the
    170          * mouse button is still pressed. This isn't something we can support,
    171          * cause it would mean transferring the data from the guest while the
    172          * mouse is still moving (thing of a 2GB file ...). So the idea is to
    173          * detect the mouse release event and only after this happened, allow
    174          * data to be retrieved. Unfortunately the QDrag object eats all events
    175          * while a drag is going on (see QDragManager in the Qt src's). So what
    176          * we do, is installing an event filter after the QDrag::exec is called
    177          * to be last in the event filter queue and therefore called before the
    178          * one installed by the QDrag object.
    179          *
    180          ** @todo: Test this on all supported platforms (X11 works).
    181          */
    182         QTimer::singleShot(0, this, SLOT(sltInstallEventFilter()));
    183     }
    184 
    185     virtual QStringList formats() const { return m_formats; }
    186     virtual bool hasFormat(const QString &mimeType) const { return m_formats.contains(mimeType); }
    187 
    188 public slots:
    189     void sltActionChanged(Qt::DropAction action) { m_defAction = action; }
    190 
    191 protected:
    192     virtual QVariant retrieveData(const QString &mimeType, QVariant::Type type) const
    193     {
    194         /* Mouse button released? */
    195         if (m_fState != Dropped)
    196             return m_data;
    197 
    198         /* Supported types. See below in the switch statement. */
    199         if (!(   type == QVariant::String
    200               || type == QVariant::ByteArray))
    201             return QVariant();
    202 
    203         CGuest guest = m_session.GetConsole().GetGuest();
    204         /* No, start getting the data from the guest. First inform the guest we
    205          * want the data in the specified mime-type. */
    206         CProgress progress = guest.DragGHDropped(mimeType, UIDnDHandler::toVBoxDnDAction(m_defAction));
    207         if (guest.isOk())
    208         {
    209             msgCenter().showModalProgressDialog(progress, tr("Dropping data ..."), ":/progress_dnd_gh_90px.png", m_pParent);
    210             if (!progress.GetCanceled())
    211             {
    212                 if (progress.isOk() && progress.GetResultCode() == 0)
    213                 {
    214                     /* After the data is successfully arrived from the guest, we query it from Main. */
    215                     QVector<uint8_t> data = guest.DragGHGetData();
    216                     if (!data.isEmpty())
    217                     {
    218                         /* Todo: not sure what to add here more: needs more testing. */
    219                         switch (type)
    220                         {
    221                             case QVariant::String:
    222                             {
    223                                 m_data = QVariant(QString(reinterpret_cast<const char*>(data.data())));
    224                                 break;
    225                             }
    226                             case QVariant::ByteArray:
    227                             {
    228                                 QByteArray ba(reinterpret_cast<const char*>(data.constData()), data.size());
    229                                 m_data = QVariant(ba);
    230                                 break;
    231                             }
    232                             default: break;
    233                         }
    234                     }
    235                     m_fState = Finished;
    236                 }
    237                 else
    238                     msgCenter().cannotDropData(progress, m_pParent);
    239             }
    240             else
    241                 m_fState = Canceled;
    242         }
    243         else
    244             msgCenter().cannotDropData(guest, m_pParent);
    245         return m_data;
    246     }
    247 
    248     bool eventFilter(QObject * /* pObject */, QEvent *pEvent)
    249     {
    250         switch (pEvent->type())
    251         {
    252             case QEvent::MouseButtonRelease: m_fState = Dropped; break;
    253             case QEvent::KeyPress:
    254             {
    255                 if (static_cast<QKeyEvent*>(pEvent)->key() == Qt::Key_Escape)
    256                     m_fState = Canceled;
    257                 break;
    258             }
    259         }
    260 
    261         /* Propagate the event further. */
    262         return false;
    263     }
    264 
    265 private slots:
    266     void sltInstallEventFilter() { qApp->installEventFilter(this); }
    267 
    268 private:
    269     /* Private members. */
    270     QWidget          *m_pParent;
    271     CSession          m_session;
    272     QStringList       m_formats;
    273     Qt::DropAction    m_defAction;
    274     Qt::DropActions   m_actions;
    275     mutable State     m_fState;
    276     mutable QVariant  m_data;
    277 };
    278 
    279 void UIDnDHandler::dragGHPending(CSession &session, ulong screenId, QWidget *pParent /* = 0 */)
    280 {
     148int UIDnDHandler::dragGHPending(CSession &session, ulong screenId, QWidget *pParent /* = NULL */)
     149{
     150    int rc;
     151#ifdef VBOX_WITH_DRAG_AND_DROP_GH
    281152    /*
    282153     * How this works: Host is asking the guest if there is any DnD
     
    289160     */
    290161    CGuest guest = session.GetConsole().GetGuest();
    291     QVector<QString> formats;
    292     QVector<KDragAndDropAction> actions;
    293     KDragAndDropAction defaultAction = guest.DragGHPending(screenId, formats, actions);
     162    QVector<QString> vecFmtGuest;
     163    QVector<KDragAndDropAction> vecActions;
     164    KDragAndDropAction defaultAction = guest.DragGHPending(screenId, vecFmtGuest, vecActions);
     165    LogFlowFunc(("defaultAction=%d, numFormats=%d\n", defaultAction, vecFmtGuest.size()));
     166
     167    /*
     168     * Do guest -> host format conversion, if needed.
     169     * On X11 this already maps to the Xdnd protocol.
     170     ** @todo What about MacOS X Carbon Drag Manager?
     171     *
     172     * See: https://www.iana.org/assignments/media-types/media-types.xhtml
     173     */
     174    LogFlowFunc(("Number of guest formats: %d\n", vecFmtGuest.size()));
     175    QStringList lstFmtNative;
     176    for (int i = 0; i < vecFmtGuest.size(); i++)
     177    {
     178        const QString &strFmt = vecFmtGuest.at(i);
     179        LogFlowFunc(("\tFormat %d: %s\n", i,
     180                     strFmt.toAscii().constData()));
     181#ifdef RT_OS_WINDOWS
     182        if (   strFmt.contains("text", Qt::CaseInsensitive)
     183            && !lstFmtNative.contains("text/plain"))
     184        {
     185            lstFmtNative << "text/plain";
     186        }
     187#else
     188        lstFmtNative << strFmt;
     189#endif
     190    }
     191
     192    LogFlowFunc(("Number of native formats: %d\n", lstFmtNative.size()));
     193#ifdef DEBUG
     194    for (int i = 0; i < lstFmtNative.size(); i++)
     195        LogFlowFunc(("\tFormat %d: %s\n", i, lstFmtNative.at(i).toAscii().constData()));
     196#endif
    294197
    295198    if (    defaultAction != KDragAndDropAction_Ignore
    296         && !formats.isEmpty())
     199        && !lstFmtNative.isEmpty())
    297200    {
    298         QDrag *pDrag = new QDrag(pParent);
    299         /* pMData is transfered to the QDrag object, so no need for deletion. */
    300         UIDnDMimeData *pMData = new UIDnDMimeData(session, formats.toList(), toQtDnDAction(defaultAction), toQtDnDActions(actions), pParent);
    301         /* Inform the mime data object of any changes in the current action. */
    302         connect(pDrag, SIGNAL(actionChanged(Qt::DropAction)),
    303                 pMData, SLOT(sltActionChanged(Qt::DropAction)));
    304         pDrag->setMimeData(pMData);
    305         /* Fire it up. */
    306         pDrag->exec(toQtDnDActions(actions), toQtDnDAction(defaultAction));
     201        try
     202        {
     203            QDrag *pDrag = new QDrag(pParent);
     204
     205            /* pMData is transfered to the QDrag object, so no need for deletion. */
     206            UIDnDMimeData *pMData = new UIDnDMimeData(session, lstFmtNative,
     207                                                      toQtDnDAction(defaultAction),
     208                                                      toQtDnDActions(vecActions), pParent);
     209
     210            /* Inform the MIME data object of any changes in the current action. */
     211            connect(pDrag, SIGNAL(actionChanged(Qt::DropAction)),
     212                    pMData, SLOT(sltDropActionChanged(Qt::DropAction)));
     213
     214            /* Fire it up.
     215             *
     216             * On Windows this will start a modal operation using OLE's
     217             * DoDragDrop() method, so this call will block until the DnD operation
     218             * is finished. */
     219            pDrag->setMimeData(pMData);
     220            pDrag->exec(toQtDnDActions(vecActions), toQtDnDAction(defaultAction));
     221
     222            rc = VINF_SUCCESS;
     223        }
     224        catch (std::bad_alloc)
     225        {
     226            rc = VERR_NO_MEMORY;
     227        }
    307228    }
     229    else
     230        rc = VINF_SUCCESS;
     231#else
     232    rc = VERR_NOT_SUPPORTED;
     233#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
     234
     235    LogFlowFuncLeaveRC(rc);
     236    return rc;
    308237}
    309238
     
    341270Qt::DropAction UIDnDHandler::toQtDnDAction(KDragAndDropAction action)
    342271{
     272    Qt::DropAction dropAct = Qt::IgnoreAction;
    343273    if (action == KDragAndDropAction_Copy)
    344         return Qt::CopyAction;
     274        dropAct = Qt::CopyAction;
    345275    if (action == KDragAndDropAction_Move)
    346         return Qt::MoveAction;
     276        dropAct = Qt::MoveAction;
    347277    if (action == KDragAndDropAction_Link)
    348         return Qt::LinkAction;
    349 
    350     return Qt::IgnoreAction;
    351 }
    352 
    353 Qt::DropActions UIDnDHandler::toQtDnDActions(const QVector<KDragAndDropAction> &actions)
    354 {
    355     Qt::DropActions a = 0;
    356     for (int i = 0; i < actions.size(); ++i)
     278        dropAct = Qt::LinkAction;
     279
     280    LogFlowFunc(("dropAct=0x%x\n", dropAct));
     281    return dropAct;
     282}
     283
     284Qt::DropActions UIDnDHandler::toQtDnDActions(const QVector<KDragAndDropAction> &vecActions)
     285{
     286    Qt::DropActions dropActs = Qt::IgnoreAction;
     287    for (int i = 0; i < vecActions.size(); i++)
    357288    {
    358         switch (actions.at(i))
    359         {
    360             case KDragAndDropAction_Ignore: a |= Qt::IgnoreAction; break;
    361             case KDragAndDropAction_Copy:   a |= Qt::CopyAction; break;
    362             case KDragAndDropAction_Move:   a |= Qt::MoveAction; break;
    363             case KDragAndDropAction_Link:   a |= Qt::LinkAction; break;
     289        switch (vecActions.at(i))
     290        {
     291            case KDragAndDropAction_Ignore:
     292                dropActs |= Qt::IgnoreAction;
     293                break;
     294            case KDragAndDropAction_Copy:
     295                dropActs |= Qt::CopyAction;
     296                break;
     297            case KDragAndDropAction_Move:
     298                dropActs |= Qt::MoveAction;
     299                break;
     300            case KDragAndDropAction_Link:
     301                dropActs |= Qt::LinkAction;
     302                break;
     303            default:
     304                break;
    364305        }
    365306    }
    366     return a;
     307
     308    LogFlowFunc(("dropActions=0x%x\n", dropActs));
     309    return dropActs;
    367310}
    368311
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h

    r44529 r50265  
     1/* $Id$ */
    12/** @file
    23 *
     
    67
    78/*
    8  * Copyright (C) 2011-2012 Oracle Corporation
     9 * Copyright (C) 2011-2014 Oracle Corporation
    910 *
    1011 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2324#include "COMEnums.h"
    2425
    25 /* Forward declarations: */
     26/* Forward declarations. */
    2627class QMimeData;
    2728class CSession;
    2829class CGuest;
    2930
    30 /* Todo: check for making this a full static class when possible. */
     31/** @todo Check for making this a full static class when possible. */
    3132class UIDnDHandler: public QObject
    3233{
    3334public:
    34     /* Singleton */
    35     static UIDnDHandler* instance()
     35    /* Singleton factory. */
     36    static UIDnDHandler* instance(void)
    3637    {
    3738        if (!m_pInstance)
     
    3940        return m_pInstance;
    4041    }
    41     static void destroy() { delete m_pInstance; m_pInstance = 0; }
     42    static void destroy(void)
     43    {
     44        if (m_pInstance)
     45        {
     46            delete m_pInstance;
     47            m_pInstance = NULL;
     48        }
     49    }
    4250
    43     /* Host -> Guest */
    44     Qt::DropAction dragHGEnter(CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = 0);
    45     Qt::DropAction dragHGMove (CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = 0);
    46     Qt::DropAction dragHGDrop (CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = 0);
    47     void           dragHGLeave(CGuest &guest, ulong screenId, QWidget *pParent = 0);
     51    /* Host -> Guest. */
     52    Qt::DropAction dragHGEnter(CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = NULL);
     53    Qt::DropAction dragHGMove (CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = NULL);
     54    Qt::DropAction dragHGDrop (CGuest &guest, ulong screenId, int x, int y, Qt::DropAction proposedAction, Qt::DropActions possibleActions, const QMimeData *pMimeData, QWidget *pParent = NULL);
     55    void           dragHGLeave(CGuest &guest, ulong screenId, QWidget *pParent = NULL);
    4856
    49     /* Guest -> Host */
    50     void           dragGHPending(CSession &session, ulong screenId, QWidget *pParent = 0);
     57    /* Guest -> Host. */
     58    int            dragGHPending(CSession &session, ulong screenId, QWidget *pParent = NULL);
    5159
    5260private:
    5361    static UIDnDHandler *m_pInstance;
    5462
    55     UIDnDHandler();
    56     ~UIDnDHandler() {}
     63    UIDnDHandler(void);
     64    virtual ~UIDnDHandler(void) {}
    5765
    58     /* Private helpers */
     66    /* Private helpers. */
    5967    static KDragAndDropAction          toVBoxDnDAction(Qt::DropAction action);
    6068    static QVector<KDragAndDropAction> toVBoxDnDActions(Qt::DropActions actions);
    6169    static Qt::DropAction              toQtDnDAction(KDragAndDropAction action);
    62     static Qt::DropActions             toQtDnDActions(const QVector<KDragAndDropAction> &actions);
     70    static Qt::DropActions             toQtDnDActions(const QVector<KDragAndDropAction> &vecActions);
    6371
    6472    friend class UIDnDMimeData;
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp

    r50248 r50265  
    11691169}
    11701170
    1171 void UIMachineView::handleGHDnd()
     1171void UIMachineView::handleGHDnd(void)
    11721172{
    11731173    /* The guest object to talk to. */
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp

    r48950 r50265  
    907907                || cpnt.y() > iCh - 1)
    908908            {
    909                 if ((mouseButtons.testFlag(Qt::LeftButton)))
     909                bool fHandledGHDnD
     910                    = RT_BOOL(mouseButtons.testFlag(Qt::LeftButton));
     911                if (fHandledGHDnD)
    910912                {
    911913                    m_views[uScreenId]->handleGHDnd();
    912 
    913                     return false;
     914                    return true;
    914915                }
    915916            }
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette