QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 
34 QgsLocator::QgsLocator( QObject *parent )
35  : QObject( parent )
36 {
37  qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
38 }
39 
41 {
42  cancelRunningQuery();
43  qDeleteAll( mFilters );
44 }
45 
47 {
48  cancelRunningQuery();
49  mFilters.removeAll( filter );
50  delete filter;
51 }
52 
53 QList<QgsLocatorFilter *> QgsLocator::filters( const QString &prefix )
54 {
55  if ( !prefix.isEmpty() )
56  {
57  QList<QgsLocatorFilter *> filters = QList<QgsLocatorFilter *>();
58  for ( QgsLocatorFilter *filter : mFilters )
59  {
60  if ( !filter->activePrefix().isEmpty() && filter->activePrefix() == prefix )
61  {
62  filters << filter;
63  }
64  }
65  return filters;
66  }
67  else
68  {
69  return mFilters;
70  }
71 }
72 
73 QMap<QString, QgsLocatorFilter *> QgsLocator::prefixedFilters() const
74 {
75  QMap<QString, QgsLocatorFilter *> filters = QMap<QString, QgsLocatorFilter *>();
76  for ( QgsLocatorFilter *filter : mFilters )
77  {
78  if ( !filter->activePrefix().isEmpty() && filter->enabled() )
79  {
80  filters.insertMulti( filter->activePrefix(), filter );
81  }
82  }
83  return filters;
84 }
85 
87 {
88  mFilters.append( filter );
89  filter->setParent( this );
90 
91  // restore settings
92  QgsSettings settings;
93  bool enabled = settings.value( QStringLiteral( "locator_filters/enabled_%1" ).arg( filter->name() ), true, QgsSettings::Section::Gui ).toBool();
94  bool byDefault = settings.value( QStringLiteral( "locator_filters/default_%1" ).arg( filter->name() ), filter->useWithoutPrefix(), QgsSettings::Section::Gui ).toBool();
95  QString prefix = settings.value( QStringLiteral( "locator_filters/prefix_%1" ).arg( filter->name() ), filter->prefix(), QgsSettings::Section::Gui ).toString();
96  if ( prefix.isEmpty() )
97  {
98  prefix = filter->prefix();
99  }
100 
101  if ( !prefix.isEmpty() )
102  {
103  if ( CORE_FILTERS.contains( filter->name() ) )
104  {
105  //inbuilt filter, no prefix check
106  filter->setActivePrefix( prefix );
107  }
108  else if ( prefix.length() >= 3 || prefix != filter->prefix() )
109  {
110  // for plugins either the native prefix is >3 char or it has been customized by user
111  filter->setActivePrefix( prefix );
112  }
113  else
114  {
115  // otherwise set it to empty string (not NULL)
116  filter->setActivePrefix( QString( "" ) );
117  }
118  }
119 
120  filter->setEnabled( enabled );
121  filter->setUseWithoutPrefix( byDefault );
122 }
123 
124 void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback )
125 {
126  QgsLocatorContext context( c );
127  // ideally this should not be required, as well behaved callers
128  // will NOT fire up a new fetchResults call while an existing one is
129  // operating/waiting to be canceled...
130  cancelRunningQuery();
131 
132  // if no feedback object was passed, create one that is owned by this object
133  // to ensure that filters ALWAYS receive a valid feedback
134  if ( !feedback )
135  {
136  mOwnedFeedback.reset( new QgsFeedback() );
137  feedback = mOwnedFeedback.get();
138  }
139  else
140  {
141  mOwnedFeedback.reset( nullptr );
142  }
143  mFeedback = feedback;
144 
145  QList< QgsLocatorFilter * > activeFilters;
146  QString searchString = string;
147  QString prefix = searchString.left( std::max( searchString.indexOf( ' ' ), 0 ) );
148  if ( !prefix.isEmpty() )
149  {
150  for ( QgsLocatorFilter *filter : qgis::as_const( mFilters ) )
151  {
152  if ( filter->activePrefix() == prefix && filter->enabled() )
153  {
154  activeFilters << filter;
155  }
156  }
157  context.usingPrefix = !activeFilters.empty();
158  }
159  if ( !activeFilters.isEmpty() )
160  {
161  searchString = searchString.mid( prefix.length() + 1 );
162  }
163  else
164  {
165  for ( QgsLocatorFilter *filter : qgis::as_const( mFilters ) )
166  {
167  if ( filter->useWithoutPrefix() && filter->enabled() )
168  {
169  activeFilters << filter;
170  }
171  }
172  }
173 
174  QList< QgsLocatorFilter *> threadedFilters;
175  for ( QgsLocatorFilter *filter : qgis::as_const( activeFilters ) )
176  {
177  filter->clearPreviousResults();
178  std::unique_ptr< QgsLocatorFilter > clone( filter->clone() );
179  connect( clone.get(), &QgsLocatorFilter::resultFetched, clone.get(), [this, filter]( QgsLocatorResult result )
180  {
181  result.filter = filter;
182  filterSentResult( result );
183  } );
184  clone->prepare( searchString, context );
185 
186  if ( clone->flags() & QgsLocatorFilter::FlagFast )
187  {
188  // filter is fast enough to fetch results on the main thread
189  clone->fetchResults( searchString, context, feedback );
190  }
191  else
192  {
193  // run filter in background
194  threadedFilters.append( clone.release() );
195  }
196  }
197 
198  mActiveThreads.clear();
199  for ( QgsLocatorFilter *filter : qgis::as_const( threadedFilters ) )
200  {
201  QThread *thread = new QThread();
202  mActiveThreads.append( thread );
203  filter->moveToThread( thread );
204  connect( thread, &QThread::started, filter, [filter, searchString, context, feedback]
205  {
206  if ( !feedback->isCanceled() )
207  filter->fetchResults( searchString, context, feedback );
208  filter->emit finished();
209  }, Qt::QueuedConnection );
210  connect( filter, &QgsLocatorFilter::finished, thread, &QThread::quit );
211  connect( filter, &QgsLocatorFilter::finished, filter, &QgsLocatorFilter::deleteLater );
212  connect( thread, &QThread::finished, thread, [this, thread]
213  {
214  mActiveThreads.removeAll( thread );
215  if ( mActiveThreads.empty() )
216  emit finished();
217  } );
218  connect( thread, &QThread::finished, thread, &QThread::deleteLater );
219  thread->start();
220  }
221 
222  if ( mActiveThreads.empty() )
223  emit finished();
224 }
225 
227 {
228  cancelRunningQuery();
229 }
230 
232 {
233  if ( mFeedback )
234  mFeedback->cancel();
235 }
236 
238 {
239  return !mActiveThreads.empty();
240 }
241 
243 {
244  for ( QgsLocatorFilter *filter : qgis::as_const( mFilters ) )
245  {
246  if ( filter->enabled() )
247  {
248  filter->clearPreviousResults();
249  }
250  }
251 }
252 
253 void QgsLocator::filterSentResult( QgsLocatorResult result )
254 {
255  // if query has been canceled then discard any results we receive
256  if ( mFeedback->isCanceled() )
257  return;
258 
259  emit foundResult( result );
260 }
261 
262 void QgsLocator::cancelRunningQuery()
263 {
264  if ( !mActiveThreads.empty() )
265  {
266  // cancel existing job
267  mFeedback->cancel();
268  while ( !mActiveThreads.empty() )
269  {
270  QCoreApplication::processEvents();
271  }
272  }
273 }
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:86
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:231
void resultFetched(const QgsLocatorResult &result)
Should be emitted by filters whenever they encounter a matching result during within their fetchResul...
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:85
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:34
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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:124
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:40
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:237
void setEnabled(bool enabled)
Sets whether the filter is enabled.
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:53
void finished()
Emitted when locator has finished a query, either as a result of successful completion or early cance...
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
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
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
void cancel()
Cancels any current running query, and blocks until query is completely canceled by all filters...
Definition: qgslocator.cpp:226
void finished()
Emitted when the filter finishes fetching results.
Encapsulates the properties relating to the context of a locator search.
Encapsulates properties of an individual matching result found by a QgsLocatorFilter.
Abstract base class for filters which collect locator results.
bool useWithoutPrefix() const
Returns true if the filter should be used when no prefix is entered.
void setActivePrefix(const QString &activePrefix)
Sets the prefix as being used by the locator.
void foundResult(const QgsLocatorResult &result)
Emitted whenever a filter encounters a matching result after the fetchResults() method is called...
bool usingPrefix
Will be true if search is being conducted using a filter prefix.
virtual QString prefix() const
Returns the search prefix character(s) for this filter.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
virtual QString name() const =0
Returns the unique name for the filter.
static const QList< QString > CORE_FILTERS
List of core filters (i.e. not plugin filters)
Definition: qgslocator.h:64
Filter finds results quickly and can be safely run in the main thread.
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:73
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:46
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:242