VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp@ 82781

Last change on this file since 82781 was 80652, checked in by vboxsync, 5 years ago

FE/Qt: bugref:8938. Converting connection syntaxes. contd.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.4 KB
Line 
1/* $Id: UIDnDHandler.cpp 80652 2019-09-08 18:19:53Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDnDHandler class implementation.
4 */
5
6/*
7 * Copyright (C) 2011-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QApplication>
20#include <QDrag>
21#include <QKeyEvent>
22#include <QStringList>
23#include <QTimer>
24#include <QUrl>
25#include <QWidget>
26
27/* VirtualBox interface declarations: */
28#include <VBox/com/VirtualBox.h>
29
30/* GUI includes: */
31#include "UIDnDHandler.h"
32#ifdef VBOX_WITH_DRAG_AND_DROP_GH
33#include "CDnDSource.h"
34#ifdef RT_OS_WINDOWS
35# include "UIDnDDataObject_win.h"
36# include "UIDnDDropSource_win.h"
37#endif
38#include "UIDnDMIMEData.h"
39#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
40#include "UIMessageCenter.h"
41#include "UISession.h"
42
43/* COM includes: */
44#include "CConsole.h"
45#include "CGuest.h"
46#include "CGuestDnDSource.h"
47#include "CGuestDnDTarget.h"
48#include "CSession.h"
49
50#ifdef LOG_GROUP
51 #undef LOG_GROUP
52#endif
53#define LOG_GROUP LOG_GROUP_GUEST_DND
54#include <VBox/log.h>
55#include <iprt/err.h>
56
57#if 0
58# ifdef DEBUG
59# include <QTextStream>
60# include <QFile>
61/** Enable this to log debug output of a Qt debug build to a file defined by DEBUG_DND_QT_LOGFILE. */
62# define DEBUG_DND_QT
63# ifdef RT_OS_WINDOWS
64# define DEBUG_DND_QT_LOGFILE "c:\\temp\\VBoxQt.log"
65# else
66# define DEBUG_DND_QT_LOGFILE "/var/tmp/vboxqt.log"
67# endif
68# endif /* DEBUG */
69#endif
70
71
72UIDnDHandler::UIDnDHandler(UISession *pSession, QWidget *pParent)
73 : m_pSession(pSession)
74 , m_pParent(pParent)
75 , m_enmOpMode(DNDMODE_UNKNOWN)
76 , m_fIsPending(false)
77 , m_fDataRetrieved(false)
78#ifndef RT_OS_WINDOWS
79 , m_pMIMEData(NULL)
80#endif
81{
82 AssertPtr(pSession);
83 m_dndSource = static_cast<CDnDSource>(pSession->guest().GetDnDSource());
84 m_dndTarget = static_cast<CDnDTarget>(pSession->guest().GetDnDTarget());
85}
86
87UIDnDHandler::~UIDnDHandler(void)
88{
89}
90
91/*
92 * Frontend -> Target.
93 */
94
95Qt::DropAction UIDnDHandler::dragEnter(ulong screenID, int x, int y,
96 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
97 const QMimeData *pMimeData)
98{
99 LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
100 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
101
102 if ( m_enmOpMode != DNDMODE_UNKNOWN
103 && m_enmOpMode != DNDMODE_HOSTTOGUEST)
104 return Qt::IgnoreAction;
105
106 /* Ask the guest for starting a DnD event. */
107 KDnDAction result = m_dndTarget.Enter(screenID,
108 x,
109 y,
110 toVBoxDnDAction(proposedAction),
111 toVBoxDnDActions(possibleActions),
112 pMimeData->formats().toVector());
113 if (m_dndTarget.isOk())
114 {
115 setOpMode(DNDMODE_HOSTTOGUEST);
116 return toQtDnDAction(result);
117 }
118
119 return Qt::IgnoreAction;
120}
121
122Qt::DropAction UIDnDHandler::dragMove(ulong screenID, int x, int y,
123 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
124 const QMimeData *pMimeData)
125{
126 LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
127 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
128
129 if (m_enmOpMode != DNDMODE_HOSTTOGUEST)
130 return Qt::IgnoreAction;
131
132 /* Notify the guest that the mouse has been moved while doing
133 * a drag'n drop operation. */
134 KDnDAction result = m_dndTarget.Move(screenID,
135 x,
136 y,
137 toVBoxDnDAction(proposedAction),
138 toVBoxDnDActions(possibleActions),
139 pMimeData->formats().toVector());
140 if (m_dndTarget.isOk())
141 return toQtDnDAction(result);
142
143 return Qt::IgnoreAction;
144}
145
146Qt::DropAction UIDnDHandler::dragDrop(ulong screenID, int x, int y,
147 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
148 const QMimeData *pMimeData)
149{
150 LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
151 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
152
153 if (m_enmOpMode != DNDMODE_HOSTTOGUEST)
154 return Qt::IgnoreAction;
155
156 /* The format the guest requests. */
157 QString strFormat;
158 /* Ask the guest for dropping data. */
159 KDnDAction enmResult = m_dndTarget.Drop(screenID,
160 x,
161 y,
162 toVBoxDnDAction(proposedAction),
163 toVBoxDnDActions(possibleActions),
164 pMimeData->formats().toVector(), strFormat);
165
166 /* Has the guest accepted the drop event? */
167 if ( m_dndTarget.isOk()
168 && enmResult != KDnDAction_Ignore)
169 {
170 LogFlowFunc(("strFormat=%s ...\n", strFormat.toUtf8().constData()));
171
172 QByteArray arrBytes;
173
174 /*
175 * Does the host support the format requested by the guest?
176 * Lookup the format in the MIME data object.
177 */
178 AssertPtr(pMimeData);
179 if (pMimeData->formats().indexOf(strFormat) >= 0)
180 {
181 arrBytes = pMimeData->data(strFormat);
182 Assert(!arrBytes.isEmpty());
183 }
184 /*
185 * The host does not support the format requested by the guest.
186 * This can happen if the host wants to send plan text, for example, but
187 * the guest requested something else, e.g. an URI list.
188 *
189 * In that case dictate the guest to use a fixed format from the host,
190 * so instead returning the requested URI list, return the original
191 * data format from the host. The guest has to try to deal with that then.
192 **/
193 else
194 {
195 LogRel3(("DnD: Guest requested a different format '%s'\n", strFormat.toUtf8().constData()));
196 LogRel3(("DnD: The host offered:\n"));
197#if 0
198 for (QStringList::iterator itFmt = pMimeData->formats().begin();
199 itFmt != pMimeData->formats().end(); itFmt++)
200 {
201 QString strTemp = *itFmt;
202 LogRel3(("DnD: \t%s\n", strTemp.toUtf8().constData()));
203 }
204#endif
205 if (pMimeData->hasText())
206 {
207 LogRel3(("DnD: Converting data to text ...\n"));
208 arrBytes = pMimeData->text().toUtf8();
209 strFormat = "text/plain;charset=utf-8";
210 }
211 else
212 {
213 LogRel(("DnD: Error: Could not convert host format to guest format\n"));
214 enmResult = KDnDAction_Ignore;
215 }
216 }
217
218 if (arrBytes.size()) /* Anything to send? */
219 {
220 /* Convert data to a vector. */
221 QVector<uint8_t> vecData(arrBytes.size()); /** @todo Can this throw or anything? */
222 AssertReleaseMsg(vecData.size() == arrBytes.size(), ("Drag and drop format buffer size does not match"));
223 memcpy(vecData.data(), arrBytes.constData(), arrBytes.size());
224
225 /* Send data to the guest. */
226 LogRel3(("DnD: Host is sending %d bytes of data as '%s'\n", vecData.size(), strFormat.toUtf8().constData()));
227 CProgress progress = m_dndTarget.SendData(screenID, strFormat, vecData);
228
229 if (m_dndTarget.isOk())
230 {
231 msgCenter().showModalProgressDialog(progress,
232 tr("Dropping data ..."), ":/progress_dnd_hg_90px.png",
233 m_pParent);
234
235 LogFlowFunc(("Transfer fCompleted=%RTbool, fCanceled=%RTbool, hr=%Rhrc\n",
236 progress.GetCompleted(), progress.GetCanceled(), progress.GetResultCode()));
237
238 BOOL fCanceled = progress.GetCanceled();
239 if ( !fCanceled
240 && ( !progress.isOk()
241 || progress.GetResultCode() != 0))
242 {
243 msgCenter().cannotDropDataToGuest(progress, m_pParent);
244 enmResult = KDnDAction_Ignore;
245 }
246 }
247 else
248 {
249 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
250 enmResult = KDnDAction_Ignore;
251 }
252 }
253 else /* Error. */
254 enmResult = KDnDAction_Ignore;
255 }
256
257 /*
258 * Since the mouse button has been release this in any case marks
259 * the end of the current transfer direction. So reset the current
260 * mode as well here.
261 */
262 setOpMode(DNDMODE_UNKNOWN);
263
264 return toQtDnDAction(enmResult);
265}
266
267void UIDnDHandler::dragLeave(ulong screenID)
268{
269 LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32\n", m_enmOpMode, screenID));
270
271 if (m_enmOpMode == DNDMODE_HOSTTOGUEST)
272 {
273 m_dndTarget.Leave(screenID);
274 setOpMode(DNDMODE_UNKNOWN);
275 }
276}
277
278#ifdef DEBUG_DND_QT
279QTextStream *g_pStrmLogQt = NULL; /* Output stream for Qt debug logging. */
280
281/* static */
282void UIDnDHandler::debugOutputQt(QtMsgType type, const QMessageLogContext &context, const QString &strMessage)
283{
284 QString strMsg;
285 switch (type)
286 {
287 case QtWarningMsg:
288 strMsg += "[W]";
289 break;
290 case QtCriticalMsg:
291 strMsg += "[C]";
292 break;
293 case QtFatalMsg:
294 strMsg += "[F]";
295 break;
296 case QtDebugMsg:
297 default:
298 strMsg += "[D]";
299 break;
300 }
301
302 if (g_pStrmLogQt)
303 (*g_pStrmLogQt) << strMsg << " " << strMessage << endl;
304}
305#endif /* DEBUG_DND_QT */
306
307/*
308 * Source -> Frontend.
309 */
310
311int UIDnDHandler::dragStartInternal(const QStringList &lstFormats,
312 Qt::DropAction defAction, Qt::DropActions actions)
313{
314 RT_NOREF(defAction);
315 int rc = VINF_SUCCESS;
316
317#ifdef VBOX_WITH_DRAG_AND_DROP_GH
318
319 LogFlowFunc(("defAction=0x%x\n", defAction));
320 LogFlowFunc(("Number of formats: %d\n", lstFormats.size()));
321# ifdef DEBUG
322 for (int i = 0; i < lstFormats.size(); i++)
323 LogFlowFunc(("\tFormat %d: %s\n", i, lstFormats.at(i).toUtf8().constData()));
324# endif
325
326# ifdef DEBUG_DND_QT
327 QFile *pFileDebugQt = new QFile(DEBUG_DND_QT_LOGFILE);
328 if (pFileDebugQt->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
329 {
330 g_pStrmLogQt = new QTextStream(pFileDebugQt);
331
332 qInstallMessageHandler(UIDnDHandler::debugOutputQt);
333 qDebug("========================================================================");
334 }
335# endif
336
337# ifdef RT_OS_WINDOWS
338 UIDnDDataObject *pDataObject = new UIDnDDataObject(this, lstFormats);
339 if (!pDataObject)
340 return VERR_NO_MEMORY;
341 UIDnDDropSource *pDropSource = new UIDnDDropSource(m_pParent, pDataObject);
342 if (!pDropSource)
343 return VERR_NO_MEMORY;
344
345 DWORD dwOKEffects = DROPEFFECT_NONE;
346 if (actions)
347 {
348 if (actions & Qt::CopyAction)
349 dwOKEffects |= DROPEFFECT_COPY;
350 if (actions & Qt::MoveAction)
351 dwOKEffects |= DROPEFFECT_MOVE;
352 if (actions & Qt::LinkAction)
353 dwOKEffects |= DROPEFFECT_LINK;
354 }
355
356 DWORD dwEffect;
357 LogRel2(("DnD: Starting drag and drop operation\n", dwOKEffects));
358 LogRel3(("DnD: DoDragDrop dwOKEffects=0x%x\n", dwOKEffects));
359 HRESULT hr = ::DoDragDrop(pDataObject, pDropSource, dwOKEffects, &dwEffect);
360 LogRel3(("DnD: DoDragDrop ended with hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
361
362 if (pDropSource)
363 pDropSource->Release();
364 if (pDataObject)
365 pDataObject->Release();
366
367# else /* !RT_OS_WINDOWS */
368
369 QDrag *pDrag = new QDrag(m_pParent);
370 if (!pDrag)
371 return VERR_NO_MEMORY;
372
373 /* Note: pMData is transferred to the QDrag object, so no need for deletion. */
374 m_pMIMEData = new UIDnDMIMEData(this, lstFormats, defAction, actions);
375 if (!m_pMIMEData)
376 {
377 delete pDrag;
378 return VERR_NO_MEMORY;
379 }
380
381 /* Inform the MIME data object of any changes in the current action. */
382 connect(pDrag, &QDrag::actionChanged,
383 m_pMIMEData, &UIDnDMIMEData::sltDropActionChanged);
384
385 /* Invoke this handler as data needs to be retrieved by our derived QMimeData class. */
386 connect(m_pMIMEData, &UIDnDMIMEData::sigGetData,
387 this, &UIDnDHandler::sltGetData);
388
389 /*
390 * Set MIME data object and start the (modal) drag'n drop operation on the host.
391 * This does not block Qt's event loop, however (on Windows it would).
392 */
393 pDrag->setMimeData(m_pMIMEData);
394 LogFlowFunc(("Executing modal drag'n drop operation ...\n"));
395
396 Qt::DropAction dropAction;
397# ifdef RT_OS_DARWIN
398# ifdef VBOX_WITH_DRAG_AND_DROP_PROMISES
399 dropAction = pDrag->exec(actions, defAction);
400# else
401 /* Without having VBOX_WITH_DRAG_AND_DROP_PROMISES enabled drag and drop
402 * will not work on OS X! It also requires some handcrafted patches within Qt
403 * (which also needs VBOX_WITH_DRAG_AND_DROP_PROMISES set there). */
404 dropAction = Qt::IgnoreAction;
405 rc = VERR_NOT_SUPPORTED;
406# endif
407# else /* !RT_OS_DARWIN */
408 dropAction = pDrag->exec(actions, defAction);
409# endif /* RT_OS_DARWIN */
410 LogRel3(("DnD: Ended with dropAction=%ld\n", UIDnDHandler::toVBoxDnDAction(dropAction)));
411
412 /* Note: The UIDnDMimeData object will not be not accessible here anymore,
413 * since QDrag had its ownership and deleted it after the (blocking)
414 * QDrag::exec() call. */
415
416 /* pDrag will be cleaned up by Qt automatically. */
417
418# endif /* !RT_OS_WINDOWS */
419
420 reset();
421
422#ifdef DEBUG_DND_QT
423 if (g_pStrmLogQt)
424 {
425 delete g_pStrmLogQt;
426 g_pStrmLogQt = NULL;
427 }
428
429 if (pFileDebugQt)
430 {
431 pFileDebugQt->close();
432 delete pFileDebugQt;
433 }
434#endif /* DEBUG_DND_QT */
435
436#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
437
438 rc = VERR_NOT_SUPPORTED;
439
440#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
441
442 LogFlowFuncLeaveRC(rc);
443 return rc;
444}
445
446int UIDnDHandler::dragCheckPending(ulong screenID)
447{
448 int rc;
449#ifdef VBOX_WITH_DRAG_AND_DROP_GH
450
451 LogFlowFunc(("enmOpMode=%RU32, fIsPending=%RTbool, screenID=%RU32\n", m_enmOpMode, m_fIsPending, screenID));
452
453 {
454 QMutexLocker AutoReadLock(&m_ReadLock);
455
456 if ( m_enmOpMode != DNDMODE_UNKNOWN
457 && m_enmOpMode != DNDMODE_GUESTTOHOST) /* Wrong mode set? */
458 return VINF_SUCCESS;
459
460 if (m_fIsPending) /* Pending operation is in progress. */
461 return VINF_SUCCESS;
462 }
463
464 QMutexLocker AutoWriteLock(&m_WriteLock);
465 m_fIsPending = true;
466 AutoWriteLock.unlock();
467
468 /**
469 * How this works: Source is asking the target if there is any DnD
470 * operation pending, when the mouse leaves the guest window. On
471 * return there is some info about a running DnD operation
472 * (or defaultAction is KDnDAction_Ignore if not). With
473 * this information we create a Qt QDrag object with our own QMimeType
474 * implementation and call exec.
475 *
476 * Note: This function *blocks* until the actual drag'n drop operation
477 * has been finished (successfully or not)!
478 */
479 CGuest guest = m_pSession->guest();
480
481 /* Clear our current data set. */
482 m_dataSource.lstFormats.clear();
483 m_dataSource.vecActions.clear();
484
485 /* Ask the guest if there is a drag and drop operation pending (on the guest). */
486 QVector<QString> vecFormats;
487 m_dataSource.defaultAction = m_dndSource.DragIsPending(screenID, vecFormats, m_dataSource.vecActions);
488
489 LogRelMax3(10, ("DnD: Default action is: 0x%x\n", m_dataSource.defaultAction));
490 LogRelMax3(10, ("DnD: Number of supported guest actions: %d\n", m_dataSource.vecActions.size()));
491 for (int i = 0; i < m_dataSource.vecActions.size(); i++)
492 LogRelMax3(10, ("DnD: \tAction %d: 0x%x\n", i, m_dataSource.vecActions.at(i)));
493
494 LogRelMax3(10, ("DnD: Number of supported guest formats: %d\n", vecFormats.size()));
495 for (int i = 0; i < vecFormats.size(); i++)
496 {
497 const QString &strFmtGuest = vecFormats.at(i);
498 LogRelMax3(10, ("DnD: \tFormat %d: %s\n", i, strFmtGuest.toUtf8().constData()));
499 }
500
501 LogFlowFunc(("defaultAction=0x%x, vecFormatsSize=%d, vecActionsSize=%d\n",
502 m_dataSource.defaultAction, vecFormats.size(), m_dataSource.vecActions.size()));
503
504 if ( m_dataSource.defaultAction != KDnDAction_Ignore
505 && vecFormats.size())
506 {
507 for (int i = 0; i < vecFormats.size(); i++)
508 {
509 const QString &strFormat = vecFormats.at(i);
510 m_dataSource.lstFormats << strFormat;
511 }
512
513 rc = VINF_SUCCESS; /* There's a valid pending drag and drop operation on the guest. */
514 }
515 else /* No format data from the guest arrived yet. */
516 rc = VERR_NO_DATA;
517
518 AutoWriteLock.relock();
519 m_fIsPending = false;
520 AutoWriteLock.unlock();
521
522#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
523
524 NOREF(screenID);
525
526 rc = VERR_NOT_SUPPORTED;
527
528#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
529
530 LogFlowFuncLeaveRC(rc);
531 return rc;
532}
533
534int UIDnDHandler::dragStart(ulong screenID)
535{
536 int rc;
537#ifdef VBOX_WITH_DRAG_AND_DROP_GH
538
539 NOREF(screenID);
540
541 LogFlowFuncEnter();
542
543 /* Sanity checks. */
544 if ( !m_dataSource.lstFormats.size()
545 || m_dataSource.defaultAction == KDnDAction_Ignore
546 || !m_dataSource.vecActions.size())
547 {
548 return VERR_INVALID_PARAMETER;
549 }
550
551 setOpMode(DNDMODE_GUESTTOHOST);
552
553 rc = dragStartInternal(m_dataSource.lstFormats,
554 toQtDnDAction(m_dataSource.defaultAction), toQtDnDActions(m_dataSource.vecActions));
555
556#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
557
558 NOREF(screenID);
559
560 rc = VERR_NOT_SUPPORTED;
561
562#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
563
564 LogFlowFuncLeaveRC(rc);
565 return rc;
566}
567
568int UIDnDHandler::dragStop(ulong screenID)
569{
570 int rc;
571#ifdef VBOX_WITH_DRAG_AND_DROP_GH
572
573 NOREF(screenID);
574
575 reset();
576 rc = VINF_SUCCESS;
577
578#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
579
580 NOREF(screenID);
581
582 rc = VERR_NOT_SUPPORTED;
583
584#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
585
586 LogFlowFuncLeaveRC(rc);
587 return rc;
588}
589
590void UIDnDHandler::reset(void)
591{
592 LogFlowFuncEnter();
593
594 m_fDataRetrieved = false;
595 m_fIsPending = false;
596
597 setOpMode(DNDMODE_UNKNOWN);
598}
599
600int UIDnDHandler::retrieveData(Qt::DropAction dropAction,
601 const QString &strMIMEType,
602 QVector<uint8_t> &vecData)
603{
604 if (!strMIMEType.compare("application/x-qt-mime-type-name", Qt::CaseInsensitive))
605 return VINF_SUCCESS;
606
607 int rc;
608 if (!m_fDataRetrieved)
609 {
610 /*
611 * Retrieve the actual data from the guest.
612 */
613 rc = retrieveDataInternal(dropAction, strMIMEType, m_vecData);
614 if (RT_FAILURE(rc))
615 LogRel3(("DnD: Receiving data failed: %Rrc\n", rc));
616 else
617 m_fDataRetrieved = true;
618 }
619 else /* Data already received, supply cached version. */
620 rc = VINF_SUCCESS;
621
622 if (RT_SUCCESS(rc))
623 vecData = m_vecData;
624
625 return rc;
626}
627
628int UIDnDHandler::retrieveData( Qt::DropAction dropAction,
629 const QString &strMIMEType,
630 QVariant::Type vaType,
631 QVariant &vaData)
632{
633 QVector<uint8_t> vecData;
634 int rc = retrieveData(dropAction, strMIMEType, vecData);
635 if (RT_SUCCESS(rc))
636 {
637 /* If no/an invalid variant is set, try to guess the variant type.
638 * This can happen on OS X. */
639 if (vaType == QVariant::Invalid)
640 vaType = UIDnDMIMEData::getVariantType(strMIMEType);
641
642 rc = UIDnDMIMEData::getDataAsVariant(vecData, strMIMEType, vaType, vaData);
643 }
644
645 LogFlowFuncLeaveRC(rc);
646 return rc;
647}
648
649int UIDnDHandler::retrieveDataInternal( Qt::DropAction dropAction,
650 const QString &strMIMEType,
651 QVector<uint8_t> &vecData)
652{
653 LogRel3(("DnD: Retrieving data from guest as '%s' (%d)\n", qPrintable(strMIMEType), dropAction));
654
655 int rc = VINF_SUCCESS;
656
657 /* Indicate to the guest that we have dropped the data on the host.
658 * The guest then will initiate the actual "drop" operation into our proxy on the guest. */
659 Assert(!m_dndSource.isNull());
660 CProgress progress = m_dndSource.Drop(strMIMEType,
661 UIDnDHandler::toVBoxDnDAction(dropAction));
662 LogFlowFunc(("Source: isOk=%RTbool\n", m_dndSource.isOk()));
663 if (m_dndSource.isOk())
664 {
665 /* Send a mouse event with released mouse buttons into the guest that triggers
666 * the "drop" event in our proxy window on the guest. */
667 AssertPtr(m_pSession);
668 m_pSession->mouse().PutMouseEvent(0, 0, 0, 0, 0);
669
670 msgCenter().showModalProgressDialog(progress,
671 tr("Retrieving data ..."), ":/progress_dnd_gh_90px.png",
672 m_pParent);
673
674 LogFlowFunc(("Progress: fCanceled=%RTbool, fCompleted=%RTbool, isOk=%RTbool, hrc=%Rhrc\n",
675 progress.GetCanceled(), progress.GetCompleted(), progress.isOk(), progress.GetResultCode()));
676
677 if (!progress.GetCanceled())
678 {
679 rc = ( progress.isOk()
680 && progress.GetResultCode() == 0)
681 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
682
683 if (RT_SUCCESS(rc))
684 {
685 /* After we successfully retrieved data from the source we query it from Main. */
686 vecData = m_dndSource.ReceiveData(); /** @todo QVector.size() is "int" only!? */
687 if (m_dndSource.isOk())
688 {
689 if (vecData.isEmpty())
690 rc = VERR_NO_DATA;
691 }
692 else
693 {
694 msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
695 rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
696 }
697 }
698 else
699 msgCenter().cannotDropDataToHost(progress, m_pParent);
700 }
701 else /* Don't pop up a message. */
702 rc = VERR_CANCELLED;
703 }
704 else
705 {
706 msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
707 rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
708 }
709
710 setOpMode(DNDMODE_UNKNOWN);
711
712 LogFlowFuncLeaveRC(rc);
713 return rc;
714}
715
716void UIDnDHandler::setOpMode(DNDOPMODE enmMode)
717{
718 QMutexLocker AutoWriteLock(&m_WriteLock);
719 m_enmOpMode = enmMode;
720 LogFunc(("Operation mode is now: %RU32\n", m_enmOpMode));
721}
722
723int UIDnDHandler::sltGetData( Qt::DropAction dropAction,
724 const QString &strMIMEType,
725 QVariant::Type vaType,
726 QVariant &vaData)
727{
728 int rc = retrieveData(dropAction, strMIMEType, vaType, vaData);
729 LogFlowFuncLeaveRC(rc);
730 return rc;
731}
732
733/*
734 * Drag and Drop helper methods
735 */
736
737/* static */
738KDnDAction UIDnDHandler::toVBoxDnDAction(Qt::DropAction action)
739{
740 if (action == Qt::CopyAction)
741 return KDnDAction_Copy;
742 if (action == Qt::MoveAction)
743 return KDnDAction_Move;
744 if (action == Qt::LinkAction)
745 return KDnDAction_Link;
746
747 return KDnDAction_Ignore;
748}
749
750/* static */
751QVector<KDnDAction> UIDnDHandler::toVBoxDnDActions(Qt::DropActions actions)
752{
753 QVector<KDnDAction> vbActions;
754 if (actions.testFlag(Qt::IgnoreAction))
755 vbActions << KDnDAction_Ignore;
756 if (actions.testFlag(Qt::CopyAction))
757 vbActions << KDnDAction_Copy;
758 if (actions.testFlag(Qt::MoveAction))
759 vbActions << KDnDAction_Move;
760 if (actions.testFlag(Qt::LinkAction))
761 vbActions << KDnDAction_Link;
762
763 return vbActions;
764}
765
766/* static */
767Qt::DropAction UIDnDHandler::toQtDnDAction(KDnDAction action)
768{
769 Qt::DropAction dropAct = Qt::IgnoreAction;
770 if (action == KDnDAction_Copy)
771 dropAct = Qt::CopyAction;
772 if (action == KDnDAction_Move)
773 dropAct = Qt::MoveAction;
774 if (action == KDnDAction_Link)
775 dropAct = Qt::LinkAction;
776
777 LogFlowFunc(("dropAct=0x%x\n", dropAct));
778 return dropAct;
779}
780
781/* static */
782Qt::DropActions UIDnDHandler::toQtDnDActions(const QVector<KDnDAction> &vecActions)
783{
784 Qt::DropActions dropActs = Qt::IgnoreAction;
785 for (int i = 0; i < vecActions.size(); i++)
786 {
787 switch (vecActions.at(i))
788 {
789 case KDnDAction_Ignore:
790 dropActs |= Qt::IgnoreAction;
791 break;
792 case KDnDAction_Copy:
793 dropActs |= Qt::CopyAction;
794 break;
795 case KDnDAction_Move:
796 dropActs |= Qt::MoveAction;
797 break;
798 case KDnDAction_Link:
799 dropActs |= Qt::LinkAction;
800 break;
801 default:
802 break;
803 }
804 }
805
806 LogFlowFunc(("dropActions=0x%x\n", int(dropActs)));
807 return dropActs;
808}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use