Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.cpp
T
2026-08-13 18:33:10 +02:00

644 lines
23 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** Event Dispatcher for Qt5 to delegate mouse and keyboard event to another event dispatcher
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 15.04.2015, JT : First edition
**
** -----------------------------------------------------------------------------------------------
*/
#include "event_mouse_navigator.h"
#include "rubberband.h"
#include <QtCore/QMetaMethod>
#include <QtCore/QtMath>
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QGesture>
#include <QtWidgets/QWidget>
#include <MapImpl/map_impl.h>
#include <Map/situation_map_utils.h>
#include <arcgis/Map.h>
#include <arcgis/Layer.h>
#include <arcgis/FeatureLayer.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Graphic.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/SceneGraphicsView.h>
namespace SituationMap { namespace RB
{
int EventMouseNavigator::_pickTolerance = 5;
qreal EventMouseNavigator::_zoomFactorBase = 1.002;
EventMouseNavigator::EventMouseNavigator(QWidget * getEventsFrom, SituationMap::MapImpl *sitMap )
: AbstractEventDispatcher(getEventsFrom)
, _sitMap(sitMap)
, _cursorChanged(false)
, _viewPortPos(QPointF(-1, -1))
, _selectionMode(SelectionMode::NoSelection)
, _pendingRequest(RequestType::None)
, _mousePanningActive(false)
, _mousePanningActiveForCenterMapMidButton(false)
#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING
, _mouseZoomTaskWatcher()
#endif
, _pressedMouseButton(Qt::NoButton)
{
// prepare identify:
switch (_sitMap->mode())
{
case Mode::Mode_2D:
connect( _sitMap->getEsriMapGraphicsView()
, &Esri::ArcGISRuntime::MapGraphicsView::identifyGraphicsOverlaysCompleted
, this
, &EventMouseNavigator::onGetPickingResult );
break;
case Mode::Mode_3D:
connect( _sitMap->getEsriSceneGraphicsView()
, &Esri::ArcGISRuntime::SceneGraphicsView::identifyGraphicsOverlaysCompleted
, this
, &EventMouseNavigator::onGetPickingResult );
break;
default:
// empty
break;
}
} // EventMouseNavigator::EventMouseNavigator
bool EventMouseNavigator::eventFilter(QObject * watched, QEvent * e)
{
Q_UNUSED(watched);
QString debugString(QLatin1String("unhandled event"));
if (hasFinished()) return true; // don't accept new events if dispatcher is going to finish
if (hasActiveEvent()) return true; // don't accept new events before last event has been handled completly
if (e)
{
bool handled( false );
setActiveEvent( true );
switch( e->type() )
{
case QEvent::Enter :
{
_pressedMouseButton = Qt::NoButton;
emit enterEvent();
} break;
case QEvent::Leave :
{
emit leaveEvent();
_pressedMouseButton = Qt::NoButton;
} break;
case QEvent::MouseButtonPress :
{
handled = false;
debugString = QLatin1String("mouse button press event");
QMouseEvent * mouseEvent = static_cast<QMouseEvent*>(e);
_pressedMouseButton = mouseEvent->button();
_mousePanningActive = false;
_mouseMoveRefPos = mouseEvent->pos(); // for panning
_restoreMouseCursor = getCursorFromWidget();
if (Qt::MidButton == mouseEvent->button())
{
handled = true;
_mousePanningActive = true;
_mousePanningActiveForCenterMapMidButton = false;
// setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) );
}
} break;
case QEvent::TouchBegin :
{
handled = false;
debugString = QLatin1String("Touch button press event");
QTouchEvent * mouseEvent = static_cast<QTouchEvent*>(e);
_pressedMouseButton = Qt::MouseButton::LeftButton;
_mousePanningActive = false;
if (mouseEvent->touchPoints().size() > 0)
{
const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0);
_mouseMoveRefPos = tp.pos().toPoint(); // for panning
_restoreMouseCursor = getCursorFromWidget();
}
} break;
case QEvent::MouseButtonRelease :
{
debugString = QLatin1String("mouse button release event");
QMouseEvent * mouseEvent = static_cast<QMouseEvent*>(e);
if (Qt::LeftButton == mouseEvent->button())
{
if (_cursorChanged)
{
setCursorToWidget(_restoreMouseCursor);
_cursorChanged = false;
}
/*
* The ESRI::MapView is doing panning on mouse move when left mouse button is pressed.
* To prevent selection in this case check mouse move distance.
*/
if (Qt::LeftButton == _pressedMouseButton && ((mouseEvent->pos() - _mouseMoveRefPos).manhattanLength() < 3) )
{
if (Rubberband::activeRubberbands(_sitMap).isEmpty())
{
handled = false; // pass event to esri map to click buttons
selectionRequested(mouseEvent->localPos());
// do not handled = true; because some buttons in GraphicsView needs to get the event later.
}
}
else
handled = true; // may be a double click catched at higher event filter
}
if (Qt::RightButton == mouseEvent->button())
{
handled = true;
pickRequested( mouseEvent->localPos() );
}
if ((Qt::MidButton == mouseEvent->button()))
{
if (!_mousePanningActiveForCenterMapMidButton)
{
// press MidButton and Release MidButton without panning in the meantime:
// center map to current mouse position:
// set viewpoint : (perform this only in 2D - MapGraphicsView <-> MapSceneView)
// (3D had different screenToLocation...)
Esri::ArcGISRuntime::MapGraphicsView *esriMapGraphicsView = _sitMap->getEsriMapGraphicsView();
if (esriMapGraphicsView)
{
const QPoint &mousePos = mouseEvent->pos();
const Esri::ArcGISRuntime::Point & mapCoordEsri = esriMapGraphicsView->screenToLocation(mousePos.x(), mousePos.y());
const Esri::ArcGISRuntime::Viewpoint & curVp = esriMapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale);
const Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale());
esriMapGraphicsView->setViewpointAndWait(newVp); // sync / andWAIT
_getEventsFrom -> update();
}
}
}
if ((_mousePanningActive) || (Qt::MidButton == mouseEvent->button()))
{
handled = (Qt::MidButton == mouseEvent->button());
setCursorToWidget( _restoreMouseCursor );
}
_mousePanningActive = false;
} break;
case QEvent::TouchEnd :
{
debugString = QLatin1String("Touch button release event");
QTouchEvent * mouseEvent = static_cast<QTouchEvent*>(e);
if (mouseEvent->touchPoints().size() > 0)
{
const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0);
if (_cursorChanged)
{
setCursorToWidget(_restoreMouseCursor);
_cursorChanged = false;
}
/*
* The ESRI::MapView is doing panning on mouse move when left mouse button is pressed.
* To prevent selection in this case check mouse move distance.
*/
if (Qt::LeftButton == _pressedMouseButton && ((tp.pos() - _mouseMoveRefPos).manhattanLength() < 3) )
{
if (Rubberband::activeRubberbands(_sitMap).isEmpty())
{
handled = false; // pass event to esri map to click buttons
selectionRequested(tp.pos());
// do not handled = true; because some buttons in GraphicsView needs to get the event later.
}
}
else
handled = true; // may be a double click catched at higher event filter
}
_mousePanningActive = false;
} break;
case QEvent::MouseMove :
{
handled = false;
debugString = QLatin1String("mouse button move event");
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e);
if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent)))
{
emit mouseMovedEvent(mouseEvent->pos().x(), mouseEvent->pos().y());
}
if (_mousePanningActive)
{
handled = true;
if (mouseEvent->buttons().testFlag(Qt::MidButton))
{
_mousePanningActiveForCenterMapMidButton = true;
// move viewpoint : (perform this only in 2D - MapGraphicsView <-> MapSceneView)
// (3D had different screenToLocation...)
Esri::ArcGISRuntime::MapGraphicsView *esriMapGraphicsView = _sitMap->getEsriMapGraphicsView();
if (esriMapGraphicsView)
{
QPointF deltaMove = mouseEvent->pos()-_mouseMoveRefPos;
QPointF winCenter = QPointF( qreal(_getEventsFrom->width())*0.5, qreal(_getEventsFrom->height())*0.5 );
QPointF newCenter = winCenter-deltaMove;
_mouseMoveRefPos = mouseEvent->pos();
const Esri::ArcGISRuntime::Point & mapCoordEsri = esriMapGraphicsView->screenToLocation(newCenter.x(), newCenter.y());
const Esri::ArcGISRuntime::Viewpoint & curVp = esriMapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale);
const Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale());
esriMapGraphicsView->setViewpointAndWait(newVp); // sync / andWAIT
_getEventsFrom -> update();
DEBUG_LOG(_logger) << LOGFUNCTION << " Panning to " << mapCoordEsri.x() << " - " << mapCoordEsri.y()
<< " newCenter pos=" << newCenter
<< " mouse pos=" << mouseEvent->pos()
<< " delta pos=" << deltaMove;
}
}
else
{
setCursorToWidget( _restoreMouseCursor );
_mousePanningActive = false;
}
// : move viewpoint
}
} break;
case QEvent::TouchUpdate :
{
handled = false;
debugString = QLatin1String("touch button move event");
QTouchEvent *mouseEvent = static_cast<QTouchEvent*>(e);
if (mouseEvent->touchPoints().size() > 0)
{
const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0);
if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent)))
{
emit mouseMovedEvent(tp.pos().x(), tp.pos().y());
}
if (_mousePanningActive)
{
handled = true;
}
else
{
setCursorToWidget( _restoreMouseCursor );
_mousePanningActive = false;
}
}
} break;
#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING
case QEvent::Wheel :
{
debugString = QLatin1String("mouse wheel event");
handled = true;
if ((!_mouseZoomTaskWatcher.isValid()) || (_mouseZoomTaskWatcher.isDone()))
{
QWheelEvent * wheelEvent = static_cast<QWheelEvent*>(e);
qreal delta = wheelEvent->angleDelta().manhattanLength();
bool zoomIn = (wheelEvent->angleDelta().x() < 0) || (wheelEvent->angleDelta().y() < 0);
qreal zoomFactor = qreal( qRound( qPow( _zoomFactorBase, (zoomIn) ? delta : -delta ) * 10 )) / 10.0;
QPointF winCenter = QPointF( qreal(_getEventsFrom->width())*0.5, qreal(_getEventsFrom->height())*0.5 );
const QPoint & cursorPosWidget = _getEventsFrom->mapFromGlobal( QCursor::pos() ); // e doesn't have a position get position from cursor here
Esri::ArcGISRuntime::MapView * esriMapView = _sitMap->getEsriMapGraphicsView();
const Esri::ArcGISRuntime::Point & mapCenter = esriMapView -> screenToLocation(winCenter.x(), winCenter.y());
const Esri::ArcGISRuntime::Point & cursorPosMap = esriMapView -> screenToLocation(cursorPosWidget.x(), cursorPosWidget.y());
double mapScale = esriMapView -> mapScale() * zoomFactor;
double mapX = (-cursorPosMap.x()+mapCenter.x())*zoomFactor + cursorPosMap.x();
double mapY = (-cursorPosMap.y()+mapCenter.y())*zoomFactor + cursorPosMap.y();
_mouseZoomTaskWatcher = esriMapView->setViewpointCenter( Esri::ArcGISRuntime::Point(mapX,mapY,esriMapView->map()->spatialReference()), mapScale );
if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::mouseWheelEvent) ))
{
emit mouseWheelEvent(zoomFactor, QPointF(cursorPosMap.x(), cursorPosMap.y()) );
}
}
} break;
#endif
case QEvent::Gesture:
{
QGestureEvent * gestureEvent = static_cast<QGestureEvent*>(e);
if (gestureEvent)
{
const QList<QGesture*> & gestureList = gestureEvent->gestures();
for (auto gesture : gestureList)
{
Qt::GestureType gestureType = gesture->gestureType();
Qt::GestureState gestureState = gesture->state();
// QTapGesture * tapGesture = nullptr;
// QTapAndHoldGesture * tapAndHoldGesture = nullptr;
QPanGesture * panGesture = nullptr;
QPinchGesture * pinchGesture = nullptr;
// QSwipeGesture * swipeGesture = nullptr;
QString typeName;
QString stateName;
switch (gestureType)
{
// case Qt::TapGesture : tapGesture = (QTapGesture *)gesture; typeName = QLatin1String("TapGesture"); break;
// case Qt::TapAndHoldGesture : tapAndHoldGesture = (QTapAndHoldGesture*)gesture; typeName = QLatin1String("TapAndHoldGesture"); break;
case Qt::PanGesture : panGesture = (QPanGesture *)gesture; typeName = QLatin1String("PanGesture"); break;
case Qt::PinchGesture : pinchGesture = (QPinchGesture *)gesture; typeName = QLatin1String("PinchGesture"); break;
// case Qt::SwipeGesture : swipeGesture = (QSwipeGesture *)gesture; typeName = QLatin1String("SwipeGesture"); break;
default: break;
}
switch (gestureState)
{
case Qt::NoGesture : stateName = QLatin1String("NoGesture"); break;
case Qt::GestureStarted : stateName = QLatin1String("GestureStarted"); break;
case Qt::GestureUpdated : stateName = QLatin1String("GestureUpdated"); break;
case Qt::GestureFinished : stateName = QLatin1String("GestureFinished"); break;
case Qt::GestureCanceled : stateName = QLatin1String("GestureCanceled"); break;
}
DEBUG_LOG(_logger) << LOGFUNCTION << " Gesture type=" << typeName << " Gesture state=" << stateName;
// tapGesture = nullptr;
panGesture = nullptr; // ToDo: check: Why this?
if (panGesture)
{
QPointF deltaMove = panGesture->delta();
QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 );
QPoint newCenter = winCenter-QPoint((int)deltaMove.x(),(int)deltaMove.y());
const Esri::ArcGISRuntime::Point mapCoordEsri =
_sitMap->getEsriMapGraphicsView()->screenToLocation(newCenter.x(), newCenter.y());
Esri::ArcGISRuntime::Viewpoint curVp = _sitMap->getEsriMapGraphicsView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale);
Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale());
_sitMap->getEsriMapGraphicsView()->setViewpointAndWait(newVp);
_getEventsFrom -> update();
if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(panGesture, true);
}
if (pinchGesture)
{
///// QPinchGesture::ChangeFlags changeFlags = pinchGesture->changeFlags();
///// if (changeFlags & QPinchGesture::RotationAngleChanged)
///// {
///// qreal deltaPhi = pinchGesture->rotationAngle() - pinchGesture->lastRotationAngle();
///// _esriMap -> setRotation( _esriMap->rotation()-deltaPhi );
///// }
///// if (changeFlags & QPinchGesture::ScaleFactorChanged) _esriMap -> zoom( 1.0/pinchGesture->scaleFactor() );
_getEventsFrom -> update();
if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(pinchGesture, true);
}
}
}
} break;
default: break;
} // switch
if (!hasFinished()) setActiveEvent( false );
if (handled) e->accept();
DEBUG_LOG(_logger) << LOGFUNCTION
<< " Event '" << debugString << "'"
<< " event->type() '" << e->type() << "'"
<< " handled = " << handled
<< " mouse panning is active = " << _mousePanningActive;
//
// 'if (handled) return true;' lead to bad behaviour at 3D: globe is often connected to mouse, without any button pressed...
//
// if (handled) return true;
}
return AbstractEventDispatcher::eventFilter(watched,e);
} // EventMouseNavigator::eventFilter
bool EventMouseNavigator::checkRunningPickingTask()
{
if ((!_pickingTask.isCanceled()) && (_pendingRequest != RequestType::None))
{
_pickingTask.cancel();
_pendingRequest = RequestType::None;
return true;
}
return false;
} // void EventMouseNavigator::checkRunningPickingTask()
void EventMouseNavigator::selectionRequested( const QPointF & pos )
{
// do the work only in case of slot connections are installed
if (! isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::selectionRequest) ))
{
return;
}
if (checkRunningPickingTask())
{
qWarning() << LOGFUNCTION << " request is still pending!";
}
_pendingRequest = RequestType::Selection;
_viewPortPos = pos;
requestPicking(pos);
} // EventMouseNavigator::selectionRequested
void EventMouseNavigator::pickRequested( const QPointF &pos )
{
// do the work only in case of slot connections are installed
if (! isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::contextMenuRequest) ))
{
return;
}
if (checkRunningPickingTask())
{
qWarning() << LOGFUNCTION << " request is still pending!";
}
_pendingRequest = RequestType::Picking;
_viewPortPos = pos;
requestPicking(pos);
} // EventMouseNavigator::pickRequested
void EventMouseNavigator::requestPicking(const QPointF &pos)
{
_pickingTask.cancel(); // cancel old task
// store new task
bool returnPopUpsOnly = false;
int maximumResultsPerOverlay = std::numeric_limits<int>::max(); // huge number;
_pickingTask = _sitMap->getEsriGeoView()->identifyGraphicsOverlays(pos.x(), pos.y(), _pickTolerance,
returnPopUpsOnly, maximumResultsPerOverlay);
//qWarning() << " XXXXXXX" << LOGFUNCTION;
// check new task
if (!_pickingTask.isValid())
{
_pendingRequest = RequestType::None;
qWarning() << LOGFUNCTION << " _identfyGraphicsOverlayTask invalid";
}
} // EventMouseNavigator::requestPicking
void EventMouseNavigator::onGetPickingResult(const QUuid &taskId, const QList<Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult*> &identifyResults)
{
// qWarning() << " YYYYYYY" << LOGFUNCTION;
if (_pickingTask.taskId() != taskId)
{
qDebug() << " taskId does not match";
return;
}
const QList<SituationMap::UniqueMapId> & selectedIdList = mapToUniqueMapId(identifyResults);
// qWarning() << " _pendingRequest="<< (int)_pendingRequest << " count= " << _selectedIdList.count();
switch (_pendingRequest)
{
case RequestType::Picking:
emit contextMenuRequest(selectedIdList, _viewPortPos);
break;
case RequestType::Selection:
emit selectionRequest(selectedIdList, _viewPortPos);
break;
case RequestType::None:
//emtpy
break;
} // switch
// reset some 'flags' to enable a new request:
_pendingRequest = RequestType::None;
_viewPortPos = QPointF(-1, -1);
} // EventMouseNavigator::onGetPickingResult
QList<UniqueMapId> EventMouseNavigator::mapToUniqueMapId(const QList<Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult *> &identifyResults)
{
QList<SituationMap::UniqueMapId> idList;
for (auto iResult : identifyResults)
{
// qWarning() << " process result of layer: " << iResult->graphicsOverlay();
const QList<Esri::ArcGISRuntime::Graphic*> &gResults = iResult->graphics();
for (auto pickedGraphic : gResults)
{
// qWarning() << " process pickedGraphic: " << pickedGraphic;
if (pickedGraphic->attributes())
{
// qWarning() << " process pickedGraphic cont: " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute));
// qWarning() << " process pickedGraphic cont: " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::IsPickableAttribute));
bool ok;
SituationMap::UniqueMapId uniqueMapId = pickedGraphic->attributes()->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toUInt(&ok);
if (ok)
{
// qWarning() << " process pickedGraphic uid: " << uniqueMapId;
if (!idList.contains(uniqueMapId))
{
idList += uniqueMapId;
}
}
else
{
ERROR_LOG(_logger) << " process pickedGraphic " << pickedGraphic
<< " uid: '" << pickedGraphic->attributes()->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toString()
<< "' (has uid attribute is " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute)) << ")";
}
}
} // foreach inner
} // foreach outter
return idList;
} // EventMouseNavigator::mapToUniqueMapId
} } /* namespace SituationMap::RB */