1 /* SPDX-FileCopyrightText: 2020-2021 Tobias Leupold <tobias.leupold@gmx.de>
2
3 SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
4 */
5
6 // Local includes
7 #include "BookmarksList.h"
8 #include "SharedObjects.h"
9 #include "Settings.h"
10 #include "MapWidget.h"
11 #include "CoordinatesDialog.h"
12
13 // KDE includes
14 #include <KLocalizedString>
15
16 // Qt includes
17 #include <QDebug>
18 #include <QMenu>
19 #include <QInputDialog>
20 #include <QMessageBox>
21 #include <QListWidgetItem>
22 #include <QApplication>
23
BookmarksList(SharedObjects * sharedObjects,QWidget * parent)24 BookmarksList::BookmarksList(SharedObjects *sharedObjects, QWidget *parent)
25 : QListWidget(parent),
26 m_settings(sharedObjects->settings()),
27 m_elevationEngine(sharedObjects->elevationEngine()),
28 m_mapWidget(sharedObjects->mapWidget())
29 {
30 setSortingEnabled(true);
31
32 // Load the saved bookmarks
33 m_bookmarks = m_settings->bookmarks();
34 const auto labels = m_bookmarks.keys();
35 for (const auto &label : labels) {
36 auto *item = new QListWidgetItem(label);
37 addItem(item);
38 }
39
40 // Show the first bookmark's coordinates if we have bookmarks
41 if (! m_bookmarks.isEmpty()) {
42 setCurrentItem(0);
43 }
44
45 connect(this, &QListWidget::itemClicked, this, &BookmarksList::centerBookmark);
46 connect(this, &QListWidget::currentItemChanged, this, &BookmarksList::itemHighlighted);
47
48 connect(m_elevationEngine, &ElevationEngine::elevationProcessed,
49 this, &BookmarksList::elevationProcessed);
50 connect(m_elevationEngine, &ElevationEngine::lookupFailed,
51 this, &BookmarksList::restoreAfterElevationLookup);
52
53 // Context menu
54
55 m_contextMenu = new QMenu(this);
56
57 auto *newBookmarkAction = m_contextMenu->addAction(i18n("Add new bookmark for current map "
58 "center"));
59 connect(newBookmarkAction, &QAction::triggered, this, &BookmarksList::newBookmark);
60
61 auto *newManualBookmarkAction = m_contextMenu->addAction(i18n("Add new manual bookmark"));
62 connect(newManualBookmarkAction, &QAction::triggered, this, &BookmarksList::newManualBookmark);
63
64 m_contextMenu->addSeparator();
65
66 m_renameBookmark = m_contextMenu->addAction(i18n("Rename bookmark"));
67 connect(m_renameBookmark, &QAction::triggered, this, &BookmarksList::renameBookmark);
68
69 m_editCoordinates = m_contextMenu->addAction(i18n("Edit coordinates"));
70 connect(m_editCoordinates, &QAction::triggered, this, &BookmarksList::editCoordinates);
71
72 m_contextMenu->addSeparator();
73
74 m_lookupElevation = m_contextMenu->addAction(i18n("Lookup elevation"));
75 connect(m_lookupElevation, &QAction::triggered, this, &BookmarksList::lookupElevation);
76
77 m_setElevation = m_contextMenu->addAction(i18n("Set elevation manually"));
78 connect(m_setElevation, &QAction::triggered, this, &BookmarksList::setElevation);
79
80 m_contextMenu->addSeparator();
81
82 m_deleteBookmark = m_contextMenu->addAction(i18n("Delete bookmark"));
83 connect(m_deleteBookmark, &QAction::triggered, this, &BookmarksList::deleteBookmark);
84
85 setContextMenuPolicy(Qt::CustomContextMenu);
86 connect(this, &QWidget::customContextMenuRequested, this, &BookmarksList::showContextMenu);
87 }
88
showContextMenu(const QPoint & point)89 void BookmarksList::showContextMenu(const QPoint &point)
90 {
91 m_contextMenuItem = itemAt(point);
92
93 const bool itemSelected = m_contextMenuItem != nullptr;
94 m_renameBookmark->setEnabled(itemSelected);
95 m_lookupElevation->setEnabled(itemSelected);
96 m_setElevation->setEnabled(itemSelected);
97 m_deleteBookmark->setEnabled(itemSelected);
98 m_editCoordinates->setEnabled(itemSelected);
99
100 m_contextMenu->exec(mapToGlobal(point));
101 }
102
newBookmark()103 void BookmarksList::newBookmark()
104 {
105 auto [ label, okay ] = getString(i18n("New Bookmark"), i18n("Label for the new bookmark:"));
106 if (! okay) {
107 return;
108 }
109
110 label = label.simplified();
111 const auto originalLabel = label;
112 if (label.isEmpty()) {
113 label = i18n("Untitled");
114 }
115
116 saveBookmark(label, m_mapWidget->currentCenter());
117 }
118
newManualBookmark()119 void BookmarksList::newManualBookmark()
120 {
121 CoordinatesDialog dialog(CoordinatesDialog::Mode::ManualBookmark,
122 m_settings->lookupElevationAutomatically());
123 if (! dialog.exec()) {
124 return;
125 }
126
127 saveBookmark(dialog.label(),
128 Coordinates(dialog.lon(), dialog.lat(), dialog.alt(), true));
129 }
130
saveBookmark(QString label,const Coordinates & coordinates)131 void BookmarksList::saveBookmark(QString label, const Coordinates &coordinates)
132 {
133 const QString originalLabel = label;
134 QString searchLabel = label;
135 int addition = 0;
136 while (! findItems(searchLabel, Qt::MatchExactly).isEmpty()) {
137 addition++;
138 searchLabel = i18nc("Adding a consecutive number to a bookmark label to make it unique",
139 "%1 (%2)", label, addition);
140 }
141 label = searchLabel;
142
143 m_bookmarks[label] = coordinates;
144
145 auto *item = new QListWidgetItem(label);
146 addItem(item);
147 setCurrentItem(item);
148
149 if (m_settings->lookupElevationAutomatically()) {
150 requestElevation(label);
151 }
152
153 emit bookmarksChanged();
154
155 if (label != originalLabel) {
156 QMessageBox::warning(this, i18n("Add new bookmark"),
157 i18n("The bookmark \"%1\" already exists, created \"%2\" instead.",
158 originalLabel, label));
159 }
160
161 m_mapWidget->centerCoordinates(coordinates);
162 }
163
requestElevation(const QString & id)164 void BookmarksList::requestElevation(const QString &id)
165 {
166 QApplication::setOverrideCursor(Qt::WaitCursor);
167 setEnabled(false);
168 m_elevationEngine->request(ElevationEngine::Target::Bookmark, { id },
169 { m_bookmarks.value(id) });
170 }
171
itemHighlighted(QListWidgetItem * item,QListWidgetItem *)172 void BookmarksList::itemHighlighted(QListWidgetItem *item, QListWidgetItem *)
173 {
174 if (item == nullptr) {
175 emit showInfo(Coordinates());
176 return;
177 }
178
179 emit showInfo(m_bookmarks.value(item->text()));
180 }
181
centerBookmark(QListWidgetItem * item)182 void BookmarksList::centerBookmark(QListWidgetItem *item)
183 {
184 m_mapWidget->centerCoordinates(m_bookmarks.value(item->text()));
185 }
186
getString(const QString & title,const QString & label,const QString & text)187 BookmarksList::EnteredString BookmarksList::getString(const QString &title, const QString &label,
188 const QString &text)
189 {
190 bool okay = false;
191 auto string = QInputDialog::getText(this, title, label, QLineEdit::Normal, text, &okay);
192 return { string, okay };
193 }
194
renameBookmark()195 void BookmarksList::renameBookmark()
196 {
197 const auto currentLabel = m_contextMenuItem->text();
198 auto [ newLabel, okay ] = getString(i18n("Rename Bookmark"),
199 i18n("New label for the new bookmark:"), currentLabel);
200 if (! okay || newLabel == currentLabel) {
201 return;
202 }
203
204 newLabel = newLabel.simplified();
205 if (! findItems(newLabel, Qt::MatchExactly).isEmpty()) {
206 QMessageBox::warning(this, i18n("Rename Bookmark"),
207 i18n("The label \"%1\" is already in use. Please choose another "
208 "name.", newLabel));
209 return;
210 }
211
212 m_bookmarks[newLabel] = m_bookmarks.value(currentLabel);
213 m_bookmarks.remove(currentLabel);
214 m_contextMenuItem->setText(newLabel);
215
216 emit bookmarksChanged();
217 }
218
deleteBookmark()219 void BookmarksList::deleteBookmark()
220 {
221 if (QMessageBox::question(this, i18n("Delete bookmark"),
222 i18n("Really delete bookmark \"%1\"?", m_contextMenuItem->text()),
223 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
224
225 return;
226 }
227
228 m_bookmarks.remove(m_contextMenuItem->text());
229 const auto *item = takeItem(row(m_contextMenuItem));
230 delete item;
231
232 emit bookmarksChanged();
233 }
234
elevationProcessed(ElevationEngine::Target target,const QVector<QString> & ids,const QVector<double> & elevations)235 void BookmarksList::elevationProcessed(ElevationEngine::Target target, const QVector<QString> &ids,
236 const QVector<double> &elevations)
237 {
238 if (target != ElevationEngine::Target::Bookmark) {
239 return;
240 }
241
242 restoreAfterElevationLookup();
243 const auto id = ids.at(0);
244 m_bookmarks[id].setAlt(elevations.at(0));
245 emit showInfo(m_bookmarks.value(id));
246 }
247
restoreAfterElevationLookup()248 void BookmarksList::restoreAfterElevationLookup()
249 {
250 if (! isEnabled()) {
251 QApplication::restoreOverrideCursor();
252 setEnabled(true);
253 }
254 }
255
lookupElevation()256 void BookmarksList::lookupElevation()
257 {
258 requestElevation(m_contextMenuItem->text());
259 }
260
setElevation()261 void BookmarksList::setElevation()
262 {
263 const auto id = m_contextMenuItem->text();
264
265 bool okay = false;
266 auto elevation = QInputDialog::getDouble(this, i18n("Set elevation"),
267 i18n("Elevation for \"%1\" (m)", id),
268 m_bookmarks.value(id).alt(), KGeoTag::minimalAltitude,
269 KGeoTag::maximalAltitude, 1, &okay);
270 if (! okay) {
271 return;
272 }
273
274 elevationProcessed(ElevationEngine::Target::Bookmark, { id }, { elevation });
275 }
276
bookmarks() const277 const QHash<QString, Coordinates> *BookmarksList::bookmarks() const
278 {
279 return &m_bookmarks;
280 }
281
editCoordinates()282 void BookmarksList::editCoordinates()
283 {
284 const auto id = m_contextMenuItem->text();
285 auto &coordinates = m_bookmarks[id];
286
287 CoordinatesDialog dialog(CoordinatesDialog::Mode::EditCoordinates, false, coordinates,
288 i18nc("A quoted bookmark label", "\"%1\"", id));
289 if (! dialog.exec()) {
290 return;
291 }
292
293 coordinates = dialog.coordinates();
294 m_mapWidget->centerCoordinates(coordinates);
295 emit showInfo(coordinates);
296 }
297