Marble

MarbleInputHandler.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <[email protected]>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <[email protected]>
5 // SPDX-FileCopyrightText: 2014 Adam Dabrowski <[email protected]>
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 
30 namespace Marble {
31 
32 const int TOOLTIP_START_INTERVAL = 1000;
33 
34 class Q_DECL_HIDDEN MarbleInputHandler::Protected
35 {
36 public:
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 
51 MarbleInputHandler::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 
64 MarbleInputHandler::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 
74 MarbleInputHandler::~MarbleInputHandler()
75 {
76  delete d->m_mouseWheelTimer;
77  delete d;
78 }
79 
80 void MarbleInputHandler::setPositionSignalConnected(bool connected)
81 {
82  d->m_positionSignalConnected = connected;
83 }
84 
85 bool MarbleInputHandler::isPositionSignalConnected() const
86 {
87  return d->m_positionSignalConnected;
88 }
89 
90 void 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 
102 bool MarbleInputHandler::isMouseButtonPopupEnabled(Qt::MouseButton mouseButton) const
103 {
104  return !(d->m_disabledMouseButtons & mouseButton);
105 }
106 
107 void MarbleInputHandler::setPanViaArrowsEnabled(bool enabled)
108 {
109  d->m_panViaArrowsEnabled = enabled;
110 }
111 
112 bool MarbleInputHandler::panViaArrowsEnabled() const
113 {
114  return d->m_panViaArrowsEnabled;
115 }
116 
117 void MarbleInputHandler::setInertialEarthRotationEnabled(bool enabled)
118 {
119  d->m_inertialEarthRotation = enabled;
120 }
121 
122 bool MarbleInputHandler::inertialEarthRotationEnabled() const
123 {
124  return d->m_inertialEarthRotation;
125 }
126 
127 void MarbleInputHandler::setMouseViewRotationEnabled(bool enabled)
128 {
129  d->m_mouseViewRotation = enabled;
130 }
131 
132 bool MarbleInputHandler::mouseViewRotationEnabled() const
133 {
134  return d->m_mouseViewRotation;
135 }
136 
137 void MarbleInputHandler::stopInertialEarthRotation()
138 {
139 }
140 
141 class 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 
200 MarbleDefaultInputHandler::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 
227 MarbleDefaultInputHandler::Private::~Private()
228 {
229 }
230 
231 MarbleDefaultInputHandler::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 
257 MarbleDefaultInputHandler::~MarbleDefaultInputHandler()
258 {
259  delete d;
260 }
261 
262 void MarbleDefaultInputHandler::stopInertialEarthRotation()
263 {
264  d->m_kineticSpinning.stop();
265 }
266 
267 void 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 
290 void 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 
301 void 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 
313 bool 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 
328 bool 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 
375 bool 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;
398  case Qt::GestureStarted:
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;
405  case Qt::GestureUpdated:
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;
417  case Qt::GestureFinished:
418  marblePresenter->map()->viewport()->resetFocusPoint();
419  marblePresenter->setViewContext(Still);
420  break;
421  case Qt::GestureCanceled:
422  marblePresenter->map()->viewport()->resetFocusPoint();
423  marblePresenter->setViewContext(Still);
424  break;
425  }
426  return true;
427 }
428 
429 bool 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 
443 void 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 
468 void 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 
487 void 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 
531 void 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 
547 void 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 
560 void 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 
614 void 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 
630 void 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 
698 QPoint 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 
765 bool 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 
899 bool 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 
907 bool 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  {
920  case QEvent::TouchBegin:
921  case QEvent::TouchUpdate:
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));
934  case QEvent::MouseMove:
935  return handleMouseEvent(static_cast<QMouseEvent*>(e));
936  default:
937  return false;
938  }
939 }
940 
941 bool MarbleDefaultInputHandler::handleTouch(QTouchEvent*)
942 {
943  return false; //reimplement to handle in cases of QML and PinchArea element
944 }
945 
946 bool 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 
994 void MarbleDefaultInputHandler::handleMouseButtonPressAndHold(const QPoint &)
995 {
996  // Default implementation does nothing
997 }
998 
999 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 
GestureState
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
int width() const const
int x() const const
int y() const const
typedef MouseButtons
int left() const const
int top() const const
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
bool isNull() const const
@ Spherical
Spherical projection ("Orthographic")
Definition: MarbleGlobal.h:42
OpenHandCursor
Binds a QML item to a specific geodetic location in screen coordinates.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
Key_Left
PinchGesture
int height() const const
@ Linear
Linear interpolation of lon, lat and distance to ground.
Definition: MarbleGlobal.h:165
bool isValid(QStringView ifopt)
QEvent::Type type() const const
QPoint pos() const const
int delta() const const
QList::iterator begin()
QTextStream & center(QTextStream &stream)
T * data() const const
QList::iterator end()
ControlModifier
const QList< QKeySequence > & end()
QGesture * gesture(Qt::GestureType type) const const
QRect normalized() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Dec 11 2023 04:09:40 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.