[FIX #5525] Attribute table performance for large tables

The selection handling performance has been improved a lot
Introduces also more detailed signalling of selection change from QgsVectorLayer
This commit is contained in:
Matthias Kuhn 2013-04-09 11:52:43 +02:00
parent 5280e212ec
commit 47c10db1fe
27 changed files with 915 additions and 539 deletions

1
.gitignore vendored
View File

@ -41,6 +41,7 @@ doc/INSTALL.tex
scripts/Debug
scripts/RelWithDebInfo
/CMakeLists.txt.user
/CMakeLists.txt.user.*
qgis-test.ctest
i18n/*.qm
.project

View File

@ -221,8 +221,21 @@ class QgsVectorLayer : QgsMapLayer
/** The number of features that are selected in this layer */
int selectedFeatureCount();
/** Select features found within the search rectangle (in layer's coordinates) */
void select( QgsRectangle & rect, bool lock );
/**
* Select features found within the search rectangle (in layer's coordinates)
*
* @param rect The search rectangle
* @param addToSelection If set to true will not clear before selecting
*/
void select( QgsRectangle & rect, bool addToSelection );
/**
* Modifies the current selection on this layer
*
* @param selectIds Select these ids
* @param deselectIds Deselect these ids
*/
void modifySelection(QgsFeatureIds selectIds, QgsFeatureIds deselectIds );
/** Select not selected features and deselect selected ones */
void invertSelection();
@ -769,14 +782,36 @@ class QgsVectorLayer : QgsMapLayer
QVariant maximumValue( int index );
public slots:
/** Select feature by its ID, optionally emit signal selectionChanged() */
void select( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select feature by its ID
*
* @param featureId The id of the feature to select
*/
void select( QgsFeatureId featureId );
/** Deselect feature by its ID, optionally emit signal selectionChanged() */
void deselect( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select features by their ID
*
* @param featureIds The ids of the features to select
*/
void select( QgsFeatureIds featureIds );
/**
* Deselect feature by its ID
*
* @param featureId The id of the feature to deselect
*/
void deselect( QgsFeatureId featureId );
/**
* Deselect features by their ID
*
* @param featureIds The ids of the features to deselect
*/
void deselect( QgsFeatureIds featureIds );
/** Clear selection */
void removeSelection( bool emitSignal = true );
void removeSelection();
void triggerRepaint();

View File

@ -173,29 +173,33 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );
QgsFeatureIds layerSelectedFeatures;
if ( doDifference )
{
layerSelectedFeatures = vlayer->selectedFeaturesIds();
QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();
QgsFeatureIds selectedFeatures;
QgsFeatureIds deselectedFeatures;
QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd();
while ( i != newSelectedFeatures.constBegin() )
{
--i;
if ( layerSelectedFeatures.contains( *i ) )
{
layerSelectedFeatures.remove( *i );
deselectedFeatures.insert( *i );
}
else
{
layerSelectedFeatures.insert( *i );
selectedFeatures.insert( *i );
}
}
vlayer->modifySelection( selectedFeatures, deselectedFeatures );
}
else
{
layerSelectedFeatures = newSelectedFeatures;
vlayer->setSelectedFeatures( newSelectedFeatures );
}
vlayer->setSelectedFeatures( layerSelectedFeatures );
QApplication::restoreOverrideCursor();
}

View File

@ -144,6 +144,8 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
//QSettings settings;
//mUpdateThreshold = settings.readNumEntry("Map/updateThreshold", 1000);
}
connect ( this, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SIGNAL( selectionChanged() ) );
} // QgsVectorLayer ctor
@ -680,58 +682,79 @@ void QgsVectorLayer::drawVertexMarker( double x, double y, QPainter& p, QgsVecto
}
}
void QgsVectorLayer::select( QgsFeatureId fid, bool emitSignal )
void QgsVectorLayer::select( const QgsFeatureId& fid )
{
mSelectedFeatureIds.insert( fid );
if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
}
setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds() << fid, QgsFeatureIds(), false );
}
void QgsVectorLayer::deselect( QgsFeatureId fid, bool emitSignal )
void QgsVectorLayer::select( const QgsFeatureIds& featureIds )
{
mSelectedFeatureIds.unite( featureIds );
setCacheImage( 0 );
emit selectionChanged( featureIds, QgsFeatureIds(), false );
}
void QgsVectorLayer::deselect( const QgsFeatureId fid )
{
mSelectedFeatureIds.remove( fid );
if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
}
setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds(), QgsFeatureIds() << fid, false );
}
void QgsVectorLayer::select( QgsRectangle & rect, bool lock )
void QgsVectorLayer::deselect( const QgsFeatureIds& featureIds )
{
mSelectedFeatureIds.subtract( featureIds );
setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds(), featureIds, false );
}
void QgsVectorLayer::select(QgsRectangle & rect, bool addToSelection )
{
// normalize the rectangle
rect.normalize();
if ( !lock )
{
removeSelection( false ); // don't emit signal
}
//select all the elements
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
.setFilterRect( rect )
.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoGeometry )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeatureIds ids;
QgsFeature f;
while ( fit.nextFeature( f ) )
{
select( f.id(), false ); // don't emit signal (not to redraw it everytime)
ids << f.id();
}
// invalidate cache
setCacheImage( 0 );
if ( !addToSelection )
{
setSelectedFeatures( mSelectedFeatureIds + ids );
}
else
{
select( ids );
}
}
emit selectionChanged(); // now emit signal to redraw layer
void QgsVectorLayer::modifySelection( QgsFeatureIds selectIds, QgsFeatureIds deselectIds )
{
QgsFeatureIds intersectingIds = selectIds & deselectIds;
if ( intersectingIds.count() > 0 )
{
QgsDebugMsg( "Trying to select and deselect the same item at the same time. Unsure what to do. Selecting dubious items." );
}
mSelectedFeatureIds -= deselectIds;
mSelectedFeatureIds += selectIds;
emit selectionChanged( selectIds, deselectIds - intersectingIds, false );
}
void QgsVectorLayer::invertSelection()
@ -739,27 +762,21 @@ void QgsVectorLayer::invertSelection()
// copy the ids of selected features to tmp
QgsFeatureIds tmp = mSelectedFeatureIds;
removeSelection( false ); // don't emit signal
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
.setFlags( QgsFeatureRequest::NoGeometry )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeatureIds ids;
QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
select( fet.id(), false ); // don't emit signal
ids << fet.id();
}
for ( QgsFeatureIds::iterator iter = tmp.begin(); iter != tmp.end(); ++iter )
{
mSelectedFeatureIds.remove( *iter );
}
ids.subtract( mSelectedFeatureIds );
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
setSelectedFeatures( ids );
}
void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect )
@ -772,39 +789,31 @@ void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect )
.setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeatureIds selectIds;
QgsFeatureIds deselectIds;
QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
if ( mSelectedFeatureIds.contains( fet.id() ) )
{
deselect( fet.id(), false ); // don't emit signal
deselectIds << fet.id();
}
else
{
select( fet.id(), false ); // don't emit signal
selectIds << fet.id();
}
}
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
modifySelection( selectIds, deselectIds );
}
void QgsVectorLayer::removeSelection( bool emitSignal )
void QgsVectorLayer::removeSelection()
{
if ( mSelectedFeatureIds.size() == 0 )
return;
mSelectedFeatureIds.clear();
if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
}
setSelectedFeatures( QgsFeatureIds() );
}
void QgsVectorLayer::triggerRepaint()
@ -1267,9 +1276,6 @@ bool QgsVectorLayer::deleteSelectedFeatures()
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
triggerRepaint();
updateExtents();
@ -2547,13 +2553,14 @@ bool QgsVectorLayer::rollBack( bool deleteBuffer )
void QgsVectorLayer::setSelectedFeatures( const QgsFeatureIds& ids )
{
QgsFeatureIds deselectedFeatures = mSelectedFeatureIds - ids;
// TODO: check whether features with these ID exist
mSelectedFeatureIds = ids;
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
emit selectionChanged( ids, deselectedFeatures, true );
}
int QgsVectorLayer::selectedFeatureCount()
@ -2593,14 +2600,12 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList features, bool makeSelected )
if ( makeSelected )
{
mSelectedFeatureIds.clear();
QgsFeatureIds ids;
for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
mSelectedFeatureIds.insert( iter->id() );
ids << iter->id();
// invalidate cache
setCacheImage( 0 );
emit selectionChanged();
setSelectedFeatures ( ids );
}
return res;

View File

