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

655 lines
22 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** Implementation of base class for rubberbands
** $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 .........:
**
** 27.03.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "rubberband_base.h"
#include "rubberband_base_internal.h"
#include "rubberband_handle_proxy.h"
#include "rubberband_factory.h"
#include <Common/exception.h>
#include <Map/coord_translation_functor.h>
#include <MapImpl/map_impl.h>
// Qt
#include <QtCore/QCoreApplication>
#include <QtCore/QMetaMethod>
#include <QtWidgets/QGraphicsView>
#include <QtWidgets/QWidget>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QPushButton>
#include <QtGui/QPixmap>
#include "apply.xpm"
#include "abort.xpm"
#include "undo.xpm"
static SituationMap::Map * currentFactoryMap = nullptr;
static bool allowMultipleRubberbands = false;
namespace SituationMap { namespace RB
{
QPixmap * RubberbandBase::BaseInternalData::_applyIcon = nullptr;
QPixmap * RubberbandBase::BaseInternalData::_abortIcon = nullptr;
QPixmap * RubberbandBase::BaseInternalData::_undoIcon = nullptr;
RubberbandBase::BaseInternalData::BaseInternalData( SituationMap::Map * map, RubberbandBase * me )
: _map(map)
, _mapCtFunctor(map->coordTranslationFunctor())
, _isActive(false)
, _applyIsPossible(false)
, _undoIsPossible(false)
, _hasUndoButton(false)
, _lastClickPosition(-1,-1)
, _buttonBoxSceneAnchor()
, _buttonBoxFixPosition(-1,-1)
, _buttonProxy()
, _updateFunctorList()
, _logger(getLogger("RubberbandBase"))
{
QGraphicsView * view = map->getQGraphicsView();
if (view)
{
_restoreCursor = view->viewport()->cursor(); // workaround Qt bug. remove when fixed
}
_buttonBox = new QWidget();
_buttonBox->setObjectName(QLatin1String("RBButtonBoxWidget"));
_buttonBox->hide();
_buttonBox->setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true );
_applyButton = new QPushButton( (_applyIcon) ? *_applyIcon : QPixmap(apply_xpm), QString(), _buttonBox );
_applyButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true );
_applyButton -> setObjectName(QLatin1String("RBApply"));
_applyButton -> setAttribute(Qt::WA_AcceptTouchEvents);
_applyButton -> grabGesture( Qt::GestureType::TapGesture );
me->connect( _applyButton, &QPushButton::clicked, me, &RubberbandBase::onDo_applyButtonClicked );
_cancelButton = new QPushButton( (_abortIcon) ? *_abortIcon : QPixmap(abort_xpm), QString(), _buttonBox );
_cancelButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true );
_cancelButton -> setObjectName(QLatin1String("RBCancel"));
_cancelButton -> setAttribute(Qt::WA_AcceptTouchEvents);
_cancelButton -> grabGesture( Qt::GestureType::TapGesture );
me->connect( _cancelButton, &QPushButton::clicked, me, &RubberbandBase::onDo_cancelButtonClicked );
_undoButton = new QPushButton( (_undoIcon) ? *_undoIcon : QPixmap(undo_xpm), QString(), _buttonBox );
_undoButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true );
_undoButton -> setObjectName(QLatin1String("RBUndo"));
_undoButton -> setAttribute(Qt::WA_AcceptTouchEvents);
_undoButton -> grabGesture( Qt::GestureType::TapGesture );
_undoButton -> hide();
me->connect( _undoButton, &QPushButton::clicked, me, &RubberbandBase::onDo_undoButtonClicked );
_buttonLayout = new QHBoxLayout( _buttonBox );
_buttonLayout->setObjectName(QLatin1String("RBButtonBoxLayoutHBox"));
_buttonLayout -> addWidget( _undoButton );
_buttonLayout -> addWidget( _cancelButton );
_buttonLayout -> addWidget( _applyButton );
} /* InternalData */
Rubberband::BaseInternalData::~BaseInternalData()
{
_map = nullptr;
_mapCtFunctor = nullptr;
_buttonProxy = nullptr; // delete RefPointer
if (_buttonBox) _buttonBox -> deleteLater(); // delete all widgets of button box
_buttonBox = nullptr;
_buttonLayout = nullptr;
_applyButton = nullptr;
_cancelButton = nullptr;
_undoButton = nullptr;
} /* ~InternalData */
bool RubberbandBase::BaseInternalData::buttonProxyActive( const QPointF & pos ) const
{
if (_buttonProxy)
{
return ((_buttonProxy->isVisible()) && (_buttonProxy->isPositionInside(pos)));
}
return false;
} /* bool RubberbandBase::BaseInternalData::buttonProxyActive( const QPointF & pos ) const*/
void RubberbandBase::BaseInternalData::enableAndShowOnlyPossibleButtons()
{
_applyButton->setEnabled(_applyIsPossible);
if (_undoButton)
{
if (_hasUndoButton)
{
_undoButton -> show();
}
_undoButton -> setEnabled(_undoIsPossible);
}
} /* void RubberbandBase::InternalData::showOnlyPossibleButtons() */
static QHash<SituationMap::Map*, QList<RubberbandBase*>> &activeRubberbands()
{
static QHash<SituationMap::Map*, QList<RubberbandBase*>> hashTable;
return hashTable;
} // activeRubberbands()
void RubberbandBase::currentFactoryMap(SituationMap::Map * map) { ::currentFactoryMap = map; }
void RubberbandBase::allowMultipleRubberbands( bool ok ) { ::allowMultipleRubberbands = ok; }
void * RubberbandBase::operator new(size_t bytes)
{
QString exceptionMsg;
// be careful it is not thread safe here
if (::currentFactoryMap)
{
bool doNew( ::allowMultipleRubberbands );
if (!doNew)
{
doNew = true;
const auto rubberbands = SituationMap::RB::activeRubberbands().value(::currentFactoryMap);
for (const auto rbnd : rubberbands)
{
if (rbnd->rubberbandIsAlive())
{
doNew = false;
break;
}
}
}
if (doNew)
{
RubberbandBase::currentFactoryMap( nullptr );
RubberbandBase::allowMultipleRubberbands( false );
return ::operator new(bytes);
}
else exceptionMsg = QLatin1String("Another rubberband is already alive at moment");
RubberbandBase::currentFactoryMap( nullptr );
}
else exceptionMsg = QLatin1String("Use rubberband factory to create rubberbands. Calling new directly is forbidden!");
RubberbandBase::currentFactoryMap( nullptr );
RubberbandBase::allowMultipleRubberbands( false );
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* because "return nullptr;" is not compliante with the c++ standard
* throw exception instead
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
throw Exception( __FILE__, __LINE__, exceptionMsg );
} /* RubberbandBase::operator new */
QList<RubberbandBase*> RubberbandBase::activeRubberbands( SituationMap::Map * map )
{
QList<RubberbandBase*> ret;
const auto rubberbands = SituationMap::RB::activeRubberbands().value(map);
for (const auto rbnd : rubberbands)
{
if (rbnd && rbnd->rubberbandIsAlive())
ret += rbnd;
}
return ret;
} /* RubberbandBase::activeRubberbands */
void RubberbandBase::deleteAllActiveRubberbands(Map *map)
{
const QList<SituationMap::RB::RubberbandBase*> &rbCont = RubberbandBase::activeRubberbands(map);
qDeleteAll(rbCont);
} /* RubberbandBase::deleteAllActiveRubberbands */
bool RubberbandBase::isRubberbandActive(SituationMap::Map * map)
{
return (activeRubberbands(map).size() > 0);
} /* RubberbandBase::isRubberbandActive */
void RubberbandBase::abortActiveRubberbands( SituationMap::Map * map )
{
const QList<RubberbandBase*> & rbList = activeRubberbands(map);
for (auto rb : rbList)
{
emit rb->abort(rb);
rb->doAutoDelete();
}
} /* RubberbandBase::abortActiveRubberbands */
void RubberbandBase::deleteMySelf()
{
if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_kickTheBucket();
if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_delete();
if (_windowEventDispatcher) _windowEventDispatcher -> onDo_delete();
_viewPortEventDispatcher = nullptr;
_windowEventDispatcher = nullptr;
_baseInternal->_isActive = false;
deleteLater();
} /* RubberbandBase::deleteMySelf */
RubberbandBase::RubberbandBase( SituationMap::Map * map, QObject * parent, bool isActive )
: QObject( parent )
, EventActionInterface()
, _baseInternal( new RubberbandBase::BaseInternalData(map,this) )
, _drawColor(QColor("black"))
, _fillColor(QColor("black"))
, _handleDrawColor(QColor("black"))
, _handleFillColor(QColor("yellow"))
, _handleSelectionColor(QColor("red"))
, _frameColor(QColor("black"))
, _frameFillColor(QColor(150,150,0,50))
, _frameSelectionColor(QColor(255,0,0,50))
, _frameHandleMargin(10)
, _pickingRatio(0)
, _finishAfterConstruction(true)
, _autoDelete(false)
, _viewPortEventDispatcher(nullptr)
, _windowEventDispatcher (nullptr)
, _userData(nullptr)
{
_fillColor.setAlpha(100);
if (isActive) activate();
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* In case of multiple rubberbands can be active, it is forbidden to
* create a rubberband of the same type twice anyway.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
bool rubberbandOfSameTypeIsActive = false;
const auto rubberbands = SituationMap::RB::activeRubberbands().value(map);
for (auto rbnd : rubberbands)
{
// Just to check the class names with debugger
// const char * rbndName = typeid(*rbnd).name();
// const char * thisName = typeid(*this).name();
if (( typeid(*rbnd) == typeid(*this)) && rbnd->rubberbandIsAlive())
{
rubberbandOfSameTypeIsActive = true;
break;
}
}
if (rubberbandOfSameTypeIsActive)
{
// disable dispatcher, so the rubberband is not alive yet.
if (_viewPortEventDispatcher)
{
_viewPortEventDispatcher -> onDo_kickTheBucket();
_viewPortEventDispatcher -> onDo_delete();
}
if (_windowEventDispatcher) _windowEventDispatcher->onDo_delete();
_viewPortEventDispatcher = nullptr;
_windowEventDispatcher = nullptr;
}
else
{
SituationMap::RB::activeRubberbands()[map].append( this );
if (_windowEventDispatcher)
{
_windowEventDispatcher->setCursor(QCursor(Qt::CrossCursor));
_windowEventDispatcher->grab();
}
if (_viewPortEventDispatcher)
{
_viewPortEventDispatcher->setCursor(QCursor(Qt::CrossCursor));
_viewPortEventDispatcher->grab();
}
}
if (map)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Sending signals of RubberbandFactory to inform about rubberband creation and destroy would be
* better placed in RubberbandFactory source (rubberband_factory.cpp) but there it's a lot of
* more writing stuff ...
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
SituationMap::RB::RubberbandFactory & rbndFactory = map -> rubberbandFactory();
emit rbndFactory.rubberbandCreated();
connect( this, &QObject::destroyed, &rbndFactory, &SituationMap::RB::RubberbandFactory::rubberbandDestroyed );
}
} /* RubberbandBase::RubberbandBase */
RubberbandBase::~RubberbandBase()
{
if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_kickTheBucket();
if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_delete();
if (_windowEventDispatcher) _windowEventDispatcher -> onDo_delete();
_viewPortEventDispatcher = nullptr;
_windowEventDispatcher = nullptr;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Test if map has been already destroyed. If map exists all is nice ...
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (_baseInternal->_map)
{
SituationMap::RB::activeRubberbands()[_baseInternal->_map].removeOne( this );
emit finished( this );
}
else
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ... otherwise we have to search the rubberband
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
const auto rubberbands = SituationMap::RB::activeRubberbands();
for (auto rubberList : rubberbands)
rubberList.removeOne(this);
}
delete _baseInternal;
_baseInternal = nullptr;
} /* RubberbandBase::~RubberbandBase */
QPointF RubberbandBase::projectCoordinateToMap( CoordinateSystem cs, const QPointF & loc ) const
{
return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? loc : _baseInternal->_mapCtFunctor->geographicToMap( loc );
} /* RubberbandBase::projectCoordinateToMap */
QPointF RubberbandBase::projectCoordinateFromMap( CoordinateSystem cs, const QPointF & loc ) const
{
return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? loc : _baseInternal->_mapCtFunctor->mapToGeographic( loc );
} /* RubberbandBase::projectCoordinateFromMap */
QPointF RubberbandBase::projectCoordinateFromMap( CoordinateSystem cs, const Esri::ArcGISRuntime::Point & loc ) const
{
return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? QPointF(loc.x(), loc.y()) : _baseInternal->_mapCtFunctor->mapToGeographic( loc );
} /* RubberbandBase::projectCoordinateFromMap */
void RubberbandBase::activate()
{
if (!_baseInternal->_map) return;
if (!_baseInternal->_isActive)
{
QGraphicsView * view = _baseInternal->_map->getQGraphicsView();
if (view)
{
_viewPortEventDispatcher = new EventDispatcher(view->viewport(), this);
_windowEventDispatcher = new EventDelegater(view, _viewPortEventDispatcher);
_baseInternal->_isActive = true;
MapImpl *mapImpl = static_cast<MapImpl*>(_baseInternal->_map.data());
connect(_viewPortEventDispatcher.data(), &EventDispatcher::mouseMovedEvent,
mapImpl, &MapImpl::onHandleMouseMove);
}
}
} /* RubberbandBase::activate */
bool RubberbandBase::autoSelectAction(int x, int y)
{
_viewPortEventDispatcher->_leftPressHandled = true;
return selectAction(nullptr,x,y);
} /* RubberbandBase::autoSelectAction */
void RubberbandBase::installExternalUpdate(const std::function<bool(RubberbandBase*)> & updateFunctor)
{
_baseInternal->_updateFunctorList << updateFunctor;
} // void RubberbandBase::installExternalUpdate( const std::function<bool(RubberbandBase*)> & updateFunctor)
void RubberbandBase::externalUpdate()
{
for (const auto &update : qAsConst(_baseInternal->_updateFunctorList))
{
if (!update(this))
break;
}
} // void RubberbandBase::externalUpdate()
void RubberbandBase::updateButtonPosition()
{
if ((_baseInternal->_lastClickPosition.x() < 0) || (_baseInternal->_lastClickPosition.y() < 0))
_baseInternal->_buttonBoxSceneAnchor = sceneBbox().bottomRight();
else
_baseInternal->_buttonBoxSceneAnchor = _baseInternal->_lastClickPosition;
// _internal->_mapView->update();
} /* RubberbandBase::updateButtonPosition */
void RubberbandBase::setFixedButtonBoxPosition( QPoint widgetPos )
{
_baseInternal->_buttonBoxFixPosition = widgetPos;
activateButtons();
} /* RubberbandBase::setFixedButtonBoxPosition */
void RubberbandBase::unsetFixedButtonBoxPosition()
{
_baseInternal->_buttonBoxFixPosition = QPoint(-1,-1);
if (_baseInternal->_buttonProxy)
{
_baseInternal->_buttonProxy->setPos( _baseInternal->_buttonBoxSceneAnchor );
}
} /* RubberbandBase::unsetFixedButtonBoxPosition */
bool RubberbandBase::isButtonBoxPositionFixed() { return ((_baseInternal->_buttonBoxFixPosition.x() >= 0) && (_baseInternal->_buttonBoxFixPosition.y() >= 0)); }
void RubberbandBase::onDo_updateButtonFixCoordinates()
{
if (!_baseInternal->_buttonProxy) return;
if (isButtonBoxPositionFixed())
{
// Don't use bottom right corner of rubberband bbox to place the buttons,
// rather than use fixed window position.
const QPoint & pos = _baseInternal->_buttonBoxFixPosition;
_baseInternal->_buttonProxy->setPos( pixel2ScenePosition(pos.x(),pos.y()) );
}
} /* RubberbandStateSelect::onDo_updateButtonFixCoordinates */
void RubberbandBase::activateButtons()
{
DEBUG_LOG(_baseInternal->_logger) << LOGFUNCTION;
if (!_baseInternal->_buttonProxy)
{
_baseInternal->_buttonProxy = new RubberbandHandleProxy( _baseInternal->_map->getQGraphicsView(), _baseInternal->_buttonBox );
_baseInternal->_buttonProxy->setRestoreCursor( _baseInternal->_restoreCursor ); // workaround Qt bug. remove when fixed
connect( _baseInternal->_buttonProxy.get(), &RubberbandHandleProxy::adjusted, this, [this]()
{
this->_baseInternal->_buttonBoxSceneAnchor = this->_baseInternal->_buttonProxy->getCoordinate()._sceneCoordinate;
} );
}
_baseInternal->enableAndShowOnlyPossibleButtons();
if (isButtonBoxPositionFixed())
{
const QPoint & pos = _baseInternal->_buttonBoxFixPosition;
_baseInternal->_buttonProxy->setPos( pixel2ScenePosition(pos.x(),pos.y()) );
_baseInternal->_buttonProxy->setSceneOffset( QPointF(0,0) );
}
else
{
_baseInternal->_buttonProxy->setPos( _baseInternal->_buttonBoxSceneAnchor );
_baseInternal->_buttonProxy->setSceneOffset( QPointF(_frameHandleMargin+5,_frameHandleMargin+5) );
}
_baseInternal->_buttonProxy->show();
} /* Rubberband::activateButtons */
void RubberbandBase::deactivateButtons()
{
DEBUG_LOG(_baseInternal->_logger) << LOGFUNCTION;
if (isButtonBoxPositionFixed()) return;
if (_baseInternal->_buttonProxy) _baseInternal->_buttonProxy->hide();
} /* RubberbandBase::deactivateButtons() */
void RubberbandBase::adjustButtons()
{
if (_baseInternal->_buttonProxy)
{
activateButtons();
}
} /* RubberbandBase::adjustButtons */
void RubberbandBase::applyButtonClicked()
{
completeStateAction( _viewPortEventDispatcher, 0.0, 0.0 );
} /* RubberbandBase::applyButtonClicked */
void RubberbandBase::cancelButtonClicked()
{
abortAction( _viewPortEventDispatcher, 0.0, 0.0 );
} /* RubberbandBase::cancelButtonClicked */
void RubberbandBase::undoButtonClicked()
{
undoAction( _viewPortEventDispatcher, 0.0, 0.0 );
} /* RubberbandBase::undoButtonClicked */
void RubberbandBase::resetIconsToDefault()
{
if (BaseInternalData::_applyIcon) { delete BaseInternalData::_applyIcon; BaseInternalData::_applyIcon = nullptr; }
if (BaseInternalData::_abortIcon) { delete BaseInternalData::_abortIcon; BaseInternalData::_abortIcon = nullptr; }
if (BaseInternalData::_undoIcon ) { delete BaseInternalData::_undoIcon; BaseInternalData::_undoIcon = nullptr; }
} /* RubberbandBase::resetIconsToDefault */
void RubberbandBase::setIconApply(const QPixmap & icon)
{
if (BaseInternalData::_applyIcon == nullptr) BaseInternalData::_applyIcon = new QPixmap(icon);
else *BaseInternalData::_applyIcon = icon;
} /* RubberbandBase::setIconApply */
void RubberbandBase::setIconAbort(const QPixmap & icon)
{
if (BaseInternalData::_abortIcon == nullptr) BaseInternalData::_abortIcon = new QPixmap(icon);
else *BaseInternalData::_abortIcon = icon;
} /* RubberbandBase::setIconAbort */
void RubberbandBase::setIconUndo(const QPixmap & icon)
{
if (BaseInternalData::_undoIcon == nullptr) BaseInternalData::_undoIcon = new QPixmap(icon);
else *BaseInternalData::_undoIcon = icon;
} /* RubberbandBase::setIconUndo */
QPointF RubberbandBase::pixel2ScenePosition( int x, int y ) const
{
QGraphicsView * view = _baseInternal->_map->getQGraphicsView();
return (view) ? view->mapToScene(x, y) : QPointF(x,y);
} /* RubberbandBase::pixel2ScenePosition */
bool RubberbandBase::buttonPositionAction(AbstractEventDispatcher *, int x, int y)
{
QPointF scenePos(pixel2ScenePosition(x,y));
if (!_baseInternal->buttonProxyActive(scenePos))
{
_baseInternal->_buttonBoxSceneAnchor = scenePos;
adjustButtons();
}
return false;
} /* bool RubberbandBase::buttonPositionAction(AbstractEventDispatcher * causer, int x, int y) */
void RubberbandBase::unlinkEventDispatcher( AbstractEventDispatcher* )
{
_viewPortEventDispatcher = nullptr;
_windowEventDispatcher = nullptr;
} /* RubberbandBase::unlinkEventDispatcher */
bool RubberbandBase::abortAction( AbstractEventDispatcher * causer, int, int )
{
_baseInternal->_buttonBox->hide();
if (causer) causer->onDo_kickTheBucket();
emit abort( this );
doAutoDelete();
return true;
} /* RubberbandBase::abortAction */
bool RubberbandBase::applyAndEndAction( AbstractEventDispatcher * causer, int,int )
{
_baseInternal->_buttonBox->hide();
if (causer) causer->onDo_kickTheBucket();
emit apply( this );
doAutoDelete();
return true;
} /* RubberbandBase::applyAndEndAction */
} } /* namespace SituationMap::RB */