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 ¢er, 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