@ -258,7 +258,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
const QString displayField() const;
/** Set the preview expression, used to create a human readable preview string.
* Used e.g. in the attribute table feature list. Uses @link QgsExpression @endlink
* Used e.g. in the attribute table feature list. Uses { @link QgsExpression }.
*
* @param displayExpression The expression which will be used to preview features
* for this layer
@ -268,9 +268,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/**
* Get the preview expression, used to create a human readable preview string.
* Uses @link QgsExpression @endlink
* Uses { @link QgsExpression }
*
* @return The expression which will be used to preview features for this layer
*
* @note added in 2.0
*/
const QString displayExpression();
@ -308,25 +309,72 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QgsAttributeAction *actions() { return mActions; }
/** The number of features that are selected in this layer */
/**
* The number of features that are selected in this layer
*
* @return See description
*/
int selectedFeatureCount();
/** Select features found within the search rectangle (in layer's coordinates) */
void select( QgsRectangle & rect, bool lock );
/**
* Select features found within the search rectangle (in layer's coordinates)
*
* @param rect The search rectangle
* @param addToSelection If set to true will not clear before selecting
*
* @see invertSelectionInRectangle(QgsRectangle & rect)
*/
void select( QgsRectangle & rect, bool addToSelection );
/**
* Modifies the current selection on this layer
*
* @param selectIds Select these ids
* @param deselectIds Deselect these ids
*
* @see select(QgsFeatureIds)
* @see select(QgsFeatureId)
* @see deselect(QgsFeatureIds)
* @see deselect(QgsFeatureId)
*/
void modifySelection(QgsFeatureIds selectIds, QgsFeatureIds deselectIds );
/** Select not selected features and deselect selected ones */
void invertSelection();
/** Invert selection of features found within the search rectangle (in layer's coordinates) */
/**
* Invert selection of features found within the search rectangle (in layer's coordinates)
*
* @param rect The rectangle in which the selection of features will be inverted
*
* @see invertSelection()
*/
void invertSelectionInRectangle( QgsRectangle & rect );
/** Get a copy of the user-selected features */
/**
* Get a copy of the user-selected features
*
* @return A list of { @link QgsFeature } 's
*
* @see selectedFeaturesIds()
*/
QgsFeatureList selectedFeatures();
/** Return reference to identifiers of selected features */
/**
* Return reference to identifiers of selected features
*
* @return A list of { @link QgsFeatureId } 's
* @see selectedFeatures()
*/
const QgsFeatureIds &selectedFeaturesIds() const;
/** Change selection to the new set of features */
/**
* Change selection to the new set of features. Dismisses the current selection.
* Will emit the { @link selectionChanged( QgsFeatureIds, QgsFeatureIds, bool ) } signal with the
* clearAndSelect flag set.
*
* @param ids The ids which will be the new selection
*/
void setSelectedFeatures( const QgsFeatureIds &ids );
/** Returns the bounding box of the selected features. If there is no selection, QgsRectangle(0,0,0,0) is returned */
@ -858,14 +906,48 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QVariant maximumValue( int index );
public slots:
/** Select feature by its ID, optionally emit signal selectionChanged() */
void select( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select feature by its ID
*
* @param featureId The id of the feature to select
*
* @see select(QgsFeatureIds)
*/
void select( const QgsFeatureId &featureId );
/** Deselect feature by its ID, optionally emit signal selectionChanged() */
void deselect( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select features by their ID
*
* @param featureIds The ids of the features to select
*
* @see select(QgsFeatureId)
*/
void select( const QgsFeatureIds& featureIds );
/** Clear selection */
void removeSelection( bool emitSignal = true );
/**
* Deselect feature by its ID
*
* @param featureId The id of the feature to deselect
*
* @see deselect(QgsFeatureIds)
*/
void deselect( const QgsFeatureId featureId );
/**
* Deselect features by their ID
*
* @param featureIds The ids of the features to deselect
*
* @see deselect(QgsFeatureId)
*/
void deselect(const QgsFeatureIds& featureIds );
/**
* Clear selection
*
* @see setSelectedFeatures(const QgsFeatureIds&)
*/
void removeSelection();
void triggerRepaint();
@ -890,6 +972,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
signals:
/**
* This signal is emited when selection was changed
*
* @param selected Newly selected feature ids
* @param deselected Ids of all features which have previously been selected but are not any more
* @param clearAndSelect In case this is set to true, the old selection was dismissed and the new selection corresponds to selected
*/
void selectionChanged( const QgsFeatureIds selected, const QgsFeatureIds deselected, const bool clearAndSelect );
/** This signal is emited when selection was changed */
void selectionChanged();

View File

@ -45,6 +45,7 @@ attributetable/qgsattributetabledelegate.cpp
attributetable/qgsfeaturelistview.cpp
attributetable/qgsfeaturelistmodel.cpp
attributetable/qgsfeaturelistviewdelegate.cpp
attributetable/qgsfeatureselectionmodel.cpp
attributetable/qgsdualview.cpp
qgisgui.cpp
@ -168,6 +169,7 @@ attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsdualview.h
@ -271,8 +273,10 @@ attributetable/qgsattributetableview.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturemodel.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsdualview.h
)

View File

@ -18,6 +18,7 @@
#include <QComboBox>
#include <QPainter>
#include "qgsfeatureselectionmodel.h"
#include "qgsattributetableview.h"
#include "qgsattributetablemodel.h"
#include "qgsattributetablefiltermodel.h"
@ -27,6 +28,7 @@
#include "qgslogger.h"
// TODO: Remove this casting orgy
QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *model ) const
@ -42,33 +44,6 @@ QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *mode
return NULL;
}
int QgsAttributeTableDelegate::fieldIdx( const QModelIndex &index ) const
{
const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( index.model() );
if ( tm )
return tm->fieldIdx( index.column() );
const QgsAttributeTableFilterModel *fm = dynamic_cast<const QgsAttributeTableFilterModel *>( index.model() );
if ( fm )
return fm->masterModel()->fieldIdx( index.column() );
return -1;
}
QgsFeatureId QgsAttributeTableDelegate::featureId( const QModelIndex &index ) const
{
const QgsAttributeTableModel *tm = qobject_cast<const QgsAttributeTableModel *>( index.model() );
if ( tm )
return tm->rowToId( index.row() );
const QgsAttributeTableFilterModel *fm = dynamic_cast<const QgsAttributeTableFilterModel *>( index.model() );
if ( fm )
return fm->masterModel()->rowToId( fm->mapToSource( index ).row() );
return -1;
}
QWidget *QgsAttributeTableDelegate::createEditor(
QWidget *parent,
const QStyleOptionViewItem &option,
@ -79,15 +54,17 @@ QWidget *QgsAttributeTableDelegate::createEditor(
if ( !vl )
return NULL;
QWidget *w = QgsAttributeEditor::createAttributeEditor( parent, 0, vl, fieldIdx( index ), index.model()->data( index, Qt::EditRole ) );
int fieldIdx = index.model()->data( index, QgsAttributeTableModel::FieldIndexRole ).toInt();
QWidget *w = QgsAttributeEditor::createAttributeEditor( parent, 0, vl, fieldIdx, index.model()->data( index, Qt::EditRole ) );
if ( parent )
{
QgsAttributeTableView *tv = dynamic_cast<QgsAttributeTableView *>( parent->parentWidget() );
w->setMinimumWidth( tv->columnWidth( index.column() ) );
if ( vl->editType( fieldIdx( index ) ) == QgsVectorLayer::FileName ||
vl->editType( fieldIdx( index ) ) == QgsVectorLayer::Calendar )
if ( vl->editType( fieldIdx ) == QgsVectorLayer::FileName ||
vl->editType( fieldIdx ) == QgsVectorLayer::Calendar )
{
QLineEdit *le = w->findChild<QLineEdit*>();
le->adjustSize();
@ -104,15 +81,15 @@ void QgsAttributeTableDelegate::setModelData( QWidget *editor, QAbstractItemMode
if ( vl == NULL )
return;
int idx = fieldIdx( index );
QgsFeatureId fid = featureId( index );
int fieldIdx = model->data( index, QgsAttributeTableModel::FieldIndexRole ).toInt();
QgsFeatureId fid = model->data( index, QgsAttributeTableModel::FeatureIdRole ).toInt();
QVariant value;
if ( !QgsAttributeEditor::retrieveValue( editor, vl, idx, value ) )
if ( !QgsAttributeEditor::retrieveValue( editor, vl, fieldIdx, value ) )
return;
vl->beginEditCommand( tr( "Attribute changed" ) );
vl->changeAttributeValue( fid, idx, value, true );
vl->changeAttributeValue( fid, fieldIdx, value, true );
vl->endEditCommand();
}
@ -122,14 +99,27 @@ void QgsAttributeTableDelegate::setEditorData( QWidget *editor, const QModelInde
if ( vl == NULL )
return;
QgsAttributeEditor::setValue( editor, vl, fieldIdx( index ), index.model()->data( index, Qt::EditRole ) );
int fieldIdx = index.model()->data( index, QgsAttributeTableModel::FieldIndexRole ).toInt();
QgsAttributeEditor::setValue( editor, vl, fieldIdx, index.model()->data( index, Qt::EditRole ) );
}
void QgsAttributeTableDelegate::setFeatureSelectionModel( QgsFeatureSelectionModel *featureSelectionModel )
{
mFeatureSelectionModel = featureSelectionModel;
}
void QgsAttributeTableDelegate::paint( QPainter * painter,
const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
QItemDelegate::paint( painter, option, index );
QgsFeatureId fid = index.model()->data( index, QgsAttributeTableModel::FeatureIdRole ).toInt();
QStyleOptionViewItem myOpt = option;
if ( mFeatureSelectionModel->isSelected( fid ) )
myOpt.state |= QStyle::State_Selected;
QItemDelegate::paint( painter, myOpt, index );
if ( option.state & QStyle::State_HasFocus )
{

View File

@ -19,6 +19,7 @@
#include <QItemDelegate>
#include "qgsfeature.h"
class QgsFeatureSelectionModel;
class QPainter;
class QgsVectorLayer;
class QgsAttributeTableView;
@ -33,8 +34,6 @@ class QgsAttributeTableDelegate : public QItemDelegate
Q_OBJECT;
QgsVectorLayer *layer( const QAbstractItemModel *model ) const;
int fieldIdx( const QModelIndex &index ) const;
QgsFeatureId featureId( const QModelIndex &index ) const;
public:
/** Constructor
@ -69,6 +68,11 @@ class QgsAttributeTableDelegate : public QItemDelegate
* @param index index of field which is to be retrieved
*/
void setEditorData( QWidget *editor, const QModelIndex &index ) const;
void setFeatureSelectionModel( QgsFeatureSelectionModel* featureSelectionModel );
private:
QgsFeatureSelectionModel* mFeatureSelectionModel;
};
#endif //QGSATTRIBUTETABLEDELEGATE_H

View File

@ -44,7 +44,6 @@ bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QMod
{
if ( mSelectedOnTop )
{
// TODO: does the model index (left/right) need to be converted to first?
bool leftSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( left.row() ) );
bool rightSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( right.row() ) );
@ -63,14 +62,17 @@ bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QMod
void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
{
mSelectedOnTop = selectedOnTop;
if ( sortColumn() == -1 )
if ( mSelectedOnTop != selectedOnTop )
{
sort( 0, sortOrder() );
}
invalidate();
}
mSelectedOnTop = selectedOnTop;
if ( sortColumn() == -1 )
{
sort( 0 );
}
invalidate();
}
}
void QgsAttributeTableFilterModel::setSourceModel( QgsAttributeTableModel* sourceModel )
{
@ -79,10 +81,6 @@ void QgsAttributeTableFilterModel::setSourceModel( QgsAttributeTableModel* sourc
mMasterSelection = new QItemSelectionModel( sourceModel, this );
QSortFilterProxyModel::setSourceModel( sourceModel );
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
// Issue a selectionChanged, so the selection gets synchronized to the map canvas selection
selectionChanged();
}
bool QgsAttributeTableFilterModel::selectedOnTop()
@ -94,7 +92,7 @@ void QgsAttributeTableFilterModel::setFilteredFeatures( QgsFeatureIds ids )
{
mFilteredFeatures = ids;
mFilterMode = ShowFilteredList;
announcedInvalidateFilter();
invalidateFilter();
}
void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
@ -111,82 +109,16 @@ void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
disconnect( SLOT( extentsChanged() ) );
}
if ( filterMode == ShowSelected )
{
generateListOfVisibleFeatures();
}
mFilterMode = filterMode;
announcedInvalidateFilter();
invalidateFilter();
}
}
void QgsAttributeTableFilterModel::selectionChanged()
{
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
mMasterSelection->clear();
QItemSelection selection;
foreach ( QgsFeatureId fid, layer()->selectedFeaturesIds() )
{
selection.append( QItemSelectionRange( mTableModel->idToIndex( fid ) ) );
}
mMasterSelection->select( selection, QItemSelectionModel::ClearAndSelect );
if ( mFilterMode == ShowSelected )
{
announcedInvalidateFilter();
}
if ( mSelectedOnTop )
{
invalidate();
sort( sortColumn(), sortOrder() );
}
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( masterSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsAttributeTableFilterModel::masterSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
{
disconnect( layer(), SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
// Filter duplicate items (multiple columns)
QgsFeatureIds selectedFeatureIds;
QgsFeatureIds deselectedFeatureIds;
foreach ( QItemSelectionRange selectedRange, selected )
{
foreach ( QModelIndex selectedModelIdx, selectedRange.indexes() )
{
selectedFeatureIds << masterModel()->rowToId( selectedModelIdx.row() );
}
}
foreach ( QItemSelectionRange deselectedRange, deselected )
{
foreach ( QModelIndex deselectedModelIdx, deselectedRange.indexes() )
{
deselectedFeatureIds << masterModel()->rowToId( deselectedModelIdx.row() );
}
}
// Change selection without emitting a signal
foreach ( QgsFeatureId seletedFid, selectedFeatureIds )
{
layer()->select( seletedFid, false );
}
foreach ( QgsFeatureId deselectedFid, deselectedFeatureIds )
{
layer()->deselect( deselectedFid, false );
}
// Now emit the signal
if ( mSyncSelection )
{
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );
}
connect( layer(), SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
}
bool QgsAttributeTableFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
{
Q_UNUSED( sourceParent );
@ -233,14 +165,21 @@ bool QgsAttributeTableFilterModel::filterAcceptsRow( int sourceRow, const QModel
void QgsAttributeTableFilterModel::extentsChanged()
{
generateListOfVisibleFeatures();
announcedInvalidateFilter();
invalidateFilter();
}
void QgsAttributeTableFilterModel::announcedInvalidateFilter()
void QgsAttributeTableFilterModel::selectionChanged()
{
emit filterAboutToBeInvalidated();
invalidateFilter();
emit filterInvalidated();
if ( ShowSelected == mFilterMode )
{
generateListOfVisibleFeatures();
invalidateFilter();
}
else if ( mSelectedOnTop )
{
sort ( sortColumn(), sortOrder() );
invalidate();
}
}
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
@ -277,12 +216,13 @@ void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
renderContext.setExtent( mCanvas->mapRenderer()->rendererContext()->extent() );
renderContext.setMapToPixel( mCanvas->mapRenderer()->rendererContext()->mapToPixel() );
renderContext.setRendererScale( mCanvas->mapRenderer()->scale() );
renderer->startRender( renderContext, layer() );
}
filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter;
}
renderer->startRender( renderContext, layer() );
QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setFilterRect( rect ) );
QgsFeature f;
@ -319,20 +259,20 @@ QgsFeatureId QgsAttributeTableFilterModel::rowToId( const QModelIndex& row )
return masterModel()->rowToId( mapToSource( row ).row() );
}
QItemSelectionModel* QgsAttributeTableFilterModel::masterSelection()
QModelIndex QgsAttributeTableFilterModel::fidToIndex( QgsFeatureId fid )
{
return mMasterSelection;
return mapFromMaster( masterModel()->idToIndex( fid ) );
}
void QgsAttributeTableFilterModel::disableSelectionSync()
QModelIndexList QgsAttributeTableFilterModel::fidToIndexList( QgsFeatureId fid )
{
mSyncSelection = false;
}
QModelIndexList indexes;
foreach( QModelIndex idx, masterModel()->idToIndexList( fid ) )
{
indexes.append( mapFromMaster( idx ) );
}
void QgsAttributeTableFilterModel::enableSelectionSync()
{
mSyncSelection = true;
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );
return indexes;
}
QModelIndex QgsAttributeTableFilterModel::mapToMaster( const QModelIndex &proxyIndex ) const
@ -346,9 +286,3 @@ QModelIndex QgsAttributeTableFilterModel::mapFromMaster( const QModelIndex &sour
// Master is source
return mapFromSource( sourceIndex );
}
QItemSelection QgsAttributeTableFilterModel::mapSelectionFromMaster( const QItemSelection& sourceSelection ) const
{
// Master is source
return mapSelectionFromMaster( sourceSelection );
}

View File

@ -22,12 +22,13 @@
#include "qgsvectorlayer.h" //QgsFeatureIds
#include "qgsattributetablemodel.h"
#include "qgsfeaturemodel.h"
class QgsVectorLayerCache;
class QgsMapCanvas;
class QItemSelectionModel;
class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, public QgsFeatureModel
{
Q_OBJECT
@ -42,7 +43,6 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
};
/**
*
*
* Make sure, the master model is already loaded, so the selection will get synchronized.
*
@ -114,38 +114,13 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
*/
QgsFeatureId rowToId( const QModelIndex& row );
/**
* Returns a selection model which is mapped to the sourceModel (tableModel) of this proxy.
* This selection also contains the features not visible because of the current filter.
* Views using this filter model may update this selection and subscribe to changes in
* this selection. This selection will synchronize itself with the selection on the map
* canvas.
*
* @return The master selection
*/
QItemSelectionModel* masterSelection();
/**
* Disables selection synchronisation with the map canvas. Changes to the selection in the master
* model are propagated to the layer, but no redraw is requested until @link enableSelectionSync() @endlink
* is called.
*/
void disableSelectionSync();
/**
* Enables selection synchronisation with the map canvas. Changes to the selection in the master
* are propagated and upon every change, a redraw will be requested. This method will update the
* selection to account for any cached selection change since @link disableSelectionSync() @endlink
* was called.
*/
void enableSelectionSync();
QModelIndex fidToIndex( QgsFeatureId fid );
QModelIndexList fidToIndexList( QgsFeatureId fid );
virtual QModelIndex mapToMaster( const QModelIndex &proxyIndex ) const;
virtual QModelIndex mapFromMaster( const QModelIndex &sourceIndex ) const;
virtual QItemSelection mapSelectionFromMaster( const QItemSelection& sourceSelection ) const;
protected:
/**
* Returns true if the source row will be accepted
@ -167,25 +142,7 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
*/
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
/**
* Calls invalidateFilter on the underlying QSortFilterProxyModel, but emits the signals
* filterAboutToBeInvalidated before and the signal filterInvalidated after the changes on the
* filter happen.
*/
void announcedInvalidateFilter();
public slots:
/**
* Is called upon every change of the selection on the map canvas.
* When an update is signalled, the filter is updated and invalidated if needed.
*
*/
void selectionChanged();
void masterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
/**
* Is called upon every change of the visible extents on the map canvas.
* When a change is signalled, the filter is updated and invalidated if needed.
@ -193,19 +150,8 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
*/
void extentsChanged();
signals:
/**
* This signal is emitted, before the filter is invalidated. With the help of this signal,
* selections of views attached to this can disable synchronisation with the master selection
* before items currently not visible with the filter get removed from the selection.
*/
void filterAboutToBeInvalidated();
/**
* Is called after the filter has been invalidated and recomputed.
* See filterAboutToBeInvalidated.
*/
void filterInvalidated();
private slots:
void selectionChanged();
private:
QgsFeatureIds mFilteredFeatures;
@ -214,7 +160,6 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
bool mSelectedOnTop;
QItemSelectionModel* mMasterSelection;
QgsAttributeTableModel* mTableModel;
bool mSyncSelection;
};
#endif

