Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.4)
SET(QT_MIN_VERSION 5.4.0)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
FILE ( GLOB sourceFiles *.h *.cpp )
FIND_PACKAGE(OpenGL)
FIND_PACKAGE(Qt5 COMPONENTS Core Gui Location OpenGL Positioning Sensors Widgets REQUIRED)
INCLUDE_DIRECTORIES ( ${Qt5Widgets_INCLUDE_DIRS}, "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/include" )
add_definitions(${Qt5Widgets_DEFINITIONS})
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
ADD_EXECUTABLE( EsriMapTouchTest WIN32 ${sourceFiles} )
TARGET_LINK_LIBRARIES( EsriMapTouchTest Qt5::Core Qt5::Gui Qt5::Location Qt5::OpenGL Qt5::Sensors Qt5::Widgets )
TARGET_LINK_LIBRARIES( EsriMapTouchTest debug "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/x86/lib/EsriRuntimeQtd.lib" )
TARGET_LINK_LIBRARIES( EsriMapTouchTest optimized "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/x86/lib/EsriRuntimeQt.lib" )
@@ -0,0 +1,311 @@
/**
**
** @RDE_HEADER@
** Abstract Event Dispatcher for Qt5
** $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 .........:
**
** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee
** 25.03.2015, JT : Refactoring for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "abstract_event_dispatcher.h"
#include "gestures.h"
#include <assert.h>
#include <math.h>
#include <QtGui/QBitmap>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
#include <QtWidgets/QWidget>
#include <QtWidgets/QApplication>
#include <QtCore/QDebug> // for debugging only
namespace SituationMap { namespace RB
{
QGestureRecognizer * AbstractEventDispatcher::_applyGestureDetection = nullptr;
unsigned int AbstractEventDispatcher::_applyGestureID = 0;
AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom )
: QObject(getEventsFrom)
, _getEventsFrom(getEventsFrom)
, _cursorIsInWidget(false)
, _ignoreMouseMoveTimestamp()
, _synthesisTouchFromMouseEvents(false)
, _deleteRequested(false)
, _hasGrab(false)
, _finishFlag(false)
, _activeEvent(false)
, _dispatcherCursor()
, _whatsThisBackup()
, _eventActions(nullptr)
{
initDispatcher();
} /* AbstractEventDispatcher::AbstractEventDispatcher */
AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface * eventActions )
: QObject(getEventsFrom)
, _getEventsFrom(getEventsFrom)
, _cursorIsInWidget(false)
, _ignoreMouseMoveTimestamp()
, _synthesisTouchFromMouseEvents(false)
, _deleteRequested(false)
, _hasGrab(false)
, _finishFlag(false)
, _activeEvent(false)
, _dispatcherCursor()
, _whatsThisBackup()
, _eventActions(eventActions)
{
initDispatcher();
} /* AbstractEventDispatcher::AbstractEventDispatcher */
void AbstractEventDispatcher::initDispatcher()
{
_restoreCursor = true;
/* The methode QWidget::ownCursor doesn't exist any more.
* Try to find out if the inherited from parent
*/
// _restoreCursor = getEventsFrom->ownCursor();
if (_restoreCursor)
{
_cursorToRestore = getCursorFromWidget();
}
_restoreMouseTracking = _getEventsFrom->hasMouseTracking();
// just a test exe, but remove process Events
// QCoreApplication::process Events ( ); /// removePostedEvents( _getEventsFrom );
_getEventsFrom->setMouseTracking(true);
_getEventsFrom->raise();
_getEventsFrom->installEventFilter(this);
_synthesisTouchFromMouseEvents = QCoreApplication::testAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents );
QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false );
QCoreApplication::setAttribute( Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true );
#ifdef NEVER
static const Qt::GestureType gestures[] = { Qt::GestureType::TapGesture
, Qt::GestureType::TapAndHoldGesture
, Qt::GestureType::PanGesture
, Qt::GestureType::PinchGesture
, Qt::GestureType::SwipeGesture };
for (auto gestureType : gestures) _getEventsFrom->grabGesture(gestureType);
if (_applyGestureDetection == nullptr)
{
_applyGestureDetection = new ApplyGestureRecognizer();
_applyGestureID = QGestureRecognizer::registerRecognizer( _applyGestureDetection );
}
_getEventsFrom->grabGesture( (Qt::GestureType)_applyGestureID );
_getEventsFrom->setAttribute( Qt::WA_AcceptTouchEvents, true );
#endif
} /* AbstractEventDispatcher::initDispatcher */
AbstractEventDispatcher::~AbstractEventDispatcher()
{
_finishFlag = true;
if (_getEventsFrom)
{
_getEventsFrom->removeEventFilter(this);
_getEventsFrom->setMouseTracking( _restoreMouseTracking );
ungrab();
if (!_whatsThisBackup.isNull())
{
_getEventsFrom->setWhatsThis(_whatsThisBackup);
}
}
QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, _synthesisTouchFromMouseEvents );
} /* AbstractEventDispatcher::~AbstractEventDispatcher */
void AbstractEventDispatcher::onDo_delete()
{
if (_deleteRequested) return;
_deleteRequested = true;
QObject::deleteLater();
} /* AbstractEventDispatcher::onDo_delete */
void AbstractEventDispatcher::KickTheBucket()
{
if (_getEventsFrom == nullptr) return;
_finishFlag = true;
unlinkEventDispatcher();
_getEventsFrom->removeEventFilter(this);
_getEventsFrom->setMouseTracking( _restoreMouseTracking );
ungrab();
if (!_whatsThisBackup.isNull())
{
_getEventsFrom->setWhatsThis(_whatsThisBackup);
}
_getEventsFrom = nullptr;
emit signalKickTheBucket();
QCoreApplication::flush();
// just a test exe, but remove process Events
// QCoreApplication::process Events(); // dispatch window system events
onDo_delete();
} /* AbstractEventDispatcher::KickTheBucket */
void AbstractEventDispatcher::onDo_kickTheBucket() { KickTheBucket(); }
void AbstractEventDispatcher::setWhatsThis(const QString & whatsThis)
{
if (_whatsThisBackup.isNull())
{
_whatsThisBackup = _getEventsFrom->whatsThis();
}
_getEventsFrom->setWhatsThis(whatsThis);
} /* AbstractEventDispatcher::setWhatsThis */
void AbstractEventDispatcher::grab()
{
if (_hasGrab) return;
_hasGrab = true;
setCursorToWidget( _dispatcherCursor );
_getEventsFrom -> setFocus();
} /* AbstractEventDispatcher::Grab */
void AbstractEventDispatcher::ungrab()
{
if (!_hasGrab) return;
_hasGrab = false;
if (_restoreCursor)
{
setCursorToWidget( _cursorToRestore );
}
// else _getEventsFrom -> unsetCursor();
_getEventsFrom -> clearFocus();
} /* AbstractEventDispatcher::Ungrab */
bool AbstractEventDispatcher::eventFilter( QObject * widget, QEvent * e )
{
if (widget != ((QObject*)_getEventsFrom)) return false;
return false;
} /* AbstractEventDispatcher::eventFilter */
void AbstractEventDispatcher::setCursorToWidget( const QCursor & cursor )
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* The cursor handling of QGraphicView are not free of bugs. If it tries to restore the default cursor after setting a special cursor for one item
* it can happend that the restored cursor is not the last cursor that was set with setCursor function because the internal stored restore cursor is
* not updated with setCursor function. As work around a mouse move event is send to the widget to reset cursor handling.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
const QPoint & currentCursorPosition = _getEventsFrom->mapFromGlobal( QCursor::pos() );
#ifdef ShowBug
QMouseEvent && mouseEvent = QMouseEvent( QEvent::Type::MouseMove, QPointF(-1,-1), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier );
_ignoreMouseMoveTimestamp = mouseEvent.timestamp();
QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent );
QCoreApplication::process Events();
_getEventsFrom -> setCursor( cursor );
if (_cursorIsInWidget)
{
mouseEvent = QMouseEvent( QEvent::Type::MouseMove, currentCursorPosition, Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier );
QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent );
}
QCoreApplication::process Events();
_getEventsFrom -> update();
#else
_getEventsFrom -> setCursor( cursor );
#endif
} /* AbstractEventDispatcher::setCursorToWidget */
QCursor AbstractEventDispatcher::getCursorFromWidget()
{
const QPoint & currentCursorPosition = _getEventsFrom->mapFromGlobal( QCursor::pos() );
#ifdef ShowBug
QMouseEvent && mouseEvent = QMouseEvent( QEvent::Type::MouseMove, QPointF(-1,-1), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier );
_ignoreMouseMoveTimestamp = mouseEvent.timestamp();
QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent );
QCoreApplication::process Events();
const QCursor & currentCursor = _getEventsFrom->cursor();
if (_cursorIsInWidget)
{
mouseEvent = QMouseEvent( QEvent::Type::MouseMove, currentCursorPosition, Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier );
QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent );
}
QCoreApplication::process Events();
_getEventsFrom -> update();
#else
const QCursor & currentCursor = _getEventsFrom->cursor();
#endif
return currentCursor;
} /* AbstractEventDispatcher::getCursorFromWidget */
} } /* namespace SituationMap::RB */
@@ -0,0 +1,232 @@
/**
**
** @RDE_HEADER@
** Abstract Event Dispatcher for Qt5
** $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 .........:
**
** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee
** 25.03.2015, JT : Refactoring for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <functional>
#include <QtCore/QObject>
#include <QtCore/QEvent>
#include <QtCore/QList>
#include <QtCore/QString>
#include <QtCore/QTime>
#include <QtGui/QCursor>
class QWidget;
class QWheelEvent;
class QGestureRecognizer;
namespace SituationMap { namespace RB
{
/**
@class AbstractEventDispatcher abstract_event_dispatcher.h
@brief Base class of Qt event filter implementations to handle user interactions of map window
An EventDispatcher installs an event filter on a QWidget to receive all events that are send to
the widget. Depending on the type of event and the state of the event dispatcher the event will
be processed by the dispatcher and stopped or forwarded to the QWidget. In case of processing
the event methodes of the EventActionInterface are called to controll a rubberband.
@see EventDelegater, EventDispatcher, EventMouseNavigator
*/
class AbstractEventDispatcher : public QObject
{
friend class EventDelegater;
Q_OBJECT
public:
/**
@brief Logical interface between EventDispatcher and Rubberband
Defines interface functions the EventDispatcher will call to control a rubberband. The rubberband class
is derived from EventActionInterface and overrides the interface functions to implement them. The mapping
user interaction to interface action is defined the derived classes of AbstractEventDispatcher.
*/
class EventActionInterface
{
public:
EventActionInterface() = default;
virtual ~EventActionInterface() = default;
virtual void unlinkEventDispatcher( AbstractEventDispatcher * causer ) = 0;
virtual bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool multiSelectAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual void resetAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool applyAndEndAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
virtual bool abortAction ( AbstractEventDispatcher * causer, int x, int y ) = 0;
}; /* EventActionInterface */
/**
@name Construction & Destruction
@{
*/
AbstractEventDispatcher( QWidget * getEventsFrom );
AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface* );
virtual ~AbstractEventDispatcher();
AbstractEventDispatcher(const AbstractEventDispatcher &) = delete;
AbstractEventDispatcher & operator = (const AbstractEventDispatcher &) = delete;
/** @} */
/**
Conceal deleteLater slot. Use onDo_delete() instead to avoid "QCoreApplication::postEvent: Unexpected null receiver" warning.
*/
void deleteLater() = delete;
inline void setEventActions(EventActionInterface * eventActions) { _eventActions = eventActions; }
inline bool hasGrab() const { return _hasGrab; }
inline void setCursor(const QCursor & cursor) { _dispatcherCursor = cursor; }
void setWhatsThis(const QString & whatsThis);
virtual void grab();
virtual void ungrab();
public slots:
/**
@brief Slot calling internal \c KickTheBucket methode
Calls internal methode \c KickTheBucket. Be careful using this function. Should only be used
from implementations of \c EventActionInterface methodes or \c EventDelegater class.
*/
void onDo_kickTheBucket();
/**
@brief Slot calling \c deleteLater function of QObject
Never use \c delete directly for EventDispatcher class. Instead of use \c onDo_delete() function.
*/
void onDo_delete();
protected:
QWidget * _getEventsFrom;
bool _cursorIsInWidget;
inline bool hasFinished() { return _finishFlag; }
inline bool hasActiveEvent() { return _activeEvent; }
inline void setActiveEvent(bool val) { _activeEvent = val; }
/**
@brief Destroying EventDispatcher as result of an incoming event.
As result of an incomming event (e.g. Keypress ESC) the EventDispatcher will stop, emiting signal \c signalKickTheBucket()
and calls methode \c EventActionInterface::unlinkEventDispatcher() before setup \c deleteLater() call to destroy it self.
*/
virtual void KickTheBucket();
inline void unlinkEventDispatcher()
{
if (_eventActions)
{
_eventActions -> unlinkEventDispatcher( this );
_eventActions = nullptr;
}
} /* unlinkEventDispatcher */
inline bool callEventAction( bool( EventActionInterface::*actionMethodePtr)( AbstractEventDispatcher*,int,int), int x, int y )
{
if (_eventActions == nullptr) return false;
return (_eventActions->*actionMethodePtr)( this, x, y );
} /* callEventAction */
/**
Event filter function to override to implement event handling. To workaround a cursor handling bug in QGraphicsView it is
necessary to call the eventFilter function of this base class, which will monitor enter and leave events.
*/
virtual bool eventFilter( QObject * widget, QEvent * e );
inline EventActionInterface * getEventActions() const { return _eventActions; }
void setCursorToWidget( const QCursor & cursor );
QCursor getCursorFromWidget();
ulong _ignoreMouseMoveTimestamp;
bool _synthesisTouchFromMouseEvents;
static unsigned int _applyGestureID;
private:
void initDispatcher();
bool _deleteRequested;
bool _hasGrab;
bool _finishFlag;
bool _restoreCursor;
bool _restoreMouseTracking;
QCursor _cursorToRestore;
bool _activeEvent;
QCursor _dispatcherCursor;
QString _whatsThisBackup;
EventActionInterface * _eventActions;
static QGestureRecognizer * _applyGestureDetection;
signals:
void signalKickTheBucket();
}; /* EventDispatcher */
} } /* namespace SituationMap::RB */
@@ -0,0 +1,495 @@
/**
**
** @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 */
@@ -0,0 +1,154 @@
/**
**
** @RDE_HEADER@
** Event Dispatcher for Qt5 to handle mouse events navigate on map
** $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 .........:
**
** 24.04.2015, JT : first edition
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QList>
#include "abstract_event_dispatcher.h"
namespace EsriRuntimeQt
{
class Map;
} /* namespace EsriRuntimeQt */
namespace SituationMap { namespace RB
{
/**
@class EventMouseNavigator event_mouse_navigator.h
@brief Event filter that handles mouse events for general map operation like selecting object, zooming and paning the map window
Implements class \c AbstractEventDispatcher to manage event handling for general map operation like selecting object, zooming and
panning the map window. Monitoring the mouse position can be realized by connecting the \c mouseMovedEvent signal. If the user has
clicked to map objects the signal \c selectionRequest is emitted.
Following events are handled:
| Input-Event | Interface Function | Description |
| :--------------------------------------------- | : ----------------- | : --------------------------------------------------------------------------- |
| Mouse move without any modifier | | Move Cursor. |
| Mouse move with pressed left button | | Panning of map if more then 3 pixels moved. |
| Mouse move with pressed middle button | | Panning of map. |
| Mouse wheel | | Redirect to \c Esri map for zooming. |
| Left mouse button release | | Emits signal \c selectionRequest with list of map objects at cursor position. |
@see AbstractEventDispatcher, EventDelegater, EventDispatcher
*/
class EventMouseNavigator : public AbstractEventDispatcher
{
Q_OBJECT
public:
/**
@name Construction & Destruction
@{
*/
EventMouseNavigator(const EventMouseNavigator &) = delete;
EventMouseNavigator & operator = (const EventMouseNavigator &) = delete;
EventMouseNavigator( QWidget * getEventsFrom, EsriRuntimeQt::Map * esriMap );
virtual ~EventMouseNavigator() {}
/** @} */
inline static void setPickTolerance( int value = 20 ) { _pickTolerance = value; }
signals:
/**
Will be emitted if the user clicks to map objects with the left mouse button.
*/
void selectionRequest( const QList< uint > & idList, QPoint viewPortPos );
/**
Will be emitted if the user clicks to the map with the rigth mouse button.
*/
void contextMenuRequest( const QList< uint > & idList, QPoint viewPortPos );
/**
Signal will be emitted if a mouse move is done.
*/
void mouseMovedEvent( int x, int y );
protected:
virtual bool eventFilter(QObject*, QEvent*);
enum SelectionMode
{
NoSelection = 0x00,
SingleSelection = 0x01,
MultiSelection = 0x02,
FrameSelection = 0x04
}; // SelectionMode
Q_DECLARE_FLAGS(SelectionModes,SelectionMode)
EsriRuntimeQt::Map * _esriMap;
QPoint _mouseMoveRefPos;
QCursor _restoreMouseCursor;
static int _pickTolerance;
SelectionModes _selectionMode;
QList<uint> _selectedIdList; // List of selected object-ids. Is completly handled in selectionRequested()
unsigned int _idConnectionCount; // counter to avoid multiple signals selectionRequest, see implementation of selectionRequested()
bool _mousePanningActive; // is panning with mouse active
/**
Find out which map objects are at given mouse position and emits the signal selectionRequest
*/
void selectionRequested( const QPoint & );
/**
Find out which map objects are at given mouse position and emits the signal contextMenuRequest
*/
void pickRequested( const QPoint & );
/**
Helper function to get all map objets at a given position
@param pos position in window coordinates
@param pickIdList List to store uid's of picked map objects. Don't use a local variable here because the picking is a asynchrone function
@param append If true the uid's are appended at end of list. Otherwise the list will be cleared first.
@param pickSignal Function pointer to signal to be emitted at end.
*/
void pickObjects( const QPoint & pos
, QList<uint> & pickIdList
, bool append
, void (EventMouseNavigator::*pickSignal)(const QList< uint>&,QPoint) );
}; /* EventMouseNavigator */
} } /* namespace SituationMap::RB */
+102
View File
@@ -0,0 +1,102 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
#include "gestures.h"
#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtWidgets/QWidget>
namespace SituationMap { namespace RB {
// TabGestureRecognizer
ApplyGestureRecognizer::ApplyGestureRecognizer() : QGestureRecognizer()
{
}
ApplyGestureRecognizer::~ApplyGestureRecognizer()
{}
QGesture * ApplyGestureRecognizer::create( QObject * target )
{
qDebug() << "ApplyGesture create";
if (target && target->isWidgetType())
{
((QWidget*)target)->setAttribute(Qt::WA_AcceptTouchEvents);
}
return new ApplyGesture();
} // TabGestureRecognizer::create
QGestureRecognizer::Result ApplyGestureRecognizer::recognize( QGesture * state, QObject* , QEvent * event)
{
ApplyGesture * q = static_cast<ApplyGesture*>(state);
const QTouchEvent * ev = static_cast<const QTouchEvent*>(event);
QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture;
switch (event->type())
{
case QEvent::TouchBegin:
{
qDebug() << "ApplyGesture begin";
q->_position = ev->touchPoints().at(0).pos();
q->setHotSpot(ev->touchPoints().at(0).screenPos());
q->_tripleTouch = (ev->touchPoints().size() == 3);
result = QGestureRecognizer::TriggerGesture;
} break;
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
if (q->state() != Qt::NoGesture && ev->touchPoints().size() >= 1 && ev->touchPoints().size() <= 3)
{
if (ev->touchPoints().size() == 3) q->_tripleTouch = true;
qDebug() << "ApplyGesture update _tripleTouch=" << q->_tripleTouch;
QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
enum { TapRadius = 40 };
if (delta.manhattanLength() <= TapRadius)
{
if (event->type() == QEvent::TouchEnd)
{
result = (q->_tripleTouch) ? QGestureRecognizer::FinishGesture : QGestureRecognizer::CancelGesture;
}
else result = QGestureRecognizer::TriggerGesture;
}
}
} break;
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
result = QGestureRecognizer::Ignore;
break;
default:
result = QGestureRecognizer::Ignore;
break;
}
return result;
} // TabGestureRecognizer::recognize( QGesture * state, QObject * watched, QEvent * event)
void ApplyGestureRecognizer::reset( QGesture * state )
{
ApplyGesture * q = static_cast<ApplyGesture*>(state);
q->_tripleTouch = false;
} // TabGestureRecognizer::reset( QGesture * gesture )
}} // namspaces
+71
View File
@@ -0,0 +1,71 @@
/**
**
** @RDE_HEADER@
** Gesture recognition
** $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 .........:
**
** 09.12.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QObject>
#include <QtWidgets/QGesture>
#include <QtWidgets/QGestureRecognizer>
namespace SituationMap { namespace RB
{
class ApplyGestureRecognizer : public QGestureRecognizer
{
public:
ApplyGestureRecognizer();
virtual ~ApplyGestureRecognizer();
QGesture * create( QObject * target ) override;
QGestureRecognizer::Result recognize( QGesture * gesture, QObject * watched, QEvent * event) override;
void reset( QGesture * gesture ) override;
}; // ApplyGestureRecognizer
class ApplyGesture : public QGesture
{
friend class ApplyGestureRecognizer;
public:
ApplyGesture() = default;
~ApplyGesture() = default;
ApplyGesture( const ApplyGesture & ) = delete;
ApplyGesture & operator = ( const ApplyGesture & ) = delete;
QPointF position() const { return _position; }
private:
QPointF _position;
bool _tripleTouch;
}; // ApplyGesture
}} // namespaces
+83
View File
@@ -0,0 +1,83 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
#include <QtWidgets/QApplication>
#include <QtWidgets/QGraphicsScene>
#include <QtWidgets/QGraphicsView>
#include <QtWidgets/QGraphicsProxyWidget>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QHBoxLayout>
#include "Map.h"
#include "MapGraphicsView.h"
#include "ArcGISLocalTiledLayer.h"
#include "event_mouse_navigator.h"
int main (int argc, char* argv[])
{
QApplication app ( argc, argv );
// under windows for ArcGIS 10.2.6 and higher
#ifdef Q_OS_WIN
QCoreApplication::setAttribute( Qt::AA_UseOpenGLES );
#endif
QApplication::setApplicationName(QString("Esri - Map Touch Test"));
QWidget top;
QHBoxLayout * layout = new QHBoxLayout( &top );
QTextEdit * console = new QTextEdit( &top );
EsriRuntimeQt::Map * map = new EsriRuntimeQt::Map();
EsriRuntimeQt::MapGraphicsView * mapGraphicsView = EsriRuntimeQt::MapGraphicsView::create(map);
QString samplePath = EsriRuntimeQt::ArcGISRuntime::installDirectory();
samplePath.append("/sdk/samples/data/tpks/Topographic.tpk");
samplePath = QDir::toNativeSeparators(samplePath);
if (!QFileInfo::exists(samplePath))
{
qDebug() << "Load map from not existing file" << samplePath;
}
EsriRuntimeQt::ArcGISLocalTiledLayer * geoMapBaseLayer = new EsriRuntimeQt::ArcGISLocalTiledLayer( samplePath, &top );
geoMapBaseLayer -> setName( "Base Map Layer" );
QObject::connect( geoMapBaseLayer, &EsriRuntimeQt::ArcGISLocalTiledLayer::layerCreateError , []() { qDebug() << "Loading map failed!"; } );
QObject::connect( geoMapBaseLayer, &EsriRuntimeQt::ArcGISLocalTiledLayer::layerCreateComplete, []() { qDebug() << "Loading map finished."; } );
map->setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation );
map->addLayer(geoMapBaseLayer);
map->setWrapAroundEnabled(true);
map->grid()->setType( EsriRuntimeQt::GridType::Mgrs );
map->grid()->setVisible(false);
mapGraphicsView -> setFixedSize( 600, 400 );
SituationMap::RB::EventMouseNavigator * eventDispatcher = new SituationMap::RB::EventMouseNavigator( mapGraphicsView->viewport(), map );
layout->addWidget( mapGraphicsView );
layout->addWidget( console );
top.show();
QPushButton * button = new QPushButton( QString("Touch or Click me"), nullptr ); // button must be toplevel widget
QGraphicsProxyWidget * widgetProxy = mapGraphicsView->scene()->addWidget( button );
widgetProxy -> setPos( mapGraphicsView->mapToScene(250,200) );
QObject::connect( button, &QPushButton::clicked, [console]() { console->append(QString("Button has been clicked.\n")); });
QObject::connect( button, &QPushButton::pressed, [console]() { console->append(QString("Button has been pressed.\n")); });
return app.exec();
} // main (int argc, char* argv[])