File indexing completed on 2024-04-21 03:49:32

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com>
0004 // SPDX-FileCopyrightText: 2015 Ilya Kowalewski <illya.kovalevskyy@gmail.com>
0005 //
0006 
0007 // self
0008 #include "EditPlacemarkDialog.h"
0009 #include "ui_EditPlacemarkDialog.h"
0010 #include "ui_ElevationWidget.h"
0011 
0012 // Qt
0013 #include <QMessageBox>
0014 #include <QFileInfo>
0015 #include <QColorDialog>
0016 #include <QCheckBox>
0017 
0018 // Marble
0019 #include "GeoDataStyle.h"
0020 #include "GeoDataIconStyle.h"
0021 #include "GeoDataLabelStyle.h"
0022 #include "GeoDataPlacemark.h"
0023 #include "MarbleWidget.h"
0024 #include "MarbleLocale.h"
0025 #include "FormattedTextWidget.h"
0026 #include "StyleBuilder.h"
0027 #include "osm/OsmTagEditorWidget.h"
0028 #include "osm/OsmPlacemarkData.h"
0029 #include "osm/OsmRelationManagerWidget.h"
0030 
0031 namespace Marble {
0032 
0033 class Q_DECL_HIDDEN EditPlacemarkDialog::Private : public Ui::UiEditPlacemarkDialog
0034 {
0035 public:
0036     Private( GeoDataPlacemark *placemark );
0037     ~Private();
0038 
0039     GeoDataPlacemark *m_placemark;
0040 
0041     // Attached to label/icon/text color selectors.
0042     QColorDialog *m_iconColorDialog;
0043     QColorDialog *m_labelColorDialog;
0044 
0045     // Used to restore if the Cancel button is pressed.
0046     QString m_initialDescription;
0047     QString m_initialName;
0048     GeoDataCoordinates m_initialCoords;
0049     GeoDataStyle m_initialStyle;
0050     GeoDataPlacemark::GeoDataVisualCategory m_initialVisualCategory;
0051     OsmPlacemarkData m_initialOsmData;
0052     QString m_styleColorTabName;
0053     bool m_initialIsPlacemarkVisible;
0054     bool m_initialIsBaloonVisible;
0055     bool m_initialDescriptionIsCDATA;
0056     bool m_hadInitialOsmData;
0057     QString m_initialId;
0058     Ui::ElevationWidget *m_elevationWidget;
0059     OsmTagEditorWidget *m_osmTagEditorWidget;
0060     OsmRelationManagerWidget *m_osmRelationManagerWidget;
0061     MarbleLocale::MeasureUnit m_elevationUnit;
0062     QString m_initialTargetId;
0063     bool m_isReadOnly;
0064 };
0065 
0066 EditPlacemarkDialog::Private::Private( GeoDataPlacemark *placemark ) :
0067     Ui::UiEditPlacemarkDialog(),
0068     m_placemark( placemark ),
0069     m_iconColorDialog( nullptr ),
0070     m_labelColorDialog( nullptr ),
0071     m_osmTagEditorWidget( nullptr ),
0072     m_osmRelationManagerWidget( nullptr ),
0073     m_isReadOnly(false)
0074 {
0075     // nothing to do
0076 }
0077 
0078 EditPlacemarkDialog::Private::~Private()
0079 {
0080     delete m_elevationWidget;
0081     delete m_osmTagEditorWidget;
0082     delete m_osmRelationManagerWidget;
0083     delete m_iconColorDialog;
0084     delete m_labelColorDialog;
0085 }
0086 
0087 EditPlacemarkDialog::EditPlacemarkDialog( GeoDataPlacemark *placemark,
0088                                           const QHash<qint64, OsmPlacemarkData> *relations,
0089                                           QWidget *parent ) :
0090     QDialog( parent ),
0091     d( new Private( placemark ) )
0092 {
0093     d->setupUi( this );
0094 
0095     // Store initial style so that it can be restored if the 'Cancel' button is pressed.
0096     d->m_initialStyle = *placemark->style();
0097 
0098     d->m_initialVisualCategory = placemark->visualCategory();
0099 
0100 
0101     d->m_hadInitialOsmData = placemark->hasOsmData();
0102     if ( d->m_hadInitialOsmData ) {
0103         d->m_initialOsmData = placemark->osmData();
0104     }
0105 
0106 
0107     // If the placemark has just been created, assign    it a default name.
0108     if ( placemark->name().isNull() ) {
0109         placemark->setName( tr("Untitled Placemark") );
0110     }
0111     // Setup id, name, icon link and latitude/longitude values.
0112     d->m_header->setId( placemark->id() );
0113     d->m_initialId = placemark->id();
0114     d->m_header->setName( placemark->name() );
0115     d->m_initialName = placemark->name();
0116     d->m_isPlacemarkVisible->setChecked( placemark->isVisible() );
0117     d->m_initialIsPlacemarkVisible = placemark->isVisible();
0118     d->m_header->setIconLink( placemark->style()->iconStyle().iconPath() );
0119     d->m_header->setTargetId( placemark->targetId() );
0120     d->m_initialTargetId = placemark->targetId();
0121     MarbleWidget* marbleWidget = dynamic_cast<MarbleWidget*>( parent );
0122     if( marbleWidget != nullptr ) {
0123         const AngleUnit defaultAngleUnit = marbleWidget->defaultAngleUnit();
0124         const GeoDataCoordinates::Notation notation =
0125             (defaultAngleUnit == DecimalDegree) ? GeoDataCoordinates::Decimal :
0126             (defaultAngleUnit == DMSDegree) ?     GeoDataCoordinates::DMS :
0127             /* else, UTM */                       GeoDataCoordinates::UTM;
0128         d->m_header->setNotation( notation );
0129     }
0130     connect( d->m_header, SIGNAL(valueChanged()), this, SLOT(
0131                  updateTextAnnotation()) );
0132 
0133     d->m_formattedTextWidget->setText( placemark->description() );
0134     d->m_initialDescription = placemark->description();
0135     d->m_initialDescriptionIsCDATA = placemark->descriptionIsCDATA();
0136     d->m_isBalloonVisible->setChecked( placemark->isBalloonVisible() );
0137     d->m_initialIsBaloonVisible = placemark->isBalloonVisible();
0138 
0139     d->m_header->setLatitude( placemark->coordinate().latitude( GeoDataCoordinates::Degree ) );
0140     d->m_header->setLongitude( placemark->coordinate().longitude( GeoDataCoordinates::Degree ) );
0141     d->m_initialCoords = GeoDataCoordinates( d->m_header->longitude(),
0142                                              d->m_header->latitude(),
0143                                              0,
0144                                              GeoDataCoordinates::Degree );
0145 
0146     // There's no point showing Relations and Tags tabs if the editor was not
0147     // loaded from the annotate plugin ( loaded from tourWidget.. )
0148     if ( relations ) {
0149         // Adding the osm tag editor widget tab
0150         d->m_osmTagEditorWidget = new OsmTagEditorWidget( placemark, this );
0151         d->tabWidget->addTab( d->m_osmTagEditorWidget, tr( "Tags" ) );
0152         QObject::connect( d->m_osmTagEditorWidget, SIGNAL(placemarkChanged(GeoDataFeature*)),
0153                           this, SLOT(updateTextAnnotation()) );
0154 
0155         // Adding the osm relation editor widget tab
0156         d->m_osmRelationManagerWidget = new OsmRelationManagerWidget( placemark, relations, this );
0157         d->tabWidget->addTab( d->m_osmRelationManagerWidget, tr( "Relations" ) );
0158         QObject::connect( d->m_osmRelationManagerWidget, SIGNAL(relationCreated(OsmPlacemarkData)),
0159                           this, SIGNAL(relationCreated(OsmPlacemarkData)) );
0160     }
0161 
0162     // Adding the elevation widget tab
0163     d->m_elevationWidget = new Ui::ElevationWidget;
0164     QWidget *elevationTab = new QWidget;
0165     d->m_elevationWidget->setupUi( elevationTab );
0166     d->tabWidget->addTab( elevationTab, tr("Elevation") );
0167     qreal altitude = d->m_placemark->coordinate().altitude();
0168     MarbleLocale *locale = MarbleGlobal::getInstance()->locale();
0169     if ( altitude == 0.0 ) {
0170         switch ( locale->measurementSystem() ) {
0171         case MarbleLocale::MetricSystem:
0172             d->m_elevationUnit = MarbleLocale::Meter;
0173             break;
0174         case MarbleLocale::ImperialSystem:
0175             d->m_elevationUnit = MarbleLocale::Foot;
0176             break;
0177         case MarbleLocale::NauticalSystem:
0178             d->m_elevationUnit = MarbleLocale::NauticalMile;
0179             break;
0180         }
0181 
0182         d->m_elevationWidget->elevationSpinBox->setSuffix( locale->unitAbbreviation((d->m_elevationUnit)) );
0183     } else {
0184         qreal convertedAltitude;
0185         const MarbleLocale::MeasurementSystem currentSystem = locale->measurementSystem();
0186         locale->meterToTargetUnit( altitude, currentSystem, convertedAltitude, d->m_elevationUnit );
0187         d->m_elevationWidget->elevationSpinBox->setValue( convertedAltitude );
0188         d->m_elevationWidget->elevationSpinBox->setSuffix( locale->unitAbbreviation(d->m_elevationUnit) );
0189     }
0190 
0191     // Adjust icon and label scales.
0192     d->m_iconScale->setValue( placemark->style()->iconStyle().scale() );
0193     connect( d->m_iconScale, SIGNAL(valueChanged(double)), this, SLOT(updateTextAnnotation()) );
0194 
0195     d->m_labelScale->setValue( placemark->style()->labelStyle().scale() );
0196     connect( d->m_labelScale, SIGNAL(valueChanged(double)), this, SLOT(updateTextAnnotation()) );
0197 
0198 
0199     // Adjust the current color of the two push buttons' pixmap to resemble the label and icon colors.
0200     const GeoDataLabelStyle labelStyle = placemark->style()->labelStyle();
0201     const GeoDataIconStyle iconStyle = placemark->style()->iconStyle();
0202 
0203     QPixmap labelPixmap( d->m_labelButton->iconSize().width(),
0204                          d->m_labelButton->iconSize().height() );
0205     labelPixmap.fill( labelStyle.color() );
0206     d->m_labelButton->setIcon( QIcon( labelPixmap ) );
0207 
0208     QPixmap iconPixmap( d->m_iconButton->iconSize().width(),
0209                         d->m_iconButton->iconSize().height() );
0210     iconPixmap.fill( iconStyle.color() );
0211     d->m_iconButton->setIcon( QIcon( iconPixmap ) );
0212 
0213     // Setup the color dialogs.
0214     d->m_labelColorDialog = new QColorDialog( this );
0215     d->m_labelColorDialog->setOption( QColorDialog::ShowAlphaChannel );
0216     d->m_labelColorDialog->setCurrentColor( labelStyle.color() );
0217     connect( d->m_labelButton, SIGNAL(clicked()), d->m_labelColorDialog, SLOT(exec()) );
0218     connect( d->m_labelColorDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updateLabelDialog(QColor)) );
0219     connect( d->m_labelColorDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updateTextAnnotation()) );
0220 
0221     d->m_iconColorDialog = new QColorDialog( this );
0222     d->m_iconColorDialog->setOption( QColorDialog::ShowAlphaChannel );
0223     d->m_iconColorDialog->setCurrentColor( iconStyle.color() );
0224     connect( d->m_iconButton, SIGNAL(clicked()), d->m_iconColorDialog, SLOT(exec()) );
0225     connect( d->m_iconColorDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updateIconDialog(QColor)) );
0226     connect( d->m_iconColorDialog, SIGNAL(colorSelected(QColor)), this, SLOT(updateTextAnnotation()) );
0227 
0228     connect( d->m_isBalloonVisible, SIGNAL(toggled(bool)), this, SLOT(updateTextAnnotation()) );
0229 
0230     // Promote "Ok" button to default button.
0231     d->buttonBox->button( QDialogButtonBox::Ok )->setDefault( true );
0232 
0233     connect( d->buttonBox->button( QDialogButtonBox::Ok ), SIGNAL(pressed()), this, SLOT(checkFields()) );
0234     connect( this, SIGNAL(accepted()), SLOT(updateTextAnnotation()) );
0235     connect( this, SIGNAL(accepted()), SLOT(updatePlacemarkAltitude()) );
0236     connect( this, SIGNAL(finished(int)), SLOT(restoreInitial(int)) );
0237 
0238     // Ensure that the dialog gets deleted when closing it (either when clicking OK or
0239     // Close).
0240     connect( this, SIGNAL(finished(int)), SLOT(deleteLater()) );
0241 }
0242 
0243 EditPlacemarkDialog::~EditPlacemarkDialog()
0244 {
0245     delete d;
0246 }
0247 
0248 void EditPlacemarkDialog::setLabelColor( const QColor &color )
0249 {
0250     d->m_labelColorDialog->setCurrentColor(color);
0251     updateLabelDialog(color);
0252 }
0253 
0254 QStringList EditPlacemarkDialog::idFilter() const
0255 {
0256     return d->m_header->idFilter();
0257 }
0258 
0259 QStringList EditPlacemarkDialog::targetIds() const
0260 {
0261     return d->m_header->targetIdList();
0262 }
0263 
0264 bool EditPlacemarkDialog::isTargetIdFieldVisible() const
0265 {
0266     return d->m_header->isTargetIdVisible();
0267 }
0268 
0269 bool EditPlacemarkDialog::isIdFieldVisible() const
0270 {
0271     return d->m_header->isIdVisible();
0272 }
0273 
0274 void EditPlacemarkDialog::updateDialogFields()
0275 {
0276     d->m_header->setLatitude( d->m_placemark->coordinate().latitude( GeoDataCoordinates::Degree ) );
0277     d->m_header->setLongitude( d->m_placemark->coordinate().longitude( GeoDataCoordinates::Degree ) );
0278 }
0279 
0280 void EditPlacemarkDialog::setIdFilter(const QStringList &filter)
0281 {
0282     d->m_header->setIdFilter( filter );
0283 }
0284 
0285 void EditPlacemarkDialog::setTargetIds(const QStringList &targetIds)
0286 {
0287     d->m_header->setTargetIdList( targetIds );
0288 }
0289 
0290 void EditPlacemarkDialog::setTargetIdFieldVisible(bool visible)
0291 {
0292     d->m_header->setTargetIdVisible( visible );
0293 }
0294 
0295 void EditPlacemarkDialog::setIdFieldVisible(bool visible)
0296 {
0297     d->m_header->setIdVisible( visible );
0298 }
0299 
0300 bool EditPlacemarkDialog::isReadOnly() const
0301 {
0302     return d->m_isReadOnly;
0303 }
0304 
0305 void EditPlacemarkDialog::setReadOnly(bool state)
0306 {
0307     d->m_isReadOnly = state;
0308     d->m_header->setReadOnly(state);
0309     d->m_formattedTextWidget->setReadOnly(state);
0310     d->m_isBalloonVisible->setDisabled(state);
0311     d->m_isPlacemarkVisible->setDisabled(state);
0312     d->style_color_tab->setDisabled(state);
0313 }
0314 
0315 void EditPlacemarkDialog::updateTextAnnotation()
0316 {
0317     if (isReadOnly()) return;
0318 
0319     d->m_placemark->setDescription( d->m_formattedTextWidget->text() );
0320     //allow for HTML in the description
0321     d->m_placemark->setDescriptionCDATA( true );
0322     d->m_placemark->setName( d->m_header->name() );
0323     d->m_placemark->setCoordinate( GeoDataCoordinates( d->m_header->longitude(),
0324                                                                          d->m_header->latitude(),
0325                                                                          0,
0326                                                                          GeoDataCoordinates::Degree ) );
0327 
0328     d->m_placemark->setVisible( d->m_isPlacemarkVisible->isChecked() );
0329     d->m_placemark->setBalloonVisible( d->m_isBalloonVisible->isChecked() );
0330     d->m_placemark->setId( d->m_header->id() );
0331     d->m_placemark->setTargetId( d->m_header->targetId() );
0332 
0333 
0334     if ( !d->m_header->iconLink().isEmpty() ) {
0335         QFileInfo fileInfo( d->m_header->iconLink() );
0336 
0337         GeoDataStyle::Ptr newStyle(new GeoDataStyle( *d->m_placemark->style() ));
0338         if ( fileInfo.exists() ) {
0339             newStyle->iconStyle().setIconPath( d->m_header->iconLink() );
0340         }
0341 
0342         newStyle->iconStyle().setScale( d->m_iconScale->value() );
0343         newStyle->labelStyle().setScale( d->m_labelScale->value() );
0344         newStyle->iconStyle().setColor( d->m_iconColorDialog->currentColor() );
0345         newStyle->labelStyle().setColor( d->m_labelColorDialog->currentColor() );
0346         d->m_placemark->setStyle( newStyle );
0347     }
0348     else {
0349         const OsmPlacemarkData osmData = d->m_osmTagEditorWidget->placemarkData();
0350         const GeoDataPlacemark::GeoDataVisualCategory category = StyleBuilder::determineVisualCategory(osmData);
0351         if (category != GeoDataPlacemark::None) {
0352             d->m_placemark->setStyle(GeoDataStyle::Ptr());
0353             d->m_placemark->setVisualCategory( category );
0354         }
0355     }
0356 
0357     emit textAnnotationUpdated( d->m_placemark );
0358 }
0359 
0360 void EditPlacemarkDialog::checkFields()
0361 {
0362     if (isReadOnly()) {
0363         accept();
0364         return;
0365     }
0366 
0367     if ( d->m_header->name().isEmpty() ) {
0368         QMessageBox::warning( this,
0369                               tr( "No name specified" ),
0370                               tr( "Please specify a name for this placemark." ) );
0371     } else if ( isIdFieldVisible() && d->m_header->id().isEmpty() ) {
0372         QMessageBox::warning( this,
0373                               tr( "No ID specified" ),
0374                               tr( "Please specify a ID for this placemark." ) );
0375     } else if ( isIdFieldVisible() && !d->m_header->isIdValid() ) {
0376         QMessageBox::warning( this,
0377                               tr( "ID is invalid" ),
0378                               tr( "Please specify a valid ID for this placemark." ) );
0379     } else if (d->m_header->iconLink().isEmpty() && d->m_placemark->visualCategory() == GeoDataPlacemark::None) {
0380         QMessageBox::warning( this,
0381                               tr( "No image specified" ),
0382                               tr( "Please specify an icon for this placemark or add a valid tag." ) );
0383     } else if( !d->m_header->iconLink().isEmpty() && !QFileInfo( d->m_header->iconLink() ).exists() ) {
0384         QMessageBox::warning( this,
0385                               tr( "Invalid icon path" ),
0386                               tr( "Please specify a valid path for the icon file." ) );
0387     } else {
0388         accept();
0389     }
0390 }
0391 
0392 void EditPlacemarkDialog::updateLabelDialog( const QColor &color )
0393 {
0394     if (isReadOnly()) return;
0395 
0396     QPixmap labelPixmap( d->m_labelButton->iconSize().width(),
0397                          d->m_labelButton->iconSize().height() );
0398     labelPixmap.fill( color );
0399     d->m_labelButton->setIcon( QIcon( labelPixmap ) );
0400 }
0401 
0402 void EditPlacemarkDialog::updateIconDialog( const QColor &color )
0403 {
0404     if (isReadOnly()) return;
0405 
0406     QPixmap iconPixmap( d->m_iconButton->iconSize().width(),
0407                         d->m_iconButton->iconSize().height() );
0408     iconPixmap.fill( color );
0409     d->m_iconButton->setIcon( QIcon( iconPixmap ) );
0410 }
0411 
0412 void EditPlacemarkDialog::updatePlacemarkAltitude()
0413 {
0414     if (isReadOnly()) return;
0415 
0416     GeoDataCoordinates coord = d->m_placemark->coordinate();
0417     qreal altitude = d->m_elevationWidget->elevationSpinBox->value();
0418 
0419     switch (d->m_elevationUnit) {
0420     case MarbleLocale::Meter:
0421         break;
0422     case MarbleLocale::Milimeter:
0423         altitude *= MM2M;
0424         break;
0425     case MarbleLocale::Kilometer:
0426         altitude *= KM2METER;
0427         break;
0428     case MarbleLocale::Centimeter:
0429         altitude *= CM2M;
0430         break;
0431     case MarbleLocale::Foot:
0432         altitude *= FT2M;
0433         break;
0434     case MarbleLocale::Inch:
0435         altitude *= IN2M;
0436         break;
0437     case MarbleLocale::Yard:
0438         altitude *= YD2M;
0439         break;
0440     case MarbleLocale::Mile:
0441         altitude *= MI2KM * KM2METER;
0442         break;
0443     case MarbleLocale::NauticalMile:
0444         altitude *= NM2KM * KM2METER;
0445         break;
0446     default:
0447         break;
0448     }
0449 
0450     coord.setAltitude(altitude);
0451     d->m_placemark->setCoordinate(coord);
0452 }
0453 
0454 void EditPlacemarkDialog::restoreInitial( int result )
0455 {
0456     if ( isReadOnly() || result ) {
0457         return;
0458     }
0459 
0460     if ( d->m_placemark->name() != d->m_initialName ) {
0461         d->m_placemark->setName( d->m_initialName );
0462     }
0463 
0464     if ( isIdFieldVisible() && d->m_placemark->id() != d->m_initialId ) {
0465         d->m_placemark->setId( d->m_initialId );
0466     }
0467 
0468     if ( isTargetIdFieldVisible() && d->m_placemark->targetId() != d->m_initialTargetId ) {
0469         d->m_placemark->setTargetId( d->m_initialTargetId );
0470     }
0471 
0472     if ( d->m_placemark->description() != d->m_initialDescription ) {
0473         d->m_placemark->setDescription( d->m_initialDescription );
0474     }
0475 
0476     if ( d->m_placemark->descriptionIsCDATA() != d->m_initialDescriptionIsCDATA ) {
0477         d->m_placemark->setDescriptionCDATA( d->m_initialDescriptionIsCDATA );
0478     }
0479 
0480     if ( d->m_placemark->coordinate().latitude( GeoDataCoordinates::Degree ) !=
0481          d->m_initialCoords.latitude( GeoDataCoordinates::Degree ) ||
0482          d->m_placemark->coordinate().longitude( GeoDataCoordinates::Degree ) !=
0483          d->m_initialCoords.longitude( GeoDataCoordinates::Degree ) ) {
0484         d->m_placemark->setCoordinate( d->m_initialCoords );
0485     }
0486 
0487     if ( d->m_placemark->visualCategory() != d->m_initialVisualCategory ) {
0488         d->m_placemark->setVisualCategory( d->m_initialVisualCategory );
0489     }
0490 
0491     if ( *d->m_placemark->style() != d->m_initialStyle ) {
0492         d->m_placemark->setStyle( GeoDataStyle::Ptr(new GeoDataStyle( d->m_initialStyle )) );
0493     }
0494 
0495     if( d->m_placemark->isVisible() != d->m_initialIsPlacemarkVisible ) {
0496         d->m_placemark->setVisible( d->m_initialIsPlacemarkVisible );
0497     }
0498 
0499     if( d->m_hadInitialOsmData ) {
0500         d->m_placemark->setOsmData( d->m_initialOsmData );
0501     }
0502 
0503     if( d->m_placemark->isBalloonVisible() != d->m_initialIsBaloonVisible ) {
0504         d->m_placemark->setVisible( d->m_initialIsBaloonVisible );
0505     }
0506 
0507     emit textAnnotationUpdated( d->m_placemark );
0508 }
0509 
0510 }
0511 
0512 #include "moc_EditPlacemarkDialog.cpp"