View File

@ -381,6 +381,19 @@ QModelIndex QgsAttributeTableModel::idToIndex( QgsFeatureId id ) const
return index( idToRow( id ), 0 );
}
QModelIndexList QgsAttributeTableModel::idToIndexList(QgsFeatureId id) const
{
QModelIndexList indexes;
int row = idToRow( id );
for ( int column = 0; column < columnCount(); ++column )
{
indexes.append( index( row, column ) );
}
return indexes;
}
QgsFeatureId QgsAttributeTableModel::rowToId( const int row ) const
{
if ( !mRowIdMap.contains( row ) )
@ -449,15 +462,30 @@ QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orient
QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() || ( role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::EditRole && role != SortRole ) )
if ( !index.isValid() ||
( role != Qt::TextAlignmentRole
&& role != Qt::DisplayRole
&& role != Qt::EditRole
&& role != SortRole
&& role != FeatureIdRole
&& role != FieldIndexRole
)
)
return QVariant();
QgsFeatureId rowId = rowToId( index.row() );
if ( role == FeatureIdRole )
return rowId;
if ( index.column() >= mFieldCount )
return role == Qt::DisplayRole ? rowId : QVariant();
int fieldId = mAttributes[ index.column()];
if ( role == FieldIndexRole )
return fieldId;
const QgsField& field = layer()->pendingFields()[ fieldId ];
QVariant::Type fldType = field.type();

