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"