496 lines
19 KiB
C++
Executable File
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 */
|