View File

@ -45,7 +45,9 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
public:
enum Role
{
SortRole = Qt::UserRole + 1
SortRole = Qt::UserRole + 1,
FeatureIdRole = Qt::UserRole + 2,
FieldIndexRole = Qt::UserRole + 3
};
public:
@ -130,6 +132,8 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
QModelIndex idToIndex( QgsFeatureId id ) const;
QModelIndexList idToIndexList( QgsFeatureId id ) const;
/**
* get field index from column
*/

View File

@ -27,12 +27,14 @@
#include "qgsvectordataprovider.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsfeatureselectionmodel.h"
QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
: QTableView( parent ),
mMasterModel( NULL ),
mFilterModel( NULL ),
mActionPopup( NULL )
: QTableView( parent )
, mMasterModel( NULL )
, mFilterModel( NULL )
, mFeatureSelectionModel( NULL )
, mActionPopup( NULL )
{
QSettings settings;
restoreGeometry( settings.value( "/BetterAttributeTable/geometry" ).toByteArray() );
@ -40,13 +42,17 @@ QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
verticalHeader()->setDefaultSectionSize( 20 );
horizontalHeader()->setHighlightSections( false );
setItemDelegate( new QgsAttributeTableDelegate( this ) );
mTableDelegate = new QgsAttributeTableDelegate( this );
setItemDelegate( mTableDelegate );
setSelectionBehavior( QAbstractItemView::SelectRows );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setSortingEnabled( true );
verticalHeader()->viewport()->installEventFilter( this );
connect( verticalHeader(), SIGNAL( sectionPressed(int) ), this, SLOT(selectRow(int) ) );
connect( verticalHeader(), SIGNAL( sectionEntered(int) ), this, SLOT(_q_selectRow(int) ) );
}
QgsAttributeTableView::~QgsAttributeTableView()
@ -70,6 +76,11 @@ void QgsAttributeTableView::setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVec
mFilterModel = new QgsAttributeTableFilterModel( canvas, mMasterModel, mMasterModel );
setModel( mFilterModel );
delete mFeatureSelectionModel;
mFeatureSelectionModel = new QgsFeatureSelectionModel( mFilterModel, mFilterModel, layerCache->layer (), mFilterModel );
connect( mFeatureSelectionModel, SIGNAL(requestRepaint(QModelIndexList)), this, SLOT( repaintRequested(QModelIndexList) ) );
connect( mFeatureSelectionModel, SIGNAL( requestRepaint() ), this, SLOT( repaintRequested() ) );
setSelectionModel ( mFeatureSelectionModel );
delete oldModel;
delete filterModel;
@ -82,11 +93,11 @@ bool QgsAttributeTableView::eventFilter(QObject *object, QEvent *event)
switch ( event->type() )
{
case QEvent::MouseButtonPress:
mFilterModel->disableSelectionSync();
mFeatureSelectionModel->enableSync( false );
break;
case QEvent::MouseButtonRelease:
mFilterModel->enableSelectionSync();
mFeatureSelectionModel->enableSync( true );
break;
default:
@ -101,9 +112,6 @@ void QgsAttributeTableView::setModel( QgsAttributeTableFilterModel* filterModel
if ( mFilterModel )
{
// Cleanup old model stuff if present
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
disconnect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), this, SLOT( onFilterAboutToBeInvalidated() ) );
disconnect( mFilterModel, SIGNAL( filterInvalidated() ), this, SLOT( onFilterInvalidated() ) );
}
@ -111,24 +119,21 @@ void QgsAttributeTableView::setModel( QgsAttributeTableFilterModel* filterModel
mFilterModel = filterModel;
QTableView::setModel( filterModel );
delete mFeatureSelectionModel;
mFeatureSelectionModel = new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFilterModel->layer(), mFilterModel );
setSelectionModel ( mFeatureSelectionModel );
mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
connect( mFeatureSelectionModel, SIGNAL(requestRepaint(QModelIndexList)), this, SLOT( repaintRequested(QModelIndexList) ) );
connect( mFeatureSelectionModel, SIGNAL(requestRepaint()), this, SLOT( repaintRequested() ) );
if ( filterModel )
{
// Connect new model stuff
mMasterSelection = mFilterModel->masterSelection();
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
connect( mFilterModel, SIGNAL( filterAboutToBeInvalidated() ), SLOT( onFilterAboutToBeInvalidated() ) );
connect( mFilterModel, SIGNAL( filterInvalidated() ), SLOT( onFilterInvalidated() ) );
}
}
QItemSelectionModel* QgsAttributeTableView::masterSelection()
{
return mMasterSelection;
}
void QgsAttributeTableView::closeEvent( QCloseEvent *e )
{
Q_UNUSED( e );
@ -179,47 +184,24 @@ void QgsAttributeTableView::keyPressEvent( QKeyEvent *event )
}
}
void QgsAttributeTableView::onFilterAboutToBeInvalidated()
void QgsAttributeTableView::repaintRequested( QModelIndexList indexes )
{
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
foreach( const QModelIndex index, indexes )
{
update( index );
}
}
void QgsAttributeTableView::onFilterInvalidated()
void QgsAttributeTableView::repaintRequested()
{
QItemSelection localSelection = mFilterModel->mapSelectionFromSource( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsAttributeTableView::onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
QItemSelection masterSelected = mFilterModel->mapSelectionToSource( selected );
QItemSelection masterDeselected = mFilterModel->mapSelectionToSource( deselected );
mMasterSelection->select( masterSelected, QItemSelectionModel::Select );
mMasterSelection->select( masterDeselected, QItemSelectionModel::Deselect );
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsAttributeTableView::onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
Q_UNUSED( selected )
Q_UNUSED( deselected )
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
// Synchronizing the whole selection seems to work faster than using the deltas (Deselecting takes pretty long)
QItemSelection localSelection = mFilterModel->mapSelectionFromSource( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
setDirtyRegion( viewport()->rect() );
}
void QgsAttributeTableView::selectAll()
{
QItemSelection selection;
selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
selectionModel()->select( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
}
void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent* event )
@ -252,3 +234,51 @@ void QgsAttributeTableView::contextMenuEvent( QContextMenuEvent* event )
mActionPopup->popup( event->globalPos() );
}
}
void QgsAttributeTableView::selectRow( int row )
{
selectRow( row, true );
}
void QgsAttributeTableView::_q_selectRow( int row )
{
selectRow( row, false );
}
void QgsAttributeTableView::selectRow( int row, bool anchor )
{
if ( selectionBehavior() == QTableView::SelectColumns
|| ( selectionMode() == QTableView::SingleSelection
&& selectionBehavior() == QTableView::SelectItems))
return;
if ( row >= 0 && row < model()->rowCount() )
{
int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
QModelIndex index = model()->index( row, column );
QItemSelectionModel::SelectionFlags command = selectionCommand( index );
selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
if ( ( anchor && !( command & QItemSelectionModel::Current ) )
|| ( selectionMode() == QTableView::SingleSelection ) )
mRowSectionAnchor = row;
if ( selectionMode() != QTableView::SingleSelection
&& command.testFlag( QItemSelectionModel::Toggle ) )
{
if ( anchor )
mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
command &= ~QItemSelectionModel::Toggle;
command |= mCtrlDragSelectionFlag;
if ( !anchor )
command |= QItemSelectionModel::Current;
}
QModelIndex tl = model()->index( qMin( mRowSectionAnchor, row ), 0 );
QModelIndex br = model()->index( qMax( mRowSectionAnchor, row ), model()->columnCount() - 1 );
if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
setSelection( visualRect( tl ) | visualRect( br ), command );
else
mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
}
}

