QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgslocator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslocator.cpp
3  --------------
4  begin : May 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgslocator.h"
19 #include "qgssettings.h"
20 #include <QtConcurrent>
21 #include <functional>
22 
23 const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiteral( "actions" )
24  << QStringLiteral( "processing_alg" )
25  << QStringLiteral( "layertree" )
26  << QStringLiteral( "layouts" )
27  << QStringLiteral( "features" )
28  << QStringLiteral( "allfeatures" )
29  << QStringLiteral( "calculator" )
30  << QStringLiteral( "bookmarks" )
31  << QStringLiteral( "optionpages" )
32  << QStringLiteral( "edit_features" )
33  << QStringLiteral( "goto" )
34  << QStringLiteral( "nominatimgeocoder" ) ;
35 
36 QgsLocator::QgsLocator( QObject *parent )
37  : QObject( parent )
38 {
39  qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
40 }
41 
43 {
44  cancelRunningQuery();
45  qDeleteAll( mFilters );
46 }
47 
49 {
50  cancelRunningQuery();
51  mFilters.removeAll( filter );
52  delete filter;
53 }
54 
55 QList<QgsLocatorFilter *> QgsLocator::filters( const QString &prefix )
56 {
57  if ( !prefix.isEmpty() )
58  {
59  QList<QgsLocatorFilter *> filters = QList<QgsLocatorFilter *>();
60  for ( QgsLocatorFilter *filter : mFilters )
61  {
62  if ( !filter->activePrefix().isEmpty() && filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 )
63  {
64  filters << filter;
65  }
66  }
67  return filters;
68  }
69  else
70  {
71  return mFilters;
72  }
73 }
74 
75 QMap<QString, QgsLocatorFilter *> QgsLocator::prefixedFilters() const
76 {
77  QMap<QString, QgsLocatorFilter *> filters = QMap<QString, QgsLocatorFilter *>();
78  for ( QgsLocatorFilter *filter : mFilters )
79  {
80  if ( !filter->activePrefix().isEmpty() && filter->enabled() )
81  {
82 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
83  filters.insertMulti( filter->activePrefix(), filter );
84 #else
85  filters.insert( filter->activePrefix(), filter );
86 #endif
87  }
88  }
89  return filters;
90 }
91 
93 {
94  mFilters.append( filter );
95  filter->setParent( this );
96 
97  // restore settings
98  bool enabled = QgsLocator::settingsLocatorFilterEnabled.value( filter->name() );
99  bool byDefault = QgsLocator::settingsLocatorFilterDefault.value( filter->name(), true, filter->useWithoutPrefix() );
100  QString prefix = QgsLocator::settingsLocatorFilterPrefix.value( filter->name(), true, filter->prefix() );
101  if ( prefix.isEmpty() )
102  {
103  prefix = filter->prefix();
104  }
105 
106  if ( !prefix.isEmpty() )
107  {
108  if ( CORE_FILTERS.contains( filter->name() ) )
109  {
110  //inbuilt filter, no prefix check
111  filter->setActivePrefix( prefix );
112  }
113  else if ( prefix.length() >= 3 || prefix != filter->prefix() )
114  {
115  // for plugins either the native prefix is >3 char or it has been customized by user
116  filter->setActivePrefix( prefix );
117  }
118  else
119  {
120  // otherwise set it to empty string (not NULL)
121  filter->setActivePrefix( QString( "" ) );
122  }
123  }
124 
125  filter->setEnabled( enabled );
126  filter->setUseWithoutPrefix( byDefault );
127 }
128 
129 void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback )
130 {
131  mAutocompletionList.clear();
132 
133  QgsLocatorContext context( c );
134  // ideally this should not be required, as well behaved callers
135  // will NOT fire up a new fetchResults call while an existing one is
136  // operating/waiting to be canceled...
137  cancelRunningQuery();
138 
139  // if no feedback object was passed, create one that is owned by this object
140  // to ensure that filters ALWAYS receive a valid feedback
141  if ( !feedback )
142  {
143  mOwnedFeedback.reset( new QgsFeedback() );
144  feedback = mOwnedFeedback.get();
145  }
146  else
147  {
148  mOwnedFeedback.reset( nullptr );
149  }
150  mFeedback = feedback;
151 
152  QList< QgsLocatorFilter * > activeFilters;
153  QString searchString = string;
154  QString prefix = searchString.left( std::max( static_cast<int>( searchString.indexOf( ' ' ) ), 0 ) );
155  if ( !prefix.isEmpty() )
156  {
157  for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
158  {
159  if ( filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 && filter->enabled() )
160  {
161  activeFilters << filter;
162  }
163  }
164  context.usingPrefix = !activeFilters.empty();
165  }
166  if ( !activeFilters.isEmpty() )
167  {
168  searchString = searchString.mid( prefix.length() + 1 );
169  }
170  else
171  {
172  for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
173  {
174  if ( filter->useWithoutPrefix() && filter->enabled() )
175  {
176  activeFilters << filter;
177  }
178  }
179  }
180 
181  QList< QgsLocatorFilter *> threadedFilters;
182  for ( QgsLocatorFilter *filter : std::as_const( activeFilters ) )
183  {
184  filter->clearPreviousResults();
185  std::unique_ptr< QgsLocatorFilter > clone( filter->clone() );
186  connect( clone.get(), &QgsLocatorFilter::resultFetched, clone.get(), [this, filter]( QgsLocatorResult result )
187  {
188  result.filter = filter;
189  filterSentResult( result );
190  } );
191  QStringList autoCompleteList = clone->prepare( searchString, context );
192  if ( context.usingPrefix )
193  {
194  for ( int i = 0; i < autoCompleteList.length(); i++ )
195  {
196  autoCompleteList[i].prepend( QStringLiteral( "%1 " ).arg( prefix ) );
197  }
198  }
199  mAutocompletionList.append( autoCompleteList );
200 
201  if ( clone->flags() & QgsLocatorFilter::FlagFast )
202  {
203  // filter is fast enough to fetch results on the main thread
204  clone->fetchResults( searchString, context, feedback );
205  }
206  else
207  {
208  threadedFilters.append( clone.release() );
209  }
210  }
211 
212  mActiveThreads.clear();
213  for ( QgsLocatorFilter *filter : std::as_const( threadedFilters ) )
214  {
215  QThread *thread = new QThread();
216  mActiveThreads.append( thread );
217  filter->moveToThread( thread );
218  connect( thread, &QThread::started, filter, [filter, searchString, context, feedback]
219  {
220  int delay = filter->fetchResultsDelay();
221  while ( delay > 0 )
222  {
223  if ( feedback->isCanceled() )
224  break;
225  QThread::msleep( 50 );
226  delay -= 50;
227  }
228  if ( !feedback->isCanceled() )
229  filter->fetchResults( searchString, context, feedback );
230  filter->emit finished();
231  }, Qt::QueuedConnection );
232  connect( filter, &QgsLocatorFilter::finished, thread, &QThread::quit );
233  connect( filter, &QgsLocatorFilter::finished, filter, &QgsLocatorFilter::deleteLater );
234  connect( thread, &QThread::finished, thread, [this, thread]
235  {
236  mActiveThreads.removeAll( thread );
237  if ( mActiveThreads.empty() )
238  emit finished();
239  } );
240  connect( thread, &QThread::finished, thread, &QThread::deleteLater );
241  thread->start();
242  }
243 
244  emit searchPrepared();
245 
246  if ( mActiveThreads.empty() )
247  emit finished();
248 }
249 
251 {
252  cancelRunningQuery();
253 }
254 
256 {
257  if ( mFeedback )
258  mFeedback->cancel();
259 }
260 
262 {
263  return !mActiveThreads.empty();
264 }
265 
267 {
268  for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
269  {
270  if ( filter->enabled() )
271  {
272  filter->clearPreviousResults();
273  }
274  }
275 }
276 
277 void QgsLocator::filterSentResult( QgsLocatorResult result )
278 {
279  // if query has been canceled then discard any results we receive
280  if ( mFeedback->isCanceled() )
281  return;
282 
283  emit foundResult( result );
284 }
285 
286 void QgsLocator::cancelRunningQuery()
287 {
288  if ( !mActiveThreads.empty() )
289  {
290  // cancel existing job
291  mFeedback->cancel();
292  while ( !mActiveThreads.empty() )
293  {
294  QCoreApplication::processEvents();
295  }
296  }
297 }
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:85
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Encapsulates the properties relating to the context of a locator search.
bool usingPrefix
Will be true if search is being conducted using a filter prefix.
Abstract base class for filters which collect locator results.
void setEnabled(bool enabled)
Sets whether the filter is enabled.
void setActivePrefix(const QString &activePrefix)
Sets the prefix as being used by the locator.
virtual QString prefix() const
Returns the search prefix character(s) for this filter.
bool useWithoutPrefix() const
Returns true if the filter should be used when no prefix is entered.
@ FlagFast
Filter finds results quickly and can be safely run in the main thread.
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
void finished()
Emitted when the filter finishes fetching results.
virtual QString name() const =0
Returns the unique name for the filter.
void resultFetched(const QgsLocatorResult &result)
Should be emitted by filters whenever they encounter a matching result during within their fetchResul...
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
static const QList< QString > CORE_FILTERS
List of core filters (i.e. not plugin filters)
Definition: qgslocator.h:65
void searchPrepared()
Emitted when locator has prepared the search (.
void cancel()
Cancels any current running query, and blocks until query is completely canceled by all filters.
Definition: qgslocator.cpp:250
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:36
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:92
static const QgsSettingsEntryBool settingsLocatorFilterEnabled
Settings entry locator filter enabled.
Definition: qgslocator.h:158
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called.
void fetchResults(const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback=nullptr)
Triggers the background fetching of filter results for a specified search string.
Definition: qgslocator.cpp:129
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:266
static const QgsSettingsEntryBool settingsLocatorFilterDefault
Settings entry locator filter default value.
Definition: qgslocator.h:160
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:42
Q_DECL_DEPRECATED QMap< QString, QgsLocatorFilter * > prefixedFilters() const
Returns a map of prefix to filter, for all registered filters with valid prefixes.
Definition: qgslocator.cpp:75
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:261
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:255
static const QgsSettingsEntryString settingsLocatorFilterPrefix
Settings entry locator filter prefix.
Definition: qgslocator.h:162
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:55
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:48
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c