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
236void EditPlacemarkDialog::setLabelColor(const QColor &color)
237{
238 d->m_labelColorDialog->setCurrentColor(color);
239 updateLabelDialog(color);
240}
241
242QStringList EditPlacemarkDialog::idFilter() const
243{
244 return d->m_header->idFilter();
245}
246
247QStringList EditPlacemarkDialog::targetIds() const
248{
249 return d->m_header->targetIdList();
250}
251
252bool EditPlacemarkDialog::isTargetIdFieldVisible() const
253{
254 return d->m_header->isTargetIdVisible();
255}
256
257bool EditPlacemarkDialog::isIdFieldVisible() const
258{
259 return d->m_header->isIdVisible();
260}
261
262void EditPlacemarkDialog::updateDialogFields()
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
268void EditPlacemarkDialog::setIdFilter(const QStringList &filter)
269{
270 d->m_header->setIdFilter(filter);
271}
272
273void EditPlacemarkDialog::setTargetIds(const QStringList &targetIds)
274{
275 d->m_header->setTargetIdList(targetIds);
276}
277
278void EditPlacemarkDialog::setTargetIdFieldVisible(bool visible)
279{
280 d->m_header->setTargetIdVisible(visible);
281}
282
283void EditPlacemarkDialog::setIdFieldVisible(bool visible)
284{
285 d->m_header->setIdVisible(visible);
286}
287
288bool EditPlacemarkDialog::isReadOnly() const
289{
290 return d->m_isReadOnly;
291}
292
293void EditPlacemarkDialog::setReadOnly(bool state)
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.
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 const
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-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:43 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.