View File

@ -24,7 +24,8 @@
class QgsAttributeTableModel;
class QgsAttributeTableFilterModel;
class QgsVectorLayerCache;
class QgsFeatureSelectionModel;
class QgsAttributeTableDelegate;
class QgsMapCanvas;
class QgsVectorLayer;
class QMenu;
@ -48,12 +49,6 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
virtual void setModel( QgsAttributeTableFilterModel* filterModel );
/**
* The selection used for synchronisation with other views.
*
*/
QItemSelectionModel* masterSelection();
/**
* Autocreates the models
* @param layerCache The @link QgsVectorLayerCache @endlink to use ( as backend )
@ -137,19 +132,23 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
void finished();
public slots:
void onFilterAboutToBeInvalidated();
void onFilterInvalidated();
void onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
void onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
void repaintRequested( QModelIndexList indexes );
void repaintRequested();
virtual void selectAll();
virtual void selectRow( int row );
virtual void _q_selectRow( int row );
private:
void selectRow( int row, bool anchor );
QgsAttributeTableModel* mMasterModel;
QgsAttributeTableFilterModel* mFilterModel;
QgsFeatureSelectionModel* mFeatureSelectionModel;
QgsAttributeTableDelegate* mTableDelegate;
QAbstractItemModel* mModel; // Most likely the filter model
QMenu *mActionPopup;
QgsVectorLayerCache* mLayerCache;
QItemSelectionModel* mMasterSelection;
int mRowSectionAnchor;
QItemSelectionModel::SelectionFlag mCtrlDragSelectionFlag;
};
#endif

View File

@ -220,6 +220,9 @@ void QgsDualView::initModels( QgsMapCanvas* mapCanvas )
void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
{
if ( !feat.isValid() )
return;
// Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
QgsAttributeDialog* oldDialog = mAttributeDialog;

View File

@ -89,7 +89,7 @@ QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const
return QVariant::fromValue( featInfo );
}
return QVariant();
return sourceModel()->data( mapToSource( index ), role );
}
Qt::ItemFlags QgsFeatureListModel::flags( const QModelIndex &index ) const
@ -102,11 +102,6 @@ QgsAttributeTableModel* QgsFeatureListModel::masterModel()
return mFilterModel->masterModel();
}
QItemSelectionModel* QgsFeatureListModel::masterSelection()
{
return mFilterModel->masterSelection();
}
bool QgsFeatureListModel::setDisplayExpression( const QString expression )
{
const QgsFields fields = mFilterModel->layer()->dataProvider()->fields();
@ -239,12 +234,12 @@ int QgsFeatureListModel::rowCount( const QModelIndex& parent ) const
return sourceModel()->rowCount();
}
void QgsFeatureListModel::disableSelectionSync()
QModelIndex QgsFeatureListModel::fidToIndex(QgsFeatureId fid)
{
mFilterModel->disableSelectionSync();
return mapFromMaster( masterModel()->idToIndex( fid ) );
}
void QgsFeatureListModel::enableSelectionSync()
QModelIndexList QgsFeatureListModel::fidToIndexList(QgsFeatureId fid)
{
mFilterModel->enableSelectionSync();
return QModelIndexList() << fidToIndex( fid );
}

View File

@ -7,13 +7,14 @@
#include <QVariant>
#include <QItemSelectionModel>
#include "qgsfeaturemodel.h"
#include "qgsfeature.h" // QgsFeatureId
class QgsAttributeTableFilterModel;
class QgsAttributeTableModel;
class QgsVectorLayerCache;
class QgsFeatureListModel : public QAbstractProxyModel
class QgsFeatureListModel : public QAbstractProxyModel, public QgsFeatureModel
{
Q_OBJECT
@ -41,17 +42,6 @@ class QgsFeatureListModel : public QAbstractProxyModel
QgsAttributeTableModel* masterModel();
/**
* Returns a selection model which is mapped to the sourceModel (tableModel) of this proxy.
* This selection also contains the features not visible because of the current filter.
* Views using this filter model may update this selection and subscribe to changes in
* this selection. This selection will synchronize itself with the selection on the map
* canvas.
*
* @return The master selection
*/
QItemSelectionModel* masterSelection();
/**
* @param expression A {@link QgsExpression} compatible string.
* @return true if the expression could be set, false if there was a parse error.
@ -85,20 +75,8 @@ class QgsFeatureListModel : public QAbstractProxyModel
virtual int columnCount( const QModelIndex&parent = QModelIndex() ) const;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
/**
* Disables selection synchronisation with the map canvas. Changes to the selection in the master
* model are propagated to the layer, but no redraw is requested until @link enableSelectionSync() @endlink
* is called.
*/
void disableSelectionSync();
/**
* Enables selection synchronisation with the map canvas. Changes to the selection in the master
* are propagated and upon every change, a redraw will be requested. This method will update the
* selection to account for any cached selection change since @link disableSelectionSync() @endlink
* was called.
*/
void enableSelectionSync();
QModelIndex fidToIndex( QgsFeatureId fid );
QModelIndexList fidToIndexList( QgsFeatureId fid );
public slots:
void onBeginRemoveRows( const QModelIndex& parent, int first, int last );

