Skip to content

Commit 6bf4b92

Browse files
committed
Merge pull request #3080 from nyalldawson/select_tools
[FEATURE] Improved map select tool behaviour
2 parents d73210a + cd9f47a commit 6bf4b92

7 files changed

+125
-74
lines changed

src/app/qgsmaptoolselect.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ void QgsMapToolSelect::canvasReleaseEvent( QgsMapMouseEvent* e )
5050
QgsMapToolSelectUtils::expandSelectRectangle( selectRect, vlayer, e->pos() );
5151
QgsMapToolSelectUtils::setRubberBand( mCanvas, selectRect, &rubberBand );
5252
QgsGeometry* selectGeom = rubberBand.asGeometry();
53-
bool doDifference = e->modifiers() & Qt::ControlModifier;
54-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, selectGeom, false, doDifference, true );
53+
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, selectGeom, e );
5554
delete selectGeom;
5655
rubberBand.reset( QGis::Polygon );
5756
}

src/app/qgsmaptoolselectfreehand.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ void QgsMapToolSelectFreehand::canvasReleaseEvent( QgsMapMouseEvent* e )
8585
if ( mRubberBand->numberOfVertices() > 2 )
8686
{
8787
QgsGeometry* shapeGeom = mRubberBand->asGeometry();
88-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, shapeGeom,
89-
e->modifiers() & Qt::ShiftModifier,
90-
e->modifiers() & Qt::ControlModifier, singleSelect );
88+
if ( singleSelect )
89+
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, shapeGeom, e );
90+
else
91+
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, shapeGeom, e );
9192
delete shapeGeom;
9293
}
9394

src/app/qgsmaptoolselectpolygon.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void QgsMapToolSelectPolygon::canvasPressEvent( QgsMapMouseEvent* e )
5454
if ( mRubberBand->numberOfVertices() > 2 )
5555
{
5656
QgsGeometry* polygonGeom = mRubberBand->asGeometry();
57-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, polygonGeom, e );
57+
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, polygonGeom, e );
5858
delete polygonGeom;
5959
}
6060
mRubberBand->reset( QGis::Polygon );

src/app/qgsmaptoolselectradius.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ void QgsMapToolSelectRadius::canvasReleaseEvent( QgsMapMouseEvent* e )
9292
setRadiusRubberBand( radiusEdge );
9393
}
9494
QgsGeometry* radiusGeometry = mRubberBand->asGeometry();
95-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, radiusGeometry, e );
95+
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, radiusGeometry, e );
9696
delete radiusGeometry;
9797
mRubberBand->reset( QGis::Polygon );
9898
delete mRubberBand;

src/app/qgsmaptoolselectrectangle.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,10 @@ void QgsMapToolSelectFeatures::canvasReleaseEvent( QgsMapMouseEvent* e )
106106
QgsGeometry* selectGeom = mRubberBand->asGeometry();
107107
if ( !mDragging )
108108
{
109-
bool doDifference = e->modifiers() & Qt::ControlModifier;
110-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, selectGeom, false, doDifference, true );
109+
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, selectGeom, e );
111110
}
112111
else
113-
QgsMapToolSelectUtils::setSelectFeatures( mCanvas, selectGeom, e );
112+
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, selectGeom, e );
114113

115114
delete selectGeom;
116115

src/app/qgsmaptoolselectutils.cpp

+73-49
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,80 @@ void QgsMapToolSelectUtils::expandSelectRectangle( QRect& selectRect,
8585
selectRect.setBottom( point.y() + boxSize );
8686
}
8787

