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