Marble

EditPlacemarkDialog.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2014 Calin Cruceru <crucerucalincristian@gmail.com>
4// SPDX-FileCopyrightText: 2015 Ilya Kowalewski <illya.kovalevskyy@gmail.com>
5//
6
7// self
8#include "EditPlacemarkDialog.h"
9#include "ui_EditPlacemarkDialog.h"
10#include "ui_ElevationWidget.h"
11
12// Qt
13#include <QCheckBox>
14#include <QColorDialog>
15#include <QFileInfo>
16#include <QMessageBox>
17
18// Marble
19#include "FormattedTextWidget.h"
20#include "GeoDataIconStyle.h"
21#include "GeoDataLabelStyle.h"
22#include "GeoDataPlacemark.h"
23#include "GeoDataStyle.h"
24#include "MarbleLocale.h"
25#include "MarbleWidget.h"
26#include "StyleBuilder.h"
27#include "osm/OsmPlacemarkData.h"
28#include "osm/OsmRelationManagerWidget.h"
29#include "osm/OsmTagEditorWidget.h"
30
31namespace Marble
32{
33
34class Q_DECL_HIDDEN EditPlacemarkDialog::Private : public Ui::UiEditPlacemarkDialog
35{
36public:
37 Private(GeoDataPlacemark *placemark);
38 ~Private();
39
40 GeoDataPlacemark *m_placemark;
41
42 // Attached to label/icon/text color selectors.
43 QColorDialog *m_iconColorDialog;
44 QColorDialog *m_labelColorDialog;
45
46 // Used to restore if the Cancel button is pressed.
47 QString m_initialDescription;
48 QString m_initialName;
49 GeoDataCoordinates m_initialCoords;
50 GeoDataStyle m_initialStyle;
51 GeoDataPlacemark::GeoDataVisualCategory m_initialVisualCategory;
52 OsmPlacemarkData m_initialOsmData;
53 QString m_styleColorTabName;
54 bool m_initialIsPlacemarkVisible;
55 bool m_initialIsBaloonVisible;
56 bool m_initialDescriptionIsCDATA;
57 bool m_hadInitialOsmData;
58 QString m_initialId;
59 Ui::ElevationWidget *m_elevationWidget;
60 OsmTagEditorWidget *m_osmTagEditorWidget;
61 OsmRelationManagerWidget *m_osmRelationManagerWidget;
62 MarbleLocale::MeasureUnit m_elevationUnit;
63 QString m_initialTargetId;
64 bool m_isReadOnly;
65};
66
67EditPlacemarkDialog::Private::Private(GeoDataPlacemark *placemark)
68 : Ui::UiEditPlacemarkDialog()
69 , m_placemark(placemark)
70 , m_iconColorDialog(nullptr)
71 , m_labelColorDialog(nullptr)
72 , m_osmTagEditorWidget(nullptr)
73 , m_osmRelationManagerWidget(nullptr)
74 , m_isReadOnly(false)
75{
76 // nothing to do
77}
78
79EditPlacemarkDialog::Private::~Private()
80{
81 delete m_elevationWidget;
82 delete m_osmTagEditorWidget;
83 delete m_osmRelationManagerWidget;
84 delete m_iconColorDialog;
85 delete m_labelColorDialog;
86}
87
88EditPlacemarkDialog::EditPlacemarkDialog(GeoDataPlacemark *placemark, const QHash<qint64, OsmPlacemarkData> *relations, QWidget *parent)
89 : QDialog(parent)
90 , d(new Private(placemark))
91{
92 d->setupUi(this);
93
94 // Store initial style so that it can be restored if the 'Cancel' button is pressed.
95 d->m_initialStyle = *placemark->style();
96
97 d->m_initialVisualCategory = placemark->visualCategory();
98
99 d->m_hadInitialOsmData = placemark->hasOsmData();
100 if (d->m_hadInitialOsmData) {
101 d->m_initialOsmData = placemark->osmData();
102 }
103
104 // If the placemark has just been created, assign it a default name.
105 if (placemark->name().isNull()) {
106 placemark->setName(tr("Untitled Placemark"));
107 }
108 // Setup id, name, icon link and latitude/longitude values.
109 d->m_header->setId(placemark->id());
110 d->m_initialId = placemark->id();
111 d->m_header->setName(placemark->name());
112 d->m_initialName = placemark->name();
113 d->m_isPlacemarkVisible->setChecked(placemark->isVisible());
114 d->m_initialIsPlacemarkVisible = placemark->isVisible();
115 d->m_header->setIconLink(placemark->style()->iconStyle().iconPath());
116 d->m_header->setTargetId(placemark->targetId());
117 d->m_initialTargetId = placemark->targetId();
118 auto marbleWidget = dynamic_cast<MarbleWidget *>(parent);
119 if (marbleWidget != nullptr) {
120 const AngleUnit defaultAngleUnit = marbleWidget->defaultAngleUnit();
121 const GeoDataCoordinates::Notation notation = (defaultAngleUnit == DecimalDegree) ? GeoDataCoordinates::Decimal
122 : (defaultAngleUnit == DMSDegree) ? GeoDataCoordinates::DMS
123 :
124 /* else, UTM */ GeoDataCoordinates::UTM;
125 d->m_header->setNotation(notation);
126 }
127 connect(d->m_header, SIGNAL(valueChanged()), this, SLOT(updateTextAnnotation()));
128
129 d->m_formattedTextWidget->setText(placemark->description());
130 d->m_initialDescription = placemark->description();
131 d->m_initialDescriptionIsCDATA = placemark->descriptionIsCDATA();
132 d->m_isBalloonVisible->setChecked(placemark->isBalloonVisible());
133 d->m_initialIsBaloonVisible = placemark->isBalloonVisible();
134
135 d->m_header->setLatitude(placemark->coordinate().latitude(GeoDataCoordinates::Degree));
136 d->m_header->setLongitude(placemark->coordinate().longitude(GeoDataCoordinates::Degree));
137 d->m_initialCoords = GeoDataCoordinates(d->m_header->longitude(), d->m_header->latitude(), 0, GeoDataCoordinates::Degree);
138
139 // There's no point showing Relations and Tags tabs if the editor was not
140 // loaded from the annotate plugin ( loaded from tourWidget.. )
141 if (relations) {
142 // Adding the osm tag editor widget tab
143 d->m_osmTagEditorWidget = new OsmTagEditorWidget(placemark, this);
144 d->tabWidget->addTab(d->m_osmTagEditorWidget, tr("Tags"));
145 connect(d->m_osmTagEditorWidget, &OsmTagEditorWidget::placemarkChanged, this, &EditPlacemarkDialog::updateTextAnnotation);
146
147 // Adding the osm relation editor widget tab
148 d->m_osmRelationManagerWidget = new OsmRelationManagerWidget(placemark, relations, this);
149 d->tabWidget->addTab(d->m_osmRelationManagerWidget, tr("Relations"));
150 connect(d->m_osmRelationManagerWidget, &OsmRelationManagerWidget::relationCreated, this, &EditPlacemarkDialog::relationCreated);
151 }
152
153 // Adding the elevation widget tab
154 d->m_elevationWidget = new Ui::ElevationWidget;
155 auto elevationTab = new QWidget;
156 d->m_elevationWidget->setupUi(elevationTab);
157 d->tabWidget->addTab(elevationTab, tr("Elevation"));
158 qreal altitude = d->m_placemark->coordinate().altitude();
159 MarbleLocale *locale = MarbleGlobal::getInstance()->locale();
160 if (altitude == 0.0) {
161 switch (locale->measurementSystem()) {
162 case MarbleLocale::MetricSystem:
163 d->m_elevationUnit = MarbleLocale::Meter;
164 break;
165 case MarbleLocale::ImperialSystem:
166 d->m_elevationUnit = MarbleLocale::Foot;
167 break;
168 case MarbleLocale::NauticalSystem:
169 d->m_elevationUnit = MarbleLocale::NauticalMile;
170 break;
171 }
172
173 d->m_elevationWidget->elevationSpinBox->setSuffix(locale->unitAbbreviation((d->m_elevationUnit)));
174 } else {
175 qreal convertedAltitude;
176 const MarbleLocale::MeasurementSystem currentSystem = locale->measurementSystem();
177 locale->meterToTargetUnit(altitude, currentSystem, convertedAltitude, d->m_elevationUnit);
178 d->m_elevationWidget->elevationSpinBox->setValue(convertedAltitude);
179 d->m_elevationWidget->elevationSpinBox->setSuffix(locale->unitAbbreviation(d->m_elevationUnit));
180 }
181
182 // Adjust icon and label scales.
183 d->m_iconScale->setValue(placemark->style()->iconStyle().scale());
184 connect(d->m_iconScale, &QDoubleSpinBox::valueChanged, this, &EditPlacemarkDialog::updateTextAnnotation);
185
186 d->m_labelScale->setValue(placemark->style()->labelStyle().scale());
187 connect(d->m_labelScale, &QDoubleSpinBox::valueChanged, this, &EditPlacemarkDialog::updateTextAnnotation);
188
189 // Adjust the current color of the two push buttons' pixmap to resemble the label and icon colors.
190 const GeoDataLabelStyle labelStyle = placemark->style()->labelStyle();
191 const GeoDataIconStyle iconStyle = placemark->style()->iconStyle();
192
193 QPixmap labelPixmap(d->m_labelButton->iconSize().width(), d->m_labelButton->iconSize().height());
194 labelPixmap.fill(labelStyle.color());
195 d->m_labelButton->setIcon(QIcon(labelPixmap));
196
197 QPixmap iconPixmap(d->m_iconButton->iconSize().width(), d->m_iconButton->iconSize().height());
198 iconPixmap.fill(iconStyle.color());
199 d->m_iconButton->setIcon(QIcon(iconPixmap));
200
201 // Setup the color dialogs.
202 d->m_labelColorDialog = new QColorDialog(this);
203 d->m_labelColorDialog->setOption(QColorDialog::ShowAlphaChannel);
204 d->m_labelColorDialog->setCurrentColor(labelStyle.color());
205 connect(d->m_labelButton, &QAbstractButton::clicked, d->m_labelColorDialog, &QDialog::exec);
206 connect(d->m_labelColorDialog, &QColorDialog::colorSelected, this, &EditPlacemarkDialog::updateLabelDialog);
207 connect(d->m_labelColorDialog, &QColorDialog::colorSelected, this, &EditPlacemarkDialog::updateTextAnnotation);
208
209 d->m_iconColorDialog = new QColorDialog(this);
210 d->m_iconColorDialog->setOption(QColorDialog::ShowAlphaChannel);
211 d->m_iconColorDialog->setCurrentColor(iconStyle.color());
212 connect(d->m_iconButton, &QAbstractButton::clicked, d->m_iconColorDialog, &QDialog::exec);
213 connect(d->m_iconColorDialog, &QColorDialog::colorSelected, this, &EditPlacemarkDialog::updateIconDialog);
214 connect(d->m_iconColorDialog, &QColorDialog::colorSelected, this, &EditPlacemarkDialog::updateTextAnnotation);
215
216 connect(d->m_isBalloonVisible, &QAbstractButton::toggled, this, &EditPlacemarkDialog::updateTextAnnotation);
217
218 // Promote "Ok" button to default button.
219 d->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
220
221 connect(d->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::pressed, this, &EditPlacemarkDialog::checkFields);
222 connect(this, &QDialog::accepted, this, &EditPlacemarkDialog::updateTextAnnotation);
223 connect(this, &QDialog::accepted, this, &EditPlacemarkDialog::updatePlacemarkAltitude);
224 connect(this, &QDialog::finished, this, &EditPlacemarkDialog::restoreInitial);
225
226 // Ensure that the dialog gets deleted when closing it (either when clicking OK or
227 // Close).
229}
230
231EditPlacemarkDialog::~EditPlacemarkDialog()
232{
233 delete d;
234}
235
237{
238 d->m_labelColorDialog->setCurrentColor(color);
239 updateLabelDialog(color);
240}
241
243{
244 return d->m_header->idFilter();
245}
246
248{
249 return d->m_header->targetIdList();
250}
251
253{
254 return d->m_header->isTargetIdVisible();
255}
256
258{
259 return d->m_header->isIdVisible();
260}
261
263{
264 d->m_header->setLatitude(d->m_placemark->coordinate().latitude(GeoDataCoordinates::Degree));
265 d->m_header->setLongitude(d->m_placemark->coordinate().longitude(GeoDataCoordinates::Degree));
266}
267
269{
270 d->m_header->setIdFilter(filter);
271}
272
274{
275 d->m_header->setTargetIdList(targetIds);
276}
277
279{
280 d->m_header->setTargetIdVisible(visible);
281}
282
284{
285 d->m_header->setIdVisible(visible);
286}
287
289{
290 return d->m_isReadOnly;
291}
292
294{
295 d->m_isReadOnly = state;
296 d->m_header->setReadOnly(state);
297 d->m_formattedTextWidget->setReadOnly(state);
298 d->m_isBalloonVisible->setDisabled(state);
299 d->m_isPlacemarkVisible->setDisabled(state);
300 d->style_color_tab->setDisabled(state);
301}
302
303void EditPlacemarkDialog::updateTextAnnotation()
304{
305 if (isReadOnly())
306 return;
307
308 d->m_placemark->setDescription(d->m_formattedTextWidget->text());
309 // allow for HTML in the description
310 d->m_placemark->setDescriptionCDATA(true);
311 d->m_placemark->setName(d->m_header->name());
312 d->m_placemark->setCoordinate(GeoDataCoordinates(d->m_header->longitude(), d->m_header->latitude(), 0, GeoDataCoordinates::Degree));
313
314 d->m_placemark->setVisible(d->m_isPlacemarkVisible->isChecked());
315 d->m_placemark->setBalloonVisible(d->m_isBalloonVisible->isChecked());
316 d->m_placemark->setId(d->m_header->id());
317 d->m_placemark->setTargetId(d->m_header->targetId());
318
319 if (!d->m_header->iconLink().isEmpty()) {
320 GeoDataStyle::Ptr newStyle(new GeoDataStyle(*d->m_placemark->style()));
321 if (QFileInfo::exists(d->m_header->iconLink())) {
322 newStyle->iconStyle().setIconPath(d->m_header->iconLink());
323 }
324
325 newStyle->iconStyle().setScale(d->m_iconScale->value());
326 newStyle->labelStyle().setScale(d->m_labelScale->value());
327 newStyle->iconStyle().setColor(d->m_iconColorDialog->currentColor());
328 newStyle->labelStyle().setColor(d->m_labelColorDialog->currentColor());
329 d->m_placemark->setStyle(newStyle);
330 } else {
331 const OsmPlacemarkData osmData = d->m_osmTagEditorWidget->placemarkData();
332 const GeoDataPlacemark::GeoDataVisualCategory category = StyleBuilder::determineVisualCategory(osmData);
333 if (category != GeoDataPlacemark::None) {
334 d->m_placemark->setStyle(GeoDataStyle::Ptr());
335 d->m_placemark->setVisualCategory(category);
336 }
337 }
338
339 Q_EMIT textAnnotationUpdated(d->m_placemark);
340}
341
342void EditPlacemarkDialog::checkFields()
343{
344 if (isReadOnly()) {
345 accept();
346 return;
347 }
348
349 if (d->m_header->name().isEmpty()) {
350 QMessageBox::warning(this, tr("No name specified"), tr("Please specify a name for this placemark."));
351 } else if (isIdFieldVisible() && d->m_header->id().isEmpty()) {
352 QMessageBox::warning(this, tr("No ID specified"), tr("Please specify a ID for this placemark."));
353 } else if (isIdFieldVisible() && !d->m_header->isIdValid()) {
354 QMessageBox::warning(this, tr("ID is invalid"), tr("Please specify a valid ID for this placemark."));
355 } else if (d->m_header->iconLink().isEmpty() && d->m_placemark->visualCategory() == GeoDataPlacemark::None) {
356 QMessageBox::warning(this, tr("No image specified"), tr("Please specify an icon for this placemark or add a valid tag."));
357 } else if (!d->m_header->iconLink().isEmpty() && !QFileInfo::exists(d->m_header->iconLink())) {
358 QMessageBox::warning(this, tr("Invalid icon path"), tr("Please specify a valid path for the icon file."));
359 } else {
360 accept();
361 }
362}
363
364void EditPlacemarkDialog::updateLabelDialog(const QColor &color)
365{
366 if (isReadOnly())
367 return;
368
369 QPixmap labelPixmap(d->m_labelButton->iconSize().width(), d->m_labelButton->iconSize().height());
370 labelPixmap.fill(color);
371 d->m_labelButton->setIcon(QIcon(labelPixmap));
372}
373
374void EditPlacemarkDialog::updateIconDialog(const QColor &color)
375{
376 if (isReadOnly())
377 return;
378
379 QPixmap iconPixmap(d->m_iconButton->iconSize().width(), d->m_iconButton->iconSize().height());
380 iconPixmap.fill(color);
381 d->m_iconButton->setIcon(QIcon(iconPixmap));
382}
383
384void EditPlacemarkDialog::updatePlacemarkAltitude()
385{
386 if (isReadOnly())
387 return;
388
389 GeoDataCoordinates coord = d->m_placemark->coordinate();
390 qreal altitude = d->m_elevationWidget->elevationSpinBox->value();
391
392 switch (d->m_elevationUnit) {
393 case MarbleLocale::Meter:
394 break;
395 case MarbleLocale::Milimeter:
396 altitude *= MM2M;
397 break;
398 case MarbleLocale::Kilometer:
399 altitude *= KM2METER;
400 break;
401 case MarbleLocale::Centimeter:
402 altitude *= CM2M;
403 break;
404 case MarbleLocale::Foot:
405 altitude *= FT2M;
406 break;
407 case MarbleLocale::Inch:
408 altitude *= IN2M;
409 break;
410 case MarbleLocale::Yard:
411 altitude *= YD2M;
412 break;
413 case MarbleLocale::Mile:
414 altitude *= MI2KM * KM2METER;
415 break;
416 case MarbleLocale::NauticalMile:
417 altitude *= NM2KM * KM2METER;
418 break;
419 default:
420 break;
421 }
422
423 coord.setAltitude(altitude);
424 d->m_placemark->setCoordinate(coord);
425}
426
427void EditPlacemarkDialog::restoreInitial(int result)
428{
429 if (isReadOnly() || result) {
430 return;
431 }
432
433 if (d->m_placemark->name() != d->m_initialName) {
434 d->m_placemark->setName(d->m_initialName);
435 }
436
437 if (isIdFieldVisible() && d->m_placemark->id() != d->m_initialId) {
438 d->m_placemark->setId(d->m_initialId);
439 }
440
441 if (isTargetIdFieldVisible() && d->m_placemark->targetId() != d->m_initialTargetId) {
442 d->m_placemark->setTargetId(d->m_initialTargetId);
443 }
444
445 if (d->m_placemark->description() != d->m_initialDescription) {
446 d->m_placemark->setDescription(d->m_initialDescription);
447 }
448
449 if (d->m_placemark->descriptionIsCDATA() != d->m_initialDescriptionIsCDATA) {
450 d->m_placemark->setDescriptionCDATA(d->m_initialDescriptionIsCDATA);
451 }
452
453 if (d->m_placemark->coordinate().latitude(GeoDataCoordinates::Degree) != d->m_initialCoords.latitude(GeoDataCoordinates::Degree)
454 || d->m_placemark->coordinate().longitude(GeoDataCoordinates::Degree) != d->m_initialCoords.longitude(GeoDataCoordinates::Degree)) {
455 d->m_placemark->setCoordinate(d->m_initialCoords);
456 }
457
458 if (d->m_placemark->visualCategory() != d->m_initialVisualCategory) {
459 d->m_placemark->setVisualCategory(d->m_initialVisualCategory);
460 }
461
462 if (*d->m_placemark->style() != d->m_initialStyle) {
463 d->m_placemark->setStyle(GeoDataStyle::Ptr(new GeoDataStyle(d->m_initialStyle)));
464 }
465
466 if (d->m_placemark->isVisible() != d->m_initialIsPlacemarkVisible) {
467 d->m_placemark->setVisible(d->m_initialIsPlacemarkVisible);
468 }
469
470 if (d->m_hadInitialOsmData) {
471 d->m_placemark->setOsmData(d->m_initialOsmData);
472 }
473
474 if (d->m_placemark->isBalloonVisible() != d->m_initialIsBaloonVisible) {
475 d->m_placemark->setVisible(d->m_initialIsBaloonVisible);
476 }
477
478 Q_EMIT textAnnotationUpdated(d->m_placemark);
479}
480
481}
482
483#include "moc_EditPlacemarkDialog.cpp"
This file contains the headers for MarbleWidget.
A 3d point representation.
void setAltitude(const qreal altitude)
set the altitude of the Point in meters
Notation
enum used to specify the notation / numerical system
@ Decimal
"Decimal" notation (base-10)
The EditPlacemarkDialog class deals with customizing placemarks.
void setLabelColor(const QColor &color)
setLabelColor tells the dialog what the label color is
void setTargetIdFieldVisible(bool visible)
setTargetIdFieldVisible tells the dialog whether targetId field should be shown.
void setReadOnly(bool state)
Protecting data from input fields changes.
bool isIdFieldVisible() const
isIdFieldVisible tells if targetId field is shown.
void setIdFilter(const QStringList &filter)
setIdFilter sets filter for id of placemark.
void setTargetIds(const QStringList &targetIds)
setTargetIds sets ids which could be target of placemark.
bool isReadOnly() const
isReadOnly tells whether the data from input fields is protected
void updateDialogFields()
updateDialogFields is connected to a signal from AnnotatePlugin in order to update some fields in the...
bool isTargetIdFieldVisible() const
isTargetIdFieldVisible tells if targetId field is shown.
QStringList idFilter() const
idFilter gets filter for id of placemark
QStringList targetIds() const
targetIds gets ids which could be target of placemark.
void setIdFieldVisible(bool visible)
setIdFieldVisible tells the dialog whether id field should be shown.
A 3d point representation.
a class representing a point of interest on the map
an addressable style group
Category category(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
AngleUnit
This enum is used to choose the unit chosen to measure angles.
@ UTM
UTM.
@ DMSDegree
Degrees in DMS notation.
@ DecimalDegree
Degrees in decimal notation.
void clicked(bool checked)
void toggled(bool checked)
void colorSelected(const QColor &color)
void accepted()
virtual int exec()
void finished(int result)
void valueChanged(double d)
bool exists(const QString &path)
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
void deleteLater()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setupUi(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:52:08 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.