Marble

MarbleInputHandler.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5// SPDX-FileCopyrightText: 2014 Adam Dabrowski <adamdbrw@gmail.com>
6//
7
8#include "MarbleInputHandler.h"
9
10#include <QPoint>
11#include <QPointer>
12#include <QTimer>
13#include <QCursor>
14#include <QMouseEvent>
15#include <QPixmap>
16#include <QGestureEvent>
17#include <QPinchGesture>
18
19#include "kineticmodel.h"
20#include "MarbleGlobal.h"
21#include "MarbleDebug.h"
22#include "MarbleMap.h"
23#include "MarbleAbstractPresenter.h"
24#include "ViewportParams.h"
25#include "AbstractFloatItem.h"
26#include "AbstractDataPluginItem.h"
27#include "AbstractProjection.h"
28#include "RenderPlugin.h"
29
30namespace Marble {
31
32const int TOOLTIP_START_INTERVAL = 1000;
33
34class Q_DECL_HIDDEN MarbleInputHandler::Protected
35{
36public:
37 Protected(MarbleAbstractPresenter *marblePresenter);
38
39 MarbleAbstractPresenter *const m_marblePresenter;
40 bool m_positionSignalConnected;
41 QTimer *m_mouseWheelTimer;
42 Qt::MouseButtons m_disabledMouseButtons;
43 qreal m_wheelZoomTargetDistance;
44 bool m_panViaArrowsEnabled;
45 bool m_inertialEarthRotation;
46 bool m_mouseViewRotation;
47 int m_steps;
48 const int m_discreteZoomSteps = 120;
49};
50
51MarbleInputHandler::Protected::Protected(MarbleAbstractPresenter *marblePresenter)
52 : m_marblePresenter( marblePresenter ),
53 m_positionSignalConnected( false ),
54 m_mouseWheelTimer( nullptr ),
55 m_disabledMouseButtons( Qt::NoButton ),
56 m_wheelZoomTargetDistance( 0.0 ),
57 m_panViaArrowsEnabled( true ),
58 m_inertialEarthRotation( true ),
59 m_mouseViewRotation( true ),
60 m_steps(0)
61{
62}
63
64MarbleInputHandler::MarbleInputHandler(MarbleAbstractPresenter *marblePresenter)
65 : d(new Protected(marblePresenter))
66{
67 d->m_mouseWheelTimer = new QTimer( this );
68 connect(d->m_mouseWheelTimer, SIGNAL(timeout()), this, SLOT(restoreViewContext()));
69
70 connect(d->m_marblePresenter->map(), SIGNAL(renderPluginInitialized(RenderPlugin*)),
71 this, SLOT(installPluginEventFilter(RenderPlugin*)));
72}
73
74MarbleInputHandler::~MarbleInputHandler()
75{
76 delete d->m_mouseWheelTimer;
77 delete d;
78}
79
80void MarbleInputHandler::setPositionSignalConnected(bool connected)
81{
82 d->m_positionSignalConnected = connected;
83}
84
85bool MarbleInputHandler::isPositionSignalConnected() const
86{
87 return d->m_positionSignalConnected;
88}
89
90void MarbleInputHandler::setMouseButtonPopupEnabled(Qt::MouseButton mouseButton, bool enabled)
91{
92 if (enabled)
93 {
94 d->m_disabledMouseButtons &= ~Qt::MouseButtons(mouseButton);
95 }
96 else
97 {
98 d->m_disabledMouseButtons |= mouseButton;
99 }
100}
101
102bool MarbleInputHandler::isMouseButtonPopupEnabled(Qt::MouseButton mouseButton) const
103{
104 return !(d->m_disabledMouseButtons & mouseButton);
105}
106
107void MarbleInputHandler::setPanViaArrowsEnabled(bool enabled)
108{
109 d->m_panViaArrowsEnabled = enabled;
110}
111
112bool MarbleInputHandler::panViaArrowsEnabled() const
113{
114 return d->m_panViaArrowsEnabled;
115}
116
117void MarbleInputHandler::setInertialEarthRotationEnabled(bool enabled)
118{
119 d->m_inertialEarthRotation = enabled;
120}
121
122bool MarbleInputHandler::inertialEarthRotationEnabled() const
123{
124 return d->m_inertialEarthRotation;
125}
126
127void MarbleInputHandler::setMouseViewRotationEnabled(bool enabled)
128{
129 d->m_mouseViewRotation = enabled;
130}
131
132bool MarbleInputHandler::mouseViewRotationEnabled() const
133{
134 return d->m_mouseViewRotation;
135}
136
137void MarbleInputHandler::stopInertialEarthRotation()
138{
139}
140
141class Q_DECL_HIDDEN MarbleDefaultInputHandler::Private
142{
143 public:
144 Private();
145 ~Private();
146
147 QPixmap m_curpmtl;
148 QPixmap m_curpmtc;
149 QPixmap m_curpmtr;
150 QPixmap m_curpmcr;
151 QPixmap m_curpmcl;
152 QPixmap m_curpmbl;
153 QPixmap m_curpmbc;
154 QPixmap m_curpmbr;
155
156 QCursor m_arrowCur[3][3];
157
158 // Indicates if the left mouse button has been pressed already.
159 bool m_leftPressed;
160 // Indicates if the middle mouse button has been pressed already.
161 bool m_midPressed;
162 // The mouse pointer x position when the left mouse button has been pressed.
163 int m_leftPressedX;
164 // The mouse pointer y position when the left mouse button has been pressed.
165 int m_leftPressedY;
166 // The mouse pointer y position when the middle mouse button has been pressed.
167 int m_midPressedY;
168 int m_startingRadius;
169
170 // Indicates if the right mouse button has been pressed already.
171 bool m_rightPressed;
172 // Point where the right mouse button has been pressed on.
173 QPoint m_rightOrigin;
174 // Position to calculate the heading.
175 // Indicates previous position since mouse has been moved.
176 QPoint m_rightPosition;
177 // Indicates the heading when the right mouse button has been pressed
178 // and mouse is moving.
179 qreal m_heading;
180
181 // The center longitude in radian when the left mouse button has been pressed.
182 qreal m_leftPressedLon;
183 // The center latitude in radian when the left mouse button has been pressed.
184 qreal m_leftPressedLat;
185
186 int m_dragThreshold;
187 QTimer m_lmbTimer;
188 QTimer m_pressAndHoldTimer;
189
190 // Models to handle the kinetic spinning.
191 KineticModel m_kineticSpinning;
192
193 QPoint m_selectionOrigin;
194
195 QPointer<AbstractDataPluginItem> m_lastToolTipItem;
196 QTimer m_toolTipTimer;
197 QPoint m_toolTipPosition;
198};
199
200MarbleDefaultInputHandler::Private::Private()
201 : m_leftPressed(false),
202 m_midPressed(false),
203 m_rightPressed(false),
204 m_heading(0),
205 m_dragThreshold(MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 15 : 3)
206{
207 m_curpmtl.load(QStringLiteral(":/marble/cursor/tl.png"));
208 m_curpmtc.load(QStringLiteral(":/marble/cursor/tc.png"));
209 m_curpmtr.load(QStringLiteral(":/marble/cursor/tr.png"));
210 m_curpmcr.load(QStringLiteral(":/marble/cursor/cr.png"));
211 m_curpmcl.load(QStringLiteral(":/marble/cursor/cl.png"));
212 m_curpmbl.load(QStringLiteral(":/marble/cursor/bl.png"));
213 m_curpmbc.load(QStringLiteral(":/marble/cursor/bc.png"));
214 m_curpmbr.load(QStringLiteral(":/marble/cursor/br.png"));
215
216 m_arrowCur[0][0] = QCursor( m_curpmtl, 2, 2 );
217 m_arrowCur[1][0] = QCursor( m_curpmtc, 10, 3 );
218 m_arrowCur[2][0] = QCursor( m_curpmtr, 19, 2 );
219 m_arrowCur[0][1] = QCursor( m_curpmcl, 3, 10 );
220 m_arrowCur[1][1] = QCursor( Qt::OpenHandCursor );
221 m_arrowCur[2][1] = QCursor( m_curpmcr, 18, 10 );
222 m_arrowCur[0][2] = QCursor( m_curpmbl, 2, 19 );
223 m_arrowCur[1][2] = QCursor( m_curpmbc, 11, 18 );
224 m_arrowCur[2][2] = QCursor( m_curpmbr, 19, 19 );
225}
226
227MarbleDefaultInputHandler::Private::~Private()
228{
229}
230
231MarbleDefaultInputHandler::MarbleDefaultInputHandler(MarbleAbstractPresenter *marblePresenter)
232 : MarbleInputHandler(marblePresenter),
233 d(new Private())
234{
235 d->m_toolTipTimer.setSingleShot(true);
236 d->m_toolTipTimer.setInterval(TOOLTIP_START_INTERVAL);
237 connect(&d->m_toolTipTimer, SIGNAL(timeout()), this, SLOT(openItemToolTip()));
238 d->m_lmbTimer.setSingleShot(true);
239 connect(&d->m_lmbTimer, SIGNAL(timeout()), this, SLOT(lmbTimeout()));
240
241 d->m_kineticSpinning.setUpdateInterval(35);
242 connect(&d->m_kineticSpinning, SIGNAL(positionChanged(qreal,qreal)),
243 MarbleInputHandler::d->m_marblePresenter, SLOT(centerOn(qreal,qreal)));
244 connect(&d->m_kineticSpinning, SIGNAL(headingChanged(qreal)),
245 MarbleInputHandler::d->m_marblePresenter, SLOT(headingOn(qreal)));
246 connect(&d->m_kineticSpinning, SIGNAL(finished()), SLOT(restoreViewContext()));
247
248 // Left and right mouse button signals.
249 connect(this, SIGNAL(rmbRequest(int,int)), this, SLOT(showRmbMenu(int,int)));
250 connect(this, SIGNAL(lmbRequest(int,int)), this, SLOT(showLmbMenu(int,int)));
251
252 d->m_pressAndHoldTimer.setInterval(800);
253 d->m_pressAndHoldTimer.setSingleShot(true);
254 connect(&d->m_pressAndHoldTimer, SIGNAL(timeout()), this, SLOT(handlePressAndHold()));
255}
256
257MarbleDefaultInputHandler::~MarbleDefaultInputHandler()
258{
259 delete d;
260}
261
262void MarbleDefaultInputHandler::stopInertialEarthRotation()
263{
264 d->m_kineticSpinning.stop();
265}
266
267void MarbleDefaultInputHandler::lmbTimeout()
268{
269 if (!selectionRubber()->isVisible())
270 {
271 qreal clickedLon = 0;
272 qreal clickedLat = 0;
273
274 bool isPointOnGlobe = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates( d->m_leftPressedX, d->m_leftPressedY,
275 clickedLon, clickedLat,
276 GeoDataCoordinates::Degree );
277 emit lmbRequest(d->m_leftPressedX, d->m_leftPressedY);
278
279 /**
280 * emit mouse click only when the clicked
281 * position is within the globe.
282 */
283 if ( isPointOnGlobe ) {
284 emit mouseClickGeoPosition( clickedLon, clickedLat,
285 GeoDataCoordinates::Degree );
286 }
287 }
288}
289
290void MarbleInputHandler::restoreViewContext()
291{
292 // Needs to stop the timer since it repeats otherwise.
293 d->m_mouseWheelTimer->stop();
294
295 // Redraw the map with the quality set for Still (if necessary).
296 d->m_marblePresenter->setViewContext(Still);
297 d->m_marblePresenter->map()->viewport()->resetFocusPoint();
298 d->m_wheelZoomTargetDistance = 0.0;
299}
300
301void MarbleDefaultInputHandler::hideSelectionIfCtrlReleased(QEvent *e)
302{
303 if (selectionRubber()->isVisible() && e->type() == QEvent::MouseMove)
304 {
305 QMouseEvent *event = static_cast<QMouseEvent*>(e);
306 if (!(event->modifiers() & Qt::ControlModifier))
307 {
308 selectionRubber()->hide();
309 }
310 }
311}
312
313bool MarbleDefaultInputHandler::handleDoubleClick(QMouseEvent *event)
314{
315 qreal mouseLon;
316 qreal mouseLat;
317 const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(),
318 mouseLon, mouseLat, GeoDataCoordinates::Radian);
319 if(isMouseAboveMap)
320 {
321 d->m_pressAndHoldTimer.stop();
322 d->m_lmbTimer.stop();
323 MarbleInputHandler::d->m_marblePresenter->moveTo(event->pos(), 0.67);
324 }
325 return acceptMouse();
326}
327
328bool MarbleDefaultInputHandler::handleWheel(QWheelEvent *wheelevt)
329{
330 MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
331 marblePresenter->setViewContext(Animation);
332
333 if( (MarbleInputHandler::d->m_steps > 0 && wheelevt->delta() < 0) ||
334 (MarbleInputHandler::d->m_steps < 0 && wheelevt->delta() > 0) )
335 {
336 MarbleInputHandler::d->m_steps = wheelevt->delta();
337 }
338 else
339 {
340 MarbleInputHandler::d->m_steps += wheelevt->delta();
341 }
342
343 if (marblePresenter->map()->discreteZoom())
344 {
345 if(qAbs(MarbleInputHandler::d->m_steps) >= MarbleInputHandler::d->m_discreteZoomSteps)
346 {
347 marblePresenter->zoomAtBy(wheelevt->pos(), MarbleInputHandler::d->m_steps);
348 MarbleInputHandler::d->m_steps = 0;
349 }
350 }
351 else
352 {
353 qreal zoom = marblePresenter->zoom();
354 qreal target = MarbleInputHandler::d->m_wheelZoomTargetDistance;
355 if (marblePresenter->animationsEnabled() && target > 0.0)
356 {
357 // Do not use intermediate (interpolated) distance values caused by animations
358 zoom = marblePresenter->zoomFromDistance(target);
359 }
360 qreal newDistance = marblePresenter->distanceFromZoom(zoom + MarbleInputHandler::d->m_steps);
361 MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance;
362 marblePresenter->zoomAt(wheelevt->pos(), newDistance);
363 if (MarbleInputHandler::d->m_inertialEarthRotation)
364 {
365 d->m_kineticSpinning.jumpToPosition(MarbleInputHandler::d->m_marblePresenter->centerLongitude(),
366 MarbleInputHandler::d->m_marblePresenter->centerLatitude());
367 }
368 MarbleInputHandler::d->m_steps = 0;
369 }
370
371 MarbleInputHandler::d->m_mouseWheelTimer->start(400);
372 return true;
373}
374
375bool MarbleDefaultInputHandler::handlePinch(const QPointF &center, qreal scaleFactor, Qt::GestureState state)
376{
377 qreal destLat;
378 qreal destLon;
379
380 MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
381
382 bool isValid = marblePresenter->map()->geoCoordinates(center.x(), center.y(),
383 destLon, destLat, GeoDataCoordinates::Radian );
384
385 if (isValid)
386 {
387 marblePresenter->map()->viewport()->setFocusPoint(GeoDataCoordinates(destLon, destLat));
388 }
389
390 qreal zoom, target, newDistance;
391
392 qreal zoomDelta = scaleFactor > 1.0 ? scaleFactor : -1.0/scaleFactor;
393
394 switch (state)
395 {
396 case Qt::NoGesture:
397 break;
399 marblePresenter->setViewContext(Animation);
400 d->m_pressAndHoldTimer.stop();
401 d->m_lmbTimer.stop();
402 d->m_midPressed = false;
403 d->m_leftPressed = false;
404 break;
406 zoom = marblePresenter->zoom();
407 target = MarbleInputHandler::d->m_wheelZoomTargetDistance;
408 if (marblePresenter->animationsEnabled() && target > 0.0)
409 {
410 // Do not use intermediate (interpolated) distance values caused by animations
411 zoom = marblePresenter->zoomFromDistance(target);
412 }
413 newDistance = marblePresenter->distanceFromZoom(zoom + 20 * zoomDelta);
414 MarbleInputHandler::d->m_wheelZoomTargetDistance = newDistance;
415 marblePresenter->zoomAt(center.toPoint(), newDistance);
416 break;
418 marblePresenter->map()->viewport()->resetFocusPoint();
419 marblePresenter->setViewContext(Still);
420 break;
422 marblePresenter->map()->viewport()->resetFocusPoint();
423 marblePresenter->setViewContext(Still);
424 break;
425 }
426 return true;
427}
428
429bool MarbleDefaultInputHandler::handleGesture(QGestureEvent *ge)
430{
431 QPinchGesture *pinch = static_cast<QPinchGesture*>(ge->gesture(Qt::PinchGesture));
432 if (!pinch)
433 {
434 return false;
435 }
436
437 qreal scaleFactor = pinch->scaleFactor();
438 QPointF center = pinch->centerPoint();
439
440 return handlePinch(center, scaleFactor, pinch->state());
441}
442
443void MarbleDefaultInputHandler::checkReleasedMove(QMouseEvent *event)
444{
445 // To prevent error from lost MouseButtonRelease events
446 if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::LeftButton))
447 {
448 if (d->m_leftPressed)
449 {
450 d->m_leftPressed = false;
451
452 if (MarbleInputHandler::d->m_inertialEarthRotation)
453 {
454 d->m_kineticSpinning.start();
455 }
456 else
457 {
458 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
459 }
460 }
461 }
462 if (event->type() == QEvent::MouseMove && !(event->buttons() & Qt::MiddleButton))
463 {
464 d->m_midPressed = false;
465 }
466}
467
468void MarbleDefaultInputHandler::handleMouseButtonPress(QMouseEvent *event)
469{
470 if (event->button() == Qt::LeftButton )
471 {
472 d->m_pressAndHoldTimer.start();
473 handleLeftMouseButtonPress(event);
474 }
475
476 if ( event->button() == Qt::MiddleButton )
477 {
478 handleMiddleMouseButtonPress(event);
479 }
480
481 if ( event->button() == Qt::RightButton )
482 {
483 handleRightMouseButtonPress(event);
484 }
485}
486
487void MarbleDefaultInputHandler::handleLeftMouseButtonPress(QMouseEvent *event)
488{
489 // silently enable the animation context without triggering a repaint
490 MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(true);
491 MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
492 MarbleInputHandler::d->m_marblePresenter->map()->blockSignals(false);
493
494 if (isMouseButtonPopupEnabled(Qt::LeftButton))
495 {
496 d->m_lmbTimer.start(400);
497 }
498
499 d->m_leftPressed = true;
500 d->m_midPressed = false;
501 selectionRubber()->hide();
502
503 // On the single event of a mouse button press these
504 // values get stored, to enable us to e.g. calculate the
505 // distance of a mouse drag while the mouse button is
506 // still down.
507 d->m_leftPressedX = event->x();
508 d->m_leftPressedY = event->y();
509
510 // Calculate translation of center point
511 d->m_leftPressedLon = MarbleInputHandler::d->m_marblePresenter->centerLongitude();
512 d->m_leftPressedLat = MarbleInputHandler::d->m_marblePresenter->centerLatitude();
513
514 if (MarbleInputHandler::d->m_inertialEarthRotation)
515 {
516 d->m_kineticSpinning.stop();
517 d->m_kineticSpinning.setPosition(d->m_leftPressedLon, d->m_leftPressedLat);
518 }
519
520 if (event->modifiers() & Qt::ControlModifier)
521 {
522 mDebug() << "Starting selection";
523 d->m_pressAndHoldTimer.stop();
524 d->m_lmbTimer.stop();
525 d->m_selectionOrigin = event->pos();
526 selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, QSize()));
527 selectionRubber()->show();
528 }
529}
530
531void MarbleDefaultInputHandler::handleMiddleMouseButtonPress(QMouseEvent *event)
532{
533 d->m_midPressed = true;
534 d->m_leftPressed = false;
535 d->m_startingRadius = MarbleInputHandler::d->m_marblePresenter->radius();
536 d->m_midPressedY = event->y();
537
538 if (MarbleInputHandler::d->m_inertialEarthRotation)
539 {
540 d->m_kineticSpinning.start();
541 }
542
543 selectionRubber()->hide();
544 MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
545}
546
547void MarbleDefaultInputHandler::handleRightMouseButtonPress(QMouseEvent *event)
548{
549 d->m_rightPressed = true;
550 d->m_rightOrigin = event->pos();
551 d->m_rightPosition = event->pos();
552 d->m_heading = MarbleInputHandler::d->m_marblePresenter->map()->heading();
553 if (MarbleInputHandler::d->m_inertialEarthRotation)
554 {
555 d->m_kineticSpinning.stop();
556 d->m_kineticSpinning.setHeading(d->m_heading);
557 }
558}
559
560void MarbleDefaultInputHandler::handleMouseButtonRelease(QMouseEvent *event)
561{
562 if (event->button() == Qt::LeftButton)
563 {
564 d->m_pressAndHoldTimer.stop();
565 //emit current coordinates to be interpreted
566 //as requested
567 emit mouseClickScreenPosition(d->m_leftPressedX, d->m_leftPressedY);
568
569 d->m_leftPressed = false;
570 if (MarbleInputHandler::d->m_inertialEarthRotation)
571 {
572 d->m_kineticSpinning.start();
573 }
574 else
575 {
576 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
577 }
578 }
579
580 if (event->button() == Qt::MiddleButton)
581 {
582 d->m_midPressed = false;
583
584 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
585 }
586
587 if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::RightButton)
588 {
589 if (d->m_rightOrigin == event->pos())
590 {
591 emit rmbRequest(event->x(), event->y());
592 }
593 d->m_rightPressed = false;
594
595 if (MarbleInputHandler::d->m_inertialEarthRotation)
596 {
597 d->m_kineticSpinning.start();
598 }
599 else
600 {
601 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
602 }
603 }
604
605 if (event->type() == QEvent::MouseButtonRelease && event->button() == Qt::LeftButton
606 && selectionRubber()->isVisible())
607 {
608 mDebug() << "Leaving selection";
609 MarbleInputHandler::d->m_marblePresenter->setSelection(selectionRubber()->geometry());
610 selectionRubber()->hide();
611 }
612}
613
614void MarbleDefaultInputHandler::notifyPosition(bool isMouseAboveMap, qreal mouseLon, qreal mouseLat)
615{
616 // emit the position string only if the signal got attached
617 if (MarbleInputHandler::d->m_positionSignalConnected) {
618 if (!isMouseAboveMap)
619 {
620 emit mouseMoveGeoPosition(QCoreApplication::translate( "Marble", NOT_AVAILABLE));
621 }
622 else
623 {
624 QString position = GeoDataCoordinates(mouseLon, mouseLat).toString();
625 emit mouseMoveGeoPosition(position);
626 }
627 }
628}
629
630void MarbleDefaultInputHandler::adjustCursorShape(const QPoint &mousePosition, const QPoint &mouseDirection)
631{
632 // Find out if there are data items and if one has defined an action
634 = MarbleInputHandler::d->m_marblePresenter->map()->whichItemAt(mousePosition);
635 bool dataAction = false;
639 for (; it != end && dataAction == false && toolTipItem.isNull(); ++it)
640 {
641 if ((*it)->action())
642 {
643 dataAction = true;
644 }
645
646 if (!(*it)->toolTip().isNull() && toolTipItem.isNull())
647 {
648 toolTipItem = (*it);
649 }
650 }
651
652 if (toolTipItem.isNull()) {
653 d->m_toolTipTimer.stop();
654 }
655 else if (!( d->m_lastToolTipItem.data() == toolTipItem.data()))
656 {
657 d->m_toolTipTimer.start();
658 d->m_lastToolTipItem = toolTipItem;
659 d->m_toolTipPosition = mousePosition;
660 }
661 else
662 {
663 if (!d->m_toolTipTimer.isActive())
664 {
665 d->m_toolTipTimer.start();
666 }
667 d->m_toolTipPosition = mousePosition;
668 }
669
670 if (!dataAction && !MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition)) {
671 if (!d->m_leftPressed)
672 {
673 d->m_arrowCur [1][1] = QCursor(Qt::OpenHandCursor);
674 }
675 else
676 {
677 d->m_arrowCur [1][1] = QCursor(Qt::ClosedHandCursor);
678 }
679 }
680 else
681 {
682 if (!d->m_leftPressed)
683 {
684 d->m_arrowCur [1][1] = QCursor(Qt::PointingHandCursor);
685 }
686 }
687
688 if (panViaArrowsEnabled())
689 {
690 setCursor(d->m_arrowCur[mouseDirection.x()+1][mouseDirection.y()+1]);
691 }
692 else
693 {
694 setCursor(d->m_arrowCur[1][1]);
695 }
696}
697
698QPoint MarbleDefaultInputHandler::mouseMovedOutside(QMouseEvent *event)
699{ //Returns a 2d vector representing the direction in which the mouse left
700 int dirX = 0;
701 int dirY = 0;
702 int polarity = MarbleInputHandler::d->m_marblePresenter->viewport()->polarity();
703
704 if (d->m_leftPressed) {
705 d->m_leftPressed = false;
706
707 if (MarbleInputHandler::d->m_inertialEarthRotation)
708 {
709 d->m_kineticSpinning.start();
710 }
711 }
712
713 QRect boundingRect = MarbleInputHandler::d->m_marblePresenter->viewport()->mapRegion().boundingRect();
714
715 if (boundingRect.width() != 0)
716 {
717 dirX = (int)( 3 * (event->x() - boundingRect.left()) / boundingRect.width()) - 1;
718 }
719 if (dirX > 1)
720 {
721 dirX = 1;
722 }
723 if (dirX < -1)
724 {
725 dirX = -1;
726 }
727
728 if (boundingRect.height() != 0)
729 {
730 dirY = (int)(3 * (event->y() - boundingRect.top()) / boundingRect.height()) - 1;
731 }
732 if (dirY > 1)
733 {
734 dirY = 1;
735 }
736 if (dirY < -1)
737 {
738 dirY = -1;
739 }
740
741 if (event->button() == Qt::LeftButton && event->type() == QEvent::MouseButtonPress
742 && panViaArrowsEnabled() && !d->m_kineticSpinning.hasVelocity())
743 {
744 d->m_pressAndHoldTimer.stop();
745 d->m_lmbTimer.stop();
746 qreal moveStep = MarbleInputHandler::d->m_marblePresenter->moveStep();
747 if (polarity < 0)
748 {
749 MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(+dirX), moveStep * (qreal)(+dirY));
750 }
751 else
752 {
753 MarbleInputHandler::d->m_marblePresenter->rotateBy(-moveStep * (qreal)(-dirX), moveStep * (qreal)(+dirY));
754 }
755 }
756
757 if (!MarbleInputHandler::d->m_inertialEarthRotation)
758 {
759 MarbleInputHandler::d->m_marblePresenter->setViewContext(Still);
760 }
761
762 return QPoint(dirX, dirY);
763}
764
765bool MarbleDefaultInputHandler::handleMouseEvent(QMouseEvent *event)
766{
767 QPoint direction;
768
769 checkReleasedMove(event);
770
771 // Do not handle (and therefore eat) mouse press and release events
772 // that occur above visible float items. Mouse motion events are still
773 // handled, however.
774 if (event->type() != QEvent::MouseMove && !selectionRubber()->isVisible())
775 {
776 auto const floatItems = MarbleInputHandler::d->m_marblePresenter->map()->floatItems();
777 for (AbstractFloatItem *floatItem: floatItems)
778 {
779 if ( floatItem->enabled() && floatItem->visible()
780 && floatItem->contains( event->pos() ) )
781 {
782 d->m_pressAndHoldTimer.stop();
783 d->m_lmbTimer.stop();
784 return false;
785 }
786 }
787 }
788
789 qreal mouseLon;
790 qreal mouseLat;
791 const bool isMouseAboveMap = MarbleInputHandler::d->m_marblePresenter->map()->geoCoordinates(event->x(), event->y(),
792 mouseLon, mouseLat, GeoDataCoordinates::Radian);
793 notifyPosition(isMouseAboveMap, mouseLon, mouseLat);
794
795 QPoint mousePosition(event->x(), event->y());
796
797 if (isMouseAboveMap || selectionRubber()->isVisible()
798 || MarbleInputHandler::d->m_marblePresenter->map()->hasFeatureAt(mousePosition))
799 {
800 if (event->type() == QEvent::MouseButtonPress)
801 {
802 handleMouseButtonPress(event);
803 }
804
805 if (event->type() == QEvent::MouseButtonRelease)
806 {
807 handleMouseButtonRelease(event);
808 }
809
810 const bool supportsViewportRotation = MarbleInputHandler::d->m_marblePresenter->map()->projection() == Spherical;
811
812 // Regarding all kinds of mouse moves:
813 if (d->m_leftPressed && !selectionRubber()->isVisible())
814 {
815 qreal radius = (qreal)(MarbleInputHandler::d->m_marblePresenter->radius());
816 qreal deltax = event->x() - d->m_leftPressedX;
817 qreal deltay = event->y() - d->m_leftPressedY;
818
819 if (qAbs(deltax) > d->m_dragThreshold
820 || qAbs(deltay) > d->m_dragThreshold
821 || !d->m_lmbTimer.isActive())
822 {
823 MarbleInputHandler::d->m_marblePresenter->setViewContext(Animation);
824
825 d->m_pressAndHoldTimer.stop();
826 d->m_lmbTimer.stop();
827 Quaternion quat = Quaternion::fromSpherical( - M_PI/2 * deltax / radius, + M_PI/2 * deltay / radius );
828 if (supportsViewportRotation) {
829 const qreal heading = MarbleInputHandler::d->m_marblePresenter->map()->heading();
830 const Quaternion rotation = Quaternion::fromEuler( 0, 0, heading * DEG2RAD );
831 quat.rotateAroundAxis( rotation );
832 }
833 qreal lon, lat;
834 quat.getSpherical( lon, lat );
835 const qreal posLon = d->m_leftPressedLon + RAD2DEG * lon;
836 const qreal posLat = d->m_leftPressedLat + RAD2DEG * lat;
837 MarbleInputHandler::d->m_marblePresenter->centerOn(posLon, posLat);
838 if (MarbleInputHandler::d->m_inertialEarthRotation)
839 {
840 d->m_kineticSpinning.setPosition(posLon, posLat);
841 }
842 }
843 }
844
845 if (d->m_midPressed)
846 {
847 int eventy = event->y();
848 int dy = d->m_midPressedY - eventy;
849 MarbleInputHandler::d->m_marblePresenter->setRadius(d->m_startingRadius * pow(1.005, dy));
850 }
851
852 if (d->m_rightPressed && supportsViewportRotation && MarbleInputHandler::d->m_mouseViewRotation)
853 {
854 qreal centerX, centerY;
855 MarbleInputHandler::d->m_marblePresenter->map()->screenCoordinates(
856 MarbleInputHandler::d->m_marblePresenter->centerLongitude(),
857 MarbleInputHandler::d->m_marblePresenter->centerLatitude(), centerX, centerY);
858
859 // Deltas from previous position.
860 int dx = event->x() - d->m_rightPosition.x();
861 int dy = event->y() - d->m_rightPosition.y();
862
863 d->m_rightPosition = event->pos();
864
865 // Moving on the bottom should be opposite direction.
866 int sign = event->y() > centerY ? -1 : 1;
867 // Left top and right bottom sides for y axis should be opposite direction.
868 if ((event->x() < centerX && event->y() < centerY) || (event->x() > centerX && event->y() > centerY))
869 {
870 dy *= -1;
871 }
872
873 const qreal speedFactor = 0.3;
874 d->m_heading += (dx + dy) * sign * speedFactor;
875 MarbleInputHandler::d->m_marblePresenter->map()->setHeading(d->m_heading);
876 if (MarbleInputHandler::d->m_inertialEarthRotation)
877 {
878 d->m_kineticSpinning.setHeading(d->m_heading);
879 }
880 }
881
882 if (selectionRubber()->isVisible())
883 {
884 // We change selection.
885 selectionRubber()->setGeometry(QRect(d->m_selectionOrigin, event->pos()).normalized());
886 }
887 }
888 else
889 {
890 direction = mouseMovedOutside(event);
891 }
892
893 if (MarbleInputHandler::d->m_marblePresenter->viewContext() != Animation) {
894 adjustCursorShape(mousePosition, direction);
895 }
896 return acceptMouse();
897}
898
899bool MarbleDefaultInputHandler::acceptMouse()
900{
901 // let others, especially float items, still process the event
902 // Note: This caused a bug in combination with oxygen, see https://bugs.kde.org/show_bug.cgi?id=242414
903 // and changing it a related regression, see https://bugs.kde.org/show_bug.cgi?id=324862
904 return false;
905}
906
907bool MarbleDefaultInputHandler::eventFilter(QObject* o, QEvent* e)
908{
909 Q_UNUSED(o);
910
911 if (layersEventFilter(o, e))
912 {
913 return true;
914 }
915
916 hideSelectionIfCtrlReleased(e);
917
918 switch (e->type())
919 {
922 case QEvent::TouchEnd:
923 return handleTouch(static_cast<QTouchEvent *>(e));
924 case QEvent::KeyPress:
925 return handleKeyPress(static_cast<QKeyEvent *>(e));
926 case QEvent::Gesture:
927 return handleGesture(static_cast<QGestureEvent *>(e));
928 case QEvent::Wheel:
929 return handleWheel(static_cast<QWheelEvent*>(e));
931 return handleDoubleClick(static_cast<QMouseEvent*>(e));
935 return handleMouseEvent(static_cast<QMouseEvent*>(e));
936 default:
937 return false;
938 }
939}
940
941bool MarbleDefaultInputHandler::handleTouch(QTouchEvent*)
942{
943 return false; //reimplement to handle in cases of QML and PinchArea element
944}
945
946bool MarbleDefaultInputHandler::handleKeyPress(QKeyEvent* event)
947{
948 if ( event->type() == QEvent::KeyPress ) {
949 MarbleAbstractPresenter *marblePresenter = MarbleInputHandler::d->m_marblePresenter;
950 bool handled = true;
951 switch ( event->key() ) {
952 case Qt::Key_Left:
953 stopInertialEarthRotation();
954 marblePresenter->moveByStep(-1, 0, Marble::Linear);
955 break;
956 case Qt::Key_Right:
957 stopInertialEarthRotation();
958 marblePresenter->moveByStep(1, 0, Marble::Linear);
959 break;
960 case Qt::Key_Up:
961 stopInertialEarthRotation();
962 marblePresenter->moveByStep(0, -1, Marble::Linear);
963 break;
964 case Qt::Key_Down:
965 stopInertialEarthRotation();
966 marblePresenter->moveByStep(0, 1, Marble::Linear);
967 break;
968 case Qt::Key_Plus:
969 if (event->modifiers() != Qt::ControlModifier) {
970 stopInertialEarthRotation();
971 marblePresenter->zoomIn();
972 }
973 break;
974 case Qt::Key_Minus:
975 if (event->modifiers() != Qt::ControlModifier) {
976 stopInertialEarthRotation();
977 marblePresenter->zoomOut();
978 }
979 break;
980 case Qt::Key_Home:
981 stopInertialEarthRotation();
982 marblePresenter->goHome();
983 break;
984 default:
985 handled = false;
986 break;
987 }
988
989 return handled;
990 }
991 return false;
992}
993
994void MarbleDefaultInputHandler::handleMouseButtonPressAndHold(const QPoint &)
995{
996 // Default implementation does nothing
997}
998
999void MarbleDefaultInputHandler::handlePressAndHold()
1000{
1001 handleMouseButtonPressAndHold(QPoint(d->m_leftPressedX, d->m_leftPressedY));
1002}
1003
1004const AbstractDataPluginItem *MarbleDefaultInputHandler::lastToolTipItem() const
1005{
1006 return d->m_lastToolTipItem;
1007}
1008
1009QTimer* MarbleDefaultInputHandler::toolTipTimer()
1010{
1011 return &d->m_toolTipTimer;
1012}
1013
1014QPoint MarbleDefaultInputHandler::toolTipPosition() const
1015{
1016 return d->m_toolTipPosition;
1017}
1018
1019}
1020
1021#include "moc_MarbleInputHandler.cpp"
1022
This file contains the headers for AbstractProjection.
This file contains the headers for MarbleMap.
This file contains the headers for ViewportParams.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
bool isValid(QStringView ifopt)
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
@ Linear
Linear interpolation of lon, lat and distance to ground.
@ Spherical
Spherical projection ("Orthographic")
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
Type type() const const
QGesture * gesture(Qt::GestureType type) const const
iterator begin()
iterator end()
int x() const const
int y() const const
T * data() const const
bool isNull() const const
int height() const const
int left() const const
QRect normalized() const const
int top() const const
int width() const const
OpenHandCursor
GestureState
PinchGesture
Key_Left
ControlModifier
typedef MouseButtons
QTextStream & center(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:00:07 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.