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 "MarbleWidget.h"
12#include "MarbleModel.h"
13#include "MarblePlacemarkModel.h"
14#include "GeoDataLookAt.h"
15#include "GeoDataTreeModel.h"
16#include "GeoDataDocument.h"
17#include "GeoDataFolder.h"
18#include "GeoDataPlacemark.h"
19#include "PositionTracking.h"
20#include "SearchRunnerManager.h"
21#include "routing/RoutingManager.h"
22#include "routing/RouteRequest.h"
23
24#include <QAbstractListModel>
25#include <QTimer>
26#include <QPainter>
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 QVector<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 QVector<GeoDataPlacemark> viaPoints() const;
53
54 MarbleModel *const m_marbleModel;
55
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 QVector<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 QVector<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 QVector<GeoDataPlacemark*> bookmarks = folder->placemarkList();
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
126QVector<GeoDataPlacemark> TargetModel::viaPoints() const
127{
128 if ( !m_showRoutingItems ) {
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: return tr( "Current Location: %1" ).arg( currentLocation.toString() ) ;
166 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/gps.png"));
167 case MarblePlacemarkModel::CoordinateRole: {
168 return QVariant::fromValue( currentLocation );
169 }
170 }
171 }
172
173 return QVariant();
174}
175
176QVariant TargetModel::routeData ( const QVector<GeoDataPlacemark> &via, int index, int role ) const
177{
178 RouteRequest* request = m_marbleModel->routingManager()->routeRequest();
179 switch( role ) {
180 case Qt::DisplayRole: return via.at( index ).name();
181 case Qt::DecorationRole: return QIcon( request->pixmap( index ) );
182 case MarblePlacemarkModel::CoordinateRole: {
183 const GeoDataCoordinates coordinates = via.at( index ).coordinate();
184 return QVariant::fromValue( coordinates );
185 }
186 }
187
188 return QVariant();
189}
190
191QVariant TargetModel::homeData ( int role ) const
192{
193 switch( role ) {
194 case Qt::DisplayRole: return tr( "Home" );
195 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/go-home.png"));
196 case MarblePlacemarkModel::CoordinateRole: {
197 qreal lon( 0.0 ), lat( 0.0 );
198 int zoom( 0 );
199 m_marbleModel->home( lon, lat, zoom );
200 const GeoDataCoordinates coordinates = GeoDataCoordinates( lon, lat, 0, GeoDataCoordinates::Degree );
201 return QVariant::fromValue( coordinates );
202 }
203 }
204
205 return QVariant();
206}
207
208QVariant TargetModel::bookmarkData ( int index, int role ) const
209{
210 switch( role ) {
211 case Qt::DisplayRole: {
212 const GeoDataFolder *folder = geodata_cast<GeoDataFolder>(m_bookmarks[index]->parent());
213 Q_ASSERT( folder && "Internal bookmark representation has changed. Please report this as a bug at https://bugs.kde.org." );
214 if ( folder ) {
215 return QString(folder->name() + QLatin1String(" / ") + m_bookmarks[index]->name());
216 }
217 return QVariant();
218 }
219 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/bookmarks.png"));
220 case MarblePlacemarkModel::CoordinateRole: return QVariant::fromValue( m_bookmarks[index]->lookAt()->coordinates() );
221 }
222
223 return QVariant();
224}
225
226
227QVariant TargetModel::data ( const QModelIndex & index, int role ) const
228{
229 if ( index.isValid() && index.row() >= 0 && index.row() < rowCount() ) {
230 int row = index.row();
231 bool const isCurrentLocation = row == 0 && m_hasCurrentLocation;
232 int homeOffset = m_hasCurrentLocation ? 1 : 0;
233 QVector<GeoDataPlacemark> via = viaPoints();
234 bool const isRoute = row >= homeOffset && row < homeOffset + via.size();
235
236 if ( isCurrentLocation ) {
237 return currentLocationData( role );
238 } else if ( isRoute ) {
239 int routeIndex = row - homeOffset;
240 Q_ASSERT( routeIndex >= 0 && routeIndex < via.size() );
241 return routeData( via, routeIndex, role );
242 } else {
243 int bookmarkIndex = row - homeOffset - via.size();
244 if ( bookmarkIndex == 0 ) {
245 return homeData( role );
246 } else {
247 --bookmarkIndex;
248 Q_ASSERT( bookmarkIndex >= 0 && bookmarkIndex < m_bookmarks.size() );
249 return bookmarkData( bookmarkIndex, role );
250 }
251 }
252 }
253
254 return QVariant();
255}
256
257void TargetModel::setShowRoutingItems( bool show )
258{
259 m_showRoutingItems = show;
260 beginResetModel();
261 endResetModel();
262}
263
264void GoToDialogPrivate::createProgressAnimation()
265{
266 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
267 int const iconSize = smallScreen ? 32 : 16;
268
269 // Size parameters
270 qreal const h = iconSize / 2.0; // Half of the icon size
271 qreal const q = h / 2.0; // Quarter of the icon size
272 qreal const d = 7.5; // Circle diameter
273 qreal const r = d / 2.0; // Circle radius
274
275 // Canvas parameters
276 QImage canvas( iconSize, iconSize, QImage::Format_ARGB32 );
277 QPainter painter( &canvas );
278 painter.setRenderHint( QPainter::Antialiasing, true );
279 painter.setPen( QColor ( Qt::gray ) );
280 painter.setBrush( QColor( Qt::white ) );
281
282 // Create all frames
283 for( double t = 0.0; t < 2 * M_PI; t += M_PI / 8.0 ) {
284 canvas.fill( Qt::transparent );
285 QRectF firstCircle( h - r + q * cos( t ), h - r + q * sin( t ), d, d );
286 QRectF secondCircle( h - r + q * cos( t + M_PI ), h - r + q * sin( t + M_PI ), d, d );
287 painter.drawEllipse( firstCircle );
288 painter.drawEllipse( secondCircle );
289 m_progressAnimation.push_back( QIcon( QPixmap::fromImage( canvas ) ) );
290 }
291}
292
293GoToDialogPrivate::GoToDialogPrivate( GoToDialog* parent, MarbleModel* marbleModel ) :
294 m_parent( parent),
295 m_marbleModel( marbleModel ),
296 m_targetModel( marbleModel ),
297 m_runnerManager( marbleModel ),
298 m_searchResult( new GeoDataDocument ),
299 m_currentFrame( 0 )
300{
301 setupUi( parent );
302
303 m_progressTimer.setInterval( 100 );
304}
305
306void GoToDialogPrivate::saveSelection( const QModelIndex &index )
307{
308 if ( searchButton->isChecked() && m_searchResult->size() ) {
309 QVariant coordinates = m_searchResultModel.data( index, MarblePlacemarkModel::CoordinateRole );
310 m_coordinates = coordinates.value<GeoDataCoordinates>();
311 } else {
312 QVariant coordinates = index.data( MarblePlacemarkModel::CoordinateRole );
313 m_coordinates = coordinates.value<GeoDataCoordinates>();
314 }
315 m_parent->accept();
316}
317
318void GoToDialogPrivate::startSearch()
319{
320 QString const searchTerm = searchLineEdit->text().trimmed();
321 if ( searchTerm.isEmpty() ) {
322 return;
323 }
324
325 m_runnerManager.findPlacemarks( searchTerm );
326 if ( m_progressAnimation.isEmpty() ) {
327 createProgressAnimation();
328 }
329 m_progressTimer.start();
330 progressButton->setVisible( true );
331 searchLineEdit->setEnabled( false );
332 updateResultMessage( 0 );
333}
334
335void GoToDialogPrivate::updateSearchResult( const QVector<GeoDataPlacemark*>& placemarks )
336{
337 m_searchResultModel.setRootDocument( nullptr );
338 m_searchResult->clear();
339 for (GeoDataPlacemark *placemark: placemarks) {
340 m_searchResult->append( new GeoDataPlacemark( *placemark ) );
341 }
342 m_searchResultModel.setRootDocument( m_searchResult );
343 bookmarkListView->setModel( &m_searchResultModel );
344 updateResultMessage( m_searchResultModel.rowCount() );
345}
346
347GoToDialog::GoToDialog( MarbleModel* marbleModel, QWidget * parent, Qt::WindowFlags flags ) :
348 QDialog( parent, flags ),
349 d( new GoToDialogPrivate( this, marbleModel ) )
350{
351 d->searchLineEdit->setPlaceholderText( tr( "Address or search term" ) );
352
353 d->m_searchResultModel.setRootDocument( d->m_searchResult );
354 d->bookmarkListView->setModel( &d->m_targetModel );
355 connect( d->bookmarkListView, SIGNAL(activated(QModelIndex)),
356 this, SLOT(saveSelection(QModelIndex)) );
357 connect( d->searchLineEdit, SIGNAL(returnPressed()),
358 this, SLOT(startSearch()) );
359 d->buttonBox->button( QDialogButtonBox::Close )->setAutoDefault( false );
360 connect( d->searchButton, SIGNAL(clicked(bool)),
361 this, SLOT(updateSearchMode()) );
362 connect( d->browseButton, SIGNAL(clicked(bool)),
363 this, SLOT(updateSearchMode()) );
364 connect( &d->m_progressTimer, SIGNAL(timeout()),
365 this, SLOT(updateProgress()) );
366 connect( d->progressButton, SIGNAL(clicked(bool)),
367 this, SLOT(stopProgressAnimation()) );
368 d->updateSearchMode();
369 d->progressButton->setVisible( false );
370
371 connect( &d->m_runnerManager, SIGNAL(searchResultChanged(QVector<GeoDataPlacemark*>)),
372 this, SLOT(updateSearchResult(QVector<GeoDataPlacemark*>)) );
373 connect( &d->m_runnerManager, SIGNAL(searchFinished(QString)),
374 this, SLOT(stopProgressAnimation()) );
375}
376
377GoToDialog::~GoToDialog()
378{
379 delete d;
380}
381
382GeoDataCoordinates GoToDialog::coordinates() const
383{
384 return d->m_coordinates;
385}
386
387void GoToDialog::setShowRoutingItems( bool show )
388{
389 d->m_targetModel.setShowRoutingItems( show );
390}
391
392void GoToDialog::setSearchEnabled( bool enabled )
393{
394 d->browseButton->setVisible( enabled );
395 d->searchButton->setVisible( enabled );
396 if ( !enabled ) {
397 d->searchButton->setChecked( false );
398 d->updateSearchMode();
399 }
400}
401
402void GoToDialogPrivate::updateSearchMode()
403{
404 bool const searchEnabled = searchButton->isChecked();
405 searchLineEdit->setVisible( searchEnabled );
406 descriptionLabel->setVisible( searchEnabled );
407 progressButton->setVisible( searchEnabled && m_progressTimer.isActive() );
408 if ( searchEnabled ) {
409 bookmarkListView->setModel( &m_searchResultModel );
410 searchLineEdit->setFocus();
411 } else {
412 bookmarkListView->setModel( &m_targetModel );
413 }
414}
415
416void GoToDialogPrivate::updateProgress()
417{
418 if ( !m_progressAnimation.isEmpty() ) {
419 m_currentFrame = ( m_currentFrame + 1 ) % m_progressAnimation.size();
420 QIcon frame = m_progressAnimation[m_currentFrame];
421 progressButton->setIcon( frame );
422 }
423}
424
425void GoToDialogPrivate::stopProgressAnimation()
426{
427 searchLineEdit->setEnabled( true );
428 m_progressTimer.stop();
429 updateResultMessage( bookmarkListView->model()->rowCount() );
430 progressButton->setVisible( false );
431}
432
433void GoToDialogPrivate::updateResultMessage( int results )
434{
435 //~ singular %n result found.
436 //~ plural %n results found.
437 descriptionLabel->setText( QObject::tr( "%n result(s) found.", "Number of search results", results ) );
438}
439
440}
441
442#include "moc_GoToDialog.cpp" // needed for private slots in header
443#include "GoToDialog.moc" // needed for Q_OBJECT here in source
This file contains the headers for MarbleModel.
This file contains the headers for MarbleWidget.
A 3d point representation.
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
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
const_iterator constBegin() const const
const_iterator constEnd() 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
T qobject_cast(QObject *object)
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 Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.