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