File indexing completed on 2024-05-05 03:50:37
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2009 Andrew Manson <g.real.ate@gmail.com> 0004 // SPDX-FileCopyrightText: 2013 Thibaut Gridel <tgridel@free.fr> 0005 // SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com> 0006 // 0007 0008 // Self 0009 #include "AnnotatePlugin.h" 0010 0011 // Qt 0012 #include <QFileDialog> 0013 #include <QAction> 0014 #include <QMessageBox> 0015 #include <QtAlgorithms> 0016 #include <QColor> 0017 #include <QApplication> 0018 0019 // Marble 0020 #include "MarbleDebug.h" 0021 #include "EditGroundOverlayDialog.h" 0022 #include "EditPlacemarkDialog.h" 0023 #include "EditPolygonDialog.h" 0024 #include "GeoDataDocument.h" 0025 #include "GeoDataLatLonBox.h" 0026 #include "GeoDataParser.h" 0027 #include "GeoDataPlacemark.h" 0028 #include "GeoDataStyle.h" 0029 #include "GeoDataLabelStyle.h" 0030 #include "GeoDataLineStyle.h" 0031 #include "GeoDataPoint.h" 0032 #include "GeoDataPolyStyle.h" 0033 #include "GeoDataTreeModel.h" 0034 #include "GeoPainter.h" 0035 #include "GeoDataLatLonAltBox.h" 0036 #include "GeoDataLinearRing.h" 0037 #include "GeoDataPolygon.h" 0038 #include "GeoWriter.h" 0039 #include "KmlElementDictionary.h" 0040 #include "MarbleDirs.h" 0041 #include "MarbleModel.h" 0042 #include "MarblePlacemarkModel.h" 0043 #include "MarbleWidget.h" 0044 #include "AreaAnnotation.h" 0045 #include "PlacemarkTextAnnotation.h" 0046 #include "TextureLayer.h" 0047 #include "SceneGraphicsTypes.h" 0048 #include "MergingPolygonNodesAnimation.h" 0049 #include "MergingPolylineNodesAnimation.h" 0050 #include "MarbleWidgetPopupMenu.h" 0051 #include "PolylineAnnotation.h" 0052 #include "EditPolylineDialog.h" 0053 #include "ParsingRunnerManager.h" 0054 #include "ViewportParams.h" 0055 #include "osm/OsmPlacemarkData.h" 0056 #include "DownloadOsmDialog.h" 0057 0058 namespace Marble 0059 { 0060 0061 AnnotatePlugin::AnnotatePlugin( const MarbleModel *model ) 0062 : RenderPlugin( model ), 0063 m_isInitialized( false ), 0064 m_widgetInitialized( false ), 0065 m_marbleWidget( nullptr ), 0066 m_overlayRmbMenu(nullptr), 0067 m_polygonRmbMenu(nullptr), 0068 m_nodeRmbMenu(nullptr), 0069 m_textAnnotationRmbMenu(nullptr), 0070 m_polylineRmbMenu(nullptr), 0071 m_annotationDocument(nullptr), 0072 m_movedItem( nullptr ), 0073 m_focusItem( nullptr ), 0074 m_polylinePlacemark( nullptr ), 0075 m_polygonPlacemark( nullptr ), 0076 m_clipboardItem( nullptr ), 0077 m_drawingPolygon( false ), 0078 m_drawingPolyline( false ), 0079 m_addingPlacemark( false ), 0080 m_editingDialogIsShown( false ) 0081 { 0082 setEnabled( true ); 0083 setVisible( true ); 0084 connect( this, SIGNAL(visibilityChanged(bool,QString)), SLOT(enableModel(bool)) ); 0085 } 0086 0087 AnnotatePlugin::~AnnotatePlugin() 0088 { 0089 qDeleteAll( m_graphicsItems ); 0090 if ( m_marbleWidget ) { 0091 m_marbleWidget->model()->treeModel()->removeDocument( m_annotationDocument ); 0092 disconnect( this, SIGNAL(mouseMoveGeoPosition(QString)), m_marbleWidget, SIGNAL(mouseMoveGeoPosition(QString)) ); 0093 } 0094 0095 delete m_overlayRmbMenu; 0096 delete m_polygonRmbMenu; 0097 delete m_nodeRmbMenu; 0098 delete m_textAnnotationRmbMenu; 0099 delete m_polylineRmbMenu; 0100 0101 delete m_annotationDocument; 0102 // delete m_networkAccessManager; 0103 0104 delete m_clipboardItem; 0105 qDeleteAll(m_actions); 0106 } 0107 0108 QStringList AnnotatePlugin::backendTypes() const 0109 { 0110 return QStringList(QStringLiteral("annotation")); 0111 } 0112 0113 QString AnnotatePlugin::renderPolicy() const 0114 { 0115 return QStringLiteral("ALWAYS"); 0116 } 0117 0118 QStringList AnnotatePlugin::renderPosition() const 0119 { 0120 return QStringList(QStringLiteral("ALWAYS_ON_TOP")); 0121 } 0122 0123 QString AnnotatePlugin::name() const 0124 { 0125 return tr( "Annotation" ); 0126 } 0127 0128 QString AnnotatePlugin::guiString() const 0129 { 0130 return tr( "&Annotation" ); 0131 } 0132 0133 QString AnnotatePlugin::nameId() const 0134 { 0135 return QStringLiteral("annotation"); 0136 } 0137 0138 QString AnnotatePlugin::description() const 0139 { 0140 return tr( "Draws annotations on maps with placemarks or polygons." ); 0141 } 0142 0143 QString AnnotatePlugin::version() const 0144 { 0145 return QStringLiteral("1.0"); 0146 } 0147 0148 QString AnnotatePlugin::copyrightYears() const 0149 { 0150 return QStringLiteral("2009, 2013"); 0151 } 0152 0153 QVector<PluginAuthor> AnnotatePlugin::pluginAuthors() const 0154 { 0155 return QVector<PluginAuthor>() 0156 << PluginAuthor(QStringLiteral("Andrew Manson"), QStringLiteral("g.real.ate@gmail.com")) 0157 << PluginAuthor(QStringLiteral("Thibaut Gridel"), QStringLiteral("tgridel@free.fr")) 0158 << PluginAuthor(QStringLiteral("Calin Cruceru"), QStringLiteral("crucerucalincristian@gmail.com")); 0159 } 0160 0161 QIcon AnnotatePlugin::icon() const 0162 { 0163 return QIcon(QStringLiteral(":/icons/draw-placemark.png")); 0164 } 0165 0166 void AnnotatePlugin::initialize() 0167 { 0168 if ( !m_isInitialized ) { 0169 m_widgetInitialized = false; 0170 0171 delete m_polygonPlacemark; 0172 m_polygonPlacemark = nullptr; 0173 0174 delete m_movedItem; 0175 m_movedItem = nullptr; 0176 0177 m_drawingPolygon = false; 0178 m_drawingPolyline = false; 0179 m_addingPlacemark = false; 0180 0181 delete m_annotationDocument; 0182 m_annotationDocument = new GeoDataDocument; 0183 0184 m_annotationDocument->setName( tr("Annotations") ); 0185 m_annotationDocument->setDocumentRole( UserDocument ); 0186 0187 // Default polygon style 0188 GeoDataStyle::Ptr defaultPolygonStyle(new GeoDataStyle); 0189 GeoDataPolyStyle polyStyle; 0190 GeoDataLineStyle edgeStyle; 0191 GeoDataLabelStyle labelStyle; 0192 QColor polygonColor = QApplication::palette().highlight().color(); 0193 QColor edgeColor = QApplication::palette().light().color(); 0194 QColor labelColor = QApplication::palette().brightText().color(); 0195 polygonColor.setAlpha( 80 ); 0196 polyStyle.setColor( polygonColor ); 0197 edgeStyle.setColor( edgeColor ); 0198 labelStyle.setColor( labelColor ); 0199 defaultPolygonStyle->setId(QStringLiteral("polygon")); 0200 defaultPolygonStyle->setPolyStyle( polyStyle ); 0201 defaultPolygonStyle->setLineStyle( edgeStyle ); 0202 defaultPolygonStyle->setLabelStyle( labelStyle ); 0203 m_annotationDocument->addStyle( defaultPolygonStyle ); 0204 0205 0206 // Default polyline style 0207 GeoDataStyle::Ptr defaultPolylineStyle(new GeoDataStyle); 0208 GeoDataLineStyle lineStyle; 0209 QColor polylineColor = Qt::white; 0210 lineStyle.setColor( polylineColor ); 0211 lineStyle.setWidth( 1 ); 0212 defaultPolylineStyle->setId(QStringLiteral("polyline")); 0213 defaultPolylineStyle->setLineStyle( lineStyle ); 0214 defaultPolylineStyle->setLabelStyle( labelStyle ); 0215 m_annotationDocument->addStyle( defaultPolylineStyle ); 0216 0217 m_isInitialized = true; 0218 } 0219 } 0220 0221 bool AnnotatePlugin::isInitialized() const 0222 { 0223 return m_isInitialized; 0224 } 0225 0226 QString AnnotatePlugin::runtimeTrace() const 0227 { 0228 return QStringLiteral("Annotate Items: %1").arg(m_annotationDocument->size()); 0229 } 0230 0231 const QList<QActionGroup*> *AnnotatePlugin::actionGroups() const 0232 { 0233 return &m_actions; 0234 } 0235 0236 bool AnnotatePlugin::render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer) 0237 { 0238 Q_UNUSED( renderPos ); 0239 Q_UNUSED( layer ); 0240 0241 QListIterator<SceneGraphicsItem*> iter( m_graphicsItems ); 0242 while ( iter.hasNext() ) { 0243 iter.next()->paint( painter, viewport, "Annotation", -1 ); 0244 } 0245 0246 return true; 0247 } 0248 0249 void AnnotatePlugin::enableModel( bool enabled ) 0250 { 0251 if ( enabled ) { 0252 if ( m_marbleWidget ) { 0253 setupActions( m_marbleWidget ); 0254 m_marbleWidget->model()->treeModel()->addDocument( m_annotationDocument ); 0255 } 0256 } else { 0257 setupActions( nullptr ); 0258 if ( m_marbleWidget ) { 0259 m_marbleWidget->model()->treeModel()->removeDocument( m_annotationDocument ); 0260 } 0261 } 0262 } 0263 0264 void AnnotatePlugin::setAddingPolygonHole( bool enabled ) 0265 { 0266 if ( enabled ) { 0267 announceStateChanged( SceneGraphicsItem::AddingPolygonHole ); 0268 } else { 0269 announceStateChanged( SceneGraphicsItem::Editing ); 0270 } 0271 } 0272 0273 void AnnotatePlugin::setAddingNodes( bool enabled ) 0274 { 0275 if ( enabled ) { 0276 announceStateChanged( SceneGraphicsItem::AddingNodes ); 0277 } else { 0278 announceStateChanged( SceneGraphicsItem::Editing ); 0279 } 0280 } 0281 0282 void AnnotatePlugin::setAreaAvailable() 0283 { 0284 static_cast<AreaAnnotation*>( m_focusItem )->setBusy( false ); 0285 announceStateChanged( SceneGraphicsItem::Editing ); 0286 0287 enableAllActions( m_actions.first() ); 0288 disableFocusActions(); 0289 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicAreaAnnotation ); 0290 emit repaintNeeded(); 0291 } 0292 0293 void AnnotatePlugin::setPolylineAvailable() 0294 { 0295 static_cast<PolylineAnnotation*>( m_focusItem )->setBusy( false ); 0296 announceStateChanged( SceneGraphicsItem::Editing ); 0297 0298 enableAllActions( m_actions.first() ); 0299 disableFocusActions(); 0300 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicPolylineAnnotation ); 0301 emit repaintNeeded(); 0302 } 0303 0304 void AnnotatePlugin::askToRemoveFocusItem() 0305 { 0306 const int result = QMessageBox::question( m_marbleWidget, 0307 QObject::tr( "Remove current item" ), 0308 QObject::tr( "Are you sure you want to remove the current item?" ), 0309 QMessageBox::Yes | QMessageBox::No ); 0310 if ( result == QMessageBox::Yes ) { 0311 removeFocusItem(); 0312 } 0313 } 0314 0315 void AnnotatePlugin::removeFocusItem() 0316 { 0317 // Ground Overlays will always be a special case.. 0318 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 0319 for ( int i = 0; i < m_groundOverlayModel.rowCount(); ++i ) { 0320 const QModelIndex index = m_groundOverlayModel.index( i, 0 ); 0321 GeoDataGroundOverlay *overlay = dynamic_cast<GeoDataGroundOverlay*>( 0322 qvariant_cast<GeoDataObject*>( index.data( MarblePlacemarkModel::ObjectPointerRole ) ) ); 0323 0324 m_marbleWidget->model()->treeModel()->removeFeature( overlay ); 0325 } 0326 0327 clearOverlayFrames(); 0328 } else { 0329 disableFocusActions(); 0330 0331 m_graphicsItems.removeAll( m_focusItem ); 0332 m_marbleWidget->model()->treeModel()->removeFeature( m_focusItem->placemark() ); 0333 0334 delete m_focusItem->placemark(); 0335 delete m_focusItem; 0336 m_movedItem = nullptr; 0337 m_focusItem = nullptr; 0338 } 0339 } 0340 0341 void AnnotatePlugin::clearAnnotations() 0342 { 0343 0344 const int result = QMessageBox::question( m_marbleWidget, 0345 QObject::tr( "Clear all annotations" ), 0346 QObject::tr( "Are you sure you want to clear all annotations?" ), 0347 QMessageBox::Yes | QMessageBox::Cancel ); 0348 0349 if ( result == QMessageBox::Yes ) { 0350 disableFocusActions(); 0351 qDeleteAll( m_graphicsItems ); 0352 m_graphicsItems.clear(); 0353 m_marbleWidget->model()->treeModel()->removeDocument( m_annotationDocument ); 0354 m_annotationDocument->clear(); 0355 m_marbleWidget->model()->treeModel()->addDocument( m_annotationDocument ); 0356 0357 m_movedItem = nullptr; 0358 m_focusItem = nullptr; 0359 } 0360 } 0361 0362 void AnnotatePlugin::saveAnnotationFile() 0363 { 0364 0365 const QString filename = QFileDialog::getSaveFileName( nullptr, 0366 tr("Save Annotation File"), 0367 QString(), 0368 tr("All Supported Files (*.kml *.osm);;" 0369 "KML file (*.kml);;" 0370 "Open Street Map file (*.osm)") ); 0371 if ( !filename.isNull() ) { 0372 GeoWriter writer; 0373 // FIXME: This should be consistent with the way the loading is done. 0374 if (filename.endsWith(QLatin1String(".kml"), Qt::CaseInsensitive)) { 0375 writer.setDocumentType( kml::kmlTag_nameSpaceOgc22 ); 0376 } 0377 else if (filename.endsWith(QLatin1String(".osm"), Qt::CaseInsensitive)) { 0378 // "0.6" is the current version of osm, it is used to identify the osm writer 0379 // The reference value is kept in plugins/runner/osm/OsmElementDictionary.hz 0380 writer.setDocumentType( "0.6" ); 0381 } 0382 0383 QFile file( filename ); 0384 file.open( QIODevice::WriteOnly ); 0385 if ( !writer.write( &file, m_annotationDocument ) ) { 0386 mDebug() << "Could not write the file " << filename; 0387 } 0388 file.close(); 0389 } 0390 } 0391 0392 void AnnotatePlugin::loadAnnotationFile() 0393 { 0394 const QString filename = QFileDialog::getOpenFileName( nullptr, 0395 tr("Open Annotation File"), 0396 QString(), 0397 tr("All Supported Files (*.kml *.osm);;" 0398 "Kml Annotation file (*.kml);;" 0399 "Open Street Map file (*.osm)") ); 0400 if ( filename.isNull() ) { 0401 return; 0402 } 0403 else{ 0404 openAnnotationFile(filename); 0405 } 0406 } 0407 0408 void AnnotatePlugin::downloadOsm() 0409 { 0410 QPointer<DownloadOsmDialog> dialog=new DownloadOsmDialog(m_marbleWidget,this); 0411 dialog->show(); 0412 } 0413 0414 void AnnotatePlugin::openAnnotationFile(const QString& filename) 0415 { 0416 0417 if ( filename.isNull() ) { 0418 return; 0419 } 0420 ParsingRunnerManager manager( m_marbleWidget->model()->pluginManager(),this ); 0421 GeoDataDocument *document = manager.openFile( filename); 0422 Q_ASSERT( document ); 0423 0424 // FIXME: The same problem as in the case of copying/cutting graphic items applies here: 0425 // the files do not load properly because the geometry copy is not a deep copy. 0426 foreach ( GeoDataFeature *feature, document->featureList() ) { 0427 0428 if (const GeoDataPlacemark *placemark = geodata_cast<GeoDataPlacemark>(feature)) { 0429 GeoDataPlacemark *newPlacemark = new GeoDataPlacemark( *placemark ); 0430 0431 if (geodata_cast<GeoDataPoint>(placemark->geometry())) { 0432 PlacemarkTextAnnotation *placemark = new PlacemarkTextAnnotation( newPlacemark ); 0433 m_graphicsItems.append( placemark ); 0434 } else if (geodata_cast<GeoDataPolygon>(placemark->geometry())) { 0435 newPlacemark->setParent( m_annotationDocument ); 0436 if ( !placemark->styleUrl().isEmpty() ) { 0437 newPlacemark->setStyleUrl( placemark->styleUrl() ); 0438 } 0439 AreaAnnotation *polygonAnnotation = new AreaAnnotation( newPlacemark ); 0440 m_graphicsItems.append( polygonAnnotation ); 0441 } else if (geodata_cast<GeoDataLineString>(placemark->geometry())) { 0442 newPlacemark->setParent( m_annotationDocument ); 0443 if ( !placemark->styleUrl().isEmpty() ) { 0444 newPlacemark->setStyleUrl( placemark->styleUrl() ); 0445 } 0446 PolylineAnnotation *polylineAnnotation = new PolylineAnnotation( newPlacemark ); 0447 m_graphicsItems.append( polylineAnnotation ); 0448 } 0449 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, newPlacemark ); 0450 } else if (const GeoDataGroundOverlay *overlay = geodata_cast<GeoDataGroundOverlay>(feature)) { 0451 GeoDataGroundOverlay *newOverlay = new GeoDataGroundOverlay( *overlay ); 0452 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, newOverlay ); 0453 displayOverlayFrame( newOverlay ); 0454 } 0455 } 0456 m_marbleWidget->centerOn( document->latLonAltBox() ); 0457 0458 delete document; 0459 emit repaintNeeded( QRegion() ); 0460 } 0461 0462 bool AnnotatePlugin::eventFilter( QObject *watched, QEvent *event ) 0463 { 0464 if ( !m_widgetInitialized ) { 0465 MarbleWidget *marbleWidget = qobject_cast<MarbleWidget*>( watched ); 0466 0467 if ( marbleWidget ) { 0468 m_marbleWidget = marbleWidget; 0469 0470 addContextItems(); 0471 setupGroundOverlayModel(); 0472 setupOverlayRmbMenu(); 0473 setupPolygonRmbMenu(); 0474 setupPolylineRmbMenu(); 0475 setupNodeRmbMenu(); 0476 setupTextAnnotationRmbMenu(); 0477 setupActions( marbleWidget ); 0478 0479 m_marbleWidget->model()->treeModel()->addDocument( m_annotationDocument ); 0480 m_widgetInitialized = true; 0481 0482 connect( this, SIGNAL(mouseMoveGeoPosition(QString)), m_marbleWidget, SIGNAL(mouseMoveGeoPosition(QString)) ); 0483 0484 return true; 0485 } 0486 return false; 0487 } 0488 0489 // Accept mouse and key press events. 0490 if ( event->type() != QEvent::MouseButtonPress && 0491 event->type() != QEvent::MouseButtonRelease && 0492 event->type() != QEvent::MouseMove && 0493 event->type() != QEvent::KeyPress && 0494 event->type() != QEvent::KeyRelease ) { 0495 return false; 0496 } 0497 0498 // Handle key press events. 0499 if ( event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease ) { 0500 QKeyEvent * const keyEvent = static_cast<QKeyEvent*>( event ); 0501 Q_ASSERT( keyEvent ); 0502 0503 if ( m_focusItem && 0504 ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation || 0505 m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) ) { 0506 if ( keyEvent->type() == QEvent::KeyPress && keyEvent->key() == Qt::Key_Control ) { 0507 announceStateChanged( SceneGraphicsItem::MergingNodes ); 0508 } 0509 0510 if ( keyEvent->type() == QEvent::KeyRelease && keyEvent->key() == Qt::Key_Control ) { 0511 if ( ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation && 0512 static_cast<AreaAnnotation*>( m_focusItem )->isBusy() ) || 0513 ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation && 0514 static_cast<PolylineAnnotation*>( m_focusItem )->isBusy() ) ) { 0515 return true; 0516 } 0517 0518 announceStateChanged( SceneGraphicsItem::Editing ); 0519 } 0520 } 0521 0522 // If we have an item which has the focus and the Escape key is pressed, set item's focus 0523 // to false. 0524 if ( m_focusItem && 0525 keyEvent->type() == QEvent::KeyPress && keyEvent->key() == Qt::Key_Escape && 0526 !m_editingDialogIsShown ) { 0527 disableFocusActions(); 0528 m_focusItem->setFocus( false ); 0529 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 0530 m_focusItem = nullptr; 0531 return true; 0532 } 0533 0534 // If we have an item which has the focus and the Delete key is pressed, delete the item 0535 if ( m_focusItem && keyEvent->type() == QEvent::KeyPress && keyEvent->key() == Qt::Key_Delete && 0536 !m_editingDialogIsShown ) { 0537 askToRemoveFocusItem(); 0538 return true; 0539 } 0540 0541 return false; 0542 } 0543 0544 // Handle mouse events. 0545 QMouseEvent * const mouseEvent = dynamic_cast<QMouseEvent*>( event ); 0546 Q_ASSERT( mouseEvent ); 0547 0548 0549 // Get the geocoordinates from mouse pos screen coordinates. 0550 qreal lon, lat; 0551 const bool isOnGlobe = m_marbleWidget->geoCoordinates( mouseEvent->pos().x(), 0552 mouseEvent->pos().y(), 0553 lon, lat, 0554 GeoDataCoordinates::Radian ); 0555 if ( !isOnGlobe ) { 0556 return false; 0557 } 0558 0559 // Deal with adding polygons and polylines. 0560 if ( ( m_drawingPolygon && handleDrawingPolygon( mouseEvent ) ) || 0561 ( m_drawingPolyline && handleDrawingPolyline( mouseEvent ) ) ) { 0562 return true; 0563 } 0564 0565 // It is important to deal with Ground Overlay mouse release event here because it uses the 0566 // texture layer in order to make the rendering more efficient. 0567 if ( mouseEvent->type() == QEvent::MouseButtonRelease && m_groundOverlayModel.rowCount() ) { 0568 handleReleaseOverlay( mouseEvent ); 0569 } 0570 0571 // It is important to deal with the MouseMove event here because it changes the state of the 0572 // selected item irrespective of the longitude/latitude the cursor moved to (excepting when 0573 // it is outside the globe, which is treated above). 0574 if ( mouseEvent->type() == QEvent::MouseMove && m_movedItem && 0575 handleMovingSelectedItem( mouseEvent ) ) { 0576 setupCursor( m_movedItem ); 0577 return true; 0578 } 0579 0580 // Pass the event to Graphic Items. 0581 for ( SceneGraphicsItem *item: m_graphicsItems ) { 0582 if ( !item->containsPoint( mouseEvent->pos() ) ) { 0583 continue; 0584 } 0585 0586 // If an edit dialog is visible, do not permit right clicking on items. 0587 if ( m_editingDialogIsShown && mouseEvent->type() == QEvent::MouseButtonPress && 0588 mouseEvent->button() == Qt::RightButton) { 0589 return true; 0590 } 0591 0592 if ( !item->hasFocus() && 0593 item->graphicType() != SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 0594 if ( mouseEvent->type() == QEvent::MouseButtonPress && 0595 mouseEvent->button() == Qt::LeftButton ) { 0596 item->setFocus( true ); 0597 disableFocusActions(); 0598 enableActionsOnItemType( item->graphicType() ); 0599 0600 if ( m_focusItem && m_focusItem != item ) { 0601 m_focusItem->setFocus( false ); 0602 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 0603 clearOverlayFrames(); 0604 } 0605 } 0606 m_focusItem = item; 0607 m_marbleWidget->model()->treeModel()->updateFeature( item->placemark() ); 0608 return true; 0609 } 0610 0611 return false; 0612 } 0613 0614 if ( item->sceneEvent( event ) ) { 0615 setupCursor( item ); 0616 0617 if ( mouseEvent->type() == QEvent::MouseButtonPress ) { 0618 handleSuccessfulPressEvent( mouseEvent, item ); 0619 } else if ( mouseEvent->type() == QEvent::MouseMove ) { 0620 handleSuccessfulHoverEvent( mouseEvent, item ); 0621 } else if ( mouseEvent->type() == QEvent::MouseButtonRelease ) { 0622 handleSuccessfulReleaseEvent( mouseEvent, item ); 0623 } 0624 0625 handleRequests( mouseEvent, item ); 0626 return true; 0627 } 0628 } 0629 0630 // If the event gets here, it most probably means it is a map interaction event, or something 0631 // that has nothing to do with the annotate plugin items. We "deal" with this situation because, 0632 // for example, we may need to deselect some selected items. 0633 handleUncaughtEvents( mouseEvent ); 0634 0635 return false; 0636 } 0637 0638 bool AnnotatePlugin::handleDrawingPolygon( QMouseEvent *mouseEvent ) 0639 { 0640 const GeoDataCoordinates coords = mouseGeoDataCoordinates( mouseEvent ); 0641 0642 if ( mouseEvent->type() == QEvent::MouseMove ) { 0643 setupCursor( nullptr ); 0644 0645 emit mouseMoveGeoPosition( coords.toString() ); 0646 0647 return true; 0648 } else if ( mouseEvent->button() == Qt::LeftButton && 0649 mouseEvent->type() == QEvent::MouseButtonPress ) { 0650 0651 m_marbleWidget->model()->treeModel()->removeFeature( m_polygonPlacemark ); 0652 GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>( m_polygonPlacemark->geometry() ); 0653 poly->outerBoundary().append( coords ); 0654 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, m_polygonPlacemark ); 0655 emit nodeAdded( coords ); 0656 0657 return true; 0658 } 0659 0660 return false; 0661 } 0662 0663 bool AnnotatePlugin::handleDrawingPolyline( QMouseEvent *mouseEvent ) 0664 { 0665 const GeoDataCoordinates coords = mouseGeoDataCoordinates( mouseEvent ); 0666 0667 if ( mouseEvent->type() == QEvent::MouseMove ) { 0668 setupCursor( nullptr ); 0669 0670 emit mouseMoveGeoPosition( coords.toString() ); 0671 0672 return true; 0673 } else if ( mouseEvent->button() == Qt::LeftButton && 0674 mouseEvent->type() == QEvent::MouseButtonPress ) { 0675 0676 m_marbleWidget->model()->treeModel()->removeFeature( m_polylinePlacemark ); 0677 GeoDataLineString *line = dynamic_cast<GeoDataLineString*>( m_polylinePlacemark->geometry() ); 0678 line->append( coords ); 0679 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, m_polylinePlacemark ); 0680 emit nodeAdded( coords ); 0681 0682 return true; 0683 } 0684 0685 return false; 0686 } 0687 0688 void AnnotatePlugin::handleReleaseOverlay( QMouseEvent *mouseEvent ) 0689 { 0690 const GeoDataCoordinates coords = mouseGeoDataCoordinates( mouseEvent ); 0691 0692 for ( int i = 0; i < m_groundOverlayModel.rowCount(); ++i ) { 0693 const QModelIndex index = m_groundOverlayModel.index( i, 0 ); 0694 GeoDataGroundOverlay *overlay = dynamic_cast<GeoDataGroundOverlay*>( 0695 qvariant_cast<GeoDataObject*>( index.data( MarblePlacemarkModel::ObjectPointerRole ) ) ); 0696 0697 if ( overlay->latLonBox().contains( coords ) ) { 0698 if ( mouseEvent->button() == Qt::LeftButton ) { 0699 displayOverlayFrame( overlay ); 0700 } else if ( mouseEvent->button() == Qt::RightButton ) { 0701 showOverlayRmbMenu( overlay, mouseEvent->x(), mouseEvent->y() ); 0702 } 0703 } 0704 } 0705 } 0706 0707 bool AnnotatePlugin::handleMovingSelectedItem( QMouseEvent *mouseEvent ) 0708 { 0709 // Handling easily the mouse move by calling for each scene graphic item their own mouseMoveEvent 0710 // handler and updating their feature. 0711 if ( m_movedItem->sceneEvent( mouseEvent ) ) { 0712 m_marbleWidget->model()->treeModel()->updateFeature( m_movedItem->placemark() ); 0713 emit itemMoved( m_movedItem->placemark() ); 0714 if ( m_movedItem->graphicType() == SceneGraphicsTypes::SceneGraphicTextAnnotation ) { 0715 emit placemarkMoved(); 0716 } 0717 0718 const GeoDataCoordinates coords = mouseGeoDataCoordinates( mouseEvent ); 0719 emit mouseMoveGeoPosition( coords.toString() ); 0720 0721 return true; 0722 } 0723 0724 return false; 0725 } 0726 0727 void AnnotatePlugin::handleSuccessfulPressEvent( QMouseEvent *mouseEvent, SceneGraphicsItem *item ) 0728 { 0729 Q_UNUSED( mouseEvent ); 0730 0731 // Update the item's placemark. 0732 m_marbleWidget->model()->treeModel()->updateFeature( item->placemark() ); 0733 0734 // Store a pointer to the item for possible following move events only if its state is 0735 // either 'Editing' or 'AddingNodes' and the mouse left button has been used. 0736 if ( ( item->state() == SceneGraphicsItem::Editing || 0737 item->state() == SceneGraphicsItem::AddingNodes ) && 0738 mouseEvent->button() == Qt::LeftButton ) { 0739 m_movedItem = item; 0740 } 0741 } 0742 0743 void AnnotatePlugin::handleSuccessfulHoverEvent( QMouseEvent *mouseEvent, SceneGraphicsItem *item ) 0744 { 0745 Q_UNUSED( mouseEvent ); 0746 m_marbleWidget->model()->treeModel()->updateFeature( item->placemark() ); 0747 } 0748 0749 void AnnotatePlugin::handleSuccessfulReleaseEvent( QMouseEvent *mouseEvent, SceneGraphicsItem *item ) 0750 { 0751 Q_UNUSED( mouseEvent ); 0752 // The item gets 'deselected' (from moving) at mouse release. 0753 m_movedItem = nullptr; 0754 0755 // Update the item's placemark. 0756 m_marbleWidget->model()->treeModel()->updateFeature( item->placemark() ); 0757 } 0758 0759 void AnnotatePlugin::handleRequests( QMouseEvent *mouseEvent, SceneGraphicsItem *item ) 0760 { 0761 if ( item->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 0762 AreaAnnotation * const area = static_cast<AreaAnnotation*>( item ); 0763 0764 if ( area->request() == SceneGraphicsItem::ShowPolygonRmbMenu ) { 0765 showPolygonRmbMenu( mouseEvent->pos().x(), mouseEvent->pos().y() ); 0766 } else if ( area->request() == SceneGraphicsItem::ShowNodeRmbMenu ) { 0767 showNodeRmbMenu( mouseEvent->pos().x(), mouseEvent->pos().y() ); 0768 } else if ( area->request() == SceneGraphicsItem::StartPolygonAnimation ) { 0769 QPointer<MergingPolygonNodesAnimation> animation = area->animation(); 0770 0771 connect( animation, SIGNAL(nodesMoved()), this, SIGNAL(repaintNeeded()) ); 0772 connect( animation, SIGNAL(animationFinished()), 0773 this, SLOT(setAreaAvailable()) ); 0774 0775 area->setBusy( true ); 0776 disableActions( m_actions.first() ); 0777 animation->startAnimation(); 0778 } else if ( area->request() == SceneGraphicsItem::OuterInnerMergingWarning ) { 0779 QMessageBox::warning( m_marbleWidget, 0780 tr( "Operation not permitted" ), 0781 tr( "Cannot merge a node from polygon's outer boundary " 0782 "with a node from one of its inner boundaries." ) ); 0783 } else if ( area->request() == SceneGraphicsItem::InnerInnerMergingWarning ) { 0784 QMessageBox::warning( m_marbleWidget, 0785 tr( "Operation not permitted" ), 0786 tr( "Cannot merge two nodes from two different inner " 0787 "boundaries." ) ); 0788 } else if ( area->request() == SceneGraphicsItem::InvalidShapeWarning ) { 0789 QMessageBox::warning( m_marbleWidget, 0790 tr( "Operation not permitted" ), 0791 tr( "Cannot merge the selected nodes. Most probably " 0792 "this would make the polygon's outer boundary not " 0793 "contain all its inner boundary nodes." ) ); 0794 } else if ( area->request() == SceneGraphicsItem::RemovePolygonRequest ) { 0795 removeFocusItem(); 0796 } else if ( area->request() == SceneGraphicsItem::ChangeCursorPolygonNodeHover ) { 0797 m_marbleWidget->setCursor( Qt::PointingHandCursor ); 0798 } else if ( area->request() == SceneGraphicsItem::ChangeCursorPolygonBodyHover ) { 0799 m_marbleWidget->setCursor( Qt::SizeAllCursor ); 0800 } 0801 } else if ( item->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 0802 PolylineAnnotation * const polyline = static_cast<PolylineAnnotation*>( item ); 0803 0804 if ( polyline->request() == SceneGraphicsItem::ShowPolylineRmbMenu ) { 0805 showPolylineRmbMenu( mouseEvent->x(), mouseEvent->y() ); 0806 } else if ( polyline->request() == SceneGraphicsItem::ShowNodeRmbMenu ) { 0807 showNodeRmbMenu( mouseEvent->x(), mouseEvent->y() ); 0808 } else if ( polyline->request() == SceneGraphicsItem::StartPolylineAnimation ) { 0809 QPointer<MergingPolylineNodesAnimation> animation = polyline->animation(); 0810 0811 connect( animation, SIGNAL(nodesMoved()), this, SIGNAL(repaintNeeded()) ); 0812 connect( animation, SIGNAL(animationFinished()), 0813 this, SLOT(setPolylineAvailable()) ); 0814 0815 polyline->setBusy( true ); 0816 disableActions( m_actions.first() ); 0817 animation->startAnimation(); 0818 } else if ( polyline->request() == SceneGraphicsItem::RemovePolylineRequest ) { 0819 removeFocusItem(); 0820 } else if ( polyline->request() == SceneGraphicsItem::ChangeCursorPolylineNodeHover ) { 0821 m_marbleWidget->setCursor( Qt::PointingHandCursor ); 0822 } else if ( polyline->request() == SceneGraphicsItem::ChangeCursorPolylineLineHover ) { 0823 m_marbleWidget->setCursor( Qt::SizeAllCursor ); 0824 } 0825 } else if ( item->graphicType() == SceneGraphicsTypes::SceneGraphicTextAnnotation ) { 0826 PlacemarkTextAnnotation * const textAnnotation = static_cast<PlacemarkTextAnnotation*>( item ); 0827 0828 if ( textAnnotation->request() == SceneGraphicsItem::ShowPlacemarkRmbMenu ) { 0829 showTextAnnotationRmbMenu( mouseEvent->x(), mouseEvent->y() ); 0830 } else if ( textAnnotation->request() == SceneGraphicsItem::ChangeCursorPlacemarkHover ) { 0831 m_marbleWidget->setCursor( Qt::SizeAllCursor ); 0832 } 0833 } else if ( item->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ){ 0834 GroundOverlayFrame * const groundOverlay = static_cast<GroundOverlayFrame*>( item ); 0835 0836 if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayVerticalHover ) { 0837 m_marbleWidget->setCursor( Qt::SizeVerCursor ); 0838 } else if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayHorizontalHover ) { 0839 m_marbleWidget->setCursor( Qt::SizeHorCursor ); 0840 } else if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayBDiagHover ) { 0841 m_marbleWidget->setCursor( Qt::SizeBDiagCursor ); 0842 } else if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayFDiagHover ) { 0843 m_marbleWidget->setCursor( Qt::SizeFDiagCursor ); 0844 } else if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayBodyHover ) { 0845 m_marbleWidget->setCursor( Qt::SizeAllCursor ); 0846 } else if ( groundOverlay->request() == SceneGraphicsItem::ChangeCursorOverlayRotateHover ) { 0847 m_marbleWidget->setCursor( Qt::CrossCursor ); 0848 } 0849 } 0850 } 0851 0852 void AnnotatePlugin::handleUncaughtEvents( QMouseEvent *mouseEvent ) 0853 { 0854 Q_UNUSED( mouseEvent ); 0855 0856 // If the event is not caught by any of the annotate plugin specific items, clear the frames 0857 // (which have the meaning of deselecting the overlay). 0858 if ( !m_groundOverlayFrames.isEmpty() && 0859 mouseEvent->type() != QEvent::MouseMove && mouseEvent->type() != QEvent::MouseButtonRelease ) { 0860 clearOverlayFrames(); 0861 } 0862 0863 if ( m_focusItem && m_focusItem->graphicType() != SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 0864 if ( ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation && 0865 static_cast<AreaAnnotation*>( m_focusItem )->isBusy() ) || 0866 ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation && 0867 static_cast<PolylineAnnotation*>( m_focusItem )->isBusy() ) ) { 0868 return; 0869 } 0870 0871 m_focusItem->dealWithItemChange( nullptr ); 0872 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 0873 0874 if ( mouseEvent->type() == QEvent::MouseButtonPress ) { 0875 m_focusItem->setFocus( false ); 0876 disableFocusActions(); 0877 announceStateChanged( SceneGraphicsItem::Editing ); 0878 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 0879 m_focusItem = nullptr; 0880 } 0881 } 0882 } 0883 0884 void AnnotatePlugin::setupActions( MarbleWidget *widget ) 0885 { 0886 qDeleteAll( m_actions ); 0887 m_actions.clear(); 0888 0889 if ( !widget ) { 0890 return; 0891 } 0892 0893 QActionGroup *group = new QActionGroup( nullptr ); 0894 group->setExclusive( true ); 0895 0896 0897 QAction *selectItem = new QAction( QIcon(QStringLiteral(":/icons/edit-select.png")), 0898 tr("Select Item"), 0899 this ); 0900 selectItem->setCheckable( true ); 0901 selectItem->setChecked( true ); 0902 0903 QAction *drawPolygon = new QAction( QIcon(QStringLiteral(":/icons/draw-polygon.png")), 0904 tr("Add Polygon"), 0905 this ); 0906 connect( drawPolygon, SIGNAL(triggered()), this, SLOT(addPolygon()) ); 0907 0908 QAction *addHole = new QAction( QIcon(QStringLiteral(":/icons/polygon-draw-hole.png")), 0909 tr("Add Polygon Hole"), 0910 this ); 0911 addHole->setCheckable( true ); 0912 addHole->setEnabled( false ); 0913 connect( addHole, SIGNAL(toggled(bool)), this, SLOT(setAddingPolygonHole(bool)) ); 0914 0915 QAction *addNodes = new QAction( QIcon(QStringLiteral(":/icons/polygon-add-nodes.png")), 0916 tr("Add Nodes"), 0917 this ); 0918 addNodes->setCheckable( true ); 0919 addNodes->setEnabled( false ); 0920 connect( addNodes, SIGNAL(toggled(bool)), this, SLOT(setAddingNodes(bool)) ); 0921 0922 QAction *addTextAnnotation = new QAction( QIcon(QStringLiteral(":/icons/add-placemark.png")), 0923 tr("Add Placemark"), 0924 this ); 0925 connect( addTextAnnotation, SIGNAL(triggered()), this, SLOT(addTextAnnotation()) ); 0926 0927 QAction *addPath = new QAction( QIcon(QStringLiteral(":/icons/draw-path.png")), 0928 tr("Add Path"), 0929 this ); 0930 connect( addPath, SIGNAL(triggered()), this, SLOT(addPolyline()) ); 0931 0932 QAction *addOverlay = new QAction( QIcon(QStringLiteral(":/icons/draw-overlay.png")), 0933 tr("Add Ground Overlay"), 0934 this ); 0935 connect( addOverlay, SIGNAL(triggered()), this, SLOT(addOverlay()) ); 0936 0937 QAction *removeItem = new QAction( QIcon(QStringLiteral(":/icons/edit-delete-shred.png")), 0938 tr("Remove Item"), 0939 this ); 0940 removeItem->setEnabled( false ); 0941 connect( removeItem, SIGNAL(triggered()), this, SLOT(askToRemoveFocusItem()) ); 0942 0943 QAction *loadAnnotationFile = new QAction( QIcon(QStringLiteral(":/icons/open-for-editing.png")), 0944 tr("Load Annotation File" ), 0945 this ); 0946 connect( loadAnnotationFile, SIGNAL(triggered()), this, SLOT(loadAnnotationFile()) ); 0947 0948 QAction *saveAnnotationFile = new QAction( QIcon(QStringLiteral(":/icons/document-save-as.png")), 0949 tr("Save Annotation File"), 0950 this ); 0951 connect( saveAnnotationFile, SIGNAL(triggered()), this, SLOT(saveAnnotationFile()) ); 0952 0953 QAction *clearAnnotations = new QAction( QIcon(QStringLiteral(":/icons/remove.png")), 0954 tr("Clear all Annotations"), 0955 this ); 0956 QAction *downloadOsm = new QAction( QIcon(":/icons/download.png"), 0957 tr("Download OpenStreetMap Data"), 0958 this ); 0959 connect( downloadOsm, SIGNAL(triggered(bool)), this, SLOT(downloadOsm()) ); 0960 downloadOsm->setToolTip(tr("Download OpenStreetMap data of the visible region")); 0961 connect( drawPolygon, SIGNAL(toggled(bool)), clearAnnotations, SLOT(setDisabled(bool)) ); 0962 connect( clearAnnotations, SIGNAL(triggered()), this, SLOT(clearAnnotations()) ); 0963 0964 0965 QAction *sep1 = new QAction( this ); 0966 sep1->setSeparator( true ); 0967 QAction *sep2 = new QAction( this ); 0968 sep2->setSeparator( true ); 0969 sep2->setObjectName( "toolbarSeparator" ); 0970 QAction *sep3 = new QAction( this ); 0971 sep3->setSeparator( true ); 0972 QAction *sep4 = new QAction( this ); 0973 sep4->setSeparator( true ); 0974 0975 0976 group->addAction( loadAnnotationFile ); 0977 group->addAction( saveAnnotationFile ); 0978 group->addAction( sep1 ); 0979 group->addAction( addTextAnnotation ); 0980 group->addAction( drawPolygon ); 0981 group->addAction( addPath ); 0982 group->addAction( addOverlay ); 0983 group->addAction( sep2 ); 0984 group->addAction( selectItem ); 0985 group->addAction( addHole ); 0986 group->addAction( addNodes ); 0987 group->addAction( removeItem ); 0988 group->addAction( sep3 ); 0989 group->addAction( clearAnnotations ); 0990 group->addAction(downloadOsm); 0991 group->addAction( sep4 ); 0992 0993 m_actions.append( group ); 0994 0995 emit actionGroupsChanged(); 0996 } 0997 0998 void AnnotatePlugin::disableActions( QActionGroup *group ) 0999 { 1000 for ( int i = 0; i < group->actions().size(); ++i ) { 1001 if ( group->actions().at(i)->text() != tr("Select Item") ) { 1002 group->actions().at(i)->setEnabled( false ); 1003 } else { 1004 group->actions().at(i)->setEnabled( true ); 1005 } 1006 } 1007 } 1008 1009 void AnnotatePlugin::enableAllActions( QActionGroup *group ) 1010 { 1011 for ( int i = 0; i < group->actions().size(); ++i ) { 1012 group->actions().at(i)->setEnabled( true ); 1013 } 1014 } 1015 1016 void AnnotatePlugin::enableActionsOnItemType( const QString &type ) 1017 { 1018 if ( type == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1019 m_actions.first()->actions().at(9)->setEnabled( true ); 1020 m_actions.first()->actions().at(10)->setEnabled( true ); 1021 } else if ( type == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1022 m_actions.first()->actions().at(10)->setEnabled( true ); 1023 } 1024 1025 m_actions.first()->actions().at(11)->setEnabled( true ); 1026 } 1027 1028 void AnnotatePlugin::disableFocusActions() 1029 { 1030 m_actions.first()->actions().at(8)->setChecked( true ); 1031 1032 m_actions.first()->actions().at(9)->setEnabled( false ); 1033 m_actions.first()->actions().at(10)->setEnabled( false ); 1034 m_actions.first()->actions().at(11)->setEnabled( false ); 1035 } 1036 1037 void AnnotatePlugin::addContextItems() 1038 { 1039 MarbleWidgetPopupMenu * const menu = m_marbleWidget->popupMenu(); 1040 1041 m_pasteGraphicItem = new QAction( tr( "Paste" ), this ); 1042 m_pasteGraphicItem->setVisible( false ); 1043 connect( m_pasteGraphicItem, SIGNAL(triggered()), SLOT(pasteItem()) ); 1044 1045 QAction *separator = new QAction( this ); 1046 separator->setSeparator( true ); 1047 1048 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 1049 if ( !smallScreen ) { 1050 menu->addAction( Qt::RightButton, m_pasteGraphicItem ); 1051 menu->addAction( Qt::RightButton, separator ); 1052 } 1053 } 1054 1055 void AnnotatePlugin::setupTextAnnotationRmbMenu() 1056 { 1057 delete m_textAnnotationRmbMenu; 1058 m_textAnnotationRmbMenu = new QMenu; 1059 1060 QAction *cutItem = new QAction( tr( "Cut"), m_textAnnotationRmbMenu ); 1061 m_textAnnotationRmbMenu->addAction( cutItem ); 1062 connect( cutItem, SIGNAL(triggered()), this, SLOT(cutItem()) ); 1063 1064 QAction *copyItem = new QAction( tr( "Copy"), m_textAnnotationRmbMenu ); 1065 m_textAnnotationRmbMenu->addAction( copyItem ); 1066 connect( copyItem, SIGNAL(triggered()), this, SLOT(copyItem()) ); 1067 1068 QAction *removeItem = new QAction( tr( "Remove" ), m_textAnnotationRmbMenu ); 1069 m_textAnnotationRmbMenu->addAction( removeItem ); 1070 connect( removeItem, SIGNAL(triggered()), this, SLOT(askToRemoveFocusItem()) ); 1071 1072 m_textAnnotationRmbMenu->addSeparator(); 1073 1074 QAction *properties = new QAction( tr( "Properties" ), m_textAnnotationRmbMenu ); 1075 m_textAnnotationRmbMenu->addAction( properties ); 1076 connect( properties, SIGNAL(triggered()), this, SLOT(editTextAnnotation()) ); 1077 } 1078 1079 void AnnotatePlugin::showTextAnnotationRmbMenu( qreal x, qreal y ) 1080 { 1081 m_textAnnotationRmbMenu->popup( m_marbleWidget->mapToGlobal( QPoint( x, y ) ) ); 1082 } 1083 1084 void AnnotatePlugin::editTextAnnotation() 1085 { 1086 QPointer<EditPlacemarkDialog> dialog = new EditPlacemarkDialog( m_focusItem->placemark(), 1087 &m_osmRelations, 1088 m_marbleWidget ); 1089 connect( dialog, SIGNAL(textAnnotationUpdated(GeoDataFeature*)), 1090 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1091 connect( this, SIGNAL(placemarkMoved()), 1092 dialog, SLOT(updateDialogFields()) ); 1093 connect( dialog, SIGNAL(finished(int)), 1094 this, SLOT(stopEditingTextAnnotation(int)) ); 1095 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1096 this, SLOT(addRelation(OsmPlacemarkData)) ); 1097 1098 dialog->setLabelColor(dynamic_cast<PlacemarkTextAnnotation*>(m_focusItem)->labelColor()); 1099 1100 disableActions( m_actions.first() ); 1101 dialog->show(); 1102 m_editingDialogIsShown = true; 1103 m_editedItem = m_focusItem; 1104 } 1105 1106 void AnnotatePlugin::addTextAnnotation() 1107 { 1108 m_addingPlacemark = true; 1109 1110 // Get the normalized coordinates of the focus point. There will be automatically added a new 1111 // placemark. 1112 qreal lat = m_marbleWidget->focusPoint().latitude(); 1113 qreal lon = m_marbleWidget->focusPoint().longitude(); 1114 GeoDataCoordinates::normalizeLonLat( lon, lat ); 1115 1116 GeoDataPlacemark *placemark = new GeoDataPlacemark; 1117 placemark->setCoordinate( lon, lat ); 1118 placemark->setVisible( true ); 1119 placemark->setBalloonVisible( false ); 1120 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, placemark ); 1121 1122 PlacemarkTextAnnotation *textAnnotation = new PlacemarkTextAnnotation( placemark ); 1123 textAnnotation->setFocus( true ); 1124 m_graphicsItems.append( textAnnotation ); 1125 1126 QPointer<EditPlacemarkDialog> dialog = new EditPlacemarkDialog( placemark, &m_osmRelations, m_marbleWidget ); 1127 1128 connect( dialog, SIGNAL(textAnnotationUpdated(GeoDataFeature*)), 1129 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1130 connect( this, SIGNAL(placemarkMoved()), 1131 dialog, SLOT(updateDialogFields()) ); 1132 connect( dialog, SIGNAL(finished(int)), 1133 this, SLOT(stopEditingTextAnnotation(int)) ); 1134 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1135 this, SLOT(addRelation(OsmPlacemarkData)) ); 1136 1137 if ( m_focusItem ) { 1138 m_focusItem->setFocus( false ); 1139 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 1140 clearOverlayFrames(); 1141 } 1142 } 1143 m_focusItem = textAnnotation; 1144 m_editedItem = textAnnotation; 1145 disableActions( m_actions.first() ); 1146 1147 dialog->move( m_marbleWidget->mapToGlobal( QPoint( 0, 0 ) ) ); 1148 dialog->show(); 1149 m_editingDialogIsShown = true; 1150 } 1151 1152 void AnnotatePlugin::stopEditingTextAnnotation( int result ) 1153 { 1154 m_focusItem = m_editedItem; 1155 m_editedItem = nullptr; 1156 announceStateChanged( SceneGraphicsItem::Editing ); 1157 enableAllActions( m_actions.first() ); 1158 disableFocusActions(); 1159 1160 if ( !result && m_addingPlacemark ) { 1161 removeFocusItem(); 1162 } else { 1163 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicTextAnnotation ); 1164 } 1165 1166 m_addingPlacemark = false; 1167 m_editingDialogIsShown = false; 1168 } 1169 1170 void AnnotatePlugin::setupGroundOverlayModel() 1171 { 1172 m_editingDialogIsShown = false; 1173 m_groundOverlayModel.setSourceModel( m_marbleWidget->model()->groundOverlayModel() ); 1174 m_groundOverlayModel.setDynamicSortFilter( true ); 1175 m_groundOverlayModel.setSortRole( MarblePlacemarkModel::PopularityIndexRole ); 1176 m_groundOverlayModel.sort( 0, Qt::AscendingOrder ); 1177 } 1178 1179 void AnnotatePlugin::setupOverlayRmbMenu() 1180 { 1181 delete m_overlayRmbMenu; 1182 m_overlayRmbMenu = new QMenu; 1183 1184 QAction *editOverlay = new QAction( tr( "Properties" ), m_overlayRmbMenu ); 1185 m_overlayRmbMenu->addAction( editOverlay ); 1186 connect( editOverlay, SIGNAL(triggered()), this, SLOT(editOverlay()) ); 1187 1188 m_overlayRmbMenu->addSeparator(); 1189 1190 QAction *removeOverlay = new QAction( tr( "Remove" ), m_overlayRmbMenu ); 1191 m_overlayRmbMenu->addAction( removeOverlay ); 1192 connect( removeOverlay, SIGNAL(triggered()), this, SLOT(removeOverlay()) ); 1193 } 1194 1195 void AnnotatePlugin::addOverlay() 1196 { 1197 GeoDataGroundOverlay *overlay = new GeoDataGroundOverlay(); 1198 qreal centerLongitude = m_marbleWidget->viewport()->centerLongitude()*RAD2DEG; 1199 qreal centerLatitude = m_marbleWidget->viewport()->centerLatitude()*RAD2DEG; 1200 GeoDataLatLonAltBox box = m_marbleWidget->viewport()->viewLatLonAltBox(); 1201 qreal maxDelta = 20; 1202 qreal deltaLongitude = qMin(box.width(GeoDataCoordinates::Degree), maxDelta); 1203 qreal deltaLatitude = qMin(box.height(GeoDataCoordinates::Degree), maxDelta); 1204 qreal north = centerLatitude + deltaLatitude/4; 1205 qreal south = centerLatitude - deltaLatitude/4; 1206 qreal west = centerLongitude - deltaLongitude/4; 1207 qreal east = centerLongitude + deltaLongitude/4; 1208 overlay->latLonBox().setBoundaries( north, south, east, west, GeoDataCoordinates::Degree ); 1209 overlay->setName( tr( "Untitled Ground Overlay" ) ); 1210 QPointer<EditGroundOverlayDialog> dialog = new EditGroundOverlayDialog( 1211 overlay, 1212 m_marbleWidget->textureLayer(), 1213 m_marbleWidget ); 1214 dialog->exec(); 1215 if( dialog->result() ) { 1216 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, overlay ); 1217 displayOverlayFrame( overlay ); 1218 } 1219 else { 1220 delete overlay; 1221 } 1222 delete dialog; 1223 } 1224 1225 void AnnotatePlugin::showOverlayRmbMenu( GeoDataGroundOverlay *overlay, qreal x, qreal y ) 1226 { 1227 m_rmbOverlay = overlay; 1228 m_overlayRmbMenu->popup( m_marbleWidget->mapToGlobal( QPoint( x, y ) ) ); 1229 } 1230 1231 void AnnotatePlugin::editOverlay() 1232 { 1233 displayOverlayFrame( m_rmbOverlay ); 1234 1235 QPointer<EditGroundOverlayDialog> dialog = new EditGroundOverlayDialog( 1236 m_rmbOverlay, 1237 m_marbleWidget->textureLayer(), 1238 m_marbleWidget ); 1239 connect( dialog, SIGNAL(groundOverlayUpdated(GeoDataGroundOverlay*)), 1240 this, SLOT(updateOverlayFrame(GeoDataGroundOverlay*)) ); 1241 1242 dialog->exec(); 1243 delete dialog; 1244 } 1245 1246 void AnnotatePlugin::removeOverlay() 1247 { 1248 m_marbleWidget->model()->treeModel()->removeFeature( m_rmbOverlay ); 1249 clearOverlayFrames(); 1250 } 1251 1252 void AnnotatePlugin::displayOverlayFrame( GeoDataGroundOverlay *overlay ) 1253 { 1254 if ( m_groundOverlayFrames.keys().contains( overlay ) ) { 1255 return; 1256 } 1257 1258 GeoDataPolygon *polygon = new GeoDataPolygon( Tessellate ); 1259 polygon->outerBoundary().setTessellate( true ); 1260 1261 GeoDataPlacemark *rectangle_placemark = new GeoDataPlacemark; 1262 rectangle_placemark->setGeometry( polygon ); 1263 rectangle_placemark->setParent( m_annotationDocument ); 1264 rectangle_placemark->setStyleUrl(QStringLiteral("#polygon")); 1265 1266 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, rectangle_placemark ); 1267 1268 GroundOverlayFrame *frame = new GroundOverlayFrame( rectangle_placemark, 1269 overlay, 1270 m_marbleWidget->textureLayer() ); 1271 m_graphicsItems.append( frame ); 1272 m_groundOverlayFrames.insert( overlay, frame ); 1273 1274 if ( m_focusItem ) { 1275 m_focusItem->setFocus( false ); 1276 } 1277 m_focusItem = frame; 1278 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicGroundOverlay ); 1279 } 1280 1281 void AnnotatePlugin::updateOverlayFrame( GeoDataGroundOverlay *overlay ) 1282 { 1283 GroundOverlayFrame *frame = static_cast<GroundOverlayFrame *>( m_groundOverlayFrames.value( overlay ) ); 1284 if ( frame ) { 1285 frame->update(); 1286 } 1287 } 1288 1289 void AnnotatePlugin::clearOverlayFrames() 1290 { 1291 for ( GeoDataGroundOverlay *overlay: m_groundOverlayFrames.keys() ) { 1292 GroundOverlayFrame *frame = static_cast<GroundOverlayFrame *>( m_groundOverlayFrames.value( overlay ) ); 1293 m_graphicsItems.removeAll( m_groundOverlayFrames.value( overlay ) ); 1294 m_marbleWidget->model()->treeModel()->removeFeature( frame->placemark() ); 1295 delete frame->placemark(); 1296 delete frame; 1297 } 1298 1299 m_groundOverlayFrames.clear(); 1300 m_focusItem = nullptr; 1301 disableFocusActions(); 1302 } 1303 1304 void AnnotatePlugin::setupPolygonRmbMenu() 1305 { 1306 delete m_polygonRmbMenu; 1307 m_polygonRmbMenu = new QMenu; 1308 1309 QAction *deselectNodes = new QAction( tr( "Deselect All Nodes" ), m_polygonRmbMenu ); 1310 m_polygonRmbMenu->addAction( deselectNodes ); 1311 connect( deselectNodes, SIGNAL(triggered()), this, SLOT(deselectNodes()) ); 1312 1313 QAction *deleteAllSelected = new QAction( tr( "Delete All Selected Nodes" ), m_polygonRmbMenu ); 1314 m_polygonRmbMenu->addAction( deleteAllSelected ); 1315 connect( deleteAllSelected, SIGNAL(triggered()), this, SLOT(deleteSelectedNodes()) ); 1316 1317 m_polygonRmbMenu->addSeparator(); 1318 1319 QAction *cutPolygon = new QAction( tr( "Cut"), m_polygonRmbMenu ); 1320 m_polygonRmbMenu->addAction( cutPolygon ); 1321 connect( cutPolygon, SIGNAL(triggered()), this, SLOT(cutItem()) ); 1322 1323 QAction *copyPolygon = new QAction( tr( "Copy"), m_polygonRmbMenu ); 1324 m_polygonRmbMenu->addAction( copyPolygon ); 1325 connect( copyPolygon, SIGNAL(triggered()), this, SLOT(copyItem()) ); 1326 1327 QAction *removePolygon = new QAction( tr( "Remove" ), m_polygonRmbMenu ); 1328 m_polygonRmbMenu->addAction( removePolygon ); 1329 connect( removePolygon, SIGNAL(triggered()), this, SLOT(askToRemoveFocusItem()) ); 1330 1331 m_polygonRmbMenu->addSeparator(); 1332 1333 QAction *showEditDialog = new QAction( tr( "Properties" ), m_polygonRmbMenu ); 1334 m_polygonRmbMenu->addAction( showEditDialog ); 1335 connect( showEditDialog, SIGNAL(triggered()), this, SLOT(editPolygon()) ); 1336 } 1337 1338 void AnnotatePlugin::showPolygonRmbMenu( qreal x, qreal y ) 1339 { 1340 // We need to store the coordinates from where the rmb menu is shown so that in case of 1341 // selecting Copy/Cut, we can move the polygon. 1342 qreal lon, lat; 1343 m_marbleWidget->geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); 1344 m_fromWhereToCopy = GeoDataCoordinates( lon, lat ); 1345 1346 if ( !static_cast<AreaAnnotation*>( m_focusItem )->hasNodesSelected() ) { 1347 m_polygonRmbMenu->actions().at(1)->setEnabled( false ); 1348 m_polygonRmbMenu->actions().at(0)->setEnabled( false ); 1349 } else { 1350 m_polygonRmbMenu->actions().at(1)->setEnabled( true ); 1351 m_polygonRmbMenu->actions().at(0)->setEnabled( true ); 1352 } 1353 1354 m_polygonRmbMenu->popup( m_marbleWidget->mapToGlobal( QPoint( x, y ) ) ); 1355 } 1356 1357 void AnnotatePlugin::addPolygon() 1358 { 1359 m_drawingPolygon = true; 1360 1361 GeoDataPolygon *poly = new GeoDataPolygon( Tessellate ); 1362 poly->outerBoundary().setTessellate( true ); 1363 1364 m_polygonPlacemark = new GeoDataPlacemark; 1365 m_polygonPlacemark->setGeometry( poly ); 1366 m_polygonPlacemark->setParent( m_annotationDocument ); 1367 m_polygonPlacemark->setStyleUrl(QStringLiteral("#polygon")); 1368 1369 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, m_polygonPlacemark ); 1370 1371 AreaAnnotation *polygon = new AreaAnnotation( m_polygonPlacemark ); 1372 polygon->setState( SceneGraphicsItem::DrawingPolygon ); 1373 polygon->setFocus( true ); 1374 m_graphicsItems.append( polygon ); 1375 m_marbleWidget->update(); 1376 1377 QPointer<EditPolygonDialog> dialog = new EditPolygonDialog( m_polygonPlacemark, &m_osmRelations, m_marbleWidget ); 1378 1379 connect( dialog, SIGNAL(polygonUpdated(GeoDataFeature*)), 1380 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1381 connect( dialog, SIGNAL(finished(int)), 1382 this, SLOT(stopEditingPolygon(int)) ); 1383 connect( this, SIGNAL(nodeAdded(GeoDataCoordinates)), dialog, SLOT(handleAddingNode(GeoDataCoordinates)) ); 1384 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1385 this, SLOT(addRelation(OsmPlacemarkData)) ); 1386 1387 // If there is another graphic item marked as 'selected' when pressing 'Add Polygon', change the focus of 1388 // that item. 1389 if ( m_focusItem ) { 1390 m_focusItem->setFocus( false ); 1391 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 1392 clearOverlayFrames(); 1393 } 1394 } 1395 m_focusItem = polygon; 1396 m_editedItem = polygon; 1397 disableActions( m_actions.first() ); 1398 1399 dialog->move( m_marbleWidget->mapToGlobal( QPoint( 0, 0 ) ) ); 1400 dialog->show(); 1401 m_editingDialogIsShown = true; 1402 } 1403 1404 void AnnotatePlugin::stopEditingPolygon( int result ) 1405 { 1406 m_focusItem = m_editedItem; 1407 m_editedItem = nullptr; 1408 announceStateChanged( SceneGraphicsItem::Editing ); 1409 enableAllActions( m_actions.first() ); 1410 disableFocusActions(); 1411 1412 if ( !result && m_drawingPolygon ) { 1413 removeFocusItem(); 1414 } else { 1415 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicAreaAnnotation ); 1416 } 1417 1418 m_editingDialogIsShown = false; 1419 m_drawingPolygon = false; 1420 m_polygonPlacemark = nullptr; 1421 } 1422 1423 void AnnotatePlugin::deselectNodes() 1424 { 1425 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1426 AreaAnnotation * const area = static_cast<AreaAnnotation*>( m_focusItem ); 1427 area->deselectAllNodes(); 1428 1429 if ( area->request() == SceneGraphicsItem::NoRequest ) { 1430 m_marbleWidget->model()->treeModel()->updateFeature( area->placemark() ); 1431 } 1432 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1433 PolylineAnnotation * const polyline = static_cast<PolylineAnnotation*>( m_focusItem ); 1434 polyline->deselectAllNodes(); 1435 1436 if ( polyline->request() == SceneGraphicsItem::NoRequest ) { 1437 m_marbleWidget->model()->treeModel()->updateFeature( polyline->placemark() ); 1438 } 1439 } 1440 } 1441 1442 void AnnotatePlugin::deleteSelectedNodes() 1443 { 1444 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1445 AreaAnnotation * const area = static_cast<AreaAnnotation*>( m_focusItem ); 1446 area->deleteAllSelectedNodes(); 1447 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1448 PolylineAnnotation * const polyline = static_cast<PolylineAnnotation*>( m_focusItem ); 1449 polyline->deleteAllSelectedNodes(); 1450 } 1451 1452 if ( m_focusItem->request() == SceneGraphicsItem::NoRequest ) { 1453 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 1454 } else if ( m_focusItem->request() == SceneGraphicsItem::RemovePolygonRequest || 1455 m_focusItem->request() == SceneGraphicsItem::RemovePolylineRequest ) { 1456 removeFocusItem(); 1457 } else if ( m_focusItem->request() == SceneGraphicsItem::InvalidShapeWarning ) { 1458 QMessageBox::warning( m_marbleWidget, 1459 tr( "Operation not permitted" ), 1460 tr( "Cannot delete one of the selected nodes. Most probably " 1461 "this would make the polygon's outer boundary not " 1462 "contain all its inner boundary nodes." ) ); 1463 } 1464 } 1465 1466 void AnnotatePlugin::editPolygon() 1467 { 1468 EditPolygonDialog *dialog = new EditPolygonDialog( m_focusItem->placemark(), &m_osmRelations, m_marbleWidget ); 1469 1470 connect( dialog, SIGNAL(polygonUpdated(GeoDataFeature*)), 1471 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1472 connect( dialog, SIGNAL(finished(int)), 1473 this, SLOT(stopEditingPolygon(int)) ); 1474 connect( this, SIGNAL(itemMoved(GeoDataPlacemark*)), dialog, SLOT(handleItemMoving(GeoDataPlacemark*)) ); 1475 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1476 this, SLOT(addRelation(OsmPlacemarkData)) ); 1477 1478 disableActions( m_actions.first() ); 1479 1480 dialog->move( m_marbleWidget->mapToGlobal( QPoint( 0, 0 ) ) ); 1481 dialog->show(); 1482 m_editingDialogIsShown = true; 1483 m_editedItem = m_focusItem; 1484 } 1485 1486 void AnnotatePlugin::setupNodeRmbMenu() 1487 { 1488 delete m_nodeRmbMenu; 1489 m_nodeRmbMenu = new QMenu; 1490 1491 QAction *selectNode = new QAction( tr( "Select Node" ), m_nodeRmbMenu ); 1492 m_nodeRmbMenu->addAction( selectNode ); 1493 connect( selectNode, SIGNAL(triggered()), this, SLOT(selectNode()) ); 1494 1495 QAction *deleteNode = new QAction( tr( "Delete Node" ), m_nodeRmbMenu ); 1496 m_nodeRmbMenu->addAction( deleteNode ); 1497 connect( deleteNode, SIGNAL(triggered()), this, SLOT(deleteNode()) ); 1498 } 1499 1500 void AnnotatePlugin::showNodeRmbMenu( qreal x, qreal y ) 1501 { 1502 // Check whether the node is already selected; we change the text of the action 1503 // accordingly. 1504 bool isSelected = false; 1505 if ( ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation && 1506 static_cast<AreaAnnotation*>( m_focusItem )->clickedNodeIsSelected() ) || 1507 ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation && 1508 static_cast<PolylineAnnotation*>( m_focusItem )->clickedNodeIsSelected() ) ) { 1509 isSelected = true; 1510 } 1511 1512 m_nodeRmbMenu->actions().first()->setText( isSelected ? tr("Deselect Node") : tr("Select Node") ); 1513 m_nodeRmbMenu->popup( m_marbleWidget->mapToGlobal( QPoint( x, y ) ) ); 1514 } 1515 1516 void AnnotatePlugin::selectNode() 1517 { 1518 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1519 AreaAnnotation * const area = static_cast<AreaAnnotation*>( m_focusItem ); 1520 area->changeClickedNodeSelection(); 1521 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1522 PolylineAnnotation *const polyline = static_cast<PolylineAnnotation*>( m_focusItem ); 1523 polyline->changeClickedNodeSelection(); 1524 } 1525 1526 if ( m_focusItem->request() == SceneGraphicsItem::NoRequest ) { 1527 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 1528 } 1529 } 1530 1531 void AnnotatePlugin::deleteNode() 1532 { 1533 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1534 AreaAnnotation *area = static_cast<AreaAnnotation*>( m_focusItem ); 1535 area->deleteClickedNode(); 1536 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1537 PolylineAnnotation *polyline = static_cast<PolylineAnnotation*>( m_focusItem ); 1538 polyline->deleteClickedNode(); 1539 } 1540 1541 if ( m_focusItem->request() == SceneGraphicsItem::NoRequest ) { 1542 m_marbleWidget->model()->treeModel()->updateFeature( m_focusItem->placemark() ); 1543 } else if ( m_focusItem->request() == SceneGraphicsItem::RemovePolygonRequest || 1544 m_focusItem->request() == SceneGraphicsItem::RemovePolylineRequest ) { 1545 removeFocusItem(); 1546 } else if ( m_focusItem->request() == SceneGraphicsItem::InvalidShapeWarning ) { 1547 QMessageBox::warning( m_marbleWidget, 1548 tr( "Operation not permitted" ), 1549 tr( "Cannot delete one of the selected nodes. Most probably " 1550 "this would make the polygon's outer boundary not " 1551 "contain all its inner boundary nodes." ) ); 1552 } 1553 } 1554 1555 void AnnotatePlugin::setupPolylineRmbMenu() 1556 { 1557 delete m_polylineRmbMenu; 1558 m_polylineRmbMenu = new QMenu; 1559 1560 QAction *deselectNodes = new QAction( tr( "Deselect All Nodes" ), m_polylineRmbMenu ); 1561 m_polylineRmbMenu->addAction( deselectNodes ); 1562 connect( deselectNodes, SIGNAL(triggered()), this, SLOT(deselectNodes()) ); 1563 1564 QAction *deleteAllSelected = new QAction( tr( "Delete All Selected Nodes" ), m_polylineRmbMenu ); 1565 m_polylineRmbMenu->addAction( deleteAllSelected ); 1566 connect( deleteAllSelected, SIGNAL(triggered()), this, SLOT(deleteSelectedNodes()) ); 1567 1568 m_polylineRmbMenu->addSeparator(); 1569 1570 QAction *cutItem = new QAction( tr( "Cut"), m_polylineRmbMenu ); 1571 m_polylineRmbMenu->addAction( cutItem ); 1572 connect( cutItem, SIGNAL(triggered()), this, SLOT(cutItem()) ); 1573 1574 QAction *copyItem = new QAction( tr( "Copy"), m_polylineRmbMenu ); 1575 m_polylineRmbMenu->addAction( copyItem ); 1576 connect( copyItem, SIGNAL(triggered()), this, SLOT(copyItem()) ); 1577 1578 QAction *removeItem = new QAction( tr( "Remove" ), m_polylineRmbMenu ); 1579 m_polylineRmbMenu->addAction( removeItem ); 1580 connect( removeItem, SIGNAL(triggered()), this, SLOT(askToRemoveFocusItem()) ); 1581 1582 m_polylineRmbMenu->addSeparator(); 1583 1584 QAction *properties = new QAction( tr( "Properties" ), m_polylineRmbMenu ); 1585 m_polylineRmbMenu->addAction( properties ); 1586 connect( properties, SIGNAL(triggered()), this, SLOT(editPolyline()) ); 1587 } 1588 1589 void AnnotatePlugin::showPolylineRmbMenu( qreal x, qreal y ) 1590 { 1591 qreal lon, lat; 1592 m_marbleWidget->geoCoordinates( x, y, lon, lat, GeoDataCoordinates::Radian ); 1593 m_fromWhereToCopy = GeoDataCoordinates( lon, lat ); 1594 1595 if ( !static_cast<PolylineAnnotation*>( m_focusItem )->hasNodesSelected() ) { 1596 m_polylineRmbMenu->actions().at(1)->setEnabled( false ); 1597 m_polylineRmbMenu->actions().at(0)->setEnabled( false ); 1598 } else { 1599 m_polylineRmbMenu->actions().at(1)->setEnabled( true ); 1600 m_polylineRmbMenu->actions().at(0)->setEnabled( true ); 1601 } 1602 1603 m_polylineRmbMenu->popup( m_marbleWidget->mapToGlobal( QPoint( x, y ) ) ); 1604 } 1605 1606 void AnnotatePlugin::editPolyline() 1607 { 1608 QPointer<EditPolylineDialog> dialog = new EditPolylineDialog( m_focusItem->placemark(), 1609 &m_osmRelations, 1610 m_marbleWidget ); 1611 connect( dialog, SIGNAL(polylineUpdated(GeoDataFeature*)), 1612 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1613 connect( dialog, SIGNAL(finished(int)), 1614 this, SLOT(stopEditingPolyline(int)) ); 1615 connect( this, SIGNAL(itemMoved(GeoDataPlacemark*)), dialog, SLOT(handleItemMoving(GeoDataPlacemark*)) ); 1616 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1617 this, SLOT(addRelation(OsmPlacemarkData)) ); 1618 1619 disableActions( m_actions.first() ); 1620 dialog->show(); 1621 m_editingDialogIsShown = true; 1622 m_editedItem = m_focusItem; 1623 } 1624 1625 void AnnotatePlugin::addPolyline() 1626 { 1627 m_drawingPolyline = true; 1628 1629 m_polylinePlacemark = new GeoDataPlacemark; 1630 m_polylinePlacemark->setGeometry( new GeoDataLineString( Tessellate ) ); 1631 m_polylinePlacemark->setParent( m_annotationDocument ); 1632 m_polylinePlacemark->setStyleUrl(QStringLiteral("#polyline")); 1633 1634 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, m_polylinePlacemark ); 1635 1636 PolylineAnnotation *polyline = new PolylineAnnotation( m_polylinePlacemark ); 1637 polyline->setState( SceneGraphicsItem::DrawingPolyline ); 1638 polyline->setFocus( true ); 1639 m_graphicsItems.append( polyline ); 1640 m_marbleWidget->update(); 1641 1642 QPointer<EditPolylineDialog> dialog = new EditPolylineDialog( m_polylinePlacemark, &m_osmRelations, m_marbleWidget ); 1643 1644 connect( dialog, SIGNAL(polylineUpdated(GeoDataFeature*)), 1645 m_marbleWidget->model()->treeModel(), SLOT(updateFeature(GeoDataFeature*)) ); 1646 connect( dialog, SIGNAL(finished(int)), 1647 this, SLOT(stopEditingPolyline(int)) ); 1648 connect( this, SIGNAL(nodeAdded(GeoDataCoordinates)), dialog, SLOT(handleAddingNode(GeoDataCoordinates)) ); 1649 connect( dialog, SIGNAL(relationCreated(OsmPlacemarkData)), 1650 this, SLOT(addRelation(OsmPlacemarkData)) ); 1651 1652 if ( m_focusItem ) { 1653 m_focusItem->setFocus( false ); 1654 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicGroundOverlay ) { 1655 clearOverlayFrames(); 1656 } 1657 } 1658 m_focusItem = polyline; 1659 m_editedItem = m_focusItem; 1660 disableActions( m_actions.first() ); 1661 1662 dialog->move( m_marbleWidget->mapToGlobal( QPoint( 0, 0 ) ) ); 1663 dialog->show(); 1664 m_editingDialogIsShown = true; 1665 } 1666 1667 void AnnotatePlugin::stopEditingPolyline( int result ) 1668 { 1669 m_focusItem = m_editedItem; 1670 m_editedItem = nullptr; 1671 announceStateChanged( SceneGraphicsItem::Editing ); 1672 enableAllActions( m_actions.first() ); 1673 disableFocusActions(); 1674 1675 if ( !result && m_drawingPolyline ) { 1676 removeFocusItem(); 1677 } else { 1678 enableActionsOnItemType( SceneGraphicsTypes::SceneGraphicPolylineAnnotation ); 1679 } 1680 1681 m_editingDialogIsShown = false; 1682 m_drawingPolyline = false; 1683 m_polylinePlacemark = nullptr; 1684 } 1685 1686 void AnnotatePlugin::addRelation( const OsmPlacemarkData &relationData ) 1687 { 1688 m_osmRelations.insert( relationData.id(), relationData ); 1689 } 1690 1691 void AnnotatePlugin::announceStateChanged( SceneGraphicsItem::ActionState newState ) 1692 { 1693 for ( SceneGraphicsItem *item: m_graphicsItems ) { 1694 item->setState( newState ); 1695 m_marbleWidget->model()->treeModel()->updateFeature( item->placemark() ); 1696 } 1697 } 1698 1699 void AnnotatePlugin::setupCursor( SceneGraphicsItem *item ) 1700 { 1701 if ( !item || item->state() == SceneGraphicsItem::AddingNodes ) { 1702 m_marbleWidget->setCursor( Qt::DragCopyCursor ); 1703 } else { 1704 // Nothing to do. The other cursor changes were moved to the handleRequests() section. 1705 } 1706 } 1707 1708 void AnnotatePlugin::cutItem() 1709 { 1710 disableFocusActions(); 1711 1712 // If there is already an item copied/cut, free its memory and replace it with this one. 1713 // The same applies when copying. 1714 if ( m_clipboardItem ) { 1715 delete m_clipboardItem->placemark(); 1716 delete m_clipboardItem; 1717 m_clipboardItem = nullptr; 1718 } 1719 1720 m_clipboardItem = m_focusItem; 1721 m_pasteGraphicItem->setVisible( true ); 1722 1723 m_graphicsItems.removeAll( m_focusItem ); 1724 m_marbleWidget->model()->treeModel()->removeFeature( m_focusItem->placemark() ); 1725 1726 m_focusItem = nullptr; 1727 } 1728 1729 void AnnotatePlugin::copyItem() 1730 { 1731 if ( m_clipboardItem ) { 1732 delete m_clipboardItem->placemark(); 1733 delete m_clipboardItem; 1734 m_clipboardItem = nullptr; 1735 } 1736 1737 // Just copy the placemark and instantiate a new object based on its graphic type. 1738 // FIXME: Here is obvious a problem in the case of AreaAnnotation (when copying a 1739 // placemark which has a GeoDataPolygon geometry?). Later Edit: The same applies for 1740 // polylines (GeoDataLineString geometries). 1741 GeoDataPlacemark *placemark = new GeoDataPlacemark( *m_focusItem->placemark() ); 1742 if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicAreaAnnotation ) { 1743 m_clipboardItem = new AreaAnnotation( placemark ); 1744 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicTextAnnotation ) { 1745 m_clipboardItem = new PlacemarkTextAnnotation( placemark ); 1746 } else if ( m_focusItem->graphicType() == SceneGraphicsTypes::SceneGraphicPolylineAnnotation ) { 1747 m_clipboardItem = new PolylineAnnotation( placemark ); 1748 } 1749 1750 m_pasteGraphicItem->setVisible( true ); 1751 } 1752 1753 void AnnotatePlugin::pasteItem() 1754 { 1755 const QPoint eventPoint = m_marbleWidget->popupMenu()->mousePosition(); 1756 1757 qreal lon, lat; 1758 m_marbleWidget->geoCoordinates( eventPoint.x(), eventPoint.y(), lon, lat, GeoDataCoordinates::Radian ); 1759 const GeoDataCoordinates newCoords( lon, lat ); 1760 1761 m_clipboardItem->move( m_fromWhereToCopy, newCoords ); 1762 m_marbleWidget->model()->treeModel()->addFeature( m_annotationDocument, m_clipboardItem->placemark() ); 1763 m_graphicsItems.append( m_clipboardItem ); 1764 1765 m_clipboardItem->setFocus( true ); 1766 enableActionsOnItemType( m_clipboardItem->graphicType() ); 1767 m_focusItem = m_clipboardItem; 1768 m_clipboardItem = nullptr; 1769 1770 m_pasteGraphicItem->setVisible( false ); 1771 } 1772 1773 const GeoDataCoordinates AnnotatePlugin::mouseGeoDataCoordinates(QMouseEvent *mouseEvent) const 1774 { 1775 qreal lon, lat; 1776 m_marbleWidget->geoCoordinates( mouseEvent->pos().x(), 1777 mouseEvent->pos().y(), 1778 lon, lat, 1779 GeoDataCoordinates::Radian ); 1780 return GeoDataCoordinates( lon, lat ); 1781 } 1782 1783 } 1784 1785 #include "moc_AnnotatePlugin.cpp"