File indexing completed on 2024-05-05 03:50:38

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com>
0004 //
0005 
0006 // Self
0007 #include "EditPolygonDialog.h"
0008 #include "ui_EditPolygonDialog.h"
0009 
0010 // Qt
0011 #include <QColorDialog>
0012 #include <QMessageBox>
0013 
0014 // Marble
0015 #include "GeoDataPlacemark.h"
0016 #include "GeoDataStyle.h"
0017 #include "GeoDataLineStyle.h"
0018 #include "GeoDataPolyStyle.h"
0019 #include "GeoDataLinearRing.h"
0020 #include "GeoDataPolygon.h"
0021 #include "NodeModel.h"
0022 #include "NodeItemDelegate.h"
0023 #include "FormattedTextWidget.h"
0024 #include "StyleBuilder.h"
0025 #include "osm/OsmTagEditorWidget.h"
0026 #include "osm/OsmPlacemarkData.h"
0027 #include "osm/OsmRelationManagerWidget.h"
0028 
0029 namespace Marble {
0030 
0031 class Q_DECL_HIDDEN EditPolygonDialog::Private : public Ui::UiEditPolygonDialog
0032 {
0033 public:
0034     Private( GeoDataPlacemark *placemark );
0035     ~Private();
0036 
0037     GeoDataPlacemark *m_placemark;
0038 
0039     QColorDialog *m_linesDialog;
0040     QColorDialog *m_polyDialog;
0041 
0042     QString m_initialDescription;
0043     QString m_initialName;
0044     GeoDataStyle m_initialStyle;
0045     GeoDataLinearRing m_initialOuterBoundary;
0046     OsmPlacemarkData m_initialOsmData;
0047     bool m_hadInitialOsmData;
0048 
0049     NodeModel *m_nodeModel;
0050     NodeItemDelegate *m_delegate;
0051     OsmTagEditorWidget *m_osmTagEditorWidget;
0052     OsmRelationManagerWidget *m_osmRelationManagerWidget;
0053 
0054 };
0055 
0056 EditPolygonDialog::Private::Private( GeoDataPlacemark *placemark ) :
0057     Ui::UiEditPolygonDialog(),
0058     m_placemark( placemark ),
0059     m_linesDialog( nullptr ),
0060     m_polyDialog( nullptr ),
0061     m_nodeModel( new NodeModel ),
0062     m_osmTagEditorWidget( nullptr ),
0063     m_osmRelationManagerWidget( nullptr )
0064 {
0065     // nothing to do
0066 }
0067 
0068 EditPolygonDialog::Private::~Private()
0069 {
0070     delete m_linesDialog;
0071     delete m_polyDialog;
0072     delete m_nodeModel;
0073     delete m_delegate;
0074 }
0075 
0076 EditPolygonDialog::EditPolygonDialog( GeoDataPlacemark *placemark,
0077                                       const QHash<qint64, OsmPlacemarkData> *relations,
0078                                       QWidget *parent ) :
0079     QDialog( parent ),
0080     d( new Private( placemark ) )
0081 {
0082     d->setupUi( this );
0083 
0084     // There's no point showing Relations and Tags tabs if the editor was not
0085     // loaded from the annotate plugin ( loaded from tourWidget.. )
0086     if ( relations ) {
0087         // Adding the osm tag editor widget tab
0088         d->m_osmTagEditorWidget = new OsmTagEditorWidget( placemark, this );
0089         d->tabWidget->addTab( d->m_osmTagEditorWidget, tr( "Tags" ) );
0090         QObject::connect( d->m_osmTagEditorWidget, SIGNAL(placemarkChanged(GeoDataFeature*)),
0091                           this, SLOT(updatePolygon()) );
0092 
0093         // Adding the osm relation editor widget tab
0094         d->m_osmRelationManagerWidget = new OsmRelationManagerWidget( placemark, relations, this );
0095         d->tabWidget->addTab( d->m_osmRelationManagerWidget, tr( "Relations" ) );
0096         QObject::connect( d->m_osmRelationManagerWidget, SIGNAL(relationCreated(OsmPlacemarkData)),
0097                           this, SIGNAL(relationCreated(OsmPlacemarkData)) );
0098 
0099         adjustSize();
0100     }
0101 
0102 
0103 
0104     d->m_hadInitialOsmData = placemark->hasOsmData();
0105     if ( d->m_hadInitialOsmData ) {
0106         d->m_initialOsmData = placemark->osmData();
0107     }
0108 
0109     d->m_initialStyle = *placemark->style();
0110 
0111     // If the polygon has just been drawn, assign it a default name.
0112     if ( d->m_placemark->name().isNull() ) {
0113         d->m_placemark->setName( tr("Untitled Polygon") );
0114     }
0115     d->m_name->setText( placemark->name() );
0116     d->m_initialName = placemark->name();
0117     connect( d->m_name, SIGNAL(editingFinished()), this, SLOT(updatePolygon()) );
0118 
0119     d->m_formattedTextWidget->setText( placemark->description() );
0120     d->m_initialDescription = placemark->description();
0121 
0122     // Get the current style properties.
0123     const GeoDataLineStyle lineStyle = placemark->style()->lineStyle();
0124     const GeoDataPolyStyle polyStyle = placemark->style()->polyStyle();
0125 
0126     d->m_linesWidth->setRange( 0.1, 5.0 );
0127     d->m_linesWidth->setValue( lineStyle.width() );
0128     connect( d->m_linesWidth, SIGNAL(valueChanged(double)), this, SLOT(handleChangingStyle()) );
0129 
0130     // Adjust the "Filled"/"Not Filled" option according to its current fill.
0131     if ( polyStyle.fill() ) {
0132         d->m_filledColor->setCurrentIndex( 0 );
0133     } else {
0134         d->m_filledColor->setCurrentIndex( 1 );
0135     }
0136     connect( d->m_filledColor, SIGNAL(currentIndexChanged(int)), this, SLOT(handleChangingStyle()) );
0137 
0138     // Adjust the color buttons' icons to the current lines and polygon colors.
0139     QPixmap linesPixmap( d->m_linesColorButton->iconSize().width(),
0140                          d->m_linesColorButton->iconSize().height() );
0141     linesPixmap.fill( lineStyle.color() );
0142     d->m_linesColorButton->setIcon( QIcon( linesPixmap ) );
0143 
0144     QPixmap polyPixmap( d->m_polyColorButton->iconSize().width(),
0145                         d->m_polyColorButton->iconSize().height() );
0146     polyPixmap.fill( polyStyle.color() );
0147     d->m_polyColorButton->setIcon( QIcon( polyPixmap ) );
0148 
0149     // Setup the color dialogs.
0150     d->m_linesDialog = new QColorDialog( this );
0151     d->m_linesDialog->setOption( QColorDialog::ShowAlphaChannel );
0152     d->m_linesDialog->setCurrentColor( lineStyle.color() );
0153     connect( d->m_linesColorButton, SIGNAL(clicked()), d->m_linesDialog, SLOT(exec()) );
0154     connect( d->m_linesDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updateLinesDialog(QColor)) );
0155     connect( d->m_linesDialog, SIGNAL(colorSelected(QColor)), this, SLOT(handleChangingStyle()) );
0156 
0157     d->m_polyDialog = new QColorDialog( this );
0158     d->m_polyDialog->setOption( QColorDialog::ShowAlphaChannel );
0159     d->m_polyDialog->setCurrentColor( polyStyle.color() );
0160     connect( d->m_polyColorButton, SIGNAL(clicked()), d->m_polyDialog, SLOT(exec()) );
0161     connect( d->m_polyDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updatePolyDialog(QColor)) );
0162     connect( d->m_polyDialog, SIGNAL(colorSelected(QColor)), this, SLOT(handleChangingStyle()) );
0163 
0164     // Setting the NodeView's delegate: mainly used for the editing the polygon's nodes
0165     d->m_delegate = new NodeItemDelegate( d->m_placemark, d->m_nodeView );
0166 
0167     connect( d->m_delegate, SIGNAL(modelChanged(GeoDataPlacemark*)),
0168              this, SLOT(handleItemMoving(GeoDataPlacemark*)) );
0169     connect( d->m_delegate, SIGNAL(geometryChanged()),
0170              this, SLOT(updatePolygon()) );
0171 
0172     d->m_nodeView->setItemDelegate( d->m_delegate );
0173     d->m_nodeView->setEditTriggers( QAbstractItemView::AllEditTriggers );
0174 
0175     // Populating the model
0176     if (const GeoDataPolygon *polygon = geodata_cast<GeoDataPolygon>(placemark->geometry())) {
0177         GeoDataLinearRing outerBoundary = polygon->outerBoundary();
0178         for( int i = 0; i < outerBoundary.size(); ++i ) {
0179             d->m_nodeModel->addNode( outerBoundary.at( i ) );
0180         }
0181         d->m_initialOuterBoundary = outerBoundary;
0182     }
0183 
0184     d->m_nodeView->setModel( d->m_nodeModel );
0185 
0186     // Resize column to contents size for better UI.
0187     d->m_nodeView->resizeColumnToContents( 0 );
0188 
0189     // Promote "Ok" button to default button.
0190     d->buttonBox->button( QDialogButtonBox::Ok )->setDefault( true );
0191 
0192     connect( d->buttonBox->button( QDialogButtonBox::Ok ), SIGNAL(pressed()), this, SLOT(checkFields()) );
0193     connect( this, SIGNAL(accepted()), SLOT(updatePolygon()) );
0194     connect( this, SIGNAL(finished(int)), SLOT(restoreInitial(int)) );
0195 
0196     // Ensure that the dialog gets deleted when closing it (either when clicking OK or
0197     // Close).
0198     connect( this, SIGNAL(finished(int)), SLOT(deleteLater()) );
0199 }
0200 
0201 EditPolygonDialog::~EditPolygonDialog()
0202 {
0203     delete d;
0204 }
0205 
0206 void EditPolygonDialog::handleAddingNode( const GeoDataCoordinates &node )
0207 {
0208     d->m_nodeModel->addNode( node );
0209 }
0210 
0211 void EditPolygonDialog::handleItemMoving( GeoDataPlacemark *item )
0212 {
0213     if( item == d->m_placemark ) {
0214         d->m_nodeModel->clear();
0215         if (const auto polygon = geodata_cast<GeoDataPolygon>(d->m_placemark->geometry())) {
0216             GeoDataLinearRing outerBoundary = polygon->outerBoundary();
0217             for( int i = 0; i < outerBoundary.size(); ++i ) {
0218                 d->m_nodeModel->addNode( outerBoundary.at( i ) );
0219             }
0220         }
0221     }
0222 }
0223 
0224 void EditPolygonDialog::handleChangingStyle()
0225 {
0226 
0227     // The default style of the polygon has been changed, thus the old style URL is no longer valid
0228     d->m_placemark->setStyleUrl(QString());
0229 
0230     GeoDataStyle::Ptr style(new GeoDataStyle( *d->m_placemark->style() ));
0231     style->lineStyle().setWidth( d->m_linesWidth->value() );
0232     // 0 corresponds to "Filled" and 1 corresponds to "Not Filled".
0233     style->polyStyle().setFill( !d->m_filledColor->currentIndex() );
0234     style->setId(d->m_placemark->id() + QLatin1String("Style"));
0235 
0236 
0237     // Adjust the lines/polygon colors.
0238     // QColorDialog::currentColor() also works even if the color dialog
0239     // has not been exec'ed, while QColorDialog::selectedColor() does not.
0240     style->lineStyle().setColor( d->m_linesDialog->currentColor() );
0241     style->polyStyle().setColor( d->m_polyDialog->currentColor() );
0242 
0243     d->m_placemark->setStyle( style );
0244 
0245     updatePolygon();
0246 }
0247 
0248 void EditPolygonDialog::updatePolygon()
0249 {
0250     d->m_placemark->setName( d->m_name->text() );
0251     d->m_placemark->setDescription( d->m_formattedTextWidget->text() );
0252 
0253     // If there is not custom style initialized( default #polyline url is used ) and there is a osmTag-based style
0254     // available, set it
0255     const OsmPlacemarkData osmData = d->m_osmTagEditorWidget->placemarkData();
0256     const GeoDataPlacemark::GeoDataVisualCategory category = StyleBuilder::determineVisualCategory(osmData);
0257     if (d->m_placemark->styleUrl() == QLatin1String("#polygon") && category != GeoDataPlacemark::None) {
0258         d->m_placemark->setStyle( GeoDataStyle::Ptr() ); // first clear style so style gets set by setVisualCategory()
0259         d->m_placemark->setVisualCategory( category );
0260     }
0261 
0262     emit polygonUpdated( d->m_placemark );
0263 }
0264 
0265 void EditPolygonDialog::updateLinesDialog( const QColor &color )
0266 {
0267     QPixmap linesPixmap( d->m_linesColorButton->iconSize().width(),
0268                          d->m_linesColorButton->iconSize().height() );
0269     linesPixmap.fill( color );
0270     d->m_linesColorButton->setIcon( QIcon( linesPixmap ) );
0271 }
0272 
0273 void EditPolygonDialog::updatePolyDialog( const QColor &color )
0274 {
0275     QPixmap polyPixmap( d->m_polyColorButton->iconSize().width(),
0276                         d->m_polyColorButton->iconSize().height() );
0277     polyPixmap.fill( color );
0278     d->m_polyColorButton->setIcon( QIcon( polyPixmap ) );
0279 }
0280 
0281 void EditPolygonDialog::checkFields()
0282 {
0283     bool ok = true;
0284     if ( d->m_name->text().isEmpty() ) {
0285         QMessageBox::warning( this,
0286                               tr( "No name specified" ),
0287                               tr( "Please specify a name for this polygon." ) );
0288         ok = false;
0289     } else {
0290         if (const auto polygon = geodata_cast<GeoDataPolygon>(d->m_placemark->geometry())) {
0291             if( polygon->outerBoundary().size() < 3 ) {
0292                 QMessageBox::warning( this,
0293                                       tr( "Not enough nodes specified." ),
0294                                       tr( "Please specify at least 3 nodes for the polygon by clicking on the map." ) );
0295                 ok = false;
0296             }
0297         }
0298     }
0299     if( ok ) {
0300         accept();
0301     }
0302 }
0303 
0304 void EditPolygonDialog::restoreInitial( int result )
0305 {
0306     if ( result ) {
0307         return;
0308     }
0309 
0310     GeoDataPolygon *polygon = static_cast<GeoDataPolygon*>( d->m_placemark->geometry() );
0311     GeoDataLinearRing outerBoundary = polygon->outerBoundary();
0312 
0313     if ( outerBoundary != d->m_initialOuterBoundary ) {
0314         polygon->setOuterBoundary( d->m_initialOuterBoundary );
0315     }
0316 
0317     if ( d->m_placemark->name() != d->m_initialName ) {
0318         d->m_placemark->setName( d->m_initialName );
0319     }
0320 
0321     if ( d->m_placemark->description() != d->m_initialDescription ) {
0322         d->m_placemark->setDescription( d->m_initialDescription );
0323     }
0324 
0325     if ( *d->m_placemark->style() != d->m_initialStyle ) {
0326         d->m_placemark->setStyle( GeoDataStyle::Ptr(new GeoDataStyle( d->m_initialStyle )) );
0327     }
0328 
0329     if( d->m_hadInitialOsmData ) {
0330         d->m_placemark->setOsmData( d->m_initialOsmData );
0331     }
0332 
0333     emit polygonUpdated( d->m_placemark );
0334 }
0335 
0336 }
0337 #include "moc_EditPolygonDialog.cpp"