diff options
author | Jan-Marek Glogowski <jan-marek.glogowski@extern.cib.de> | 2019-08-08 17:59:20 +0000 |
---|---|---|
committer | Jan-Marek Glogowski <glogow@fbihome.de> | 2019-08-22 14:29:03 +0200 |
commit | 3355be0616c24c5e44b71e7623c4191ed9c69074 (patch) | |
tree | 9343975454c1656c16222ac09bd0cc9e22a43dd4 /vcl/qt5/Qt5Frame.cxx | |
parent | bafd4194b6ffbe4d6adfe01404196d787800dabe (diff) |
tdf#126560 Qt5 fix D'n'D key-modifier handling
The patch has still one problem: the key-modifier state isn't
reflected by the cursor, unless the user moves the mouse. There is
an upstream Qt bug, reported in 2016-09 against Qt 5.6.1! It is
supposed to be fixed in Qt 5.12, according to the bug report at
https://bugreports.qt.io/browse/QTBUG-56218, which is still open.
I thought about adding a configure test, but I couldn't imagine
any realistic way to write it. And after Michael Weghorn found the
bug is actually not fixed, as claimed in one of the comments, I
decided to drop the warning.
Change-Id: Ice8ebc4ea149282b4c1551e755efe3d4856cf782
Reviewed-on: https://gerrit.libreoffice.org/77174
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
Diffstat (limited to 'vcl/qt5/Qt5Frame.cxx')
-rw-r--r-- | vcl/qt5/Qt5Frame.cxx | 153 |
1 files changed, 108 insertions, 45 deletions
diff --git a/vcl/qt5/Qt5Frame.cxx b/vcl/qt5/Qt5Frame.cxx index 142cb12aa199..cd850e6c3c0f 100644 --- a/vcl/qt5/Qt5Frame.cxx +++ b/vcl/qt5/Qt5Frame.cxx @@ -36,6 +36,8 @@ #include <QtCore/QPoint> #include <QtCore/QSize> #include <QtCore/QThread> +#include <QtGui/QDragMoveEvent> +#include <QtGui/QDropEvent> #include <QtGui/QIcon> #include <QtGui/QWindow> #include <QtGui/QScreen> @@ -1187,84 +1189,145 @@ void Qt5Frame::deregisterDropTarget(Qt5DropTarget const* pDropTarget) m_pDropTarget = nullptr; } -void Qt5Frame::draggingStarted(const int x, const int y, Qt::DropActions eActions, - Qt::KeyboardModifiers eKeyMod, const QMimeData* pQMimeData) +static css::uno::Reference<css::datatransfer::XTransferable> +lcl_getXTransferable(const QMimeData* pMimeData) { - assert(m_pDropTarget); + css::uno::Reference<css::datatransfer::XTransferable> xTransferable; + const Qt5MimeData* pQt5MimeData = dynamic_cast<const Qt5MimeData*>(pMimeData); + if (!pQt5MimeData) + xTransferable = new Qt5DnDTransferable(pMimeData); + else + xTransferable = pQt5MimeData->xTransferable(); + return xTransferable; +} - sal_Int8 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE; +static sal_Int8 lcl_getUserDropAction(const QDropEvent* pEvent, const sal_Int8 nSourceActions, + const QMimeData* pMimeData) +{ + // we completely ignore all proposals by the Qt event, as they don't + // match at all with the preferred LO DnD actions. + const sal_Int8 nFilterActions + = nSourceActions | css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT; + + // check the key modifiers to detect a user-overridden DnD action + const Qt::KeyboardModifiers eKeyMod = pEvent->keyboardModifiers(); + sal_Int8 nUserDropAction = 0; if ((eKeyMod & Qt::ShiftModifier) && !(eKeyMod & Qt::ControlModifier)) nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE; else if ((eKeyMod & Qt::ControlModifier) && !(eKeyMod & Qt::ShiftModifier)) nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY; else if ((eKeyMod & Qt::ShiftModifier) && (eKeyMod & Qt::ControlModifier)) nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK; + nUserDropAction &= nFilterActions; + + // select the default DnD action, if there isn't a user preference + if (0 == nUserDropAction) + { + // default LO internal action is move, but default external action is copy + nUserDropAction = dynamic_cast<const Qt5MimeData*>(pMimeData) + ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE + : css::datatransfer::dnd::DNDConstants::ACTION_COPY; + nUserDropAction &= nFilterActions; + + // if the default doesn't match any allowed source action, fall back to the + // preferred of all allowed source actions + if (0 == nUserDropAction) + nUserDropAction = toVclDropAction(getPreferredDropAction(nSourceActions)); + + // this is "our" preference, but actually we would even prefer any default, + // if there is any + nUserDropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT; + } + return nUserDropAction; +} + +void Qt5Frame::handleDragMove(QDragMoveEvent* pEvent) +{ + assert(m_pDropTarget); + + // prepare our suggested drop action for the drop target + const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions()); + const QMimeData* pMimeData = pEvent->mimeData(); + const sal_Int8 nUserDropAction = lcl_getUserDropAction(pEvent, nSourceActions, pMimeData); css::datatransfer::dnd::DropTargetDragEnterEvent aEvent; aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget); aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDragContext*>(m_pDropTarget); - aEvent.LocationX = x; - aEvent.LocationY = y; - - // system drop action if neither Shift nor Control is held - if (!(eKeyMod & (Qt::ShiftModifier | Qt::ControlModifier))) - aEvent.DropAction = getPreferredDropAction(eActions); - // otherwise user-preferred action - else - aEvent.DropAction = nUserDropAction; - aEvent.SourceActions = toVclDropActions(eActions); + aEvent.LocationX = pEvent->pos().x(); + aEvent.LocationY = pEvent->pos().y(); + aEvent.DropAction = nUserDropAction; + aEvent.SourceActions = nSourceActions; - css::uno::Reference<css::datatransfer::XTransferable> xTransferable; - if (!pQMimeData->hasFormat(sInternalMimeType)) - xTransferable = new Qt5DnDTransferable(pQMimeData); - else - xTransferable = Qt5DragSource::m_ActiveDragSource->GetTransferable(); - - if (!m_bInDrag && xTransferable.is()) + // ask the drop target to accept our drop action + if (!m_bInDrag) { - css::uno::Sequence<css::datatransfer::DataFlavor> aFormats - = xTransferable->getTransferDataFlavors(); - aEvent.SupportedDataFlavors = aFormats; - + aEvent.SupportedDataFlavors = lcl_getXTransferable(pMimeData)->getTransferDataFlavors(); m_pDropTarget->fire_dragEnter(aEvent); m_bInDrag = true; } else m_pDropTarget->fire_dragOver(aEvent); + + // the drop target accepted our drop action => inform Qt + if (m_pDropTarget->proposedDropAction() != 0) + { + pEvent->setDropAction(getPreferredDropAction(m_pDropTarget->proposedDropAction())); + pEvent->accept(); + } + else // or maybe someone else likes it? + pEvent->ignore(); } -void Qt5Frame::dropping(const int x, const int y, Qt::KeyboardModifiers eKeyMod, - const QMimeData* pQMimeData) +void Qt5Frame::handleDrop(QDropEvent* pEvent) { assert(m_pDropTarget); + // prepare our suggested drop action for the drop target + const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions()); + const sal_Int8 nUserDropAction + = lcl_getUserDropAction(pEvent, nSourceActions, pEvent->mimeData()); + css::datatransfer::dnd::DropTargetDropEvent aEvent; aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget); aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDropContext*>(m_pDropTarget); - aEvent.LocationX = x; - aEvent.LocationY = y; - - if (!(eKeyMod & (Qt::ShiftModifier | Qt::ControlModifier))) - aEvent.DropAction = m_pDropTarget->proposedDragAction() - | css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT; - else - aEvent.DropAction = m_pDropTarget->proposedDragAction(); - aEvent.SourceActions = css::datatransfer::dnd::DNDConstants::ACTION_MOVE; - - css::uno::Reference<css::datatransfer::XTransferable> xTransferable; - if (!pQMimeData->hasFormat(sInternalMimeType)) - xTransferable = new Qt5DnDTransferable(pQMimeData); - else - xTransferable = Qt5DragSource::m_ActiveDragSource->GetTransferable(); - aEvent.Transferable = xTransferable; + aEvent.LocationX = pEvent->pos().x(); + aEvent.LocationY = pEvent->pos().y(); + aEvent.SourceActions = nSourceActions; + aEvent.DropAction = nUserDropAction; + aEvent.Transferable = lcl_getXTransferable(pEvent->mimeData()); + // ask the drop target to accept our drop action m_pDropTarget->fire_drop(aEvent); m_bInDrag = false; - if (m_pDragSource) + const bool bDropSuccessful = m_pDropTarget->dropSuccessful(); + const sal_Int8 nDropAction = m_pDropTarget->proposedDropAction(); + + // inform the drag source of the drag-origin frame of the drop result + if (pEvent->source()) { - m_pDragSource->fire_dragEnd(m_pDropTarget->proposedDragAction()); + Qt5Widget* pWidget = dynamic_cast<Qt5Widget*>(pEvent->source()); + assert(pWidget); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself + if (pWidget) + pWidget->getFrame().m_pDragSource->fire_dragEnd(nDropAction, bDropSuccessful); } + + // the drop target accepted our drop action => inform Qt + if (bDropSuccessful) + { + pEvent->setDropAction(getPreferredDropAction(nDropAction)); + pEvent->accept(); + } + else // or maybe someone else likes it? + pEvent->ignore(); +} + +void Qt5Frame::handleDragLeave() +{ + css::datatransfer::dnd::DropTargetEvent aEvent; + aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget); + m_pDropTarget->fire_dragExit(aEvent); + m_bInDrag = false; } cairo_t* Qt5Frame::getCairoContext() const |