88-
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
89-
QgsGeometry* selectGeometry,
90-
bool doContains,
91-
bool doDifference,
92-
bool singleSelect )
88+
void QgsMapToolSelectUtils::selectMultipleFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent* e )
9389
{
94-
if ( selectGeometry->type() != QGis::Polygon )
90+
QgsVectorLayer::SelectBehaviour behaviour = QgsVectorLayer::SetSelection;
91+
if ( e->modifiers() & Qt::ShiftModifier && e->modifiers() & Qt::ControlModifier )
92+
behaviour = QgsVectorLayer::IntersectSelection;
93+
else if ( e->modifiers() & Qt::ShiftModifier )
94+
behaviour = QgsVectorLayer::AddToSelection;
95+
else if ( e->modifiers() & Qt::ControlModifier )
96+
behaviour = QgsVectorLayer::RemoveFromSelection;
97+
98+
bool doContains = e->modifiers() & Qt::AltModifier;
99+
setSelectedFeatures( canvas, selectGeometry, behaviour, doContains );
100+
}
101+
102+
void QgsMapToolSelectUtils::selectSingleFeature( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent* e )
103+
{
104+
QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
105+
if ( !vlayer )
106+
return;
107+
108+
QApplication::setOverrideCursor( Qt::WaitCursor );
109+
110+
QgsFeatureIds selectedFeatures = getMatchingFeatures( canvas, selectGeometry, false, true );
111+
if ( selectedFeatures.isEmpty() )
112+
{
113+
QApplication::restoreOverrideCursor();
95114
return;
115+
}
116+
117+
QgsVectorLayer::SelectBehaviour behaviour = QgsVectorLayer::SetSelection;
96118

119+
//either shift or control modifier switches to "toggle" selection mode
120+
if ( e->modifiers() & Qt::ShiftModifier || e->modifiers() & Qt::ControlModifier )
121+
{
122+
QgsFeatureId selectId = *selectedFeatures.constBegin();
123+
QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();
124+
if ( layerSelectedFeatures.contains( selectId ) )
125+
behaviour = QgsVectorLayer::RemoveFromSelection;
126+
else
127+
behaviour = QgsVectorLayer::AddToSelection;
128+
}
129+
130+
vlayer->selectByIds( selectedFeatures, behaviour );
131+
132+
QApplication::restoreOverrideCursor();
133+
}
134+
135+
void QgsMapToolSelectUtils::setSelectedFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry,
136+
QgsVectorLayer::SelectBehaviour selectBehaviour, bool doContains, bool singleSelect )
137+
{
97138
QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
98139
if ( !vlayer )
99140
return;
100141

142+
QApplication::setOverrideCursor( Qt::WaitCursor );
143+
144+
QgsFeatureIds selectedFeatures = getMatchingFeatures( canvas, selectGeometry, doContains, singleSelect );
145+
vlayer->selectByIds( selectedFeatures, selectBehaviour );
146+
147+
QApplication::restoreOverrideCursor();
148+
}
149+
150+
151+
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool singleSelect )
152+
{
153+
QgsFeatureIds newSelectedFeatures;
154+
155+
if ( selectGeometry->type() != QGis::Polygon )
156+
return newSelectedFeatures;
157+
158+
QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
159+
if ( !vlayer )
160+
return newSelectedFeatures;
161+
101162
// toLayerCoordinates will throw an exception for any 'invalid' points in
102163
// the rubber band.
103164
// For example, if you project a world map onto a globe using EPSG 2163
@@ -121,16 +182,13 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
121182
QObject::tr( "Selection extends beyond layer's coordinate system" ),
122183
QgsMessageBar::WARNING,
123184
QgisApp::instance()->messageTimeout() );
124-
return;
185+
return newSelectedFeatures;
125186
}
126187
}
127188

128-
QApplication::setOverrideCursor( Qt::WaitCursor );
129-
130-
QgsDebugMsg( "Selection layer: " + vlayer->name() );
131-
QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() );
132-
QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) );
133-
QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) );
189+
QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 );
190+
QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.exportToWkt(), 3 );
191+
QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 );
134192

135193
QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() );
136194
context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer );
@@ -148,7 +206,6 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
148206

149207
QgsFeatureIterator fit = vlayer->getFeatures( request );
150208

151-
QgsFeatureIds newSelectedFeatures;
152209
QgsFeature f;
153210
QgsFeatureId closestFeatureId = 0;
154211
bool foundSingleFeature = false;
@@ -196,40 +253,7 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
196253

197254
QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );
198255

199-
if ( doDifference )
200-
{
201-
QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();
202-
203-
QgsFeatureIds selectedFeatures;
204-
QgsFeatureIds deselectedFeatures;
205-
206-
QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd();
207-
while ( i != newSelectedFeatures.constBegin() )
208-
{
209-
--i;
210-
if ( layerSelectedFeatures.contains( *i ) )
211-
{
212-
deselectedFeatures.insert( *i );
213-
}
214-
else
215-
{
216-
selectedFeatures.insert( *i );
217-
}
218-
}
219-
220-
vlayer->modifySelection( selectedFeatures, deselectedFeatures );
221-
}
222-
else
223-
{
224-
vlayer->selectByIds( newSelectedFeatures );
225-
}
226-
227-
QApplication::restoreOverrideCursor();
256+
return newSelectedFeatures;
228257
}
229258

