File indexing completed on 2025-01-05 03:59:34
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2008 Henry de Valence <hdevalence@gmail.com> 0004 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org> 0005 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0006 // SPDX-FileCopyrightText: 2011 Thibaut Gridel <tgridel@free.fr> 0007 0008 #include "SearchRunnerManager.h" 0009 0010 #include <QString> 0011 #include <QThreadPool> 0012 #include <QTimer> 0013 #include <QMutex> 0014 0015 #include "MarblePlacemarkModel.h" 0016 #include "MarbleModel.h" 0017 #include "Planet.h" 0018 #include "GeoDataPlacemark.h" 0019 #include "PluginManager.h" 0020 #include "ParseRunnerPlugin.h" 0021 #include "ReverseGeocodingRunnerPlugin.h" 0022 #include "SearchRunnerPlugin.h" 0023 #include "RunnerTask.h" 0024 0025 #include "digikam_debug.h" 0026 0027 namespace Marble 0028 { 0029 0030 class MarbleModel; 0031 0032 class Q_DECL_HIDDEN SearchRunnerManager::Private 0033 { 0034 public: 0035 Private( SearchRunnerManager *parent, const MarbleModel *marbleModel ); 0036 0037 template<typename T> 0038 QList<T*> plugins( const QList<T*> &plugins ) const; 0039 0040 void addSearchResult( const QVector<GeoDataPlacemark *> &result ); 0041 void cleanupSearchTask( SearchTask *task ); 0042 void notifySearchResultChange(); 0043 void notifySearchFinished(); 0044 0045 SearchRunnerManager *const q; 0046 const MarbleModel *const m_marbleModel; 0047 const PluginManager* m_pluginManager; 0048 QString m_lastSearchTerm; 0049 GeoDataLatLonBox m_lastPreferredBox; 0050 QMutex m_modelMutex; 0051 MarblePlacemarkModel m_model; 0052 QList<SearchTask *> m_searchTasks; 0053 QVector<GeoDataPlacemark *> m_placemarkContainer; 0054 }; 0055 0056 SearchRunnerManager::Private::Private( SearchRunnerManager *parent, const MarbleModel *marbleModel ) : 0057 q( parent ), 0058 m_marbleModel( marbleModel ), 0059 m_pluginManager( marbleModel->pluginManager() ), 0060 m_model( new MarblePlacemarkModel( parent ) ) 0061 { 0062 m_model.setPlacemarkContainer( &m_placemarkContainer ); 0063 qRegisterMetaType<QVector<GeoDataPlacemark *> >( "QVector<GeoDataPlacemark*>" ); 0064 } 0065 0066 template<typename T> 0067 QList<T*> SearchRunnerManager::Private::plugins( const QList<T*> &plugins ) const 0068 { 0069 QList<T*> result; 0070 for( T* plugin: plugins ) { 0071 if ( ( m_marbleModel && m_marbleModel->workOffline() && !plugin->canWorkOffline() ) ) { 0072 continue; 0073 } 0074 0075 if ( !plugin->canWork() ) { 0076 continue; 0077 } 0078 0079 if ( m_marbleModel && !plugin->supportsCelestialBody( m_marbleModel->planet()->id() ) ) 0080 { 0081 continue; 0082 } 0083 0084 result << plugin; 0085 } 0086 0087 return result; 0088 } 0089 0090 void SearchRunnerManager::Private::addSearchResult( const QVector<GeoDataPlacemark *> &result ) 0091 { 0092 qCDebug(DIGIKAM_MARBLE_LOG) << "Runner reports" << result.size() << " search results"; 0093 if( result.isEmpty() ) 0094 return; 0095 0096 m_modelMutex.lock(); 0097 int start = m_placemarkContainer.size(); 0098 int count = 0; 0099 bool distanceCompare = m_marbleModel->planet() != nullptr; 0100 for( int i=0; i<result.size(); ++i ) { 0101 bool same = false; 0102 for ( int j=0; j<m_placemarkContainer.size(); ++j ) { 0103 if ( distanceCompare && 0104 (result[i]->coordinate().sphericalDistanceTo(m_placemarkContainer[j]->coordinate()) 0105 * m_marbleModel->planet()->radius() < 1 ) ) { 0106 same = true; 0107 } 0108 } 0109 if ( !same ) { 0110 m_placemarkContainer.append( result[i] ); 0111 ++count; 0112 } 0113 } 0114 m_model.addPlacemarks( start, count ); 0115 m_modelMutex.unlock(); 0116 notifySearchResultChange(); 0117 } 0118 0119 void SearchRunnerManager::Private::cleanupSearchTask( SearchTask *task ) 0120 { 0121 m_searchTasks.removeAll( task ); 0122 qCDebug(DIGIKAM_MARBLE_LOG) << "removing search task" << m_searchTasks.size() << (quintptr)task; 0123 if ( m_searchTasks.isEmpty() ) { 0124 if( m_placemarkContainer.isEmpty() ) { 0125 notifySearchResultChange(); 0126 } 0127 notifySearchFinished(); 0128 } 0129 } 0130 0131 void SearchRunnerManager::Private::notifySearchResultChange() 0132 { 0133 Q_EMIT q->searchResultChanged(&m_model); 0134 Q_EMIT q->searchResultChanged(m_placemarkContainer); 0135 } 0136 0137 void SearchRunnerManager::Private::notifySearchFinished() 0138 { 0139 Q_EMIT q->searchFinished(m_lastSearchTerm); 0140 Q_EMIT q->placemarkSearchFinished(); 0141 } 0142 0143 SearchRunnerManager::SearchRunnerManager( const MarbleModel *marbleModel, QObject *parent ) : 0144 QObject( parent ), 0145 d( new Private( this, marbleModel ) ) 0146 { 0147 if ( QThreadPool::globalInstance()->maxThreadCount() < 4 ) { 0148 QThreadPool::globalInstance()->setMaxThreadCount( 4 ); 0149 } 0150 } 0151 0152 SearchRunnerManager::~SearchRunnerManager() 0153 { 0154 delete d; 0155 } 0156 0157 void SearchRunnerManager::findPlacemarks( const QString &searchTerm, const GeoDataLatLonBox &preferred ) 0158 { 0159 if ( searchTerm == d->m_lastSearchTerm && preferred == d->m_lastPreferredBox ) { 0160 d->notifySearchResultChange(); 0161 d->notifySearchFinished(); 0162 return; 0163 } 0164 0165 d->m_lastSearchTerm = searchTerm; 0166 d->m_lastPreferredBox = preferred; 0167 0168 d->m_searchTasks.clear(); 0169 0170 d->m_modelMutex.lock(); 0171 bool placemarkContainerChanged = false; 0172 if (!d->m_placemarkContainer.isEmpty()) { 0173 d->m_model.removePlacemarks( QString::fromUtf8("PlacemarkRunnerManager"), 0, d->m_placemarkContainer.size() ); 0174 qDeleteAll( d->m_placemarkContainer ); 0175 d->m_placemarkContainer.clear(); 0176 placemarkContainerChanged = true; 0177 } 0178 d->m_modelMutex.unlock(); 0179 if (placemarkContainerChanged) { 0180 d->notifySearchResultChange(); 0181 } 0182 0183 if ( searchTerm.trimmed().isEmpty() ) { 0184 d->notifySearchFinished(); 0185 return; 0186 } 0187 0188 QList<const SearchRunnerPlugin *> plugins = d->plugins( d->m_pluginManager->searchRunnerPlugins() ); 0189 for( const SearchRunnerPlugin *plugin: plugins ) { 0190 SearchTask *task = new SearchTask( plugin->newRunner(), this, d->m_marbleModel, searchTerm, preferred ); 0191 connect( task, SIGNAL(finished(SearchTask*)), this, SLOT(cleanupSearchTask(SearchTask*)) ); 0192 d->m_searchTasks << task; 0193 qCDebug(DIGIKAM_MARBLE_LOG) << "search task " << plugin->nameId() << " " << (quintptr)task; 0194 } 0195 0196 for( SearchTask *task: d->m_searchTasks ) { 0197 QThreadPool::globalInstance()->start( task ); 0198 } 0199 0200 if ( plugins.isEmpty() ) { 0201 d->cleanupSearchTask( nullptr ); 0202 } 0203 } 0204 0205 QVector<GeoDataPlacemark *> SearchRunnerManager::searchPlacemarks( const QString &searchTerm, const GeoDataLatLonBox &preferred, int timeout ) 0206 { 0207 QEventLoop localEventLoop; 0208 QTimer watchdog; 0209 watchdog.setSingleShot(true); 0210 connect( &watchdog, SIGNAL(timeout()), 0211 &localEventLoop, SLOT(quit())); 0212 connect(this, SIGNAL(placemarkSearchFinished()), 0213 &localEventLoop, SLOT(quit()), Qt::QueuedConnection ); 0214 0215 watchdog.start( timeout ); 0216 findPlacemarks( searchTerm, preferred ); 0217 localEventLoop.exec(); 0218 return d->m_placemarkContainer; 0219 } 0220 0221 } 0222 0223 #include "moc_SearchRunnerManager.cpp"