View File

@ -28,13 +28,15 @@
#include "qgsmapcanvas.h"
#include "qgsfeaturelistviewdelegate.h"
#include "qgsfeaturelistmodel.h"
#include "qgsfeatureselectionmodel.h"
#include <QSet>
QgsFeatureListView::QgsFeatureListView( QWidget *parent )
: QListView( parent ),
mCurrentEditSelectionModel( NULL ),
mItemDelegate( NULL ),
mEditSelectionDrag( false )
: QListView( parent )
, mCurrentEditSelectionModel( NULL )
, mFeatureSelectionModel( NULL )
, mItemDelegate( NULL )
, mEditSelectionDrag( false )
{
setSelectionMode( QAbstractItemView::ExtendedSelection );
}
@ -49,13 +51,9 @@ void QgsFeatureListView::setModel( QgsFeatureListModel* featureListModel )
QListView::setModel( featureListModel );
mModel = featureListModel;
mMasterSelection = featureListModel->masterSelection();
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
connect( mModel->sourceModel(), SIGNAL( filterAboutToBeInvalidated() ), SLOT( onFilterAboutToBeInvalidated() ) );
connect( mModel->sourceModel(), SIGNAL( filterInvalidated() ), SLOT( onFilterInvalidated() ) );
delete mFeatureSelectionModel;
mFeatureSelectionModel = new QgsFeatureSelectionModel( featureListModel, featureListModel, featureListModel->layerCache ()->layer(), this );
setSelectionModel ( mFeatureSelectionModel );
mCurrentEditSelectionModel = new QItemSelectionModel( mModel->masterModel(), this );
@ -68,6 +66,10 @@ void QgsFeatureListView::setModel( QgsFeatureListModel* featureListModel )
mItemDelegate->setEditSelectionModel( mCurrentEditSelectionModel );
setItemDelegate( mItemDelegate );
mItemDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
connect( mFeatureSelectionModel, SIGNAL(requestRepaint(QModelIndexList)), this, SLOT( repaintRequested(QModelIndexList) ) );
connect( mFeatureSelectionModel, SIGNAL(requestRepaint()), this, SLOT( repaintRequested() ) );
connect( mCurrentEditSelectionModel, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( editSelectionChanged( QItemSelection, QItemSelection ) ) );
}
@ -94,50 +96,21 @@ QString QgsFeatureListView::parserErrorString()
return mModel->parserErrorString();
}
void QgsFeatureListView::mouseMoveEvent( QMouseEvent *event )
{
QPoint pos = event->pos();
if ( mEditSelectionDrag )
{
QModelIndex index = mModel->mapToMaster( indexAt( pos ) );
mCurrentEditSelectionModel->select( index, QItemSelectionModel::ClearAndSelect );
}
else
{
QListView::mouseMoveEvent( event );
}
}
void QgsFeatureListView::mousePressEvent( QMouseEvent *event )
{
QPoint pos = event->pos();
QModelIndex index = mModel->mapToMaster( indexAt( pos ) );
if ( QgsFeatureListViewDelegate::EditElement == mItemDelegate->positionToElement( event->pos() ) )
{
mEditSelectionDrag = true;
QModelIndex index = mModel->mapToMaster( indexAt( pos ) );
mCurrentEditSelectionModel->select( index, QItemSelectionModel::ClearAndSelect );
}
else
{
mModel->disableSelectionSync();
QListView::mousePressEvent( event );
}
}
void QgsFeatureListView::mouseReleaseEvent( QMouseEvent *event )
{
if ( mEditSelectionDrag )
{
mEditSelectionDrag = false;
}
else
{
QListView::mouseReleaseEvent( event );
mModel->enableSelectionSync();
mFeatureSelectionModel->enableSync( false );
selectRow( index, true );
}
}
@ -160,49 +133,12 @@ void QgsFeatureListView::editSelectionChanged( QItemSelection deselected, QItemS
}
}
void QgsFeatureListView::onFilterAboutToBeInvalidated()
{
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsFeatureListView::onFilterInvalidated()
{
QItemSelection localSelection = mModel->mapSelectionFromMaster( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsFeatureListView::onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
disconnect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
QItemSelection masterSelected = mModel->mapSelectionToMaster( selected );
QItemSelection masterDeselected = mModel->mapSelectionToMaster( deselected );
mMasterSelection->select( masterSelected, QItemSelectionModel::Select );
mMasterSelection->select( masterDeselected, QItemSelectionModel::Deselect );
connect( mMasterSelection, SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onMasterSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsFeatureListView::onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
Q_UNUSED( selected )
Q_UNUSED( deselected )
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
// Synchronizing the whole selection seems to work faster than using the deltas (Deselecting takes pretty long)
QItemSelection localSelection = mModel->mapSelectionFromMaster( mMasterSelection->selection() );
selectionModel()->select( localSelection, QItemSelectionModel::ClearAndSelect );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
}
void QgsFeatureListView::selectAll()
{
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
QItemSelection selection;
selection.append( QItemSelectionRange( mModel->index( 0, 0 ), mModel->index( mModel->rowCount() - 1, 0 ) ) );
selectionModel()->select( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
}
void QgsFeatureListView::setEditSelection( const QgsFeatureIds &fids )
@ -216,3 +152,131 @@ void QgsFeatureListView::setEditSelection( const QgsFeatureIds &fids )
mCurrentEditSelectionModel->select( selection, QItemSelectionModel::ClearAndSelect );
}
void QgsFeatureListView::repaintRequested( QModelIndexList indexes )
{
foreach( const QModelIndex index, indexes )
{
update( index );
}
}
void QgsFeatureListView::repaintRequested()
{
setDirtyRegion( viewport()->rect() );
}
/*!
This function is called with the given \a event when a mouse move event is
sent to the widget. If a selection is in progress and new items are moved
over the selection is extended; if a drag is in progress it is continued.
*/
void QgsFeatureListView::mouseMoveEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
QModelIndex index = mModel->mapToMaster( indexAt( pos ) );
if ( mEditSelectionDrag )
{
mCurrentEditSelectionModel->select( index, QItemSelectionModel::ClearAndSelect );
}
else
{
selectRow( index, false );
}
}
/*!
This function is called with the given \a event when a mouse button is released,
after a mouse press event on the widget. If a user presses the mouse inside your
widget and then drags the mouse to another location before releasing the mouse button,
your widget receives the release event. The function will emit the clicked() signal if an
item was being pressed.
*/
void QgsFeatureListView::mouseReleaseEvent( QMouseEvent *event )
{
Q_UNUSED( event );
if ( mEditSelectionDrag )
{
mEditSelectionDrag = false;
}
else
{
mFeatureSelectionModel->enableSync( true );
}
}
void QgsFeatureListView::keyPressEvent( QKeyEvent *event )
{
if ( Qt::Key_Up == event->key () || Qt::Key_Down == event->key() )
{
int currentRow = 0;
if ( 0 != mCurrentEditSelectionModel->selectedIndexes().count() )
{
QModelIndex localIndex = mModel->mapFromMaster( mCurrentEditSelectionModel->selectedIndexes().first() );
currentRow = localIndex.row();
}
QModelIndex newLocalIndex;
QModelIndex newIndex;
switch ( event->key() )
{
case Qt::Key_Up:
newLocalIndex = mModel->index( currentRow - 1, 0 );
newIndex = mModel->mapToMaster( newLocalIndex );
if ( newIndex.isValid() )
{
mCurrentEditSelectionModel->select( newIndex, QItemSelectionModel::ClearAndSelect );
scrollTo( newLocalIndex );
}
break;
case Qt::Key_Down:
newLocalIndex = mModel->index( currentRow + 1, 0 );
newIndex = mModel->mapToMaster( newLocalIndex );
if ( newIndex.isValid() )
{
mCurrentEditSelectionModel->select( newIndex, QItemSelectionModel::ClearAndSelect );
scrollTo( newLocalIndex );
}
break;
default:
break;
}
}
else
{
QListView::keyPressEvent( event );
}
}
void QgsFeatureListView::selectRow( const QModelIndex& index, bool anchor )
{
QItemSelectionModel::SelectionFlags command = selectionCommand( index );
int row = index.row();
if ( anchor )
mRowAnchor = row;
if ( selectionMode() != QListView::SingleSelection
&& command.testFlag( QItemSelectionModel::Toggle ) )
{
if ( anchor )
mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
command &= ~QItemSelectionModel::Toggle;
command |= mCtrlDragSelectionFlag;
if ( !anchor )
command |= QItemSelectionModel::Current;
}
QModelIndex tl = model()->index( qMin( mRowAnchor, row ), 0 );
QModelIndex br = model()->index( qMax( mRowAnchor, row ), model()->columnCount() - 1 );
mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
}

View File

@ -17,11 +17,13 @@
#define QGSATTRIBUTELISTVIEW_H
#include <QListView>
#include <qdebug.h>
#include "qgsfeature.h" // For QgsFeatureIds
class QgsAttributeTableFilterModel;
class QgsFeatureListModel;
class QgsFeatureSelectionModel;
class QgsAttributeTableModel;
class QgsVectorLayer;
class QgsVectorLayerCache;
@ -102,6 +104,7 @@ class GUI_EXPORT QgsFeatureListView : public QListView
virtual void mouseMoveEvent( QMouseEvent *event );
virtual void mousePressEvent( QMouseEvent *event );
virtual void mouseReleaseEvent( QMouseEvent *event );
virtual void keyPressEvent( QKeyEvent *event );
signals:
/**
@ -130,23 +133,22 @@ class GUI_EXPORT QgsFeatureListView : public QListView
*/
virtual void selectAll();
void repaintRequested( QModelIndexList indexes );
void repaintRequested();
private slots:
void editSelectionChanged( QItemSelection deselected, QItemSelection selected );
void onFilterAboutToBeInvalidated();
void onFilterInvalidated();
void onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
void onMasterSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
private:
void selectRow(const QModelIndex &index, bool anchor );
QgsFeatureListModel *mModel;
QItemSelectionModel* mCurrentEditSelectionModel;
QItemSelectionModel* mMasterSelection;
QgsFeatureSelectionModel* mFeatureSelectionModel;
QgsFeatureListViewDelegate* mItemDelegate;
bool mEditSelectionDrag; // Is set to true when the user initiated a left button click over an edit button and still keeps pressing /**< TODO */
int mRowAnchor;
QItemSelectionModel::SelectionFlags mCtrlDragSelectionFlag;
};
#endif

View File

@ -4,6 +4,7 @@
#include "qgsfeaturelistmodel.h"
#include "qgsapplication.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgsfeatureselectionmodel.h"
#include <QHBoxLayout>
#include <QPushButton>
@ -14,6 +15,7 @@
QgsFeatureListViewDelegate::QgsFeatureListViewDelegate( QgsFeatureListModel *listModel, QObject *parent )
: QItemDelegate( parent )
, mFeatureSelectionModel( NULL )
, mListModel( listModel )
{
}
@ -30,6 +32,11 @@ QgsFeatureListViewDelegate::Element QgsFeatureListViewDelegate::positionToElemen
}
}
void QgsFeatureListViewDelegate::setFeatureSelectionModel(QgsFeatureSelectionModel *featureSelectionModel)
{
mFeatureSelectionModel = featureSelectionModel;
}
void QgsFeatureListViewDelegate::setEditSelectionModel( QItemSelectionModel* editSelectionModel )
{
mEditSelectionModel = editSelectionModel;
@ -54,7 +61,7 @@ void QgsFeatureListViewDelegate::paint( QPainter *painter, const QStyleOptionVie
QPixmap icon;
if ( option.state.testFlag( QStyle::State_Selected ) )
if ( mFeatureSelectionModel->isSelected ( index ) )
{
// Item is selected
icon = QgsApplication::getThemePixmap( "/mIconSelected.svg" );

View File

@ -8,6 +8,7 @@
class QgsVectorLayer;
class QgsFeatureListModel;
class QgsFeatureSelectionModel;
class QPosition;
class QgsFeatureListViewDelegate : public QItemDelegate
@ -26,8 +27,11 @@ class QgsFeatureListViewDelegate : public QItemDelegate
explicit QgsFeatureListViewDelegate( QgsFeatureListModel* listModel, QObject *parent = 0 );
void setEditSelectionModel( QItemSelectionModel* editSelectionModel );
Element positionToElement( const QPoint& pos );
void setFeatureSelectionModel( QgsFeatureSelectionModel* featureSelectionModel );
signals:
void editButtonClicked( QModelIndex& index );
@ -38,6 +42,7 @@ class QgsFeatureListViewDelegate : public QItemDelegate
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
private:
QgsFeatureSelectionModel* mFeatureSelectionModel;
QItemSelectionModel* mEditSelectionModel;
QgsFeatureListModel* mListModel;
};

View File

@ -0,0 +1,13 @@
#ifndef QGSFEATUREMODEL_H
#define QGSFEATUREMODEL_H
#include "qgsfeature.h" // QgsFeatureId
#include <QModelIndex>
class QgsFeatureModel
{
public:
virtual QModelIndex fidToIndex( QgsFeatureId fid ) = 0;
};
#endif // QGSFEATUREMODEL_H

View File

@ -0,0 +1,173 @@
#include "qgsattributetablemodel.h"
#include "qgsfeaturemodel.h"
#include "qgsfeatureselectionmodel.h"
#include "qgsvectorlayer.h"
#include <qdebug.h>
QgsFeatureSelectionModel::QgsFeatureSelectionModel( QAbstractItemModel* model, QgsFeatureModel* featureModel, QgsVectorLayer* layer, QObject* parent )
: QItemSelectionModel( model, parent )
, mFeatureModel( featureModel )
, mLayer( layer )
, mSyncEnabled( true )
, mClearAndSelectBuffer( false )
{
connect( mLayer, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SLOT( layerSelectionChanged(QgsFeatureIds,QgsFeatureIds,bool)) );
}
void QgsFeatureSelectionModel::enableSync( bool enable )
{
mSyncEnabled = enable;
if ( mSyncEnabled )
{
if ( mClearAndSelectBuffer )
{
mLayer->setSelectedFeatures( mSelectedBuffer );
}
else
{
mLayer->select( mSelectedBuffer );
mLayer->deselect( mDeselectedBuffer );
}
mSelectedBuffer.clear();
mDeselectedBuffer.clear();
mClearAndSelectBuffer = false;
}
}
bool QgsFeatureSelectionModel::isSelected( QgsFeatureId fid )
{
if ( mSelectedBuffer.contains( fid ) )
return true;
if ( mDeselectedBuffer.contains( fid ) )
return false;
if ( mLayer->selectedFeaturesIds().contains( fid ) )
return true;
return false;
}
bool QgsFeatureSelectionModel::isSelected( const QModelIndex &index )
{
return isSelected( index.model()->data( index, QgsAttributeTableModel::FeatureIdRole ).toInt() );
}
void QgsFeatureSelectionModel::selectFeatures( const QItemSelection &selection, QItemSelectionModel::SelectionFlags command )
{
QgsFeatureIds ids;
foreach( const QModelIndex index, selection.indexes() )
{
QgsFeatureId id = index.model()->data( index, QgsAttributeTableModel::FeatureIdRole ).toInt();
ids << id;
}
disconnect( mLayer, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SLOT( layerSelectionChanged(QgsFeatureIds,QgsFeatureIds,bool)) );
if ( command.testFlag ( QItemSelectionModel::ClearAndSelect ) )
{
if ( !mSyncEnabled )
{
mClearAndSelectBuffer = true;
foreach ( QgsFeatureId id, ids )
{
if ( !mDeselectedBuffer.remove( id ) )
{
mSelectedBuffer.insert( id );
}
}
}
else
{
mLayer->setSelectedFeatures( ids );
}
}
else if ( command.testFlag ( QItemSelectionModel::Select ) )
{
if ( !mSyncEnabled )
{
foreach ( QgsFeatureId id, ids )
{
if ( !mDeselectedBuffer.remove( id ) )
{
mSelectedBuffer.insert( id );
}
}
}
else
{
mLayer->select( ids );
}
}
else if ( command.testFlag ( QItemSelectionModel::Deselect ) )
{
if ( !mSyncEnabled )
{
foreach ( QgsFeatureId id, ids )
{
if ( !mSelectedBuffer.remove( id ) )
{
mDeselectedBuffer.insert( id );
}
}
}
else
{
mLayer->deselect( ids );
}
}
connect( mLayer, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SLOT( layerSelectionChanged(QgsFeatureIds,QgsFeatureIds,bool)) );
QModelIndexList updatedIndexes;
foreach ( QModelIndex idx, selection.indexes() )
{
updatedIndexes.append( expandIndexToRow ( idx ) );
}
emit requestRepaint( updatedIndexes );
}
void QgsFeatureSelectionModel::layerSelectionChanged( QgsFeatureIds selected, QgsFeatureIds deselected, bool clearAndSelect )
{
if ( clearAndSelect )
{
emit requestRepaint();
}
else
{
QModelIndexList updatedIndexes;
foreach ( QgsFeatureId fid, selected )
{
updatedIndexes.append( expandIndexToRow( mFeatureModel->fidToIndex( fid ) ) );
}
foreach ( QgsFeatureId fid, deselected )
{
updatedIndexes.append( expandIndexToRow( mFeatureModel->fidToIndex( fid ) ) );
}
emit requestRepaint( updatedIndexes );
}
}
QModelIndexList QgsFeatureSelectionModel::expandIndexToRow( const QModelIndex& index ) const
{
QModelIndexList indexes;
const QAbstractItemModel* model = index.model();
int row = index.row();
if ( !model )
return indexes;
for( int column = 0; column < model->columnCount(); ++column )
{
indexes.append( model->index( row, column ) );
}
return indexes;
}

View File

@ -0,0 +1,99 @@
#ifndef QGSFEATURESELECTIONMODEL_H
#define QGSFEATURESELECTIONMODEL_H
#include <QItemSelectionModel>
#include "qgsfeature.h"
class QgsVectorLayer;
class QgsFeatureModel;
class QgsFeatureSelectionModel : public QItemSelectionModel
{
Q_OBJECT
public:
explicit QgsFeatureSelectionModel( QAbstractItemModel* model, QgsFeatureModel* featureModel, QgsVectorLayer* layer, QObject* parent );
/**
* Enables or disables synchronisation to the {@link QgsVectorLayer}
* When synchronisation is disabled, any selection change will be buffered inside this selection model.
* When enabled, any buffered changes are communicated to the layer and the buffer is emptied.
* Mainly to be used for performance reasons, because selection change on the layer can cost time as it
* repaints the layer.
*
* @param enable The synchronisation status to set.
*/
void enableSync( bool enable );
/**
* Returns the selection status of a given feature id.
*
* @param fid The featureid to determine the selection status of
*
* @return The selection status
*/
virtual bool isSelected( QgsFeatureId fid );
/**
* Returns the selection status of a given QModelIndex.
*
* @param index The index to determine the selection status of
*
* @return The selection status
*/
virtual bool isSelected( const QModelIndex& index );
signals:
/**
* Request a repaint of a list of model indexes.
* Views using this model should connect to and properly process this signal.
*
* @param indexes The model indexes which need to be repainted
*/
void requestRepaint( QModelIndexList indexes );
/**
* Request a repaint of the visible items of connected views.
* Views using this model should connect to and properly process this signal.
*/
void requestRepaint();
public slots:
/**
* Overwritten to do NOTHING (we handle selection ourselves)
*
* @see selectFeatures( const QItemSelection&, SelectionFlags )
*/
virtual void select ( const QModelIndex &index, SelectionFlags command ) { Q_UNUSED( index); Q_UNUSED( command ); }
/**
* Overwritten to do NOTHING (we handle selection ourselves)
*
* @see selectFeatures( const QItemSelection&, SelectionFlags )
*/
virtual void select ( const QItemSelection &selection, SelectionFlags command ) { Q_UNUSED( selection); Q_UNUSED( command ); }
/**
* Select features on this table. Is to be used in favor of the stock select methods.
*
* @param selection The QItemSelection which will be selected
* @param command The command to apply. Select, Deselect and ClearAndSelect are processed.
*/
virtual void selectFeatures( const QItemSelection &selection, SelectionFlags command );
private slots:
virtual void layerSelectionChanged( QgsFeatureIds selected, QgsFeatureIds deselected, bool clearAndSelect );
private:
QModelIndexList expandIndexToRow( const QModelIndex& index ) const;
private:
QgsFeatureModel* mFeatureModel;
QgsVectorLayer* mLayer;
bool mSyncEnabled;
QgsFeatureIds mSelectedBuffer;
QgsFeatureIds mDeselectedBuffer;
bool mClearAndSelectBuffer;
};
#endif // QGSFEATURESELECTIONMODEL_H

View File

@ -126,7 +126,7 @@ eVisGenericEventBrowserGui::~eVisGenericEventBrowserGui( )
//On close, clear selected feature
if ( 0 != mVectorLayer )
{
mVectorLayer->removeSelection( false );
mVectorLayer->removeSelection();
}
}
@ -532,11 +532,11 @@ void eVisGenericEventBrowserGui::displayImage( )
}
//clear any selection that may be present
mVectorLayer->removeSelection( false );
mVectorLayer->removeSelection();
if ( mFeatureIds.size( ) > 0 )
{
//select the current feature in the layer
mVectorLayer->select( mFeatureIds.at( mCurrentFeatureIndex ), true );
mVectorLayer->select( mFeatureIds.at( mCurrentFeatureIndex ) );
//get a copy of the feature
QgsFeature* myFeature = featureAtId( mFeatureIds.at( mCurrentFeatureIndex ) );

View File

@ -105,8 +105,6 @@ void eVisEventIdTool::select( QgsPoint thePoint )
//Transform rectange to map coordinates
myRectangle = toLayerCoordinates( myLayer, myRectangle );
//Rather than add to the current selection, clear all selected features
myLayer->removeSelection( false );
//select features
QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) );

