QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
35 QgsLocator::QgsLocator( QObject *parent )
36  : QObject( parent )
37 {
38  qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
39 }
40 
42 {
43  cancelRunningQuery();
44  qDeleteAll( mFilters );
45 }
46 
48 {
49  cancelRunningQuery();
50  mFilters.removeAll( filter );
51  delete filter;
52 }
53 
54 QList<QgsLocatorFilter *> QgsLocator::filters( const QString &prefix )
55 {
56  if ( !prefix.isEmpty() )
57  {
58  QList<QgsLocatorFilter *> filters = QList<QgsLocatorFilter *>();
59  for ( QgsLocatorFilter *filter : mFilters )
60  {
61  if ( !filter->activePrefix().isEmpty() && filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 )
62  {
63  filters << filter;
64  }
65  }
66  return filters;
67  }
68  else
69  {
70  return mFilters;
71  }
72 }
73 
74 QMap<QString, QgsLocatorFilter *> QgsLocator::prefixedFilters() const
75 {
76  QMap<QString, QgsLocatorFilter *> filters = QMap<QString, QgsLocatorFilter *>();
77  for ( QgsLocatorFilter *filter : mFilters )
78  {
79  if ( !filter->activePrefix().isEmpty() && filter->enabled() )
80  {
81 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
82  filters.insertMulti( filter->activePrefix(), filter );
83 #else
84  filters.insert( filter->activePrefix(), filter );
85 #endif
86  }
87  }
88  return filters;
89 }
90 
92 {
93  mFilters.append( filter );
94  filter->setParent( this );
95 
96  // restore settings
97  QgsSettings settings;
98  bool enabled = settings.value( QStringLiteral( "locator_filters/enabled_%1" ).arg( filter->name() ), true, QgsSettings::Section::Gui ).toBool();
99  bool byDefault = settings.value( QStringLiteral( "locator_filters/default_%1" ).arg( filter->name() ), filter->useWithoutPrefix(), QgsSettings::Section::Gui ).toBool();
100  QString prefix = settings.value( QStringLiteral( "locator_filters/prefix_%1" ).arg( filter->name() ), filter->prefix(), QgsSettings::Section::Gui ).toString();
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( searchString.indexOf( ' ' ), 0 ) );
155  if ( !prefix.isEmpty() )
156  {
157  for ( QgsLocatorFilter *filter : qgis::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 : qgis::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 : qgis::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 : qgis::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  if ( !feedback->isCanceled() )
221  filter->fetchResults( searchString, context, feedback );
222  filter->emit finished();
223  }, Qt::QueuedConnection );
224  connect( filter, &QgsLocatorFilter::finished, thread, &QThread::quit );
225  connect( filter, &QgsLocatorFilter::finished, filter, &QgsLocatorFilter::deleteLater );
226  connect( thread, &QThread::finished, thread, [this, thread]
227  {
228  mActiveThreads.removeAll( thread );
229  if ( mActiveThreads.empty() )
230  emit finished();
231  } );
232  connect( thread, &QThread::finished, thread, &QThread::deleteLater );
233  thread->start();
234  }
235 
236  emit searchPrepared();
237 
238  if ( mActiveThreads.empty() )
239  emit finished();
240 }
241 
243 {
244  cancelRunningQuery();
245 }
246 
248 {
249  if ( mFeedback )
250  mFeedback->cancel();
251 }
252 
254 {
255  return !mActiveThreads.empty();
256 }
257 
259 {
260  for ( QgsLocatorFilter *filter : qgis::as_const( mFilters ) )
261  {
262  if ( filter->enabled() )
263  {
264  filter->clearPreviousResults();
265  }
266  }
267 }
268 
269 void QgsLocator::filterSentResult( QgsLocatorResult result )
270 {
271  // if query has been canceled then discard any results we receive
272  if ( mFeedback->isCanceled() )
273  return;
274 
275  emit foundResult( result );
276 }
277 
278 void QgsLocator::cancelRunningQuery()
279 {
280  if ( !mActiveThreads.empty() )
281  {
282  // cancel existing job
283  mFeedback->cancel();
284  while ( !mActiveThreads.empty() )
285  {
286  QCoreApplication::processEvents();
287  }
288  }
289 }
QgsLocatorFilter
Abstract base class for filters which collect locator results.
Definition: qgslocatorfilter.h:146
QgsLocatorFilter::prefix
virtual QString prefix() const
Returns the search prefix character(s) for this filter.
Definition: qgslocatorfilter.h:215
QgsLocator::CORE_FILTERS
static const QList< QString > CORE_FILTERS
List of core filters (i.e. not plugin filters)
Definition: qgslocator.h:64
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsLocator::prefixedFilters
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:74
QgsLocatorFilter::finished
void finished()
Emitted when the filter finishes fetching results.
QgsLocatorFilter::name
virtual QString name() const =0
Returns the unique name for the filter.
qgslocator.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsFeedback::cancel
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:84
QgsLocator::clearPreviousResults
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:258
QgsLocatorResult
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
Definition: qgslocatorfilter.h:40
QgsLocator::deregisterFilter
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:47
QgsLocatorContext
Encapsulates the properties relating to the context of a locator search.
Definition: qgslocatorcontext.h:32
QgsLocator::searchPrepared
void searchPrepared()
Emitted when locator has prepared the search (.
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsLocator::fetchResults
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
QgsLocatorFilter::resultFetched
void resultFetched(const QgsLocatorResult &result)
Should be emitted by filters whenever they encounter a matching result during within their fetchResul...
QgsLocator::~QgsLocator
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:41
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
c
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
Definition: porting_processing.dox:1
QgsLocator::cancel
void cancel()
Cancels any current running query, and blocks until query is completely canceled by all filters.
Definition: qgslocator.cpp:242
qgssettings.h
QgsLocator::isRunning
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:253
QgsLocatorFilter::setEnabled
void setEnabled(bool enabled)
Sets whether the filter is enabled.
Definition: qgslocatorfilter.cpp:57
QgsLocatorContext::usingPrefix
bool usingPrefix
Will be true if search is being conducted using a filter prefix.
Definition: qgslocatorcontext.h:57
QgsLocator::cancelWithoutBlocking
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:247
QgsLocator::registerFilter
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:91
QgsLocator::finished
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
QgsLocator::foundResult
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called.
QgsLocatorFilter::useWithoutPrefix
bool useWithoutPrefix() const
Returns true if the filter should be used when no prefix is entered.
Definition: qgslocatorfilter.cpp:72
QgsLocatorFilter::setActivePrefix
void setActivePrefix(const QString &activePrefix)
Sets the prefix as being used by the locator.
Definition: qgslocatorfilter.cpp:93
QgsLocatorFilter::FlagFast
@ FlagFast
Filter finds results quickly and can be safely run in the main thread.
Definition: qgslocatorfilter.h:165
QgsLocator::filters
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:54
QgsLocator::QgsLocator
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:35
QgsLocatorFilter::setUseWithoutPrefix
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
Definition: qgslocatorfilter.cpp:77