Marble

GoToDialog.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
4// SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
5//
6
7#include "GoToDialog.h"
8#include "ui_GoToDialog.h"
9
10#include "BookmarkManager.h"
11#include "GeoDataDocument.h"
12#include "GeoDataFolder.h"
13#include "GeoDataLookAt.h"
14#include "GeoDataPlacemark.h"
15#include "GeoDataTreeModel.h"
16#include "MarbleModel.h"
17#include "MarblePlacemarkModel.h"
18#include "MarbleWidget.h"
19#include "PositionTracking.h"
20#include "SearchRunnerManager.h"
21#include "routing/RouteRequest.h"
22#include "routing/RoutingManager.h"
23
24#include <QAbstractListModel>
25#include <QPainter>
26#include <QTimer>
27
28namespace Marble
29{
30
31class TargetModel : public QAbstractListModel
32{
34public:
35 TargetModel(MarbleModel *marbleModel, QObject *parent = nullptr);
36
37 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
38
39 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
40
41 void setShowRoutingItems(bool show);
42
43private:
44 QVariant currentLocationData(int role) const;
45
46 QVariant routeData(const QList<GeoDataPlacemark> &via, int index, int role) const;
47
48 QVariant homeData(int role) const;
49
50 QVariant bookmarkData(int index, int role) const;
51
52 QList<GeoDataPlacemark> viaPoints() const;
53
54 MarbleModel *const m_marbleModel;
55
56 QList<GeoDataPlacemark *> m_bookmarks;
57
58 bool m_hasCurrentLocation;
59
60 bool m_showRoutingItems;
61};
62
63class GoToDialogPrivate : public Ui::GoTo
64{
65public:
66 GoToDialog *m_parent;
67
68 GeoDataCoordinates m_coordinates;
69
70 MarbleModel *const m_marbleModel;
71
72 TargetModel m_targetModel;
73
74 SearchRunnerManager m_runnerManager;
75
76 GeoDataDocument *m_searchResult;
77
78 GeoDataTreeModel m_searchResultModel;
79
80 QTimer m_progressTimer;
81
82 int m_currentFrame;
83
84 QList<QIcon> m_progressAnimation;
85
86 GoToDialogPrivate(GoToDialog *parent, MarbleModel *marbleModel);
87
88 void saveSelection(const QModelIndex &index);
89
90 void createProgressAnimation();
91
92 void startSearch();
93
94 void updateSearchResult(const QList<GeoDataPlacemark *> &placemarks);
95
96 void updateSearchMode();
97
98 void updateProgress();
99
100 void stopProgressAnimation();
101
102 void updateResultMessage(int results);
103};
104
105TargetModel::TargetModel(MarbleModel *marbleModel, QObject *parent)
106 : QAbstractListModel(parent)
107 , m_marbleModel(marbleModel)
108 , m_hasCurrentLocation(false)
109 , m_showRoutingItems(true)
110{
111 BookmarkManager *manager = m_marbleModel->bookmarkManager();
112 for (GeoDataFolder *folder : manager->folders()) {
113 QList<GeoDataPlacemark *> bookmarks = folder->placemarkList();
114 QList<GeoDataPlacemark *>::const_iterator iter = bookmarks.constBegin();
115 QList<GeoDataPlacemark *>::const_iterator end = bookmarks.constEnd();
116
117 for (; iter != end; ++iter) {
118 m_bookmarks.push_back(*iter);
119 }
120 }
121
122 PositionTracking *tracking = m_marbleModel->positionTracking();
123 m_hasCurrentLocation = tracking && tracking->status() == PositionProviderStatusAvailable;
124}
125
126QList<GeoDataPlacemark> TargetModel::viaPoints() const
127{
128 if (!m_showRoutingItems) {
129 return {};
130 }
131
132 RouteRequest *request = m_marbleModel->routingManager()->routeRequest();
134 for (int i = 0; i < request->size(); ++i) {
135 if (request->at(i).isValid()) {
136 GeoDataPlacemark placemark;
137 placemark.setCoordinate(request->at(i));
138 placemark.setName(request->name(i));
139 result.push_back(placemark);
140 }
141 }
142 return result;
143}
144
145int TargetModel::rowCount(const QModelIndex &parent) const
146{
147 int result = 0;
148 if (!parent.isValid()) {
149 result += m_hasCurrentLocation ? 1 : 0;
150 result += viaPoints().size(); // route
151 result += 1; // home location
152 result += m_bookmarks.size(); // bookmarks
153 return result;
154 }
155
156 return result;
157}
158
159QVariant TargetModel::currentLocationData(int role) const
160{
161 const PositionTracking *tracking = m_marbleModel->positionTracking();
162 if (tracking->status() == PositionProviderStatusAvailable) {
163 GeoDataCoordinates currentLocation = tracking->currentLocation();
164 switch (role) {
165 case Qt::DisplayRole:
166 return tr("Current Location: %1").arg(currentLocation.toString());
168 return QIcon(QStringLiteral(":/icons/gps.png"));
169 case MarblePlacemarkModel::CoordinateRole: {
170 return QVariant::fromValue(currentLocation);
171 }
172 }
173 }
174
175 return {};
176}
177
178QVariant TargetModel::routeData(const QList<GeoDataPlacemark> &via, int index, int role) const
179{
180 RouteRequest *request = m_marbleModel->routingManager()->routeRequest();
181 switch (role) {
182 case Qt::DisplayRole:
183 return via.at(index).name();
185 return QIcon(request->pixmap(index));
186 case MarblePlacemarkModel::CoordinateRole: {
187 const GeoDataCoordinates coordinates = via.at(index).coordinate();
188 return QVariant::fromValue(coordinates);
189 }
190 }
191
192 return {};
193}
194
195QVariant TargetModel::homeData(int role) const
196{
197 switch (role) {
198 case Qt::DisplayRole:
199 return tr("Home");
201 return QIcon(QStringLiteral(":/icons/go-home.png"));
202 case MarblePlacemarkModel::CoordinateRole: {
203 qreal lon(0.0), lat(0.0);
204 int zoom(0);
205 m_marbleModel->home(lon, lat, zoom);
206 const GeoDataCoordinates coordinates = GeoDataCoordinates(lon, lat, 0, GeoDataCoordinates::Degree);
207 return QVariant::fromValue(coordinates);
208 }
209 }
210
211 return {};
212}
213
214QVariant TargetModel::bookmarkData(int index, int role) const
215{
216 switch (role) {
217 case Qt::DisplayRole: {
218 const GeoDataFolder *folder = geodata_cast<GeoDataFolder>(m_bookmarks[index]->parent());
219 Q_ASSERT(folder && "Internal bookmark representation has changed. Please report this as a bug at https://bugs.kde.org.");
220 if (folder) {
221 return QString(folder->name() + QLatin1StringView(" / ") + m_bookmarks[index]->name());
222 }
223 return {};
224 }
226 return QIcon(QStringLiteral(":/icons/bookmarks.png"));
227 case MarblePlacemarkModel::CoordinateRole:
228 return QVariant::fromValue(m_bookmarks[index]->lookAt()->coordinates());
229 }
230
231 return {};
232}
233
234QVariant TargetModel::data(const QModelIndex &index, int role) const
235{
236 if (index.isValid() && index.row() >= 0 && index.row() < rowCount()) {
237 int row = index.row();
238 bool const isCurrentLocation = row == 0 && m_hasCurrentLocation;
239 int homeOffset = m_hasCurrentLocation ? 1 : 0;
240 QList<GeoDataPlacemark> via = viaPoints();
241 bool const isRoute = row >= homeOffset && row < homeOffset + via.size();
242
243 if (isCurrentLocation) {
244 return currentLocationData(role);
245 } else if (isRoute) {
246 int routeIndex = row - homeOffset;
247 Q_ASSERT(routeIndex >= 0 && routeIndex < via.size());
248 return routeData(via, routeIndex, role);
249 } else {
250 int bookmarkIndex = row - homeOffset - via.size();
251 if (bookmarkIndex == 0) {
252 return homeData(role);
253 } else {
254 --bookmarkIndex;
255 Q_ASSERT(bookmarkIndex >= 0 && bookmarkIndex < m_bookmarks.size());
256 return bookmarkData(bookmarkIndex, role);
257 }
258 }
259 }
260
261 return {};
262}
263
264void TargetModel::setShowRoutingItems(bool show)
265{
266 m_showRoutingItems = show;
267 beginResetModel();
268 endResetModel();
269}
270
271void GoToDialogPrivate::createProgressAnimation()
272{
273 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
274 int const iconSize = smallScreen ? 32 : 16;
275
276 // Size parameters
277 qreal const h = iconSize / 2.0; // Half of the icon size
278 qreal const q = h / 2.0; // Quarter of the icon size
279 qreal const d = 7.5; // Circle diameter
280 qreal const r = d / 2.0; // Circle radius
281
282 // Canvas parameters
283 QImage canvas(iconSize, iconSize, QImage::Format_ARGB32);
284 QPainter painter(&canvas);
285 painter.setRenderHint(QPainter::Antialiasing, true);
286 painter.setPen(QColor(Qt::gray));
287 painter.setBrush(QColor(Qt::white));
288
289 // Create all frames
290 for (double t = 0.0; t < 2 * M_PI; t += M_PI / 8.0) {
291 canvas.fill(Qt::transparent);
292 QRectF firstCircle(h - r + q * cos(t), h - r + q * sin(t), d, d);
293 QRectF secondCircle(h - r + q * cos(t + M_PI), h - r + q * sin(t + M_PI), d, d);
294 painter.drawEllipse(firstCircle);
295 painter.drawEllipse(secondCircle);
296 m_progressAnimation.push_back(QIcon(QPixmap::fromImage(canvas)));
297 }
298}
299
300GoToDialogPrivate::GoToDialogPrivate(GoToDialog *parent, MarbleModel *marbleModel)
301 : m_parent(parent)
302 , m_marbleModel(marbleModel)
303 , m_targetModel(marbleModel)
304 , m_runnerManager(marbleModel)
305 , m_searchResult(new GeoDataDocument)
306 , m_currentFrame(0)
307{
308 setupUi(parent);
309
310 m_progressTimer.setInterval(100);
311}
312
313void GoToDialogPrivate::saveSelection(const QModelIndex &index)
314{
315 if (searchButton->isChecked() && m_searchResult->size()) {
316 QVariant coordinates = m_searchResultModel.data(index, MarblePlacemarkModel::CoordinateRole);
317 m_coordinates = coordinates.value<GeoDataCoordinates>();
318 } else {
319 QVariant coordinates = index.data(MarblePlacemarkModel::CoordinateRole);
320 m_coordinates = coordinates.value<GeoDataCoordinates>();
321 }
322 m_parent->accept();
323}
324
325void GoToDialogPrivate::startSearch()
326{
327 QString const searchTerm = searchLineEdit->text().trimmed();
328 if (searchTerm.isEmpty()) {
329 return;
330 }
331
332 m_runnerManager.findPlacemarks(searchTerm);
333 if (m_progressAnimation.isEmpty()) {
334 createProgressAnimation();
335 }
336 m_progressTimer.start();
337 progressButton->setVisible(true);
338 searchLineEdit->setEnabled(false);
339 updateResultMessage(0);
340}
341
342void GoToDialogPrivate::updateSearchResult(const QList<GeoDataPlacemark *> &placemarks)
343{
344 m_searchResultModel.setRootDocument(nullptr);
345 m_searchResult->clear();
346 for (GeoDataPlacemark *placemark : placemarks) {
347 m_searchResult->append(new GeoDataPlacemark(*placemark));
348 }
349 m_searchResultModel.setRootDocument(m_searchResult);
350 bookmarkListView->setModel(&m_searchResultModel);
351 updateResultMessage(m_searchResultModel.rowCount());
352}
353
354GoToDialog::GoToDialog(MarbleModel *marbleModel, QWidget *parent, Qt::WindowFlags flags)
355 : QDialog(parent, flags)
356 , d(new GoToDialogPrivate(this, marbleModel))
357{
358 d->searchLineEdit->setPlaceholderText(tr("Address or search term"));
359
360 d->m_searchResultModel.setRootDocument(d->m_searchResult);
361 d->bookmarkListView->setModel(&d->m_targetModel);
362 connect(d->bookmarkListView, SIGNAL(activated(QModelIndex)), this, SLOT(saveSelection(QModelIndex)));
363 connect(d->searchLineEdit, SIGNAL(returnPressed()), this, SLOT(startSearch()));
364 d->buttonBox->button(QDialogButtonBox::Close)->setAutoDefault(false);
365 connect(d->searchButton, SIGNAL(clicked(bool)), this, SLOT(updateSearchMode()));
366 connect(d->browseButton, SIGNAL(clicked(bool)), this, SLOT(updateSearchMode()));
367 connect(&d->m_progressTimer, SIGNAL(timeout()), this, SLOT(updateProgress()));
368 connect(d->progressButton, SIGNAL(clicked(bool)), this, SLOT(stopProgressAnimation()));
369 d->updateSearchMode();
370 d->progressButton->setVisible(false);
371
372 connect(&d->m_runnerManager, SIGNAL(searchResultChanged(QList<GeoDataPlacemark *>)), this, SLOT(updateSearchResult(QList<GeoDataPlacemark *>)));
373 connect(&d->m_runnerManager, SIGNAL(searchFinished(QString)), this, SLOT(stopProgressAnimation()));
374}
375
376GoToDialog::~GoToDialog()
377{
378 delete d;
379}
380
381GeoDataCoordinates GoToDialog::coordinates() const
382{
383 return d->m_coordinates;
384}
385
386void GoToDialog::setShowRoutingItems(bool show)
387{
388 d->m_targetModel.setShowRoutingItems(show);
389}
390
391void GoToDialog::setSearchEnabled(bool enabled)
392{
393 d->browseButton->setVisible(enabled);
394 d->searchButton->setVisible(enabled);
395 if (!enabled) {
396 d->searchButton->setChecked(false);
397 d->updateSearchMode();
398 }
399}
400
401void GoToDialogPrivate::updateSearchMode()
402{
403 bool const searchEnabled = searchButton->isChecked();
404 searchLineEdit->setVisible(searchEnabled);
405 descriptionLabel->setVisible(searchEnabled);
406 progressButton->setVisible(searchEnabled && m_progressTimer.isActive());
407 if (searchEnabled) {
408 bookmarkListView->setModel(&m_searchResultModel);
409 searchLineEdit->setFocus();
410 } else {
411 bookmarkListView->setModel(&m_targetModel);
412 }
413}
414
415void GoToDialogPrivate::updateProgress()
416{
417 if (!m_progressAnimation.isEmpty()) {
418 m_currentFrame = (m_currentFrame + 1) % m_progressAnimation.size();
419 QIcon frame = m_progressAnimation[m_currentFrame];
420 progressButton->setIcon(frame);
421 }
422}
423
424void GoToDialogPrivate::stopProgressAnimation()
425{
426 searchLineEdit->setEnabled(true);
427 m_progressTimer.stop();
428 updateResultMessage(bookmarkListView->model()->rowCount());
429 progressButton->setVisible(false);
430}
431
432void GoToDialogPrivate::updateResultMessage(int results)
433{
434 //~ singular %n result found.
435 //~ plural %n results found.
436 descriptionLabel->setText(QObject::tr("%n result(s) found.", "Number of search results", results));
437}
438
439}
440
441#include "GoToDialog.moc" // needed for Q_OBJECT here in source
442#include "moc_GoToDialog.cpp" // needed for private slots in header
This file contains the headers for MarbleModel.
This file contains the headers for MarbleWidget.
A 3d point representation.
QString name(GameStandardAction id)
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
const_reference at(qsizetype i) const const
void push_back(parameter_type value)
qsizetype size() const const
QVariant data(int role) const const
bool isValid() const const
int row() const const
Q_OBJECTQ_OBJECT
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
bool isEmpty() const const
QString trimmed() const const
DisplayRole
typedef WindowFlags
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void * data()
QVariant fromValue(T &&value)
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.