230-
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent * e )
231-
{
232-
bool doContains = e->modifiers() & Qt::ShiftModifier;
233-
bool doDifference = e->modifiers() & Qt::ControlModifier;
234-
setSelectFeatures( canvas, selectGeometry, doContains, doDifference );
235-
}
259+

src/app/qgsmaptoolselectutils.h

+43-15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ email : jpalmer at linz dot govt dot nz
1616
#ifndef QGSMAPTOOLSELECTUTILS_H
1717
#define QGSMAPTOOLSELECTUTILS_H
1818

19+
#include "qgsvectorlayer.h"
1920
#include <Qt>
2021
#include <QRect>
2122
#include <QPoint>
@@ -31,34 +32,61 @@ class QgsRubberBand;
3132
*/
3233
namespace QgsMapToolSelectUtils
3334
{
35+
/** Calculates a list of features matching a selection geometry and flags.
36+
* @param canvas the map canvas used to get the current selected vector layer and
37+
for any required geometry transformations
38+
* @param selectGeometry the geometry to select the layers features. This geometry
39+
must be in terms of the canvas coordinate system.
40+
* @param doContains features will only be selected if fully contained within
41+
the selection rubber band (otherwise intersection is enough).
42+
* @param singleSelect only selects the closest feature to the selectGeometry.
43+
* @returns list of features which match search geometry and parameters
44+
* @note added in QGIS 2.16
45+
*/
46+
QgsFeatureIds getMatchingFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool singleSelect );
47+
3448
/**
3549
Selects the features within currently selected layer.
36-
@param canvas The map canvas used to get the current selected vector layer and
50+
@param canvas the map canvas used to get the current selected vector layer and
3751
for any required geometry transformations
38-
@param selectGeometry The geometry to select the layers features. This geometry
52+
@param selectGeometry the geometry to select the layers features. This geometry
3953
must be in terms of the canvas coordinate system.
40-
@param doContains Features will only be selected if fully contained within
54+
@param selectBehaviour behaviour of select (ie replace selection, add to selection)
55+
@param doContains features will only be selected if fully contained within
4156
the selection rubber band (otherwise intersection is enough).
42-
@param doDifference Take the symmetric difference of the current selected
43-
features and the new features found within the provided selectGeometry.
44-
@param singleSelect Only selects the closest feature to the selectGeometry.
57+
@param singleSelect only selects the closest feature to the selectGeometry.
58+
@note added in QGIS 2.16
59+
*/
60+
void setSelectedFeatures( QgsMapCanvas* canvas,
61+
QgsGeometry* selectGeometry,
62+
QgsVectorLayer::SelectBehaviour selectBehaviour = QgsVectorLayer::SetSelection,
63+
bool doContains = true,
64+
bool singleSelect = false );
65+
66+
/**
67+
Selects multiple matching features from within currently selected layer.
68+
@param canvas the map canvas used to get the current selected vector layer and
69+
for any required geometry transformations
70+
@param selectGeometry the geometry to select the layers features. This geometry
71+
must be in terms of the canvas coordinate system.
72+
@param e MouseEvents are used to determine the current selection
73+
operations (add, subtract, contains)
74+
@note added in QGIS 2.16
75+
@see selectSingleFeature()
4576
*/
46-
void setSelectFeatures( QgsMapCanvas* canvas,
47-
QgsGeometry* selectGeometry,
48-
bool doContains = true,
49-
bool doDifference = false,
50-
bool singleSelect = false );
77+
void selectMultipleFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent * e );
5178

5279
/**
53-
Select the features within currently selected layer.
54-
@param canvas The map canvas used to get the current selected vector layer and
80+
Selects a single feature from within currently selected layer.
81+
@param canvas the map canvas used to get the current selected vector layer and
5582
for any required geometry transformations
56-
@param selectGeometry The geometry to select the layers features. This geometry
83+
@param selectGeometry the geometry to select the layers features. This geometry
5784
must be in terms of the canvas coordinate system.
5885
@param e MouseEvents are used to determine the current selection
5986
operations (add, subtract, contains)
87+
@see selectMultipleFeatures()
6088
*/
61-
void setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent * e );
89+
void selectSingleFeature( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, QMouseEvent * e );
6290

6391
/**
6492
Get the current selected canvas map layer. Returns nullptr if it is not a vector layer

0 commit comments

Comments
 (0)