/** ** ** @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 #include #include #include #include #include #include #include #include // for debugging only #include #include #include #include #include #include #include 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(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(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(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(e); // handled = true; handled = false; } break; case QEvent::Gesture: { QGestureEvent * gestureEvent = static_cast(e); if (gestureEvent) { const QList & 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 & pickIdList , bool append , void (EventMouseNavigator::*pickSignal)(const QList< uint>&,QPoint) ) { #ifdef NEVER if (!append) _selectedIdList.clear(); QList layerList = _esriMap->layers(); QList selectLayerList; /* ~~~~~~~~~~~~~~~~~~~ first look for all layers in map ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ for (auto layer : layerList) { EsriRuntimeQt::MessageGroupLayer* msgGrpLayer = qobject_cast(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(layer); if (featureLayer) { /*~~~~~~~~~~~~~~~~~~~~~~~ get selected features by direct function call ~~~~~~~~~~~~~~~~~~~~~~~~~~ */ QList 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(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(); *conn = connect( graphicsLayer, &EsriRuntimeQt::GraphicsLayer::graphicIdsComplete, this , [conn,pos,&pickIdList,graphicsLayer,pickSignal,this]( const QList& 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 */