File indexing completed on 2024-04-21 03:49:42

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2014 Adam Dabrowski <adamdbrw@gmail.com>
0006 //
0007 
0008 #include "MarbleInputHandler.h"
0009 
0010 #include <QPoint>
0011 #include <QPointer>
0012 #include <QTimer>
0013 #include <QCursor>
0014 #include <QMouseEvent>
0015 #include <QPixmap>
0016 #include <QGestureEvent>
0017 #include <QPinchGesture>
0018 
0019 #include "kineticmodel.h"
0020 #include "MarbleGlobal.h"
0021 #include "MarbleDebug.h"
0022 #include "MarbleMap.h"
0023 #include "MarbleAbstractPresenter.h"
0024 #include "ViewportParams.h"
0025 #include "AbstractFloatItem.h"
0026 #include "AbstractDataPluginItem.h"
0027 #include "AbstractProjection.h"
0028 #include "RenderPlugin.h"
0029 
0030 namespace Marble {
0031 
0032 const int TOOLTIP_START_INTERVAL = 1000;
0033 
0034 class Q_DECL_HIDDEN MarbleInputHandler::Protected
0035 {
0036 public:
0037     Protected(MarbleAbstractPresenter *marblePresenter);
0038 
0039     MarbleAbstractPresenter *const m_marblePresenter;
0040     bool m_positionSignalConnected;
0041     QTimer *m_mouseWheelTimer;
0042     Qt::MouseButtons m_disabledMouseButtons;
0043     qreal m_wheelZoomTargetDistance;
0044     bool m_panViaArrowsEnabled;
0045     bool m_inertialEarthRotation;
0046     bool m_mouseViewRotation;
0047     int m_steps;
0048     const int m_discreteZoomSteps = 120;
0049 };
0050 
0051 MarbleInputHandler::Protected::Protected(MarbleAbstractPresenter *marblePresenter)
0052     : m_marblePresenter( marblePresenter ),
0053       m_positionSignalConnected( false ),
0054       m_mouseWheelTimer( nullptr ),
0055       m_disabledMouseButtons( Qt::NoButton ),
0056       m_wheelZoomTargetDistance( 0.0 ),
0057       m_panViaArrowsEnabled( true ),
0058       m_inertialEarthRotation( true ),
0059       m_mouseViewRotation( true ),
0060       m_steps(0)
0061 {
0062 }
0063 
0064 MarbleInputHandler::MarbleInputHandler(MarbleAbstractPresenter *marblePresenter)
0065     : d(new Protected(marblePresenter))
0066 {
0067     d->m_mouseWheelTimer = new QTimer( this );
0068     connect(d->m_mouseWheelTimer, SIGNAL(timeout()), this, SLOT(restoreViewContext()));
0069 
0070     connect(d->m_marblePresenter->map(), SIGNAL(renderPluginInitialized(RenderPlugin*)),
0071              this, SLOT(installPluginEventFilter(RenderPlugin*)));
0072 }
0073 
0074 MarbleInputHandler::~MarbleInputHandler()
0075 {
0076     delete d->m_mouseWheelTimer;
0077     delete d;
0078 }
0079 
0080 void MarbleInputHandler::setPositionSignalConnected(bool connected)
0081 {
0082     d->m_positionSignalConnected = connected;
0083 }
0084 
0085 bool MarbleInputHandler::isPositionSignalConnected() const
0086 {
0087     return d->m_positionSignalConnected;
0088 }
0089 
0090 void MarbleInputHandler::setMouseButtonPopupEnabled(Qt::MouseButton mouseButton, bool enabled)
0091 {
0092     if (enabled)
0093     {
0094         d->m_disabledMouseButtons &= ~Qt::MouseButtons(mouseButton);
0095     }
0096     else
0097     {
0098         d->m_disabledMouseButtons |= mouseButton;
0099     }
0100 }
0101 
0102 bool MarbleInputHandler::isMouseButtonPopupEnabled(Qt::MouseButton mouseButton) const
0103 {
0104     return !(d->m_disabledMouseButtons & mouseButton);
0105 }
0106 
0107 void MarbleInputHandler::setPanViaArrowsEnabled(bool enabled)
0108 {
0109     d->m_panViaArrowsEnabled = enabled;
0110 }
0111 
0112 bool MarbleInputHandler::panViaArrowsEnabled() const
0113 {
0114     return d->m_panViaArrowsEnabled;
0115 }
0116 
0117 void MarbleInputHandler::setInertialEarthRotationEnabled(bool enabled)
0118 {
0119     d->m_inertialEarthRotation = enabled;
0120 }
0121 
0122 bool MarbleInputHandler::inertialEarthRotationEnabled() const
0123 {
0124     return d->m_inertialEarthRotation;
0125 }
0126 
0127 void MarbleInputHandler::setMouseViewRotationEnabled(bool enabled)
0128 {
0129     d->m_mouseViewRotation = enabled;
0130 }
0131 
0132 bool MarbleInputHandler::mouseViewRotationEnabled() const
0133 {
0134     return d->m_mouseViewRotation;
0135 }
0136 
0137 void MarbleInputHandler::stopInertialEarthRotation()
0138 {
0139 }
0140 
0141 class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private
0142 {
0143  public:
0144     Private();
0145     ~Private();
0146 
0147     QPixmap m_curpmtl;
0148     QPixmap m_curpmtc;
0149     QPixmap m_curpmtr;
0150     QPixmap m_curpmcr;
0151     QPixmap m_curpmcl;
0152     QPixmap m_curpmbl;
0153     QPixmap m_curpmbc;
0154     QPixmap m_curpmbr;
0155 
0156     QCursor m_arrowCur[3][3];
0157 
0158     // Indicates if the left mouse button has been pressed already.
0159     bool m_leftPressed;
0160     // Indicates if the middle mouse button has been pressed already.
0161     bool m_midPressed;
0162     // The mouse pointer x position when the left mouse button has been pressed.
0163     int m_leftPressedX;
0164     // The mouse pointer y position when the left mouse button has been pressed.
0165     int m_leftPressedY;
0166     // The mouse pointer y position when the middle mouse button has been pressed.
0167     int m_midPressedY;
0168     int m_startingRadius;
0169 
0170     // Indicates if the right mouse button has been pressed already.
0171     bool m_rightPressed;
0172     // Point where the right mouse button has been pressed on.
0173     QPoint m_rightOrigin;
0174     // Position to calculate the heading.
0175     // Indicates previous position since mouse has been moved.
0176     QPoint m_rightPosition;
0177     // Indicates the heading when the right mouse button has been pressed
0178     // and mouse is moving.
0179     qreal m_heading;
0180 
0181     // The center longitude in radian when the left mouse button has been pressed.
0182     qreal m_leftPressedLon;
0183     // The center latitude in radian when the left mouse button has been pressed.
0184     qreal m_leftPressedLat;
0185 
0186     int m_dragThreshold;
0187     QTimer m_lmbTimer;
0188     QTimer m_pressAndHoldTimer;
0189 
0190     // Models to handle the kinetic spinning.
0191     KineticModel m_kineticSpinning;
0192 
0193     QPoint m_selectionOrigin;
0194 
0195     QPointer<AbstractDataPluginItem> m_lastToolTipItem;
0196     QTimer m_toolTipTimer;
0197     QPoint m_toolTipPosition;
0198 };
0199 
0200 MarbleDefaultInputHandler::Private::Private()
0201     : m_leftPressed(false),
0202       m_midPressed(false),
0203       m_rightPressed(false),
0204       m_heading(0),
0205       m_dragThreshold(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 15 : 3)
0206 {
0207     m_curpmtl.load(QStringLiteral(":/marble/cursor/tl.png"));
0208     m_curpmtc.load(QStringLiteral(":/marble/cursor/tc.png"));
0209     m_curpmtr.load(QStringLiteral(":/marble/cursor/tr.png"));
0210     m_curpmcr.load(QStringLiteral(":/marble/cursor/cr.png"));
0211     m_curpmcl.load(QStringLiteral(":/marble/cursor/cl.png"));
0212     m_curpmbl.load(QStringLiteral(":/marble/cursor/bl.png"));
0213     m_curpmbc.load(QStringLiteral(":/marble/cursor/bc.png"));
0214     m_curpmbr.load(QStringLiteral(":/marble/cursor/br.png"));
0215 
0216     m_arrowCur[0][0] = QCursor( m_curpmtl, 2, 2 );
0217     m_arrowCur[1][0] = QCursor( m_curpmtc, 10, 3 );
0218     m_arrowCur[2][0] = QCursor( m_curpmtr, 19, 2 );
0219     m_arrowCur[0][1] = QCursor( m_curpmcl, 3, 10 );
0220     m_arrowCur[1][1] = QCursor( Qt::OpenHandCursor );
0221     m_arrowCur[2][1] = QCursor( m_curpmcr, 18, 10 );
0222     m_arrowCur[0][2] = QCursor( m_curpmbl, 2, 19 );
0223     m_arrowCur[1][2] = QCursor( m_curpmbc, 11, 18 );
0224     m_arrowCur[2][2] = QCursor( m_curpmbr, 19, 19 );
0225 }
0226 
0227 MarbleDefaultInputHandler::Private::~Private()
0228 {
0229 }
0230 
0231 MarbleDefaultInputHandler::MarbleDefaultInputHandler(MarbleAbstractPresenter *marblePresenter)
0232     : MarbleInputHandler(marblePresenter),
0233       d(new Private())
0234 {
0235     d->m_toolTipTimer.setSingleShot(true);
0236     d->m_toolTipTimer.setInterval(TOOLTIP_START_INTERVAL);
0237     connect(&d->m_toolTipTimer, SIGNAL(timeout()), this, SLOT(openItemToolTip()));
0238     d->m_lmbTimer.setSingleShot(true);
0239     connect(&d->m_lmbTimer, SIGNAL(timeout()), this, SLOT(lmbTimeout()));
0240 
0241     d->m_kineticSpinning.setUpdateInterval(35);
0242     connect(&d->m_kineticSpinning, SIGNAL(positionChanged(qreal,qreal)),
0243              MarbleInputHandler::d->m_marblePresenter, SLOT(centerOn(qreal,qreal)));
0244     connect(&d->m_kineticSpinning, SIGNAL(headingChanged(qreal)),
0245              MarbleInputHandler::d->m_marblePresenter, SLOT(headingOn(qreal)));
0246     connect(&d->m_kineticSpinning, SIGNAL(finished()), SLOT(restoreViewContext()));
0247 
0248     // Left and right mouse button signals.
0249     connect(this, SIGNAL(rmbRequest(int,int)), this, SLOT(showRmbMenu(int,int)));
0250     connect(this, SIGNAL(lmbRequest(int,int)), this, SLOT(showLmbMenu(int,int)));
0251 
0252     d->m_pressAndHoldTimer.setInterval(800);
0253     d->m_pressAndHoldTimer.setSingleShot(true);
0254     connect(&d->m_pressAndHoldTimer, SIGNAL(timeout()), this, SLOT(handlePressAndHold()));
0255 }
0256 
0257 MarbleDefaultInputHandler::~MarbleDefaultInputHandler()
0258 {
0259     delete d;
0260 }
0261 
0262 void MarbleDefaultInputHandler::stopInertialEarthRotation()
0263 {
0264     d->m_kineticSpinning.stop();
0265 }
0266 
0267 void MarbleDefaultInputHandler::lmbTimeout()
0268 {
0269     if (!selectionRubber()->isVisible())
0270     {
0271         qreal clickedLon = 0;
0272         qreal clickedLat = 0;
0273 
0274         bool isPointOnGlobe = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates( d->m_leftPressedX, d->m_leftPressedY,
0275                                                                         clickedLon, clickedLat,
0276                                                                         GeoDataCoordinates::Degree );
0277         emit lmbRequest(d->m_leftPressedX, d->m_leftPressedY);
0278 
0279         /**
0280          * emit mouse click only when the clicked
0281          * position is within the globe.
0282          */
0283         if ( isPointOnGlobe ) {
0284             emit mouseClickGeoPosition( clickedLon, clickedLat,
0285                                         GeoDataCoordinates::Degree );
0286         }
0287     }
0288 }
0289 
0290 void MarbleInputHandler::restoreViewContext()
0291 {
0292     // Needs to stop the timer since it repeats otherwise.
0293     d->m_mouseWheelTimer->stop();
0294 
0295     // Redraw the map with the quality set for Still (if necessary).
0296     d->m_marblePresenter->setViewContext(Still);
0297     d->m_marblePresenter->map()->viewport()->resetFocusPoint();
0298     d->m_wheelZoomTargetDistance = 0.0;
0299 }
0300 
0301 void MarbleDefaultInputHandler::hideSelectionIfCtrlReleased(QEvent *e)
0302 {
0303     if (selectionRubber()->isVisible() && e->type() == QEvent::MouseMove)
0304     {
0305         QMouseEvent *event = static_cast<QMouseEvent*>(e);
0306         if (!(event->modifiers() & Qt::ControlModifier))
0307         {
0308             selectionRubber()->hide();
0309         }
0310     }
0311 }
0312 
0313 bool MarbleDefaultInputHandler::handleDoubleClick(QMouseEvent *event)
0314 {
0315     qreal mouseLon;
0316     qreal mouseLat;
0317     const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(),
0318                                              mouseLon, mouseLat, GeoDataCoordinates::Radian);
0319     if(isMouseAboveMap)
0320     {
0321         d->m_pressAndHoldTimer.stop();
0322         d->m_lmbTimer.stop();
0323         MarbleInputHandler::d->m_marblePresenter->moveTo(event->pos(), 0.67);
0324     }
0325     return acceptMouse();
0326 }
0327 
0328 bool MarbleDefaultInputHandler::handleWheel(QWheelEvent *wheelevt)
0329 {
0330     MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
0331     marblePresenter->setViewContext(Animation);
0332 
0333     if( (MarbleInputHandler::d->m_steps > 0 && wheelevt->delta() < 0) ||
0334         (MarbleInputHandler::d->m_steps < 0 && wheelevt->delta() > 0) )
0335     {
0336         MarbleInputHandler::d->m_steps = wheelevt->delta();
0337     }
0338     else
0339     {
0340         MarbleInputHandler::d->m_steps += wheelevt->delta();
0341     }
0342 
0343     if (marblePresenter->map()->discreteZoom())
0344     {
0345         if(qAbs(MarbleInputHandler::d->m_steps) >= MarbleInputHandler::d->m_discreteZoomSteps)
0346         {
0347             marblePresenter->zoomAtBy(wheelevt->pos(), MarbleInputHandler::d->m_steps);
0348             MarbleInputHandler::d->m_steps = 0;
0349         }
0350     }
0351     else
0352     {
0353         qreal zoom = marblePresenter->zoom();
0354         qreal target = MarbleInputHandler::d->m_wheelZoomTargetDistance;
0355         if (marblePresenter->animationsEnabled() && target > 0.0)
0356         {
0357             // Do not use intermediate (interpolated) distance values caused by animations
0358             zoom = marblePresenter->zoomFromDistance(target);
0359         }
0360         qreal newDistance = marblePresenter->distanceFromZoom(zoom + MarbleInputHandler::d->m_steps);
0361         MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance;
0362         marblePresenter->zoomAt(wheelevt->pos(), newDistance);
0363         if (MarbleInputHandler::d->m_inertialEarthRotation)
0364         {
0365             d->m_kineticSpinning.jumpToPosition(MarbleInputHandler::d->m_marblePresenter->centerLongitude(),
0366                                                 MarbleInputHandler::d->m_marblePresenter->centerLatitude());
0367         }
0368         MarbleInputHandler::d->m_steps = 0;
0369     }
0370 
0371     MarbleInputHandler::d->m_mouseWheelTimer->start(400);
0372     return true;
0373 }
0374 
0375 bool MarbleDefaultInputHandler::handlePinch(const QPointF &center, qreal scaleFactor, Qt::GestureState state)
0376 {
0377     qreal  destLat;
0378     qreal  destLon;
0379 
0380     MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
0381 
0382     bool isValid = marblePresenter->map()->geoCoordinates(center.x(), center.y(),
0383                  destLon, destLat, GeoDataCoordinates::Radian );
0384 
0385     if (isValid)
0386     {
0387         marblePresenter->map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat));
0388     }
0389 
0390     qreal zoom, target, newDistance;
0391 
0392     qreal zoomDelta = scaleFactor > 1.0 ? scaleFactor : -1.0/scaleFactor;
0393 
0394     switch (state)
0395     {
0396     case Qt::NoGesture:
0397         break;
0398     case Qt::GestureStarted:
0399         marblePresenter->setViewContext(Animation);
0400         d->m_pressAndHoldTimer.stop();
0401         d->m_lmbTimer.stop();
0402         d->m_midPressed = false;
0403         d->m_leftPressed = false;
0404         break;
0405     case Qt::GestureUpdated:
0406         zoom = marblePresenter->zoom();
0407         target = MarbleInputHandler::d->m_wheelZoomTargetDistance;
0408         if (marblePresenter->animationsEnabled() && target > 0.0)
0409         {
0410             // Do not use intermediate (interpolated) distance values caused by animations
0411             zoom = marblePresenter->zoomFromDistance(target);
0412         }
0413         newDistance = marblePresenter->distanceFromZoom(zoom + 20 * zoomDelta);
0414         MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance;
0415         marblePresenter->zoomAt(center.toPoint(), newDistance);
0416         break;
0417     case Qt::GestureFinished:
0418         marblePresenter->map()->viewport()->resetFocusPoint();
0419         marblePresenter->setViewContext(Still);
0420         break;
0421     case Qt::GestureCanceled:
0422         marblePresenter->map()->viewport()->resetFocusPoint();
0423         marblePresenter->setViewContext(Still);
0424         break;
0425     }
0426     return true;
0427 }
0428 
0429 bool MarbleDefaultInputHandler::handleGesture(QGestureEvent *ge)
0430 {
0431     QPinchGesture *pinch = static_cast<QPinchGesture*>(ge->gesture(Qt::PinchGesture));
0432     if (!pinch)
0433     {
0434         return false;
0435     }
0436 
0437     qreal scaleFactor = pinch->scaleFactor();
0438     QPointF center = pinch->centerPoint();
0439 
0440     return handlePinch(center, scaleFactor, pinch->state());
0441 }
0442 
0443 void MarbleDefaultInputHandler::checkReleasedMove(QMouseEvent *event)
0444 {
0445     // To prevent error from lost MouseButtonRelease events
0446     if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::LeftButton))
0447     {
0448         if (d->m_leftPressed)
0449         {
0450             d->m_leftPressed = false;
0451 
0452             if (MarbleInputHandler::d->m_inertialEarthRotation)
0453             {
0454                 d->m_kineticSpinning.start();
0455             }
0456             else
0457             {
0458                 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
0459             }
0460         }
0461     }
0462     if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::MiddleButton))
0463     {
0464         d->m_midPressed = false;
0465     }
0466 }
0467 
0468 void MarbleDefaultInputHandler::handleMouseButtonPress(QMouseEvent *event)
0469 {
0470     if (event->button() == Qt::LeftButton )
0471     {
0472         d->m_pressAndHoldTimer.start();
0473        handleLeftMouseButtonPress(event);
0474     }
0475 
0476     if ( event->button() == Qt::MiddleButton )
0477     {
0478        handleMiddleMouseButtonPress(event);
0479     }
0480 
0481     if ( event->button() == Qt::RightButton )
0482     {
0483        handleRightMouseButtonPress(event);
0484     }
0485 }
0486 
0487 void MarbleDefaultInputHandler::handleLeftMouseButtonPress(QMouseEvent *event)
0488 {
0489     // silently enable the animation context without triggering a repaint
0490     MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(true);
0491     MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
0492     MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(false);
0493 
0494     if (isMouseButtonPopupEnabled(Qt::LeftButton))
0495     {
0496         d->m_lmbTimer.start(400);
0497     }
0498 
0499     d->m_leftPressed = true;
0500     d->m_midPressed = false;
0501     selectionRubber()->hide();
0502 
0503     // On the single event of a mouse button press these
0504     // values get stored, to enable us to e.g. calculate the
0505     // distance of a mouse drag while the mouse button is
0506     // still down.
0507     d->m_leftPressedX = event->x();
0508     d->m_leftPressedY = event->y();
0509 
0510     // Calculate translation of center point
0511     d->m_leftPressedLon = MarbleInputHandler::d->m_marblePresenter->centerLongitude();
0512     d->m_leftPressedLat = MarbleInputHandler::d->m_marblePresenter->centerLatitude();
0513 
0514     if (MarbleInputHandler::d->m_inertialEarthRotation)
0515     {
0516         d->m_kineticSpinning.stop();
0517         d->m_kineticSpinning.setPosition(d->m_leftPressedLon, d->m_leftPressedLat);
0518     }
0519 
0520     if (event->modifiers() & Qt::ControlModifier)
0521     {
0522         mDebug() << "Starting selection";
0523         d->m_pressAndHoldTimer.stop();
0524         d->m_lmbTimer.stop();
0525         d->m_selectionOrigin = event->pos();
0526         selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, QSize()));
0527         selectionRubber()->show();
0528     }
0529 }
0530 
0531 void MarbleDefaultInputHandler::handleMiddleMouseButtonPress(QMouseEvent *event)
0532 {
0533     d->m_midPressed = true;
0534     d->m_leftPressed = false;
0535     d->m_startingRadius = MarbleInputHandler::d->m_marblePresenter->radius();
0536     d->m_midPressedY = event->y();
0537 
0538     if (MarbleInputHandler::d->m_inertialEarthRotation)
0539     {
0540         d->m_kineticSpinning.start();
0541     }
0542 
0543     selectionRubber()->hide();
0544     MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
0545 }
0546 
0547 void MarbleDefaultInputHandler::handleRightMouseButtonPress(QMouseEvent *event)
0548 {
0549     d->m_rightPressed = true;
0550     d->m_rightOrigin = event->pos();
0551     d->m_rightPosition = event->pos();
0552     d->m_heading = MarbleInputHandler::d->m_marblePresenter->map()->heading();
0553     if (MarbleInputHandler::d->m_inertialEarthRotation)
0554     {
0555         d->m_kineticSpinning.stop();
0556         d->m_kineticSpinning.setHeading(d->m_heading);
0557     }
0558 }
0559 
0560 void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event)
0561 {
0562     if (event->button() == Qt::LeftButton)
0563     {
0564         d->m_pressAndHoldTimer.stop();
0565         //emit current coordinates to be interpreted
0566         //as requested
0567         emit mouseClickScreenPosition(d->m_leftPressedX, d->m_leftPressedY);
0568 
0569         d->m_leftPressed = false;
0570         if (MarbleInputHandler::d->m_inertialEarthRotation)
0571         {
0572             d->m_kineticSpinning.start();
0573         }
0574         else
0575         {
0576             MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
0577         }
0578     }
0579 
0580     if (event->button() == Qt::MiddleButton)
0581     {
0582         d->m_midPressed = false;
0583 
0584         MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
0585     }
0586 
0587     if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::RightButton)
0588     {
0589         if (d->m_rightOrigin == event->pos())
0590         {
0591             emit rmbRequest(event->x(), event->y());
0592         }
0593         d->m_rightPressed = false;
0594 
0595         if (MarbleInputHandler::d->m_inertialEarthRotation)
0596         {
0597             d->m_kineticSpinning.start();
0598         }
0599         else
0600         {
0601             MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
0602         }
0603     }
0604 
0605     if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::LeftButton
0606          && selectionRubber()->isVisible())
0607     {
0608         mDebug() << "Leaving selection";
0609         MarbleInputHandler::d->m_marblePresenter->setSelection(selectionRubber()->geometry());
0610         selectionRubber()->hide();
0611     }
0612 }
0613 
0614 void MarbleDefaultInputHandler::notifyPosition(bool isMouseAboveMap, qreal mouseLon, qreal mouseLat)
0615 {
0616     // emit the position string only if the signal got attached
0617     if (MarbleInputHandler::d->m_positionSignalConnected) {
0618         if (!isMouseAboveMap)
0619         {
0620             emit mouseMoveGeoPosition(QCoreApplication::translate( "Marble", NOT_AVAILABLE));
0621         }
0622         else
0623         {
0624             QString position = GeoDataCoordinates(mouseLon, mouseLat).toString();
0625             emit mouseMoveGeoPosition(position);
0626         }
0627     }
0628 }
0629 
0630 void MarbleDefaultInputHandler::adjustCursorShape(const QPoint &mousePosition, const QPoint &mouseDirection)
0631 {
0632     // Find out if there are data items and if one has defined an action
0633     QList<AbstractDataPluginItem *> dataItems
0634         = MarbleInputHandler::d->m_marblePresenter->map()->whichItemAt(mousePosition);
0635     bool dataAction = false;
0636     QPointer<AbstractDataPluginItem> toolTipItem;
0637     QList<AbstractDataPluginItem *>::iterator it = dataItems.begin();
0638     QList<AbstractDataPluginItem *>::iterator const end = dataItems.end();
0639     for (; it != end && dataAction == false && toolTipItem.isNull(); ++it)
0640     {
0641         if ((*it)->action())
0642         {
0643             dataAction = true;
0644         }
0645 
0646         if (!(*it)->toolTip().isNull() && toolTipItem.isNull())
0647         {
0648             toolTipItem = (*it);
0649         }
0650     }
0651 
0652     if (toolTipItem.isNull()) {
0653         d->m_toolTipTimer.stop();
0654     }
0655     else if (!( d->m_lastToolTipItem.data() == toolTipItem.data()))
0656     {
0657         d->m_toolTipTimer.start();
0658         d->m_lastToolTipItem = toolTipItem;
0659         d->m_toolTipPosition = mousePosition;
0660     }
0661     else
0662     {
0663         if (!d->m_toolTipTimer.isActive())
0664         {
0665             d->m_toolTipTimer.start();
0666         }
0667         d->m_toolTipPosition = mousePosition;
0668     }
0669 
0670     if (!dataAction && !MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) {
0671         if (!d->m_leftPressed)
0672         {
0673             d->m_arrowCur [1][1] = QCursor(Qt::OpenHandCursor);
0674         }
0675         else
0676         {
0677             d->m_arrowCur [1][1] = QCursor(Qt::ClosedHandCursor);
0678         }
0679     }
0680     else
0681     {
0682         if (!d->m_leftPressed)
0683         {
0684             d->m_arrowCur [1][1] = QCursor(Qt::PointingHandCursor);
0685         }
0686     }
0687 
0688     if (panViaArrowsEnabled())
0689     {
0690         setCursor(d->m_arrowCur[mouseDirection.x()+1][mouseDirection.y()+1]);
0691     }
0692     else
0693     {
0694         setCursor(d->m_arrowCur[1][1]);
0695     }
0696 }
0697 
0698 QPoint MarbleDefaultInputHandler::mouseMovedOutside(QMouseEvent *event)
0699 {   //Returns a 2d vector representing the direction in which the mouse left
0700     int dirX = 0;
0701     int dirY = 0;
0702     int polarity = MarbleInputHandler::d->m_marblePresenter->viewport()->polarity();
0703 
0704     if (d->m_leftPressed) {
0705         d->m_leftPressed = false;
0706 
0707         if (MarbleInputHandler::d->m_inertialEarthRotation)
0708         {
0709             d->m_kineticSpinning.start();
0710         }
0711     }
0712 
0713     QRect boundingRect = MarbleInputHandler::d->m_marblePresenter->viewport()->mapRegion().boundingRect();
0714 
0715     if (boundingRect.width() != 0)
0716     {
0717         dirX = (int)( 3 * (event->x() - boundingRect.left()) / boundingRect.width()) - 1;
0718     }
0719     if (dirX > 1)
0720     {
0721         dirX = 1;
0722     }
0723     if (dirX < -1)
0724     {
0725         dirX = -1;
0726     }
0727 
0728     if (boundingRect.height() != 0)
0729     {
0730         dirY = (int)(3 * (event->y() - boundingRect.top()) / boundingRect.height()) - 1;
0731     }
0732     if (dirY > 1)
0733     {
0734         dirY = 1;
0735     }
0736     if (dirY < -1)
0737     {
0738         dirY = -1;
0739     }
0740 
0741     if (event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress
0742             && panViaArrowsEnabled() && !d->m_kineticSpinning.hasVelocity())
0743     {
0744         d->m_pressAndHoldTimer.stop();
0745         d->m_lmbTimer.stop();
0746         qreal moveStep = MarbleInputHandler::d->m_marblePresenter->moveStep();
0747         if (polarity < 0)
0748         {
0749             MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(+dirX), moveStep * (qreal)(+dirY));
0750         }
0751         else
0752         {
0753             MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(-dirX), moveStep * (qreal)(+dirY));
0754         }
0755     }
0756 
0757     if (!MarbleInputHandler::d->m_inertialEarthRotation)
0758     {
0759         MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
0760     }
0761 
0762     return QPoint(dirX, dirY);
0763 }
0764 
0765 bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event)
0766 {
0767     QPoint direction;
0768 
0769     checkReleasedMove(event);
0770 
0771     // Do not handle (and therefore eat) mouse press and release events
0772     // that occur above visible float items. Mouse motion events are still
0773     // handled, however.
0774     if (event->type() != QEvent::MouseMove && !selectionRubber()->isVisible())
0775     {
0776         auto const floatItems = MarbleInputHandler::d->m_marblePresenter->map()->floatItems();
0777         for (AbstractFloatItem *floatItem: floatItems)
0778         {
0779             if ( floatItem->enabled() && floatItem->visible()
0780                  && floatItem->contains( event->pos() ) )
0781             {
0782                 d->m_pressAndHoldTimer.stop();
0783                 d->m_lmbTimer.stop();
0784                 return false;
0785             }
0786         }
0787     }
0788 
0789     qreal mouseLon;
0790     qreal mouseLat;
0791     const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(),
0792                                              mouseLon, mouseLat, GeoDataCoordinates::Radian);
0793     notifyPosition(isMouseAboveMap, mouseLon, mouseLat);
0794 
0795     QPoint mousePosition(event->x(), event->y());
0796 
0797     if (isMouseAboveMap || selectionRubber()->isVisible()
0798          || MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition))
0799     {
0800         if (event->type() == QEvent::MouseButtonPress)
0801         {
0802             handleMouseButtonPress(event);
0803         }
0804 
0805         if (event->type() == QEvent::MouseButtonRelease)
0806         {
0807             handleMouseButtonRelease(event);
0808         }
0809 
0810         const bool supportsViewportRotation = MarbleInputHandler::d->m_marblePresenter->map()->projection() == Spherical;
0811 
0812         // Regarding all kinds of mouse moves:
0813         if (d->m_leftPressed && !selectionRubber()->isVisible())
0814         {
0815             qreal radius = (qreal)(MarbleInputHandler::d->m_marblePresenter->radius());
0816             qreal deltax = event->x() - d->m_leftPressedX;
0817             qreal deltay = event->y() - d->m_leftPressedY;
0818 
0819             if (qAbs(deltax) > d->m_dragThreshold
0820                  || qAbs(deltay) > d->m_dragThreshold
0821                  || !d->m_lmbTimer.isActive())
0822             {
0823                 MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
0824 
0825                 d->m_pressAndHoldTimer.stop();
0826                 d->m_lmbTimer.stop();
0827                 Quaternion quat = Quaternion::fromSpherical( - M_PI/2 * deltax / radius, + M_PI/2 * deltay / radius );
0828                 if (supportsViewportRotation) {
0829                     const qreal heading = MarbleInputHandler::d->m_marblePresenter->map()->heading();
0830                     const Quaternion rotation = Quaternion::fromEuler( 0, 0, heading * DEG2RAD );
0831                     quat.rotateAroundAxis( rotation );
0832                 }
0833                 qreal lon, lat;
0834                 quat.getSpherical( lon, lat );
0835                 const qreal posLon = d->m_leftPressedLon + RAD2DEG * lon;
0836                 const qreal posLat = d->m_leftPressedLat + RAD2DEG * lat;
0837                 MarbleInputHandler::d->m_marblePresenter->centerOn(posLon, posLat);
0838                 if (MarbleInputHandler::d->m_inertialEarthRotation)
0839                 {
0840                     d->m_kineticSpinning.setPosition(posLon, posLat);
0841                 }
0842             }
0843         }
0844 
0845         if (d->m_midPressed)
0846         {
0847             int eventy = event->y();
0848             int dy = d->m_midPressedY - eventy;
0849             MarbleInputHandler::d->m_marblePresenter->setRadius(d->m_startingRadius * pow(1.005, dy));
0850         }
0851 
0852         if (d->m_rightPressed && supportsViewportRotation && MarbleInputHandler::d->m_mouseViewRotation)
0853         {
0854             qreal centerX, centerY;
0855             MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(
0856                 MarbleInputHandler::d->m_marblePresenter->centerLongitude(),
0857                 MarbleInputHandler::d->m_marblePresenter->centerLatitude(), centerX, centerY);
0858 
0859             // Deltas from previous position.
0860             int dx = event->x() - d->m_rightPosition.x();
0861             int dy = event->y() - d->m_rightPosition.y();
0862 
0863             d->m_rightPosition = event->pos();
0864 
0865             // Moving on the bottom should be opposite direction.
0866             int sign = event->y() > centerY ? -1 : 1;
0867             // Left top and right bottom sides for y axis should be opposite direction.
0868             if ((event->x() < centerX && event->y() < centerY) || (event->x() > centerX && event->y() > centerY))
0869             {
0870                 dy *= -1;
0871             }
0872 
0873             const qreal speedFactor = 0.3;
0874             d->m_heading += (dx + dy) * sign * speedFactor;
0875             MarbleInputHandler::d->m_marblePresenter->map()->setHeading(d->m_heading);
0876             if (MarbleInputHandler::d->m_inertialEarthRotation)
0877             {
0878                 d->m_kineticSpinning.setHeading(d->m_heading);
0879             }
0880         }
0881 
0882         if (selectionRubber()->isVisible())
0883         {
0884             // We change selection.
0885             selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, event->pos()).normalized());
0886         }
0887     }
0888     else
0889     {
0890         direction = mouseMovedOutside(event);
0891     }
0892 
0893     if (MarbleInputHandler::d->m_marblePresenter->viewContext() != Animation) {
0894         adjustCursorShape(mousePosition, direction);
0895     }
0896     return acceptMouse();
0897 }
0898 
0899 bool MarbleDefaultInputHandler::acceptMouse()
0900 {
0901     // let others, especially float items, still process the event
0902     // Note: This caused a bug in combination with oxygen, see https://bugs.kde.org/show_bug.cgi?id=242414
0903     // and changing it a related regression, see https://bugs.kde.org/show_bug.cgi?id=324862
0904     return false;
0905 }
0906 
0907 bool MarbleDefaultInputHandler::eventFilter(QObject* o, QEvent* e)
0908 {
0909     Q_UNUSED(o);
0910 
0911     if (layersEventFilter(o, e))
0912     {
0913         return true;
0914     }
0915 
0916     hideSelectionIfCtrlReleased(e);
0917 
0918     switch (e->type())
0919     {
0920     case QEvent::TouchBegin:
0921     case QEvent::TouchUpdate:
0922     case QEvent::TouchEnd:
0923         return handleTouch(static_cast<QTouchEvent *>(e));
0924     case QEvent::KeyPress:
0925         return handleKeyPress(static_cast<QKeyEvent *>(e));
0926     case QEvent::Gesture:
0927         return handleGesture(static_cast<QGestureEvent *>(e));
0928     case QEvent::Wheel:
0929         return handleWheel(static_cast<QWheelEvent*>(e));
0930     case QEvent::MouseButtonDblClick:
0931         return handleDoubleClick(static_cast<QMouseEvent*>(e));
0932     case QEvent::MouseButtonPress:
0933     case QEvent::MouseButtonRelease:
0934     case QEvent::MouseMove:
0935         return handleMouseEvent(static_cast<QMouseEvent*>(e));
0936     default:
0937         return false;
0938     }
0939 }
0940 
0941 bool MarbleDefaultInputHandler::handleTouch(QTouchEvent*)
0942 {
0943     return false; //reimplement to handle in cases of QML and PinchArea element
0944 }
0945 
0946 bool MarbleDefaultInputHandler::handleKeyPress(QKeyEvent* event)
0947 {
0948     if ( event->type() == QEvent::KeyPress ) {
0949         MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
0950         bool handled = true;
0951         switch ( event->key() ) {
0952         case Qt::Key_Left:
0953             stopInertialEarthRotation();
0954             marblePresenter->moveByStep(-1, 0, Marble::Linear);
0955             break;
0956         case Qt::Key_Right:
0957             stopInertialEarthRotation();
0958             marblePresenter->moveByStep(1, 0, Marble::Linear);
0959             break;
0960         case Qt::Key_Up:
0961             stopInertialEarthRotation();
0962             marblePresenter->moveByStep(0, -1, Marble::Linear);
0963             break;
0964         case Qt::Key_Down:
0965             stopInertialEarthRotation();
0966             marblePresenter->moveByStep(0, 1, Marble::Linear);
0967             break;
0968         case Qt::Key_Plus:
0969             if (event->modifiers() != Qt::ControlModifier) {
0970                 stopInertialEarthRotation();
0971                 marblePresenter->zoomIn();
0972             }
0973             break;
0974         case Qt::Key_Minus:
0975             if (event->modifiers() != Qt::ControlModifier) {
0976                 stopInertialEarthRotation();
0977                 marblePresenter->zoomOut();
0978             }
0979             break;
0980         case Qt::Key_Home:
0981             stopInertialEarthRotation();
0982             marblePresenter->goHome();
0983             break;
0984         default:
0985             handled = false;
0986             break;
0987         }
0988 
0989         return handled;
0990     }
0991     return false;
0992 }
0993 
0994 void MarbleDefaultInputHandler::handleMouseButtonPressAndHold(const QPoint &)
0995 {
0996     // Default implementation does nothing
0997 }
0998 
0999 void MarbleDefaultInputHandler::handlePressAndHold()
1000 {
1001     handleMouseButtonPressAndHold(QPoint(d->m_leftPressedX, d->m_leftPressedY));
1002 }
1003 
1004 const AbstractDataPluginItem *MarbleDefaultInputHandler::lastToolTipItem() const
1005 {
1006     return d->m_lastToolTipItem;
1007 }
1008 
1009 QTimer* MarbleDefaultInputHandler::toolTipTimer()
1010 {
1011     return &d->m_toolTipTimer;
1012 }
1013 
1014 QPoint MarbleDefaultInputHandler::toolTipPosition() const
1015 {
1016     return d->m_toolTipPosition;
1017 }
1018 
1019 }
1020 
1021 #include "moc_MarbleInputHandler.cpp"
1022