QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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 "qgsmessagelog.h"
21
22#include <QtConcurrent>
23#include <functional>
24
25const QgsSettingsEntryBool *QgsLocator::settingsLocatorFilterEnabled = new QgsSettingsEntryBool( QStringLiteral( "enabled" ), sTreeLocatorFilters, true, QObject::tr( "Locator filter enabled" ) );
26
27const QgsSettingsEntryBool *QgsLocator::settingsLocatorFilterDefault = new QgsSettingsEntryBool( QStringLiteral( "default" ), sTreeLocatorFilters, false, QObject::tr( "Locator filter default value" ) );
28
29const QgsSettingsEntryString *QgsLocator::settingsLocatorFilterPrefix = new QgsSettingsEntryString( QStringLiteral( "prefix" ), sTreeLocatorFilters, QString(), QObject::tr( "Locator filter prefix" ) );
30
31const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiteral( "actions" )
32 << QStringLiteral( "processing_alg" )
33 << QStringLiteral( "layertree" )
34 << QStringLiteral( "layouts" )
35 << QStringLiteral( "features" )
36 << QStringLiteral( "allfeatures" )
37 << QStringLiteral( "calculator" )
38 << QStringLiteral( "bookmarks" )
39 << QStringLiteral( "optionpages" )
40 << QStringLiteral( "edit_features" )
41 << QStringLiteral( "goto" )
42 << QStringLiteral( "nominatimgeocoder" ) ;
43
44QgsLocator::QgsLocator( QObject *parent )
45 : QObject( parent )
46{
47 qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
48}
49
51{
52 cancelRunningQuery();
53 qDeleteAll( mFilters );
54}
55
57{
58 cancelRunningQuery();
59 mFilters.removeAll( filter );
60 delete filter;
61}
62
63QList<QgsLocatorFilter *> QgsLocator::filters( const QString &prefix )
64{
65 if ( !prefix.isEmpty() )
66 {
67 QList<QgsLocatorFilter *> filters = QList<QgsLocatorFilter *>();
68 for ( QgsLocatorFilter *filter : mFilters )
69 {
70 if ( !filter->activePrefix().isEmpty() && filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 )
71 {
72 filters << filter;
73 }
74 }
75 return filters;
76 }
77 else
78 {
79 return mFilters;
80 }
81}
82
83QMap<QString, QgsLocatorFilter *> QgsLocator::prefixedFilters() const
84{
85 QMap<QString, QgsLocatorFilter *> filters = QMap<QString, QgsLocatorFilter *>();
86 for ( QgsLocatorFilter *filter : mFilters )
87 {
88 if ( !filter->activePrefix().isEmpty() && filter->enabled() )
89 {
90#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
91 filters.insertMulti( filter->activePrefix(), filter );
92#else
93 filters.insert( filter->activePrefix(), filter );
94#endif
95 }
96 }
97 return filters;
98}
99
101{
102 mFilters.append( filter );
103 filter->setParent( this );
104
105 // restore settings
106 bool enabled = QgsLocator::settingsLocatorFilterEnabled->value( filter->name() );
108 QString prefix = QgsLocator::settingsLocatorFilterPrefix->valueWithDefaultOverride( filter->prefix(), filter->name() );
109 if ( prefix.isEmpty() )
110 {
111 prefix = filter->prefix();
112 }
113
114 if ( !prefix.isEmpty() )
115 {
116 if ( CORE_FILTERS.contains( filter->name() ) )
117 {
118 //inbuilt filter, no prefix check
119 filter->setActivePrefix( prefix );
120 }
121 else if ( prefix.length() >= 3 || prefix != filter->prefix() )
122 {
123 // for plugins either the native prefix is >3 char or it has been customized by user
124 filter->setActivePrefix( prefix );
125 }
126 else
127 {
128 // otherwise set it to empty string (not NULL)
129 filter->setActivePrefix( QString( "" ) );
130 }
131 }
132
133 filter->setEnabled( enabled );
134 filter->setUseWithoutPrefix( byDefault );
135}
136
137void QgsLocator::fetchResults( const QString &string, const QgsLocatorContext &c, QgsFeedback *feedback )
138{
139 mAutocompletionList.clear();
140
141 QgsLocatorContext context( c );
142 // ideally this should not be required, as well behaved callers
143 // will NOT fire up a new fetchResults call while an existing one is
144 // operating/waiting to be canceled...
145 cancelRunningQuery();
146
147 // if no feedback object was passed, create one that is owned by this object
148 // to ensure that filters ALWAYS receive a valid feedback
149 if ( !feedback )
150 {
151 mOwnedFeedback.reset( new QgsFeedback() );
152 feedback = mOwnedFeedback.get();
153 }
154 else
155 {
156 mOwnedFeedback.reset( nullptr );
157 }
158 mFeedback = feedback;
159
160 QList< QgsLocatorFilter * > activeFilters;
161 QString searchString = string;
162 QString prefix = searchString.left( std::max( static_cast<int>( searchString.indexOf( ' ' ) ), 0 ) );
163 if ( !prefix.isEmpty() )
164 {
165 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
166 {
167 if ( filter->activePrefix().compare( prefix, Qt::CaseInsensitive ) == 0 && filter->enabled() )
168 {
169 activeFilters << filter;
170 }
171 }
172 context.usingPrefix = !activeFilters.empty();
173 }
174 if ( !activeFilters.isEmpty() )
175 {
176 searchString = searchString.mid( prefix.length() + 1 );
177 }
178 else
179 {
180 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
181 {
182 if ( filter->useWithoutPrefix() && filter->enabled() )
183 {
184 activeFilters << filter;
185 }
186 }
187 }
188
189 QList< QgsLocatorFilter *> threadedFilters;
190 for ( QgsLocatorFilter *filter : std::as_const( activeFilters ) )
191 {
192 filter->clearPreviousResults();
193 std::unique_ptr< QgsLocatorFilter > clone( filter->clone() );
194 if ( ! clone )
195 {
196 QgsMessageLog::logMessage( tr( "QgsLocatorFilter '%1' could not provide a valid clone" ).arg( filter->name() ), QString(), Qgis::MessageLevel::Critical );
197 continue;
198 }
199 connect( clone.get(), &QgsLocatorFilter::resultFetched, clone.get(), [this, filter]( QgsLocatorResult result )
200 {
201 result.filter = filter;
202 filterSentResult( result );
203 } );
204 QStringList autoCompleteList = clone->prepare( searchString, context );
205 if ( context.usingPrefix )
206 {
207 for ( int i = 0; i < autoCompleteList.length(); i++ )
208 {
209 autoCompleteList[i].prepend( QStringLiteral( "%1 " ).arg( prefix ) );
210 }
211 }
212 mAutocompletionList.append( autoCompleteList );
213
214 if ( clone->flags() & QgsLocatorFilter::FlagFast )
215 {
216 // filter is fast enough to fetch results on the main thread
217 clone->fetchResults( searchString, context, feedback );
218 }
219 else
220 {
221 threadedFilters.append( clone.release() );
222 }
223 }
224
225 mActiveThreads.clear();
226 for ( QgsLocatorFilter *filter : std::as_const( threadedFilters ) )
227 {
228 QThread *thread = new QThread();
229 mActiveThreads.append( thread );
230 filter->moveToThread( thread );
231 connect( thread, &QThread::started, filter, [filter, searchString, context, feedback]
232 {
233 int delay = filter->fetchResultsDelay();
234 while ( delay > 0 )
235 {
236 if ( feedback->isCanceled() )
237 break;
238 QThread::msleep( 50 );
239 delay -= 50;
240 }
241 if ( !feedback->isCanceled() )
242 filter->fetchResults( searchString, context, feedback );
243 filter->emit finished();
244 }, Qt::QueuedConnection );
245 connect( filter, &QgsLocatorFilter::finished, thread, &QThread::quit );
246 connect( filter, &QgsLocatorFilter::finished, filter, &QgsLocatorFilter::deleteLater );
247 connect( thread, &QThread::finished, thread, [this, thread]
248 {
249 mActiveThreads.removeAll( thread );
250 if ( mActiveThreads.empty() )
251 emit finished();
252 } );
253 connect( thread, &QThread::finished, thread, &QThread::deleteLater );
254 thread->start();
255 }
256
257 emit searchPrepared();
258
259 if ( mActiveThreads.empty() )
260 emit finished();
261}
262
264{
265 cancelRunningQuery();
266}
267
269{
270 if ( mFeedback )
271 mFeedback->cancel();
272}
273
275{
276 return !mActiveThreads.empty();
277}
278
280{
281 for ( QgsLocatorFilter *filter : std::as_const( mFilters ) )
282 {
283 if ( filter->enabled() )
284 {
285 filter->clearPreviousResults();
286 }
287 }
288}
289
290void QgsLocator::filterSentResult( QgsLocatorResult result )
291{
292 // if query has been canceled then discard any results we receive
293 if ( mFeedback->isCanceled() )
294 return;
295
296 emit foundResult( result );
297}
298
299void QgsLocator::cancelRunningQuery()
300{
301 if ( !mActiveThreads.empty() )
302 {
303 // cancel existing job
304 mFeedback->cancel();
305 while ( !mActiveThreads.empty() )
306 {
307 QCoreApplication::processEvents();
308 }
309 }
310}
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:108
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:68
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:263
QgsLocator(QObject *parent=nullptr)
Constructor for QgsLocator.
Definition: qgslocator.cpp:44
static const QgsSettingsEntryBool * settingsLocatorFilterDefault
Settings entry locator filter default value.
Definition: qgslocator.h:165
void registerFilter(QgsLocatorFilter *filter)
Registers a filter within the locator.
Definition: qgslocator.cpp:100
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:137
void clearPreviousResults()
Will call clearPreviousResults on all filters.
Definition: qgslocator.cpp:279
static const QgsSettingsEntryBool * settingsLocatorFilterEnabled
Settings entry locator filter enabled.
Definition: qgslocator.h:163
~QgsLocator() override
Destructor for QgsLocator.
Definition: qgslocator.cpp:50
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:83
bool isRunning() const
Returns true if a query is currently being executed by the locator.
Definition: qgslocator.cpp:274
static const QgsSettingsEntryString * settingsLocatorFilterPrefix
Settings entry locator filter prefix.
Definition: qgslocator.h:167
void cancelWithoutBlocking()
Triggers cancellation of any current running query without blocking.
Definition: qgslocator.cpp:268
QList< QgsLocatorFilter * > filters(const QString &prefix=QString())
Returns the list of filters registered in the locator.
Definition: qgslocator.cpp:63
void deregisterFilter(QgsLocatorFilter *filter)
Deregisters a filter from the locator and deletes it.
Definition: qgslocator.cpp:56
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
T valueWithDefaultOverride(const T &defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns the settings value with a defaultValueOverride and with an optional dynamicKeyPart.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
A boolean settings entry.
A string settings entry.
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