QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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().compare( prefix, Qt::CaseInsensitive ) == 0 )
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().compare( prefix, Qt::CaseInsensitive ) == 0 && 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 }
QgsLocatorFilter
Definition: qgslocatorfilter.h:145
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:73
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
Definition: qgssettings.h:61
QgsFeedback::cancel
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
Definition: qgsfeedback.h:97
QgsLocator::clearPreviousResults
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:242
QgsLocatorResult
Definition: qgslocatorfilter.h:39
QgsLocator::deregisterFilter
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:46
QgsLocatorContext
Definition: qgslocatorcontext.h:31
QgsFeedback
Definition: qgsfeedback.h:43
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:124
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:40
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:66
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:226
qgssettings.h
QgsLocator::isRunning
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:237
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:231
QgsLocator::registerFilter
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:86
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:53
QgsLocator::QgsLocator
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:34
QgsLocatorFilter::setUseWithoutPrefix
void setUseWithoutPrefix(bool useWithoutPrefix)
Sets whether the filter should be used when no prefix is entered.
Definition: qgslocatorfilter.cpp:77