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

496 lines
19 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 <assert.h>
#include <math.h>
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGesture>
#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtWidgets/QWidget>
#include <QtCore/QDebug> // for debugging only
#include <QtCore/QCoreApplication>
#include <Map.h>
#include <Layer.h>
#include <FeatureLayer.h>
#include <GraphicsLayer.h>
#include <MessageGroupLayer.h>
#include <Graphic.h>
namespace SituationMap { namespace RB
{
int EventMouseNavigator::_pickTolerance = 20;
EventMouseNavigator::EventMouseNavigator(QWidget * getEventsFrom, EsriRuntimeQt::Map * esriMap )
: AbstractEventDispatcher(getEventsFrom)
, _esriMap(esriMap)
, _selectionMode()
, _selectedIdList()
, _idConnectionCount(0)
, _mousePanningActive(false)
{
} /* 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::MouseButtonPress :
{
debugString = QLatin1String("mouse button press event");
QMouseEvent * mouseEvent = static_cast<QMouseEvent*>(e);
_mousePanningActive = false;
_mouseMoveRefPos = mouseEvent->pos();
_restoreMouseCursor = getCursorFromWidget();
if (Qt::LeftButton == mouseEvent->button())
{
handled = false;
if (mouseEvent->modifiers() == Qt::KeyboardModifier::ControlModifier)
{
_selectionMode = SelectionMode::MultiSelection;
setCursorToWidget( QCursor(Qt::CursorShape::PointingHandCursor) );
}
else if (mouseEvent->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier ))
{
_selectionMode = SelectionMode::FrameSelection;
if (mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier))
{
_selectionMode &= SelectionMode::MultiSelection;
}
else
{
_selectionMode &= SelectionMode::SingleSelection;
}
setCursorToWidget( QCursor(Qt::CursorShape::SizeAllCursor) );
}
else
{
setCursorToWidget( QCursor(Qt::CursorShape::ArrowCursor) );
_selectionMode = SelectionMode::SingleSelection;
}
}
if (Qt::MidButton == mouseEvent->button())
{
handled = (Qt::MidButton == mouseEvent->button());
_mousePanningActive = true;
setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) );
}
} break;
case QEvent::MouseButtonRelease :
{
debugString = QLatin1String("mouse button release event");
QMouseEvent * mouseEvent = static_cast<QMouseEvent*>(e);
if (Qt::LeftButton == mouseEvent->button())
{
handled = false;
if (!_mousePanningActive)
{
setCursorToWidget( _restoreMouseCursor );
selectionRequested( mouseEvent->pos() );
_selectionMode = SelectionMode::NoSelection;
// do not handled = true; because some buttons in GraphicsView needs to get the event later.
}
}
if (Qt::RightButton == mouseEvent->button())
{
handled = true;
pickRequested( mouseEvent->pos() );
}
if ((_mousePanningActive) || (Qt::MidButton == mouseEvent->button()))
{
handled = (Qt::MidButton == mouseEvent->button());
setCursorToWidget( _restoreMouseCursor );
}
_mousePanningActive = false;
} break;
case QEvent::MouseMove :
{
debugString = QLatin1String("mouse button move event");
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e);
if (mouseEvent->timestamp() != _ignoreMouseMoveTimestamp)
{
if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent)))
{
mouseMovedEvent( mouseEvent->pos().x(), mouseEvent->pos().y() );
}
if ((!_mousePanningActive) && mouseEvent->buttons().testFlag(Qt::LeftButton))
{
QPoint deltaMove = mouseEvent->pos()-_mouseMoveRefPos;
if (deltaMove.manhattanLength() > 3)
{
_mousePanningActive = true;
setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) );
}
}
if ((_mousePanningActive) || mouseEvent->buttons().testFlag(Qt::MidButton))
{
handled = true;
EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode();
QPoint deltaMove = mouseEvent->pos()-_mouseMoveRefPos;
QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 );
QPoint newCenter = winCenter-deltaMove;
const EsriRuntimeQt::Point & mapCenter = _esriMap->toMapPoint(newCenter.x(), newCenter.y());
_esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation );
_esriMap -> panTo( mapCenter );
_esriMap -> setAnimationMode(mode);
_getEventsFrom -> update();
_mouseMoveRefPos = mouseEvent->pos();
}
}
} break;
case QEvent::Wheel :
{
debugString = QLatin1String("mouse wheel event");
// QWheelEvent * wheelEvent = static_cast<QWheelEvent*>(e);
// handled = true;
handled = false;
} break;
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;
}
// tapGesture = nullptr;
panGesture = nullptr;
if (panGesture)
{
EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode();
QPointF deltaMove = panGesture->delta();
QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 );
QPoint newCenter = winCenter-QPoint((int)deltaMove.x(),(int)deltaMove.y());
const EsriRuntimeQt::Point & mapCenter = _esriMap->toMapPoint(newCenter.x(), newCenter.y());
_esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation );
_esriMap -> panTo( mapCenter );
_esriMap -> setAnimationMode(mode);
_getEventsFrom -> update();
if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(panGesture, true);
}
if (pinchGesture)
{
EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode();
QPinchGesture::ChangeFlags changeFlags = pinchGesture->changeFlags();
_esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation );
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() );
_esriMap -> setAnimationMode(mode);
_getEventsFrom -> update();
if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(pinchGesture, true);
}
}
}
} break;
default: break;
} /* switch */
if (!hasFinished()) setActiveEvent( false );
if (handled) e->accept();
if (handled) return true;
}
return AbstractEventDispatcher::eventFilter(watched,e);
} /* EventMouseNavigator::eventFilter */
void EventMouseNavigator::selectionRequested( const QPoint & pos )
{
const bool appendSelection = _selectionMode.testFlag( SelectionMode::MultiSelection );
if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::selectionRequest) ))
{
// do the work only in case of slot connections are installed
if (0 != _idConnectionCount)
{
qWarning() << LOGFUNCTION << " graphic layer id list request is still pending!";
return;
}
pickObjects( pos, _selectedIdList, appendSelection, &EventMouseNavigator::selectionRequest );
}
} /* EventMouseNavigator::selectionRequested */
void EventMouseNavigator::pickRequested( const QPoint & pos )
{
if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::contextMenuRequest) ))
{
// do the work only in case of slot connections are installed
if (0 != _idConnectionCount)
{
qWarning() << LOGFUNCTION << " graphic layer id list request is still pending!";
return;
}
pickObjects( pos, _selectedIdList, false, &EventMouseNavigator::contextMenuRequest );
}
} /* EventMouseNavigator::pickRequested */
void EventMouseNavigator::pickObjects( const QPoint & pos
, QList<uint> & pickIdList
, bool append
, void (EventMouseNavigator::*pickSignal)(const QList< uint>&,QPoint) )
{
#ifdef NEVER
if (!append) _selectedIdList.clear();
QList<EsriRuntimeQt::Layer*> layerList = _esriMap->layers();
QList<EsriRuntimeQt::Layer*> selectLayerList;
/* ~~~~~~~~~~~~~~~~~~~ first look for all layers in map ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (auto layer : layerList)
{
EsriRuntimeQt::MessageGroupLayer* msgGrpLayer = qobject_cast<EsriRuntimeQt::MessageGroupLayer*>(layer);
if (msgGrpLayer)
{
selectLayerList.append( msgGrpLayer->getLayers() );
}
else selectLayerList.append(layer);
}
/* ~~~~~~~~~~~~~~~~~~~ loop over all layers in layer list ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (auto layer : selectLayerList)
{
EsriRuntimeQt::FeatureLayer* featureLayer = qobject_cast<EsriRuntimeQt::FeatureLayer*>(layer);
if (featureLayer)
{
/*~~~~~~~~~~~~~~~~~~~~~~~ get selected features by direct function call ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
QList<qint64> fidList = featureLayer -> featureIds( pos.x(), pos.y(), _pickTolerance );
featureLayer->selectFeatures( fidList, append );
EsriRuntimeQt::FeatureTable* featureTable = featureLayer->featureTable();
for (auto featureId : fidList)
{
if (featureTable)
{
EsriRuntimeQt::Feature* feature = featureTable->feature( featureId );
if (feature)
{
bool ok;
SituationMap::UniqueMapId id;
id = feature->property( SituationMap::UniqueMapIdProperty ).toUInt( &ok );
if (ok)
{
if (!pickIdList.contains(id))
{
pickIdList += id;
}
}
}
}
} // for
} // if (featureLayer)
EsriRuntimeQt::GraphicsLayer * graphicsLayer = qobject_cast<EsriRuntimeQt::GraphicsLayer*>(layer);
if (graphicsLayer)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* In case of message symbols, in a graphic layer a slot method has to be used to get
* the ids of selected symbols. The slot installed (connected) each time
* EventMouseNavigator::selectionRequested() has been called and will be disconnect itself at
* beginning of slot function. Because the slot methode will be called later, we have to store
* some information like the _selectedIdList as member variable.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* connect here, because the layer in MessageGroupLayer can change:
* do not use QMetaObject:.Connection directly because disconnect will fail.
* See also http://stackoverflow.com/questions/13847507/qt5-new-signal-to-lambda-connections-memory-leak
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
_idConnectionCount++;
auto conn = std::make_shared<QMetaObject::Connection>();
*conn = connect( graphicsLayer, &EsriRuntimeQt::GraphicsLayer::graphicIdsComplete,
this , [conn,pos,&pickIdList,graphicsLayer,pickSignal,this]( const QList<qint64>& gIdList )
{
disconnect( *conn );
for (auto gId : gIdList)
{
QString objectName(QLatin1String(" - no graphic - "));
EsriRuntimeQt::Graphic * graphic = graphicsLayer->graphic(gId);
if (graphic)
objectName = graphic->objectName();
qDebug() << LOGFUNCTION
<< " gId=" << gId
<< " name= " << objectName;
if (graphic)
{
bool ok;
SituationMap::UniqueMapId id;
id = graphic->property( SituationMap::UniqueMapIdProperty ).toUInt( &ok );
if (ok)
{
if (!pickIdList.contains(id))
{
pickIdList += id;
}
}
}
}
// Just emit selectionRequest only once
if (_idConnectionCount <= 1)
{
(this->*pickSignal)( pickIdList, pos );
_idConnectionCount = 0;
}
else _idConnectionCount--;
} ); // end of lambda methode
graphicsLayer->graphicIds( pos.x(), pos.y(), _pickTolerance, 100 );
}
} // for layer
if (_idConnectionCount == 0)
{
(this->*pickSignal)( pickIdList, pos );
}
#endif
} // EventMouseNavigator::pickObjects
} } /* namespace SituationMap::RB */