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