View File

@ -21,6 +21,7 @@
#include <qgsapplication.h>
#include <qgsvectorlayer.h>
#include <qgsmapcanvas.h>
#include <qgsfeature.h>
class TestQgsDualView: public QObject
{
@ -31,7 +32,7 @@ class TestQgsDualView: public QObject
void init(); // will be called before each testfunction is executed.
void cleanup(); // will be called after every testfunction.
void testSelection();
void testSelectAll();
private:
QgsMapCanvas* mCanvas;
@ -78,54 +79,18 @@ void TestQgsDualView::cleanup()
delete mDualView;
}
void TestQgsDualView::testSelection()
void TestQgsDualView::testSelectAll()
{
// Select some features on the map canvas
QgsFeatureIds selectedIds;
selectedIds << 1 << 2 << 3;
mPointsLayer->setSelectedFeatures( selectedIds );
// Verify the same selection applies to the table
QVERIFY( mDualView->mFilterModel->masterSelection()->selectedIndexes().count() == 3 );
QVERIFY( mDualView->mTableView->selectionModel()->selectedRows().count() == 3 );
// Set the extent, so all features are visible
mCanvas->setExtent( QgsRectangle( -139, 22, -64, 48 ) );
// The table should also only show visible items (still all)
mDualView->setFilterMode( QgsAttributeTableFilterModel::ShowVisible );
QVERIFY( mDualView->mFilterModel->masterSelection()->selectedIndexes().count() == 3 );
QVERIFY( mDualView->mTableView->selectionModel()->selectedRows().count() == 3 );
// Set the extent, so no features are visible
mCanvas->setExtent( QgsRectangle( 0, 1, 0, 1 ) );
// The master selection should still hold all the features, while the currently visible should not
QVERIFY( mDualView->mFilterModel->masterSelection()->selectedIndexes().count() == 3 );
QVERIFY( mDualView->mTableView->selectionModel()->selectedRows().count() == 0 );
// Only show parts of the canvas, so only one selected feature is visible
mCanvas->setExtent( QgsRectangle( -139, 22, -100, 48 ) );
QVERIFY( mDualView->mFilterModel->masterSelection()->selectedIndexes().count() == 3 );
QVERIFY( mDualView->mTableView->selectionModel()->selectedRows().count() == 1 );
QVERIFY( mDualView->mTableView->selectionModel()->selectedRows().first().row() == 1 );
// Now the other way round...
// TODO: Fixme
mCanvas->setExtent( QgsRectangle( -139, 23, -100, 48 ) );
mDualView->mTableView->selectAll();
QVERIFY( mPointsLayer->selectedFeaturesIds().count() == 12 );
// Deselect a previously selected row
// List index 2 => fid 2
QVERIFY( mPointsLayer->selectedFeaturesIds().contains( 2 ) );
mDualView->mTableView->selectRow( 2 );
QVERIFY( false == mPointsLayer->selectedFeaturesIds().contains( 2 ) );
// Select a previously not selected row
// List index 6 => fid 13
QVERIFY( false == mPointsLayer->selectedFeaturesIds().contains( 13 ) );
mDualView->mTableView->selectRow( 6 );
QVERIFY( mPointsLayer->selectedFeaturesIds().contains( 13 ) );
QVERIFY( mPointsLayer->selectedFeatureCount() == 10 );
mPointsLayer->setSelectedFeatures( QgsFeatureIds() );
mCanvas->setExtent( QgsRectangle( -110, 40, -100, 48 ) );
mDualView->mTableView->selectAll();
QVERIFY( mPointsLayer->selectedFeatureCount() == 1 );
}
QTEST_MAIN( TestQgsDualView )