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