File indexing completed on 2024-04-28 11:30:56
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org> 0004 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0005 // 0006 0007 #include "GoToDialog.h" 0008 #include "ui_GoToDialog.h" 0009 0010 #include "BookmarkManager.h" 0011 #include "MarbleWidget.h" 0012 #include "MarbleModel.h" 0013 #include "MarblePlacemarkModel.h" 0014 #include "GeoDataLookAt.h" 0015 #include "GeoDataTreeModel.h" 0016 #include "GeoDataDocument.h" 0017 #include "GeoDataFolder.h" 0018 #include "GeoDataPlacemark.h" 0019 #include "PositionTracking.h" 0020 #include "SearchRunnerManager.h" 0021 #include "routing/RoutingManager.h" 0022 #include "routing/RouteRequest.h" 0023 0024 #include <QAbstractListModel> 0025 #include <QTimer> 0026 #include <QPainter> 0027 0028 namespace Marble 0029 { 0030 0031 class TargetModel : public QAbstractListModel 0032 { 0033 Q_OBJECT 0034 public: 0035 TargetModel( MarbleModel* marbleModel, QObject * parent = nullptr ); 0036 0037 int rowCount ( const QModelIndex & parent = QModelIndex() ) const override; 0038 0039 QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override; 0040 0041 void setShowRoutingItems( bool show ); 0042 0043 private: 0044 QVariant currentLocationData ( int role ) const; 0045 0046 QVariant routeData ( const QVector<GeoDataPlacemark> &via, int index, int role ) const; 0047 0048 QVariant homeData ( int role ) const; 0049 0050 QVariant bookmarkData ( int index, int role ) const; 0051 0052 QVector<GeoDataPlacemark> viaPoints() const; 0053 0054 MarbleModel *const m_marbleModel; 0055 0056 QVector<GeoDataPlacemark*> m_bookmarks; 0057 0058 bool m_hasCurrentLocation; 0059 0060 bool m_showRoutingItems; 0061 }; 0062 0063 class GoToDialogPrivate : public Ui::GoTo 0064 { 0065 public: 0066 GoToDialog* m_parent; 0067 0068 GeoDataCoordinates m_coordinates; 0069 0070 MarbleModel *const m_marbleModel; 0071 0072 TargetModel m_targetModel; 0073 0074 SearchRunnerManager m_runnerManager; 0075 0076 GeoDataDocument *m_searchResult; 0077 0078 GeoDataTreeModel m_searchResultModel; 0079 0080 QTimer m_progressTimer; 0081 0082 int m_currentFrame; 0083 0084 QVector<QIcon> m_progressAnimation; 0085 0086 GoToDialogPrivate( GoToDialog* parent, MarbleModel* marbleModel ); 0087 0088 void saveSelection( const QModelIndex &index ); 0089 0090 void createProgressAnimation(); 0091 0092 void startSearch(); 0093 0094 void updateSearchResult( const QVector<GeoDataPlacemark*>& placemarks ); 0095 0096 void updateSearchMode(); 0097 0098 void updateProgress(); 0099 0100 void stopProgressAnimation(); 0101 0102 void updateResultMessage( int results ); 0103 }; 0104 0105 TargetModel::TargetModel( MarbleModel *marbleModel, QObject * parent ) : 0106 QAbstractListModel( parent ), 0107 m_marbleModel( marbleModel ), 0108 m_hasCurrentLocation( false ), 0109 m_showRoutingItems( true ) 0110 { 0111 BookmarkManager* manager = m_marbleModel->bookmarkManager(); 0112 for( GeoDataFolder * folder: manager->folders() ) { 0113 QVector<GeoDataPlacemark*> bookmarks = folder->placemarkList(); 0114 QVector<GeoDataPlacemark*>::const_iterator iter = bookmarks.constBegin(); 0115 QVector<GeoDataPlacemark*>::const_iterator end = bookmarks.constEnd(); 0116 0117 for ( ; iter != end; ++iter ) { 0118 m_bookmarks.push_back( *iter ); 0119 } 0120 } 0121 0122 PositionTracking* tracking = m_marbleModel->positionTracking(); 0123 m_hasCurrentLocation = tracking && tracking->status() == PositionProviderStatusAvailable; 0124 } 0125 0126 QVector<GeoDataPlacemark> TargetModel::viaPoints() const 0127 { 0128 if ( !m_showRoutingItems ) { 0129 return QVector<GeoDataPlacemark>(); 0130 } 0131 0132 RouteRequest* request = m_marbleModel->routingManager()->routeRequest(); 0133 QVector<GeoDataPlacemark> result; 0134 for ( int i = 0; i < request->size(); ++i ) { 0135 if ( request->at( i ).isValid() ) { 0136 GeoDataPlacemark placemark; 0137 placemark.setCoordinate( request->at( i ) ); 0138 placemark.setName( request->name( i ) ); 0139 result.push_back( placemark ); 0140 } 0141 } 0142 return result; 0143 } 0144 0145 int TargetModel::rowCount ( const QModelIndex & parent ) const 0146 { 0147 int result = 0; 0148 if ( !parent.isValid() ) { 0149 result += m_hasCurrentLocation ? 1 : 0; 0150 result += viaPoints().size(); // route 0151 result += 1; // home location 0152 result += m_bookmarks.size(); // bookmarks 0153 return result; 0154 } 0155 0156 return result; 0157 } 0158 0159 QVariant TargetModel::currentLocationData ( int role ) const 0160 { 0161 const PositionTracking* tracking = m_marbleModel->positionTracking(); 0162 if ( tracking->status() == PositionProviderStatusAvailable ) { 0163 GeoDataCoordinates currentLocation = tracking->currentLocation(); 0164 switch( role ) { 0165 case Qt::DisplayRole: return tr( "Current Location: %1" ).arg( currentLocation.toString() ) ; 0166 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/gps.png")); 0167 case MarblePlacemarkModel::CoordinateRole: { 0168 return QVariant::fromValue( currentLocation ); 0169 } 0170 } 0171 } 0172 0173 return QVariant(); 0174 } 0175 0176 QVariant TargetModel::routeData ( const QVector<GeoDataPlacemark> &via, int index, int role ) const 0177 { 0178 RouteRequest* request = m_marbleModel->routingManager()->routeRequest(); 0179 switch( role ) { 0180 case Qt::DisplayRole: return via.at( index ).name(); 0181 case Qt::DecorationRole: return QIcon( request->pixmap( index ) ); 0182 case MarblePlacemarkModel::CoordinateRole: { 0183 const GeoDataCoordinates coordinates = via.at( index ).coordinate(); 0184 return QVariant::fromValue( coordinates ); 0185 } 0186 } 0187 0188 return QVariant(); 0189 } 0190 0191 QVariant TargetModel::homeData ( int role ) const 0192 { 0193 switch( role ) { 0194 case Qt::DisplayRole: return tr( "Home" ); 0195 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/go-home.png")); 0196 case MarblePlacemarkModel::CoordinateRole: { 0197 qreal lon( 0.0 ), lat( 0.0 ); 0198 int zoom( 0 ); 0199 m_marbleModel->home( lon, lat, zoom ); 0200 const GeoDataCoordinates coordinates = GeoDataCoordinates( lon, lat, 0, GeoDataCoordinates::Degree ); 0201 return QVariant::fromValue( coordinates ); 0202 } 0203 } 0204 0205 return QVariant(); 0206 } 0207 0208 QVariant TargetModel::bookmarkData ( int index, int role ) const 0209 { 0210 switch( role ) { 0211 case Qt::DisplayRole: { 0212 const GeoDataFolder *folder = geodata_cast<GeoDataFolder>(m_bookmarks[index]->parent()); 0213 Q_ASSERT( folder && "Internal bookmark representation has changed. Please report this as a bug at https://bugs.kde.org." ); 0214 if ( folder ) { 0215 return QString(folder->name() + QLatin1String(" / ") + m_bookmarks[index]->name()); 0216 } 0217 return QVariant(); 0218 } 0219 case Qt::DecorationRole: return QIcon(QStringLiteral(":/icons/bookmarks.png")); 0220 case MarblePlacemarkModel::CoordinateRole: return QVariant::fromValue( m_bookmarks[index]->lookAt()->coordinates() ); 0221 } 0222 0223 return QVariant(); 0224 } 0225 0226 0227 QVariant TargetModel::data ( const QModelIndex & index, int role ) const 0228 { 0229 if ( index.isValid() && index.row() >= 0 && index.row() < rowCount() ) { 0230 int row = index.row(); 0231 bool const isCurrentLocation = row == 0 && m_hasCurrentLocation; 0232 int homeOffset = m_hasCurrentLocation ? 1 : 0; 0233 QVector<GeoDataPlacemark> via = viaPoints(); 0234 bool const isRoute = row >= homeOffset && row < homeOffset + via.size(); 0235 0236 if ( isCurrentLocation ) { 0237 return currentLocationData( role ); 0238 } else if ( isRoute ) { 0239 int routeIndex = row - homeOffset; 0240 Q_ASSERT( routeIndex >= 0 && routeIndex < via.size() ); 0241 return routeData( via, routeIndex, role ); 0242 } else { 0243 int bookmarkIndex = row - homeOffset - via.size(); 0244 if ( bookmarkIndex == 0 ) { 0245 return homeData( role ); 0246 } else { 0247 --bookmarkIndex; 0248 Q_ASSERT( bookmarkIndex >= 0 && bookmarkIndex < m_bookmarks.size() ); 0249 return bookmarkData( bookmarkIndex, role ); 0250 } 0251 } 0252 } 0253 0254 return QVariant(); 0255 } 0256 0257 void TargetModel::setShowRoutingItems( bool show ) 0258 { 0259 m_showRoutingItems = show; 0260 beginResetModel(); 0261 endResetModel(); 0262 } 0263 0264 void GoToDialogPrivate::createProgressAnimation() 0265 { 0266 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0267 int const iconSize = smallScreen ? 32 : 16; 0268 0269 // Size parameters 0270 qreal const h = iconSize / 2.0; // Half of the icon size 0271 qreal const q = h / 2.0; // Quarter of the icon size 0272 qreal const d = 7.5; // Circle diameter 0273 qreal const r = d / 2.0; // Circle radius 0274 0275 // Canvas parameters 0276 QImage canvas( iconSize, iconSize, QImage::Format_ARGB32 ); 0277 QPainter painter( &canvas ); 0278 painter.setRenderHint( QPainter::Antialiasing, true ); 0279 painter.setPen( QColor ( Qt::gray ) ); 0280 painter.setBrush( QColor( Qt::white ) ); 0281 0282 // Create all frames 0283 for( double t = 0.0; t < 2 * M_PI; t += M_PI / 8.0 ) { 0284 canvas.fill( Qt::transparent ); 0285 QRectF firstCircle( h - r + q * cos( t ), h - r + q * sin( t ), d, d ); 0286 QRectF secondCircle( h - r + q * cos( t + M_PI ), h - r + q * sin( t + M_PI ), d, d ); 0287 painter.drawEllipse( firstCircle ); 0288 painter.drawEllipse( secondCircle ); 0289 m_progressAnimation.push_back( QIcon( QPixmap::fromImage( canvas ) ) ); 0290 } 0291 } 0292 0293 GoToDialogPrivate::GoToDialogPrivate( GoToDialog* parent, MarbleModel* marbleModel ) : 0294 m_parent( parent), 0295 m_marbleModel( marbleModel ), 0296 m_targetModel( marbleModel ), 0297 m_runnerManager( marbleModel ), 0298 m_searchResult( new GeoDataDocument ), 0299 m_currentFrame( 0 ) 0300 { 0301 setupUi( parent ); 0302 0303 m_progressTimer.setInterval( 100 ); 0304 } 0305 0306 void GoToDialogPrivate::saveSelection( const QModelIndex &index ) 0307 { 0308 if ( searchButton->isChecked() && m_searchResult->size() ) { 0309 QVariant coordinates = m_searchResultModel.data( index, MarblePlacemarkModel::CoordinateRole ); 0310 m_coordinates = coordinates.value<GeoDataCoordinates>(); 0311 } else { 0312 QVariant coordinates = index.data( MarblePlacemarkModel::CoordinateRole ); 0313 m_coordinates = coordinates.value<GeoDataCoordinates>(); 0314 } 0315 m_parent->accept(); 0316 } 0317 0318 void GoToDialogPrivate::startSearch() 0319 { 0320 QString const searchTerm = searchLineEdit->text().trimmed(); 0321 if ( searchTerm.isEmpty() ) { 0322 return; 0323 } 0324 0325 m_runnerManager.findPlacemarks( searchTerm ); 0326 if ( m_progressAnimation.isEmpty() ) { 0327 createProgressAnimation(); 0328 } 0329 m_progressTimer.start(); 0330 progressButton->setVisible( true ); 0331 searchLineEdit->setEnabled( false ); 0332 updateResultMessage( 0 ); 0333 } 0334 0335 void GoToDialogPrivate::updateSearchResult( const QVector<GeoDataPlacemark*>& placemarks ) 0336 { 0337 m_searchResultModel.setRootDocument( nullptr ); 0338 m_searchResult->clear(); 0339 for (GeoDataPlacemark *placemark: placemarks) { 0340 m_searchResult->append( new GeoDataPlacemark( *placemark ) ); 0341 } 0342 m_searchResultModel.setRootDocument( m_searchResult ); 0343 bookmarkListView->setModel( &m_searchResultModel ); 0344 updateResultMessage( m_searchResultModel.rowCount() ); 0345 } 0346 0347 GoToDialog::GoToDialog( MarbleModel* marbleModel, QWidget * parent, Qt::WindowFlags flags ) : 0348 QDialog( parent, flags ), 0349 d( new GoToDialogPrivate( this, marbleModel ) ) 0350 { 0351 d->searchLineEdit->setPlaceholderText( tr( "Address or search term" ) ); 0352 0353 d->m_searchResultModel.setRootDocument( d->m_searchResult ); 0354 d->bookmarkListView->setModel( &d->m_targetModel ); 0355 connect( d->bookmarkListView, SIGNAL(activated(QModelIndex)), 0356 this, SLOT(saveSelection(QModelIndex)) ); 0357 connect( d->searchLineEdit, SIGNAL(returnPressed()), 0358 this, SLOT(startSearch()) ); 0359 d->buttonBox->button( QDialogButtonBox::Close )->setAutoDefault( false ); 0360 connect( d->searchButton, SIGNAL(clicked(bool)), 0361 this, SLOT(updateSearchMode()) ); 0362 connect( d->browseButton, SIGNAL(clicked(bool)), 0363 this, SLOT(updateSearchMode()) ); 0364 connect( &d->m_progressTimer, SIGNAL(timeout()), 0365 this, SLOT(updateProgress()) ); 0366 connect( d->progressButton, SIGNAL(clicked(bool)), 0367 this, SLOT(stopProgressAnimation()) ); 0368 d->updateSearchMode(); 0369 d->progressButton->setVisible( false ); 0370 0371 connect( &d->m_runnerManager, SIGNAL(searchResultChanged(QVector<GeoDataPlacemark*>)), 0372 this, SLOT(updateSearchResult(QVector<GeoDataPlacemark*>)) ); 0373 connect( &d->m_runnerManager, SIGNAL(searchFinished(QString)), 0374 this, SLOT(stopProgressAnimation()) ); 0375 } 0376 0377 GoToDialog::~GoToDialog() 0378 { 0379 delete d; 0380 } 0381 0382 GeoDataCoordinates GoToDialog::coordinates() const 0383 { 0384 return d->m_coordinates; 0385 } 0386 0387 void GoToDialog::setShowRoutingItems( bool show ) 0388 { 0389 d->m_targetModel.setShowRoutingItems( show ); 0390 } 0391 0392 void GoToDialog::setSearchEnabled( bool enabled ) 0393 { 0394 d->browseButton->setVisible( enabled ); 0395 d->searchButton->setVisible( enabled ); 0396 if ( !enabled ) { 0397 d->searchButton->setChecked( false ); 0398 d->updateSearchMode(); 0399 } 0400 } 0401 0402 void GoToDialogPrivate::updateSearchMode() 0403 { 0404 bool const searchEnabled = searchButton->isChecked(); 0405 searchLineEdit->setVisible( searchEnabled ); 0406 descriptionLabel->setVisible( searchEnabled ); 0407 progressButton->setVisible( searchEnabled && m_progressTimer.isActive() ); 0408 if ( searchEnabled ) { 0409 bookmarkListView->setModel( &m_searchResultModel ); 0410 searchLineEdit->setFocus(); 0411 } else { 0412 bookmarkListView->setModel( &m_targetModel ); 0413 } 0414 } 0415 0416 void GoToDialogPrivate::updateProgress() 0417 { 0418 if ( !m_progressAnimation.isEmpty() ) { 0419 m_currentFrame = ( m_currentFrame + 1 ) % m_progressAnimation.size(); 0420 QIcon frame = m_progressAnimation[m_currentFrame]; 0421 progressButton->setIcon( frame ); 0422 } 0423 } 0424 0425 void GoToDialogPrivate::stopProgressAnimation() 0426 { 0427 searchLineEdit->setEnabled( true ); 0428 m_progressTimer.stop(); 0429 updateResultMessage( bookmarkListView->model()->rowCount() ); 0430 progressButton->setVisible( false ); 0431 } 0432 0433 void GoToDialogPrivate::updateResultMessage( int results ) 0434 { 0435 //~ singular %n result found. 0436 //~ plural %n results found. 0437 descriptionLabel->setText( QObject::tr( "%n result(s) found.", "Number of search results", results ) ); 0438 } 0439 0440 } 0441 0442 #include "moc_GoToDialog.cpp" // needed for private slots in header 0443 #include "GoToDialog.moc" // needed for Q_OBJECT here in source