Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #4606 from nyalldawson/feature_locator
[FEATURE] Active layer feature locator filter
  • Loading branch information
nyalldawson committed Jun 1, 2017
2 parents 2f9bfc5 + 19ca0e0 commit a6358b1
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 4 deletions.
96 changes: 96 additions & 0 deletions src/app/locator/qgsinbuiltlocatorfilters.cpp
Expand Up @@ -25,6 +25,7 @@
#include "qgsmaplayermodel.h"
#include "qgscomposition.h"
#include "qgslayoutmanager.h"
#include "qgsmapcanvas.h"
#include <QToolButton>

QgsLayerTreeLocatorFilter::QgsLayerTreeLocatorFilter( QObject *parent )
Expand Down Expand Up @@ -163,3 +164,98 @@ void QgsActionLocatorFilter::searchActions( const QString &string, QWidget *pare
}
}
}

QgsActiveLayerFeaturesLocatorFilter::QgsActiveLayerFeaturesLocatorFilter( QObject *parent )
: QgsLocatorFilter( parent )
{
setUseWithoutPrefix( false );
}

void QgsActiveLayerFeaturesLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
{
if ( string.length() < 3 )
return;

bool allowNumeric = false;
double numericValue = string.toDouble( &allowNumeric );

QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgisApp::instance()->activeLayer() );
if ( !layer )
return;

int found = 0;
QgsExpression dispExpression( layer->displayExpression() );
QgsExpressionContext context;
context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
dispExpression.prepare( &context );

// build up request expression
QStringList expressionParts;
Q_FOREACH ( const QgsField &field, layer->fields() )
{
if ( field.type() == QVariant::String )
{
expressionParts << QStringLiteral( "%1 ILIKE '%%2%'" ).arg( QgsExpression::quotedColumnRef( field.name() ),
string );
}
else if ( allowNumeric && field.isNumeric() )
{
expressionParts << QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( field.name() ) ).arg( numericValue );
}
}

QString expression = QStringLiteral( "(%1)" ).arg( expressionParts.join( QStringLiteral( " ) OR ( " ) ) );

QgsFeatureRequest req;
req.setFlags( QgsFeatureRequest::NoGeometry );
req.setFilterExpression( expression );
req.setLimit( 30 );
QgsFeature f;
QgsFeatureIterator it = layer->getFeatures( req );
while ( it.nextFeature( f ) )
{
if ( feedback->isCanceled() )
return;

QgsLocatorResult result;
result.filter = this;

context.setFeature( f );

// find matching field content
Q_FOREACH ( const QVariant &var, f.attributes() )
{
QString attrString = var.toString();
if ( attrString.contains( string, Qt::CaseInsensitive ) )
{
result.displayString = attrString;
break;
}
}
if ( result.displayString.isEmpty() )
continue; //not sure how this result slipped through...

result.description = dispExpression.evaluate( &context ).toString();

result.userData = QVariantList() << f.id() << layer->id();
result.icon = QgsMapLayerModel::iconForLayer( layer );
result.score = static_cast< double >( string.length() ) / result.displayString.size();
emit resultFetched( result );

found++;
if ( found >= 30 )
break;
}
}

void QgsActiveLayerFeaturesLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QVariantList dataList = result.userData.toList();
QgsFeatureId id = dataList.at( 0 ).toLongLong();
QString layerId = dataList.at( 1 ).toString();
QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
if ( !layer )
return;

QgisApp::instance()->mapCanvas()->zoomToFeatureIds( layer, QgsFeatureIds() << id );
}
17 changes: 17 additions & 0 deletions src/app/locator/qgsinbuiltlocatorfilters.h
Expand Up @@ -77,6 +77,23 @@ class QgsActionLocatorFilter : public QgsLocatorFilter

};

class QgsActiveLayerFeaturesLocatorFilter : public QgsLocatorFilter
{
Q_OBJECT

public:

QgsActiveLayerFeaturesLocatorFilter( QObject *parent = nullptr );
virtual QString name() const override { return QStringLiteral( "features" ); }
virtual QString displayName() const override { return tr( "Active Layer Features" ); }
virtual Priority priority() const override { return Medium; }
QString prefix() const override { return QStringLiteral( "f" ); }

void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;
};


#endif // QGSINBUILTLOCATORFILTERS_H


2 changes: 1 addition & 1 deletion src/app/qgisapp.cpp
Expand Up @@ -2673,7 +2673,7 @@ void QgisApp::createStatusBar()
<< mSnappingToolBar;

mLocatorWidget->locator()->registerFilter( new QgsActionLocatorFilter( actionObjects ) );

mLocatorWidget->locator()->registerFilter( new QgsActiveLayerFeaturesLocatorFilter() );
}

void QgisApp::setIconSizes( int size )
Expand Down
6 changes: 4 additions & 2 deletions src/app/qgisapp.h
Expand Up @@ -621,6 +621,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Process the list of URIs that have been dropped in QGIS
void handleDropUriList( const QgsMimeDataUtils::UriList &lst );

//! Returns the active map layer.
QgsMapLayer *activeLayer();

public slots:
void layerTreeViewDoubleClicked( const QModelIndex &index );
//! Make sure the insertion point for new layers is up-to-date with the current item in layer tree view
Expand Down Expand Up @@ -1139,8 +1142,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void hideDeselectedLayers();
//reimplements method from base (gui) class
void showSelectedLayers();
//! Return pointer to the active layer
QgsMapLayer *activeLayer();

//! Open the help contents in a browser
void helpContents();
//! Open the API documentation in a browser
Expand Down
3 changes: 2 additions & 1 deletion src/gui/locator/qgslocator.cpp
Expand Up @@ -63,7 +63,8 @@ void QgsLocator::registerFilter( QgsLocatorFilter *filter )
if ( !filter->prefix().isEmpty() )
{
if ( filter->name() == QStringLiteral( "actions" ) || filter->name() == QStringLiteral( "processing_alg" )
|| filter->name() == QStringLiteral( "layertree" ) || filter->name() == QStringLiteral( "layouts" ) )
|| filter->name() == QStringLiteral( "layertree" ) || filter->name() == QStringLiteral( "layouts" )
|| filter->name() == QStringLiteral( "features" ) )
{
//inbuilt filter, no prefix check
mPrefixedFilters.insert( filter->prefix(), filter );
Expand Down

0 comments on commit a6358b1

Please sign in to comment.