File indexing completed on 2024-05-05 03:50:40
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com> 0004 // 0005 0006 // Self 0007 #include "PolylineAnnotation.h" 0008 0009 // Qt 0010 #include <QApplication> 0011 #include <QPalette> 0012 #include <qmath.h> 0013 0014 // Marble 0015 #include "SceneGraphicsTypes.h" 0016 #include "GeoPainter.h" 0017 #include "PolylineNode.h" 0018 #include "GeoDataLineString.h" 0019 #include "GeoDataPlacemark.h" 0020 #include "ViewportParams.h" 0021 #include "MarbleColors.h" 0022 #include "MergingPolylineNodesAnimation.h" 0023 #include "osm/OsmPlacemarkData.h" 0024 0025 0026 namespace Marble 0027 { 0028 0029 const int PolylineAnnotation::regularDim = 15; 0030 const int PolylineAnnotation::selectedDim = 15; 0031 const int PolylineAnnotation::mergedDim = 20; 0032 const int PolylineAnnotation::hoveredDim = 20; 0033 const QColor PolylineAnnotation::regularColor = Oxygen::aluminumGray3; 0034 const QColor PolylineAnnotation::mergedColor = Oxygen::emeraldGreen6; 0035 0036 PolylineAnnotation::PolylineAnnotation( GeoDataPlacemark *placemark ) : 0037 SceneGraphicsItem( placemark ), 0038 m_viewport( nullptr ), 0039 m_regionsInitialized( false ), 0040 m_busy( false ), 0041 m_interactingObj( InteractingNothing ), 0042 m_clickedNodeIndex( -1 ), 0043 m_hoveredNodeIndex( -1 ), 0044 m_virtualHoveredNode( -1 ) 0045 0046 { 0047 setPaintLayers(QStringList() << "PolylineAnnotation"); 0048 } 0049 0050 PolylineAnnotation::~PolylineAnnotation() 0051 { 0052 delete m_animation; 0053 } 0054 0055 void PolylineAnnotation::paint(GeoPainter *painter, const ViewportParams *viewport , const QString &layer, int tileZoomLevel) 0056 { 0057 Q_UNUSED(layer); 0058 Q_UNUSED(tileZoomLevel); 0059 m_viewport = viewport; 0060 Q_ASSERT(geodata_cast<GeoDataLineString>(placemark()->geometry())); 0061 0062 painter->save(); 0063 if ( state() == SceneGraphicsItem::DrawingPolyline || !m_regionsInitialized ) { 0064 setupRegionsLists( painter ); 0065 m_regionsInitialized = true; 0066 } else { 0067 updateRegions( painter ); 0068 } 0069 0070 if ( hasFocus() ) { 0071 drawNodes( painter ); 0072 } 0073 painter->restore(); 0074 } 0075 0076 void PolylineAnnotation::setupRegionsLists( GeoPainter *painter ) 0077 { 0078 Q_ASSERT( state() == SceneGraphicsItem::DrawingPolyline || !m_regionsInitialized ); 0079 const GeoDataLineString line = static_cast<const GeoDataLineString>( *placemark()->geometry() ); 0080 0081 // Add polyline nodes. 0082 QVector<GeoDataCoordinates>::ConstIterator itBegin = line.constBegin(); 0083 QVector<GeoDataCoordinates>::ConstIterator itEnd = line.constEnd(); 0084 0085 m_nodesList.clear(); 0086 m_nodesList.reserve(line.size()); 0087 for ( ; itBegin != itEnd; ++itBegin ) { 0088 const PolylineNode newNode = PolylineNode( painter->regionFromEllipse( *itBegin, regularDim, regularDim ) ); 0089 m_nodesList.append( newNode ); 0090 } 0091 0092 // Add region from polyline so that events on polyline's 'lines' could be caught. 0093 m_polylineRegion = painter->regionFromPolyline( line, 15 ); 0094 } 0095 0096 void PolylineAnnotation::updateRegions( GeoPainter *painter ) 0097 { 0098 if ( m_busy ) { 0099 return; 0100 } 0101 0102 const GeoDataLineString line = static_cast<const GeoDataLineString>( *placemark()->geometry() ); 0103 0104 if ( state() == SceneGraphicsItem::AddingNodes ) { 0105 // Create and update virtual nodes lists when being in the AddingPolgonNodes state, to 0106 // avoid overhead in other states. 0107 m_virtualNodesList.clear(); 0108 for ( int i = 0; i < line.size() - 1; ++i ) { 0109 const QRegion newRegion( painter->regionFromEllipse( line.at(i).interpolate( line.at(i+1), 0.5 ), 0110 hoveredDim, hoveredDim ) ); 0111 m_virtualNodesList.append( PolylineNode( newRegion ) ); 0112 } 0113 } 0114 0115 0116 // Update the polyline region; 0117 m_polylineRegion = painter->regionFromPolyline( line, 15 ); 0118 0119 // Update the node lists. 0120 for ( int i = 0; i < m_nodesList.size(); ++i ) { 0121 const QRegion newRegion = m_nodesList.at(i).isSelected() ? 0122 painter->regionFromEllipse( line.at(i), selectedDim, selectedDim ) : 0123 painter->regionFromEllipse( line.at(i), regularDim, regularDim ); 0124 m_nodesList[i].setRegion( newRegion ); 0125 } 0126 } 0127 0128 void PolylineAnnotation::drawNodes( GeoPainter *painter ) 0129 { 0130 // These are the 'real' dimensions of the drawn nodes. The ones which have class scope are used 0131 // to generate the regions and they are a little bit larger, because, for example, it would be 0132 // a little bit too hard to select nodes. 0133 static const int d_regularDim = 10; 0134 static const int d_selectedDim = 10; 0135 static const int d_mergedDim = 20; 0136 static const int d_hoveredDim = 20; 0137 0138 const GeoDataLineString line = static_cast<const GeoDataLineString>( *placemark()->geometry() ); 0139 0140 QColor glowColor = QApplication::palette().highlightedText().color(); 0141 glowColor.setAlpha(120); 0142 auto const selectedColor = QApplication::palette().highlight().color(); 0143 auto const hoveredColor = selectedColor; 0144 0145 for ( int i = 0; i < line.size(); ++i ) { 0146 // The order here is important, because a merged node can be at the same time selected. 0147 if ( m_nodesList.at(i).isBeingMerged() ) { 0148 painter->setBrush( mergedColor ); 0149 painter->drawEllipse( line.at(i), d_mergedDim, d_mergedDim ); 0150 } else if ( m_nodesList.at(i).isSelected() ) { 0151 painter->setBrush( selectedColor ); 0152 painter->drawEllipse( line.at(i), d_selectedDim, d_selectedDim ); 0153 0154 if ( m_nodesList.at(i).isEditingHighlighted() || 0155 m_nodesList.at(i).isMergingHighlighted() ) { 0156 QPen defaultPen = painter->pen(); 0157 QPen newPen; 0158 newPen.setWidth( defaultPen.width() + 3 ); 0159 newPen.setColor( glowColor ); 0160 0161 painter->setBrush( Qt::NoBrush ); 0162 painter->setPen( newPen ); 0163 painter->drawEllipse( line.at(i), d_selectedDim + 2, d_selectedDim + 2 ); 0164 painter->setPen( defaultPen ); 0165 } 0166 } else { 0167 painter->setBrush( regularColor ); 0168 painter->drawEllipse( line.at(i), d_regularDim, d_regularDim ); 0169 0170 if ( m_nodesList.at(i).isEditingHighlighted() || 0171 m_nodesList.at(i).isMergingHighlighted() ) { 0172 QPen defaultPen = painter->pen(); 0173 QPen newPen; 0174 newPen.setWidth( defaultPen.width() + 3 ); 0175 newPen.setColor( glowColor ); 0176 0177 painter->setPen( newPen ); 0178 painter->setBrush( Qt::NoBrush ); 0179 painter->drawEllipse( line.at(i), d_regularDim + 2, d_regularDim + 2 ); 0180 painter->setPen( defaultPen ); 0181 } 0182 } 0183 } 0184 0185 if ( m_virtualHoveredNode != -1 ) { 0186 painter->setBrush( hoveredColor ); 0187 0188 GeoDataCoordinates newCoords; 0189 if ( m_virtualHoveredNode + 1 ) { 0190 newCoords = line.at( m_virtualHoveredNode + 1 ).interpolate( line.at( m_virtualHoveredNode ), 0.5 ); 0191 } else { 0192 newCoords = line.first().interpolate( line.last(), 0.5 ); 0193 } 0194 painter->drawEllipse( newCoords, d_hoveredDim, d_hoveredDim ); 0195 } 0196 } 0197 0198 bool PolylineAnnotation::containsPoint( const QPoint &point ) const 0199 { 0200 if ( state() == SceneGraphicsItem::Editing ) { 0201 return nodeContains( point ) != -1 || polylineContains( point ); 0202 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0203 return nodeContains( point ) != -1; 0204 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0205 return virtualNodeContains( point ) != -1 || 0206 nodeContains( point ) != -1 || 0207 polylineContains( point ); 0208 } 0209 0210 return false; 0211 } 0212 0213 int PolylineAnnotation::nodeContains( const QPoint &point ) const 0214 { 0215 if ( !hasFocus() ) { 0216 return -1; 0217 } 0218 0219 for ( int i = 0; i < m_nodesList.size(); ++i ) { 0220 if ( m_nodesList.at(i).containsPoint( point ) ) { 0221 return i; 0222 } 0223 } 0224 0225 return -1; 0226 } 0227 0228 int PolylineAnnotation::virtualNodeContains( const QPoint &point ) const 0229 { 0230 if ( !hasFocus() ) { 0231 return -1; 0232 } 0233 0234 for ( int i = 0; i < m_virtualNodesList.size(); ++i ) { 0235 if ( m_virtualNodesList.at(i).containsPoint( point ) ) 0236 return i; 0237 } 0238 0239 return -1; 0240 } 0241 0242 bool PolylineAnnotation::polylineContains( const QPoint &point ) const 0243 { 0244 return m_polylineRegion.contains( point ); 0245 } 0246 0247 void PolylineAnnotation::dealWithItemChange( const SceneGraphicsItem *other ) 0248 { 0249 Q_UNUSED( other ); 0250 0251 // So far we only deal with item changes when hovering nodes, so that 0252 // they do not remain hovered when changing the item we interact with. 0253 if ( state() == SceneGraphicsItem::Editing ) { 0254 if ( m_hoveredNodeIndex != -1 && 0255 m_hoveredNodeIndex < static_cast<GeoDataLineString*>( placemark()->geometry() )->size() ) { 0256 m_nodesList[m_hoveredNodeIndex].setFlag( PolylineNode::NodeIsEditingHighlighted, false ); 0257 } 0258 0259 m_hoveredNodeIndex = -1; 0260 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0261 if ( m_hoveredNodeIndex != -1 ) { 0262 m_nodesList[m_hoveredNodeIndex].setFlag( PolylineNode::NodeIsMergingHighlighted, false ); 0263 } 0264 0265 m_hoveredNodeIndex = -1; 0266 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0267 m_virtualHoveredNode = -1; 0268 } 0269 } 0270 0271 void PolylineAnnotation::move( const GeoDataCoordinates &source, const GeoDataCoordinates &destination ) 0272 { 0273 GeoDataLineString *lineString = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0274 GeoDataLineString oldLineString = *lineString; 0275 OsmPlacemarkData *osmData = nullptr; 0276 if ( placemark()->hasOsmData() ) { 0277 osmData = &placemark()->osmData(); 0278 } 0279 lineString->clear(); 0280 0281 const qreal deltaLat = destination.latitude() - source.latitude(); 0282 const qreal deltaLon = destination.longitude() - source.longitude(); 0283 0284 Quaternion latRectAxis = Quaternion::fromEuler( 0, destination.longitude(), 0); 0285 Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0); 0286 Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0); 0287 Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis; 0288 0289 for ( int i = 0; i < oldLineString.size(); ++i ) { 0290 const GeoDataCoordinates movedPoint = oldLineString.at(i).rotateAround(rotAxis); 0291 if ( osmData ) { 0292 osmData->changeNodeReference( oldLineString.at( i ), movedPoint ); 0293 } 0294 lineString->append( movedPoint ); 0295 } 0296 } 0297 0298 void PolylineAnnotation::setBusy( bool enabled ) 0299 { 0300 m_busy = enabled; 0301 0302 if ( !enabled && m_animation && state() == SceneGraphicsItem::MergingNodes ) { 0303 if ( m_firstMergedNode != -1 && m_secondMergedNode != -1 ) { 0304 // Update the PolylineNodes lists after the animation has finished its execution. 0305 m_nodesList[m_secondMergedNode].setFlag( PolylineNode::NodeIsMergingHighlighted, false ); 0306 m_hoveredNodeIndex = -1; 0307 0308 // Remove the merging node flag and add the NodeIsSelected flag if either one of the 0309 // merged nodes had been selected before merging them. 0310 m_nodesList[m_secondMergedNode].setFlag( PolylineNode::NodeIsMerged, false ); 0311 if ( m_nodesList[m_firstMergedNode].isSelected() ) { 0312 m_nodesList[m_secondMergedNode].setFlag( PolylineNode::NodeIsSelected ); 0313 } 0314 m_nodesList.removeAt( m_firstMergedNode ); 0315 0316 m_firstMergedNode = -1; 0317 m_secondMergedNode = -1; 0318 } 0319 0320 delete m_animation; 0321 } 0322 } 0323 0324 bool PolylineAnnotation::isBusy() const 0325 { 0326 return m_busy; 0327 } 0328 0329 void PolylineAnnotation::deselectAllNodes() 0330 { 0331 if ( state() != SceneGraphicsItem::Editing ) { 0332 return; 0333 } 0334 0335 for ( int i = 0 ; i < m_nodesList.size(); ++i ) { 0336 m_nodesList[i].setFlag( PolylineNode::NodeIsSelected, false ); 0337 } 0338 } 0339 0340 void PolylineAnnotation::deleteAllSelectedNodes() 0341 { 0342 if ( state() != SceneGraphicsItem::Editing ) { 0343 return; 0344 } 0345 0346 GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0347 OsmPlacemarkData *osmData = nullptr; 0348 if ( placemark()->hasOsmData() ) { 0349 osmData = &placemark()->osmData(); 0350 } 0351 for ( int i = 0; i < line->size(); ++i ) { 0352 if ( m_nodesList.at(i).isSelected() ) { 0353 if ( m_nodesList.size() <= 2 ) { 0354 setRequest( SceneGraphicsItem::RemovePolylineRequest ); 0355 return; 0356 } 0357 if ( osmData ) { 0358 osmData->removeNodeReference( line->at( i ) ); 0359 } 0360 m_nodesList.removeAt( i ); 0361 line->remove( i ); 0362 --i; 0363 } 0364 } 0365 } 0366 0367 void PolylineAnnotation::deleteClickedNode() 0368 { 0369 if ( state() != SceneGraphicsItem::Editing ) { 0370 return; 0371 } 0372 0373 GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0374 OsmPlacemarkData *osmData = nullptr; 0375 if ( placemark()->hasOsmData() ) { 0376 osmData = &placemark()->osmData(); 0377 } 0378 if ( m_nodesList.size() <= 2 ) { 0379 setRequest( SceneGraphicsItem::RemovePolylineRequest ); 0380 return; 0381 } 0382 0383 if ( osmData ) { 0384 osmData->removeMemberReference( m_clickedNodeIndex ); 0385 } 0386 0387 m_nodesList.removeAt( m_clickedNodeIndex ); 0388 line->remove( m_clickedNodeIndex ); 0389 } 0390 0391 void PolylineAnnotation::changeClickedNodeSelection() 0392 { 0393 if ( state() != SceneGraphicsItem::Editing ) { 0394 return; 0395 } 0396 0397 m_nodesList[m_clickedNodeIndex].setFlag( PolylineNode::NodeIsSelected, 0398 !m_nodesList[m_clickedNodeIndex].isSelected() ); 0399 } 0400 0401 bool PolylineAnnotation::hasNodesSelected() const 0402 { 0403 for ( int i = 0; i < m_nodesList.size(); ++i ) { 0404 if ( m_nodesList.at(i).isSelected() ) { 0405 return true; 0406 } 0407 } 0408 0409 return false; 0410 } 0411 0412 bool PolylineAnnotation::clickedNodeIsSelected() const 0413 { 0414 return m_nodesList[m_clickedNodeIndex].isSelected(); 0415 } 0416 0417 QPointer<MergingPolylineNodesAnimation> PolylineAnnotation::animation() 0418 { 0419 return m_animation; 0420 } 0421 0422 bool PolylineAnnotation::mousePressEvent( QMouseEvent *event ) 0423 { 0424 if ( !m_viewport || m_busy ) { 0425 return false; 0426 } 0427 0428 setRequest( SceneGraphicsItem::NoRequest ); 0429 0430 if ( state() == SceneGraphicsItem::Editing ) { 0431 return processEditingOnPress( event ); 0432 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0433 return processMergingOnPress( event ); 0434 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0435 return processAddingNodesOnPress( event ); 0436 } 0437 0438 return false; 0439 } 0440 0441 bool PolylineAnnotation::mouseMoveEvent( QMouseEvent *event ) 0442 { 0443 if ( !m_viewport || m_busy ) { 0444 return false; 0445 } 0446 0447 setRequest( SceneGraphicsItem::NoRequest ); 0448 0449 if ( state() == SceneGraphicsItem::Editing ) { 0450 return processEditingOnMove( event ); 0451 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0452 return processMergingOnMove( event ); 0453 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0454 return processAddingNodesOnMove( event ); 0455 } 0456 0457 return false; 0458 } 0459 0460 bool PolylineAnnotation::mouseReleaseEvent( QMouseEvent *event ) 0461 { 0462 if ( !m_viewport || m_busy ) { 0463 return false; 0464 } 0465 0466 setRequest( SceneGraphicsItem::NoRequest ); 0467 0468 if ( state() == SceneGraphicsItem::Editing ) { 0469 return processEditingOnRelease( event ); 0470 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0471 return processMergingOnRelease( event ); 0472 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0473 return processAddingNodesOnRelease( event ); 0474 } 0475 0476 return false; 0477 } 0478 0479 void PolylineAnnotation::dealWithStateChange( SceneGraphicsItem::ActionState previousState ) 0480 { 0481 // Dealing with cases when exiting a state has an effect on this item. 0482 if ( previousState == SceneGraphicsItem::DrawingPolyline ) { 0483 // nothing so far 0484 } else if ( previousState == SceneGraphicsItem::Editing ) { 0485 // Make sure that when changing the state, there is no highlighted node. 0486 if ( m_hoveredNodeIndex != -1 ) { 0487 m_nodesList[m_hoveredNodeIndex].setFlag( PolylineNode::NodeIsEditingHighlighted, false ); 0488 } 0489 0490 m_clickedNodeIndex = -1; 0491 m_hoveredNodeIndex = -1; 0492 } else if ( previousState == SceneGraphicsItem::MergingNodes ) { 0493 // If there was only a node selected for being merged and the state changed, 0494 // deselect it. 0495 if ( m_firstMergedNode != -1 ) { 0496 m_nodesList[m_firstMergedNode].setFlag( PolylineNode::NodeIsMerged, false ); 0497 } 0498 0499 // Make sure that when changing the state, there is no highlighted node. 0500 if ( m_hoveredNodeIndex != -1 ) { 0501 if ( m_hoveredNodeIndex != -1 ) { 0502 m_nodesList[m_hoveredNodeIndex].setFlag( PolylineNode::NodeIsEditingHighlighted, false ); 0503 } 0504 } 0505 0506 m_hoveredNodeIndex = -1; 0507 delete m_animation; 0508 } else if ( previousState == SceneGraphicsItem::AddingNodes ) { 0509 m_virtualNodesList.clear(); 0510 m_virtualHoveredNode = -1; 0511 m_adjustedNode = -1; 0512 } 0513 0514 // Dealing with cases when entering a state has an effect on this item, or 0515 // initializations are needed. 0516 if ( state() == SceneGraphicsItem::Editing ) { 0517 m_interactingObj = InteractingNothing; 0518 m_clickedNodeIndex = -1; 0519 m_hoveredNodeIndex = -1; 0520 } else if ( state() == SceneGraphicsItem::MergingNodes ) { 0521 m_firstMergedNode = -1; 0522 m_secondMergedNode = -1; 0523 m_hoveredNodeIndex = -1; 0524 m_animation = nullptr; 0525 } else if ( state() == SceneGraphicsItem::AddingNodes ) { 0526 m_virtualHoveredNode = -1; 0527 m_adjustedNode = -1; 0528 } 0529 } 0530 0531 bool PolylineAnnotation::processEditingOnPress( QMouseEvent *mouseEvent ) 0532 { 0533 if ( mouseEvent->button() != Qt::LeftButton && mouseEvent->button() != Qt::RightButton ) { 0534 return false; 0535 } 0536 0537 qreal lat, lon; 0538 m_viewport->geoCoordinates( mouseEvent->pos().x(), 0539 mouseEvent->pos().y(), 0540 lon, lat, 0541 GeoDataCoordinates::Radian ); 0542 m_movedPointCoords.set( lon, lat ); 0543 0544 // First check if one of the nodes has been clicked. 0545 m_clickedNodeIndex = nodeContains( mouseEvent->pos() ); 0546 if ( m_clickedNodeIndex != -1 ) { 0547 if ( mouseEvent->button() == Qt::RightButton ) { 0548 setRequest( SceneGraphicsItem::ShowNodeRmbMenu ); 0549 } else { 0550 Q_ASSERT( mouseEvent->button() == Qt::LeftButton ); 0551 m_interactingObj = InteractingNode; 0552 } 0553 0554 return true; 0555 } 0556 0557 // Then check if the 'interior' of the polyline has been clicked (by interior 0558 // I mean its lines excepting its nodes). 0559 if ( polylineContains( mouseEvent->pos() ) ) { 0560 if ( mouseEvent->button() == Qt::RightButton ) { 0561 setRequest( SceneGraphicsItem::ShowPolylineRmbMenu ); 0562 } else { 0563 Q_ASSERT( mouseEvent->button() == Qt::LeftButton ); 0564 m_interactingObj = InteractingPolyline; 0565 } 0566 0567 return true; 0568 } 0569 0570 return false; 0571 } 0572 0573 bool PolylineAnnotation::processEditingOnMove( QMouseEvent *mouseEvent ) 0574 { 0575 if ( !m_viewport ) { 0576 return false; 0577 } 0578 0579 qreal lon, lat; 0580 m_viewport->geoCoordinates( mouseEvent->pos().x(), 0581 mouseEvent->pos().y(), 0582 lon, lat, 0583 GeoDataCoordinates::Radian ); 0584 const GeoDataCoordinates newCoords( lon, lat ); 0585 0586 if ( m_interactingObj == InteractingNode ) { 0587 GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0588 OsmPlacemarkData *osmData = nullptr; 0589 if ( placemark()->hasOsmData() ) { 0590 osmData = &placemark()->osmData(); 0591 } 0592 0593 // Keeping the OsmPlacemarkData synchronized with the geometry 0594 if ( osmData ) { 0595 osmData->changeNodeReference( line->at( m_clickedNodeIndex ), newCoords ); 0596 } 0597 line->at(m_clickedNodeIndex) = newCoords; 0598 0599 return true; 0600 } else if ( m_interactingObj == InteractingPolyline ) { 0601 GeoDataLineString *lineString = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0602 OsmPlacemarkData *osmData = nullptr; 0603 if ( placemark()->hasOsmData() ) { 0604 osmData = &placemark()->osmData(); 0605 } 0606 const GeoDataLineString oldLineString = *lineString; 0607 lineString->clear(); 0608 0609 const qreal deltaLat = lat - m_movedPointCoords.latitude(); 0610 const qreal deltaLon = lon - m_movedPointCoords.longitude(); 0611 0612 Quaternion latRectAxis = Quaternion::fromEuler( 0, lon, 0); 0613 Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0); 0614 Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0); 0615 Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis; 0616 0617 for ( int i = 0; i < oldLineString.size(); ++i ) { 0618 const GeoDataCoordinates movedPoint = oldLineString.at(i).rotateAround(rotAxis); 0619 if ( osmData ) { 0620 osmData->changeNodeReference( oldLineString.at( i ), movedPoint ); 0621 } 0622 lineString->append( movedPoint ); 0623 } 0624 0625 m_movedPointCoords = newCoords; 0626 return true; 0627 } 0628 0629 return dealWithHovering( mouseEvent ); 0630 } 0631 0632 bool PolylineAnnotation::processEditingOnRelease( QMouseEvent *mouseEvent ) 0633 { 0634 static const int mouseMoveOffset = 1; 0635 0636 if ( mouseEvent->button() != Qt::LeftButton ) { 0637 return false; 0638 } 0639 0640 if ( m_interactingObj == InteractingNode ) { 0641 qreal x, y; 0642 m_viewport->screenCoordinates( m_movedPointCoords.longitude(), 0643 m_movedPointCoords.latitude(), 0644 x, y ); 0645 // The node gets selected only if it is clicked and not moved. 0646 if ( qFabs(mouseEvent->pos().x() - x) > mouseMoveOffset || 0647 qFabs(mouseEvent->pos().y() - y) > mouseMoveOffset ) { 0648 m_interactingObj = InteractingNothing; 0649 return true; 0650 } 0651 0652 m_nodesList[m_clickedNodeIndex].setFlag( PolylineNode::NodeIsSelected, 0653 !m_nodesList.at(m_clickedNodeIndex).isSelected() ); 0654 m_interactingObj = InteractingNothing; 0655 return true; 0656 } else if ( m_interactingObj == InteractingPolyline ) { 0657 // Nothing special happens at polyline release. 0658 m_interactingObj = InteractingNothing; 0659 return true; 0660 } 0661 0662 return false; 0663 } 0664 0665 bool PolylineAnnotation::processMergingOnPress( QMouseEvent *mouseEvent ) 0666 { 0667 if ( mouseEvent->button() != Qt::LeftButton ) { 0668 return false; 0669 } 0670 0671 GeoDataLineString line = static_cast<GeoDataLineString>( *placemark()->geometry() ); 0672 0673 const int index = nodeContains( mouseEvent->pos() ); 0674 if ( index == -1 ) { 0675 return false; 0676 } 0677 0678 // If this is the first node selected to be merged. 0679 if ( m_firstMergedNode == -1 ) { 0680 m_firstMergedNode = index; 0681 m_nodesList[index].setFlag( PolylineNode::NodeIsMerged ); 0682 } else { 0683 Q_ASSERT( m_firstMergedNode != -1 ); 0684 0685 // Clicking two times the same node results in unmarking it for merging. 0686 if ( m_firstMergedNode == index ) { 0687 m_nodesList[index].setFlag( PolylineNode::NodeIsMerged, false ); 0688 m_firstMergedNode = -1; 0689 return true; 0690 } 0691 0692 // If these two nodes are the last ones remained as part of the polyline, remove 0693 // the whole polyline. 0694 if ( line.size() <= 2 ) { 0695 setRequest( SceneGraphicsItem::RemovePolylineRequest ); 0696 return true; 0697 } 0698 m_nodesList[index].setFlag( PolylineNode::NodeIsMerged ); 0699 m_secondMergedNode = index; 0700 0701 delete m_animation; 0702 m_animation = new MergingPolylineNodesAnimation( this ); 0703 setRequest( SceneGraphicsItem::StartPolylineAnimation ); 0704 } 0705 0706 return true; 0707 } 0708 0709 bool PolylineAnnotation::processMergingOnMove( QMouseEvent *mouseEvent ) 0710 { 0711 return dealWithHovering( mouseEvent ); 0712 } 0713 0714 bool PolylineAnnotation::processMergingOnRelease( QMouseEvent *mouseEvent ) 0715 { 0716 Q_UNUSED( mouseEvent ); 0717 return true; 0718 } 0719 0720 bool PolylineAnnotation::processAddingNodesOnPress( QMouseEvent *mouseEvent ) 0721 { 0722 if ( mouseEvent->button() != Qt::LeftButton ) { 0723 return false; 0724 } 0725 0726 GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0727 0728 // If a virtual node has just been clicked, add it to the polyline and start 'adjusting' 0729 // its position. 0730 const int virtualIndex = virtualNodeContains( mouseEvent->pos() ); 0731 if ( virtualIndex != -1 && m_adjustedNode == -1 ) { 0732 Q_ASSERT( m_virtualHoveredNode == virtualIndex ); 0733 0734 line->insert( virtualIndex + 1, line->at( virtualIndex ).interpolate( line->at( virtualIndex + 1 ), 0.5 ) ); 0735 m_nodesList.insert( virtualIndex + 1, PolylineNode() ); 0736 0737 m_adjustedNode = virtualIndex + 1; 0738 m_virtualHoveredNode = -1; 0739 return true; 0740 } 0741 0742 // If a virtual node which has been previously clicked and selected to become a 0743 // 'real node' is clicked one more time, it stops from being 'adjusted'. 0744 const int realIndex = nodeContains( mouseEvent->pos() ); 0745 if ( realIndex != -1 && m_adjustedNode != -1 ) { 0746 m_adjustedNode = -1; 0747 return true; 0748 } 0749 0750 return false; 0751 } 0752 0753 bool PolylineAnnotation::processAddingNodesOnMove( QMouseEvent *mouseEvent ) 0754 { 0755 Q_ASSERT( mouseEvent->button() == Qt::NoButton ); 0756 0757 const int index = virtualNodeContains( mouseEvent->pos() ); 0758 0759 // If we are adjusting a virtual node which has just been clicked and became real, just 0760 // change its coordinates when moving it, as we do with nodes in Editing state on move. 0761 if ( m_adjustedNode != -1 ) { 0762 // The virtual node which has just been added is always the last within 0763 // GeoDataLinearRing's container.qreal lon, lat; 0764 qreal lon, lat; 0765 m_viewport->geoCoordinates( mouseEvent->pos().x(), 0766 mouseEvent->pos().y(), 0767 lon, lat, 0768 GeoDataCoordinates::Radian ); 0769 const GeoDataCoordinates newCoords( lon, lat ); 0770 GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); 0771 line->at(m_adjustedNode) = newCoords; 0772 0773 return true; 0774 0775 // If we are hovering a virtual node, store its index in order to be painted in drawNodes 0776 // method. 0777 } else if ( index != -1 ) { 0778 m_virtualHoveredNode = index; 0779 return true; 0780 } 0781 0782 return false; 0783 } 0784 0785 bool PolylineAnnotation::processAddingNodesOnRelease( QMouseEvent *mouseEvent ) 0786 { 0787 Q_UNUSED( mouseEvent ); 0788 return m_adjustedNode == -1; 0789 } 0790 0791 bool PolylineAnnotation::dealWithHovering( QMouseEvent *mouseEvent ) 0792 { 0793 const PolylineNode::PolyNodeFlag flag = state() == SceneGraphicsItem::Editing ? 0794 PolylineNode::NodeIsEditingHighlighted : 0795 PolylineNode::NodeIsMergingHighlighted; 0796 0797 const int index = nodeContains( mouseEvent->pos() ); 0798 if ( index != -1 ) { 0799 if ( !m_nodesList.at(index).isEditingHighlighted() && 0800 !m_nodesList.at(index).isMergingHighlighted() ) { 0801 // Deal with the case when two nodes are very close to each other. 0802 if ( m_hoveredNodeIndex != -1 ) { 0803 m_nodesList[m_hoveredNodeIndex].setFlag( flag, false ); 0804 } 0805 0806 m_hoveredNodeIndex = index; 0807 m_nodesList[index].setFlag( flag ); 0808 setRequest( ChangeCursorPolylineNodeHover ); 0809 } 0810 0811 return true; 0812 } else if ( m_hoveredNodeIndex != -1 ) { 0813 m_nodesList[m_hoveredNodeIndex].setFlag( flag, false ); 0814 m_hoveredNodeIndex = -1; 0815 0816 return true; 0817 } 0818 0819 // This means that the interior of the polyline has been hovered so we catch this event too. 0820 setRequest( ChangeCursorPolylineLineHover ); 0821 return true; 0822 } 0823 0824 const char *PolylineAnnotation::graphicType() const 0825 { 0826 return SceneGraphicsTypes::SceneGraphicPolylineAnnotation; 0827 } 0828 0829 }