Marble

GoToDialog.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <[email protected]>
4 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <[email protected]>
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 
28 namespace Marble
29 {
30 
31 class TargetModel : public QAbstractListModel
32 {
33  Q_OBJECT
34 public:
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 
43 private:
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 
56  QVector<GeoDataPlacemark*> m_bookmarks;
57 
58  bool m_hasCurrentLocation;
59 
60  bool m_showRoutingItems;
61 };
62 
63 class GoToDialogPrivate : public Ui::GoTo
64 {
65 public:
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 
105 TargetModel::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 
126 QVector<GeoDataPlacemark> TargetModel::viaPoints() const
127 {
128  if ( !m_showRoutingItems ) {
129  return QVector<GeoDataPlacemark>();
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 
145 int 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 
159 QVariant 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"));
168  return QVariant::fromValue( currentLocation );
169  }
170  }
171  }
172 
173  return QVariant();
174 }
175 
176 QVariant 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 ) );
183  const GeoDataCoordinates coordinates = via.at( index ).coordinate();
184  return QVariant::fromValue( coordinates );
185  }
186  }
187 
188  return QVariant();
189 }
190 
191 QVariant 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"));
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 
208 QVariant 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 
227 QVariant 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 
257 void TargetModel::setShowRoutingItems( bool show )
258 {
259  m_showRoutingItems = show;
260  beginResetModel();
261  endResetModel();
262 }
263 
264 void 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 
293 GoToDialogPrivate::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 
306 void 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 
318 void 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 
335 void 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 
347 GoToDialog::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 
377 GoToDialog::~GoToDialog()
378 {
379  delete d;
380 }
381 
383 {
384  return d->m_coordinates;
385 }
386 
388 {
389  d->m_targetModel.setShowRoutingItems( show );
390 }
391 
392 void 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 
402 void 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 
416 void 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 
425 void GoToDialogPrivate::stopProgressAnimation()
426 {
427  searchLineEdit->setEnabled( true );
428  m_progressTimer.stop();
429  updateResultMessage( bookmarkListView->model()->rowCount() );
430  progressButton->setVisible( false );
431 }
432 
433 void 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
Q_OBJECTQ_OBJECT
void setShowRoutingItems(bool show)
Toggle whether routing items (source, destination and via points) are visible.
Definition: GoToDialog.cpp:387
A 3d point representation.
DisplayRole
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
QVariant fromValue(const T &value)
T value() const const
QString trimmed() const const
void push_back(const T &value)
QVector::const_iterator constEnd() const const
typedef WindowFlags
QVariant data(int role) const const
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
const T & at(int i) const const
bool isEmpty() const const
void setSearchEnabled(bool enabled)
Toggle whether the dialog can be used to search for placemarks.
Definition: GoToDialog.cpp:392
@ CoordinateRole
The GeoDataCoordinates coordinate.
Binds a QML item to a specific geodetic location in screen coordinates.
bool isValid() const const
void show()
int row() const const
const char * name(StandardAction id)
int size() const const
GeoDataCoordinates coordinates() const
Returns the position of the item selected by the user, or a default constructed GeoDataLookAt if the ...
Definition: GoToDialog.cpp:382
QVector::const_iterator constBegin() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
const QList< QKeySequence > & end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 26 2022 04:07:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.