/** ** ** @RDE_HEADER@ ** Implementation of rubberband class to edit states of point objects ** $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 .........: ** ** 19.10.2015, JT : First edition for use with Qt5 ** ** ----------------------------------------------------------------------------------------------- */ #include "rubberband_state_select.h" #include "rubberband_point_handle.h" #include "rubberband_drawable.h" #include "rubberband_handle_proxy.h" #include "rubberband_factory.h" #include "rubberband_internal.h" #include "rubberband_base_internal.h" #include #include #include #include #include #include #include "polygon.xpm" namespace SituationMap { namespace RB { struct RubberbandStateSelect::InternalData { Q_DISABLE_COPY(InternalData) InternalData() : _logger(getLogger("RubberbandStateSelect")) {} ~InternalData() { delete _stateMenu; } QVector _states; QVector _items; QList> _stateMenuSelectedHandleList; LOGGER_T _logger; const State * getStateFromValue( int stateValue ) const { for (const State &state : _states) { if (state.value() == stateValue) return &state; } return nullptr; } /* getStateFromValue */ const State * getStateFromItem( const StateItem & item ) const { return getStateFromValue(item.stateValue()); } static QPixmap * _polygonIcon; QPushButton * _polygonButton = nullptr; QMenu * _stateMenu = nullptr; bool _stateMenuIsActive = false; }; /* RubberbandMultiPointFeature::InternalData */ QPixmap * RubberbandStateSelect::InternalData::_polygonIcon = nullptr; void RubberbandStateSelect::setIconPolygon(const QPixmap & icon) { if (InternalData::_polygonIcon == nullptr) InternalData::_polygonIcon = new QPixmap(icon); else *InternalData::_polygonIcon = icon; } /* RubberbandStateSelect::setIconPolygon */ RubberbandStateSelect::RubberbandStateSelect( SituationMap::Map * map, QObject * parent ) : Rubberband(map,parent), _internal(new InternalData()) { delete RubberbandBase::_baseInternal->_undoButton; RubberbandBase::_baseInternal->_undoButton = nullptr; _internal->_polygonButton = new QPushButton( (InternalData::_polygonIcon) ? *InternalData::_polygonIcon : QPixmap(polygon_xpm), QString(), RubberbandBase::_baseInternal->_buttonBox ); RubberbandBase::_baseInternal->_buttonLayout->insertWidget( 0, _internal->_polygonButton ); connect( _internal->_polygonButton, &QPushButton::clicked, this, &RubberbandStateSelect::onDo_areaSelectionBegin ); // Check Quartz connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged, this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates ); connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::rectChanged , this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates ); setFixedButtonBoxPosition(QPoint(5,5)); } /* RubberbandStateSelect::RubberbandStateSelect */ bool RubberbandStateSelect::setStateItems( const QVector & stateVector , const QVector & itemVector , CoordinateSystem cs ) { if (stateVector.size() < 2) return false; if (itemVector.isEmpty()) return false; // check for all states are valid for (const State & state : qAsConst(_internal->_states)) { if (!state.isValid()) return false; } bool ok = true; _internal -> _states = stateVector; _internal -> _items = itemVector; for (QVector::iterator it = _internal->_items.begin(); it != _internal->_items.end(); ++it) { if (it->isValid()) { const State * statePtr = _internal->getStateFromItem( *it ); if (statePtr == nullptr) { ok = false; statePtr = & _internal->_states.first(); } const QPointF &mapPos = projectCoordinateToMap( cs, it->coordinate() ); Esri::ArcGISRuntime::Point mapCoordinate( mapPos.x(), mapPos.y(), Rubberband::_internal->_mapView->map()->spatialReference() ); RubberbandPointHandle * handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView , Rubberband::_internal->_mapScene , mapCoordinate , statePtr->icon() ); if (handlePtr) { handlePtr->setSelection(false); handlePtr->setUserData( &(*it) ); Rubberband::_internal->_handleList.append( SituationMap::RefPointer(handlePtr) ); } } else ok = false; } update(); activateButtons(); buildStateMenu(); return ok; } /* RubberbandStateSelect::setStateItems */ const QVector & RubberbandStateSelect::stateItems() const { return _internal->_items; } /* RubberbandStateSelect::stateItems */ void RubberbandStateSelect::activateButtons() { _baseInternal->_hasUndoButton = false; _baseInternal->_applyIsPossible = (!Rubberband::_internal->_handleList.isEmpty()); _baseInternal->_undoIsPossible = false; Rubberband::activateButtons(); } /* RubberbandStateSelect::activateButton */ void RubberbandStateSelect::toggleState( RubberbandPointHandle * handlePtr ) { if (_internal->_states.size() < 2) return; if (handlePtr == nullptr) return; StateItem * itemPtr = static_cast(handlePtr->userData()); if (itemPtr == nullptr) return; if (_internal->_states.size() == 2) { const State * statePtr = _internal -> getStateFromItem( *itemPtr ); if (statePtr != nullptr) { if (statePtr == &_internal->_states.first()) statePtr = &_internal->_states.last(); else statePtr = &_internal->_states.first(); } else statePtr = &_internal->_states.first(); itemPtr->_stateValue = statePtr->value(); handlePtr->setIcon( statePtr->icon() ); } } /* RubberbandStateSelect::toggleState */ void RubberbandStateSelect::setState( RubberbandPointHandle * handlePtr, int stateValue ) { if (handlePtr == nullptr) return; const State * statePtr = _internal -> getStateFromValue(stateValue); StateItem * itemPtr = static_cast(handlePtr->userData()); if (itemPtr == nullptr) return; if (statePtr == nullptr) return; itemPtr->_stateValue = stateValue; handlePtr->setIcon( statePtr->icon() ); } /* RubberbandStateSelect::setState */ void RubberbandStateSelect::buildStateMenu() { if (_internal->_states.isEmpty()) return; _internal->_stateMenu = new QMenu(); for (const State & state : qAsConst(_internal->_states)) { QAction * action = _internal->_stateMenu->addAction( state.icon(), state.description() ); action -> setData( state.value() ); connect( action, &QAction::triggered, this, &RubberbandStateSelect::onDo_setState ); } connect( _internal->_stateMenu, &QMenu::aboutToShow, this, [this]() { this->_internal->_stateMenuSelectedHandleList.clear(); _baseInternal->_buttonBox->setDisabled(true); for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { if (handle->isSelected()) { this->_internal->_stateMenuSelectedHandleList << handle; } } } ); connect( _internal->_stateMenu, &QMenu::aboutToHide, this, [this]() { _baseInternal->_buttonBox->setEnabled(true); deSelectHandles(); } ); } /* RubberbandStateSelect::buildStateMenu */ void RubberbandStateSelect::onDo_setState(bool) { QAction * action = (QAction*) sender(); if (action == nullptr) return; int stateValue = action->data().toInt(); for (auto handle : qAsConst(_internal->_stateMenuSelectedHandleList)) setState( dynamic_cast(handle.get()), stateValue ); _internal -> _stateMenuSelectedHandleList.clear(); _internal -> _stateMenuIsActive = false; update(); } /* RubberbandStateSelect::onDo_setState */ void RubberbandStateSelect::chooseStateMenu() { // popup a window with states the user can choose _internal->_stateMenu->popup( QCursor::pos() ); _internal -> _stateMenuIsActive = true; } /* RubberbandStateSelect::chooseStateMenu */ bool RubberbandStateSelect::selectAction( AbstractEventDispatcher*, int x, int y ) { DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; _internal->_stateMenuSelectedHandleList.clear(); if (rubberbandIsAlive()) { if (_internal->_stateMenuIsActive) return true; QPointF scenePos(pixel2ScenePosition(x,y)); if (_baseInternal->buttonProxyActive(scenePos)) return false; RubberbandHandle * handlePtr = pickHandle(x,y); if (handlePtr) { handlePtr->setSelection(); enableSelectedState(); update(); deactivateButtons(); return true; } } return false; } /* RubberbandStateSelect::selectAction */ bool RubberbandStateSelect::multiSelectActionPress(AbstractEventDispatcher*, int x, int y) { DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; if (rubberbandIsAlive()) { if (_internal -> _stateMenuIsActive) return true; RubberbandHandle * handlePtr = pickHandle(x,y); if (handlePtr) { handlePtr->setSelection(); enableSelectedState(); enableMultiSelectedState(); update(); deactivateButtons(); return true; } } return false; } /* RubberbandStateSelect::multiSelectActionPress */ bool RubberbandStateSelect::multiSelectActionRelease( AbstractEventDispatcher*, int, int ) { return false; } /* RubberbandStateSelect::multiSelectActionRelease */ bool RubberbandStateSelect::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) { DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; if (rubberbandIsAlive()) { Rubberband::_internal->_lastClickPosition = pixel2ScenePosition(x,y); if (_internal->_stateMenuIsActive) { _internal -> _stateMenuIsActive = false; disenableSelectedState(); disenableMultiSelectedState(); activateButtons(); return true; } if (isEnabledSelectedState()) { bool handlePicked = (pickHandle(x,y) != nullptr); bool toggleMode = (_internal->_states.size() == 2); bool eventHandled = false; if (!toggleMode) { disenableSelectedState(); disenableMultiSelectedState(); chooseStateMenu(); return true; } for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if ((handlePointPtr) && (handlePointPtr->isSelected())) { handlePointPtr->setSelection(false); if ((handlePicked) && (toggleMode)) toggleState( handlePointPtr ); eventHandled = true; } } disenableSelectedState(); disenableMultiSelectedState(); activateButtons(); return eventHandled; } else Rubberband::setCoordinateAction(causer,x,y); } return false; } /* RubberbandStateSelect::setCoordinateAction */ bool RubberbandStateSelect::moveAction(AbstractEventDispatcher*, int x, int y) { DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; if (rubberbandIsAlive()) { if (_internal -> _stateMenuIsActive) return false; bool eventHandled = false; QPointF scenePosition(pixel2ScenePosition(x,y)); if (_baseInternal->buttonProxyActive(scenePosition)) return false; if (Rubberband::_internal->_mapView != nullptr) { if (!isEnabledMultiSelectedState()) deSelectHandles(); RubberbandHandle * handlePtr = pickHandle(x,y); if (handlePtr != nullptr) { handlePtr->setSelection(); eventHandled = true; } } if (eventHandled) update(); } return false; } /* RubberbandStateSelect::moveAction */ bool RubberbandStateSelect::movePickedAction(AbstractEventDispatcher * causer, int x, int y ) { DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; Rubberband::movePickedAction(causer,x,y); return false; // isEnabledSelectedState() || _internal->_stateMenuIsActive; } /* RubberbandStateSelect::movePickedAction */ bool RubberbandStateSelect::contextMenuAction( AbstractEventDispatcher*, int, int ) { if (!_internal -> _stateMenuIsActive) chooseStateMenu(); return true; } /* RubberbandStateSelect::contextMenuAction */ void RubberbandStateSelect::onDo_areaSelectionBegin() { bool mRbands = _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands(); _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands(true); { RB::RubberbandMultiPointFeature * selectionRB = _baseInternal->_map->rubberbandFactory().create(this); if (selectionRB == nullptr) { qWarning() << LOGFUNCTION << "Creating rubberband multi points feature failed"; } else { selectionRB->setAutoDelete(true); // when finished rubberband will commit suicide selectionRB->setDrawColor( QColor(255,0,0,255) ); selectionRB->setFillColor( QColor( 0,0,0,100) ); selectionRB -> enableClosed( true ); selectionRB -> enableFinishAfterConstruction( true ); selectionRB -> setFixedButtonBoxPosition(QPoint(15,15)); QObject::connect( selectionRB, &RB::Rubberband::apply, this, &RubberbandStateSelect::onDo_areaSelectionApply ); QObject::connect( selectionRB, &RB::Rubberband::abort, this, &RubberbandStateSelect::onDo_areaSelectionAbort ); RubberbandBase::_baseInternal->_buttonBox->setDisabled(true); } } _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands( mRbands ); } /* RubberbandStateSelect::onDo_areaSelectionBegin */ void RubberbandStateSelect::onDo_areaSelectionApply( RubberbandBase * selectionRB ) { RubberbandBase::_baseInternal->_buttonBox->setEnabled(true); if (selectionRB) { QVector polygonVertexList = selectionRB->coordinateVector( CoordinateSystem::Map ); // Looking for all handle which are inside polygon and select unsigned int hitCounter = 0; for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if (handlePointPtr) { const Esri::ArcGISRuntime::Point &v = handlePointPtr->getCoordinate()._mapCoordinate; bool inside = false; int nvert = polygonVertexList.size(); for (int i = 0, j = nvert-1; i < nvert; j = i++) { const QPointF & vertex_i = polygonVertexList[i]; const QPointF & vertex_j = polygonVertexList[j]; if ((vertex_i.y()>v.y()) != (vertex_j.y()>v.y())) { if (v.x() < (vertex_j.x() - vertex_i.x()) * (v.y() - vertex_i.y()) / (vertex_j.y() - vertex_i.y()) + vertex_i.x()) { inside = !inside; } } } if (inside) { hitCounter++; handlePointPtr->setSelection(); } } } if (hitCounter > 0) { if (_internal->_states.size() == 2) { for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { if (!handle->isSelected()) continue; handle->setSelection(false); toggleState( dynamic_cast(handle.get()) ); } } else { chooseStateMenu(); // ????? enableMultiSelectedState(); } } } } /* RubberbandStateSelect::onDo_areaSelectionApply */ void RubberbandStateSelect::onDo_areaSelectionAbort( RubberbandBase * ) { _baseInternal->_buttonBox->setEnabled(true); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Really nothing else to do here? * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ } /* RubberbandStateSelect::onDo_areaSelectionAbort */ void RubberbandStateSelect::update() { Rubberband::update(); onDo_updateButtonFixCoordinates(); } /* RubberbandPointCluster::update */ } } /* namespace SituationMap::RB */