File indexing completed on 2024-04-28 15:39:05

0001 // SPDX-FileCopyrightText: 2020-2022 Tobias Leupold <tl at stonemx dot de>
0002 //
0003 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 // Local includes
0006 #include "BookmarksList.h"
0007 #include "SharedObjects.h"
0008 #include "Settings.h"
0009 #include "MapWidget.h"
0010 #include "CoordinatesDialog.h"
0011 
0012 // KDE includes
0013 #include <KLocalizedString>
0014 
0015 // Qt includes
0016 #include <QDebug>
0017 #include <QMenu>
0018 #include <QInputDialog>
0019 #include <QMessageBox>
0020 #include <QListWidgetItem>
0021 #include <QApplication>
0022 
0023 BookmarksList::BookmarksList(SharedObjects *sharedObjects, QWidget *parent)
0024     : QListWidget(parent),
0025       m_settings(sharedObjects->settings()),
0026       m_elevationEngine(sharedObjects->elevationEngine()),
0027       m_mapWidget(sharedObjects->mapWidget())
0028 {
0029     setSortingEnabled(true);
0030 
0031     // Load the saved bookmarks
0032     m_bookmarks = m_settings->bookmarks();
0033     const auto labels = m_bookmarks.keys();
0034     for (const auto &label : labels) {
0035         auto *item = new QListWidgetItem(label);
0036         addItem(item);
0037     }
0038 
0039     // Show the first bookmark's coordinates if we have bookmarks
0040     if (! m_bookmarks.isEmpty()) {
0041         setCurrentItem(0);
0042     }
0043 
0044     connect(this, &QListWidget::itemClicked, this, &BookmarksList::centerBookmark);
0045     connect(this, &QListWidget::currentItemChanged, this, &BookmarksList::itemHighlighted);
0046 
0047     connect(m_elevationEngine, &ElevationEngine::elevationProcessed,
0048             this, &BookmarksList::elevationProcessed);
0049     connect(m_elevationEngine, &ElevationEngine::lookupFailed,
0050             this, &BookmarksList::restoreAfterElevationLookup);
0051 
0052     // Context menu
0053 
0054     m_contextMenu = new QMenu(this);
0055 
0056     auto *newBookmarkAction = m_contextMenu->addAction(i18n("Add new bookmark for current map "
0057                                                             "center"));
0058     connect(newBookmarkAction, &QAction::triggered, this, &BookmarksList::newBookmark);
0059 
0060     auto *newManualBookmarkAction = m_contextMenu->addAction(i18n("Add new manual bookmark"));
0061     connect(newManualBookmarkAction, &QAction::triggered, this, &BookmarksList::newManualBookmark);
0062 
0063     m_contextMenu->addSeparator();
0064 
0065     m_renameBookmark = m_contextMenu->addAction(i18n("Rename bookmark"));
0066     connect(m_renameBookmark, &QAction::triggered, this, &BookmarksList::renameBookmark);
0067 
0068     m_editCoordinates = m_contextMenu->addAction(i18n("Edit coordinates"));
0069     connect(m_editCoordinates, &QAction::triggered, this, &BookmarksList::editCoordinates);
0070 
0071     m_contextMenu->addSeparator();
0072 
0073     m_lookupElevation = m_contextMenu->addAction(i18n("Lookup elevation"));
0074     connect(m_lookupElevation, &QAction::triggered, this, &BookmarksList::lookupElevation);
0075 
0076     m_setElevation = m_contextMenu->addAction(i18n("Set elevation manually"));
0077     connect(m_setElevation, &QAction::triggered, this, &BookmarksList::setElevation);
0078 
0079     m_contextMenu->addSeparator();
0080 
0081     m_deleteBookmark = m_contextMenu->addAction(i18n("Delete bookmark"));
0082     connect(m_deleteBookmark, &QAction::triggered, this, &BookmarksList::deleteBookmark);
0083 
0084     setContextMenuPolicy(Qt::CustomContextMenu);
0085     connect(this, &QWidget::customContextMenuRequested, this, &BookmarksList::showContextMenu);
0086 }
0087 
0088 void BookmarksList::showContextMenu(const QPoint &point)
0089 {
0090     m_contextMenuItem = itemAt(point);
0091 
0092     const bool itemSelected = m_contextMenuItem != nullptr;
0093     m_renameBookmark->setEnabled(itemSelected);
0094     m_lookupElevation->setEnabled(itemSelected);
0095     m_setElevation->setEnabled(itemSelected);
0096     m_deleteBookmark->setEnabled(itemSelected);
0097     m_editCoordinates->setEnabled(itemSelected);
0098 
0099     m_contextMenu->exec(mapToGlobal(point));
0100 }
0101 
0102 void BookmarksList::newBookmark()
0103 {
0104     auto [ label, okay ] = getString(i18n("New Bookmark"), i18n("Label for the new bookmark:"));
0105     if (! okay) {
0106         return;
0107     }
0108 
0109     label = label.simplified();
0110     const auto originalLabel = label;
0111     if (label.isEmpty()) {
0112         label = i18n("Untitled");
0113     }
0114 
0115     saveBookmark(label, m_mapWidget->currentCenter());
0116 }
0117 
0118 void BookmarksList::newManualBookmark()
0119 {
0120     CoordinatesDialog dialog(CoordinatesDialog::Mode::ManualBookmark,
0121                              m_settings->lookupElevationAutomatically());
0122     if (! dialog.exec()) {
0123         return;
0124     }
0125 
0126     saveBookmark(dialog.label(),
0127                  Coordinates(dialog.lon(), dialog.lat(), dialog.alt(), true));
0128 }
0129 
0130 void BookmarksList::saveBookmark(QString label, const Coordinates &coordinates)
0131 {
0132     const QString originalLabel = label;
0133     QString searchLabel = label;
0134     int addition = 0;
0135     while (! findItems(searchLabel, Qt::MatchExactly).isEmpty()) {
0136         addition++;
0137         searchLabel = i18nc("Adding a consecutive number to a bookmark label to make it unique",
0138                             "%1 (%2)", label, addition);
0139     }
0140     label = searchLabel;
0141 
0142     m_bookmarks[label] = coordinates;
0143 
0144     auto *item = new QListWidgetItem(label);
0145     addItem(item);
0146     setCurrentItem(item);
0147 
0148     if (m_settings->lookupElevationAutomatically()) {
0149         requestElevation(label);
0150     }
0151 
0152     Q_EMIT bookmarksChanged();
0153 
0154     if (label != originalLabel) {
0155         QMessageBox::warning(this, i18n("Add new bookmark"),
0156             i18n("The bookmark \"%1\" already exists, created \"%2\" instead.",
0157                  originalLabel, label));
0158     }
0159 
0160     m_mapWidget->centerCoordinates(coordinates);
0161 }
0162 
0163 void BookmarksList::requestElevation(const QString &id)
0164 {
0165     QApplication::setOverrideCursor(Qt::WaitCursor);
0166     setEnabled(false);
0167     m_elevationEngine->request(ElevationEngine::Target::Bookmark, { id },
0168                                { m_bookmarks.value(id) });
0169 }
0170 
0171 void BookmarksList::itemHighlighted(QListWidgetItem *item, QListWidgetItem *)
0172 {
0173     if (item == nullptr) {
0174         Q_EMIT showInfo(Coordinates());
0175         return;
0176     }
0177 
0178     Q_EMIT showInfo(m_bookmarks.value(item->text()));
0179 }
0180 
0181 void BookmarksList::centerBookmark(QListWidgetItem *item)
0182 {
0183     m_mapWidget->centerCoordinates(m_bookmarks.value(item->text()));
0184 }
0185 
0186 BookmarksList::EnteredString BookmarksList::getString(const QString &title, const QString &label,
0187                                                       const QString &text)
0188 {
0189     bool okay = false;
0190     auto string = QInputDialog::getText(this, title, label, QLineEdit::Normal, text, &okay);
0191     return { string, okay };
0192 }
0193 
0194 void BookmarksList::renameBookmark()
0195 {
0196     const auto currentLabel = m_contextMenuItem->text();
0197     auto [ newLabel, okay ] = getString(i18n("Rename Bookmark"),
0198                                         i18n("New label for the new bookmark:"), currentLabel);
0199     if (! okay || newLabel == currentLabel) {
0200         return;
0201     }
0202 
0203     newLabel = newLabel.simplified();
0204     if (! findItems(newLabel, Qt::MatchExactly).isEmpty()) {
0205         QMessageBox::warning(this, i18n("Rename Bookmark"),
0206                              i18n("The label \"%1\" is already in use. Please choose another "
0207                                   "name.", newLabel));
0208         return;
0209     }
0210 
0211     m_bookmarks[newLabel] = m_bookmarks.value(currentLabel);
0212     m_bookmarks.remove(currentLabel);
0213     m_contextMenuItem->setText(newLabel);
0214 
0215     Q_EMIT bookmarksChanged();
0216 }
0217 
0218 void BookmarksList::deleteBookmark()
0219 {
0220     if (QMessageBox::question(this, i18n("Delete bookmark"),
0221         i18n("Really delete bookmark \"%1\"?", m_contextMenuItem->text()),
0222         QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
0223 
0224         return;
0225     }
0226 
0227     m_bookmarks.remove(m_contextMenuItem->text());
0228     const auto *item = takeItem(row(m_contextMenuItem));
0229     delete item;
0230 
0231     Q_EMIT bookmarksChanged();
0232 }
0233 
0234 void BookmarksList::elevationProcessed(ElevationEngine::Target target, const QVector<QString> &ids,
0235                                        const QVector<double> &elevations)
0236 {
0237     if (target != ElevationEngine::Target::Bookmark) {
0238         return;
0239     }
0240 
0241     restoreAfterElevationLookup();
0242     const auto id = ids.at(0);
0243     m_bookmarks[id].setAlt(elevations.at(0));
0244     Q_EMIT showInfo(m_bookmarks.value(id));
0245 }
0246 
0247 void BookmarksList::restoreAfterElevationLookup()
0248 {
0249     if (! isEnabled()) {
0250         QApplication::restoreOverrideCursor();
0251         setEnabled(true);
0252     }
0253 }
0254 
0255 void BookmarksList::lookupElevation()
0256 {
0257     requestElevation(m_contextMenuItem->text());
0258 }
0259 
0260 void BookmarksList::setElevation()
0261 {
0262     const auto id = m_contextMenuItem->text();
0263 
0264     bool okay = false;
0265     auto elevation = QInputDialog::getDouble(this, i18n("Set elevation"),
0266                                              i18n("Elevation for \"%1\" (m)", id),
0267                                              m_bookmarks.value(id).alt(), KGeoTag::minimalAltitude,
0268                                              KGeoTag::maximalAltitude, 1, &okay);
0269     if (! okay) {
0270         return;
0271     }
0272 
0273     elevationProcessed(ElevationEngine::Target::Bookmark, { id }, { elevation });
0274 }
0275 
0276 const QHash<QString, Coordinates> *BookmarksList::bookmarks() const
0277 {
0278     return &m_bookmarks;
0279 }
0280 
0281 void BookmarksList::editCoordinates()
0282 {
0283     const auto id = m_contextMenuItem->text();
0284     auto &coordinates = m_bookmarks[id];
0285 
0286     CoordinatesDialog dialog(CoordinatesDialog::Mode::EditCoordinates, false, coordinates,
0287                              i18nc("A quoted bookmark label", "\"%1\"", id));
0288     if (! dialog.exec()) {
0289         return;
0290     }
0291 
0292     coordinates = dialog.coordinates();
0293     m_mapWidget->centerCoordinates(coordinates);
0294     Q_EMIT showInfo(coordinates);
0295 }