mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[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:
parent
5280e212ec
commit
47c10db1fe
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,6 +41,7 @@ doc/INSTALL.tex
|
||||
scripts/Debug
|
||||
scripts/RelWithDebInfo
|
||||
/CMakeLists.txt.user
|
||||
/CMakeLists.txt.user.*
|
||||
qgis-test.ctest
|
||||
i18n/*.qm
|
||||
.project
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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" );
|
||||
|
@ -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;
|
||||
};
|
||||
|
13
src/gui/attributetable/qgsfeaturemodel.h
Normal file
13
src/gui/attributetable/qgsfeaturemodel.h
Normal 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
|
173
src/gui/attributetable/qgsfeatureselectionmodel.cpp
Normal file
173
src/gui/attributetable/qgsfeatureselectionmodel.cpp
Normal 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;
|
||||
}
|
99
src/gui/attributetable/qgsfeatureselectionmodel.h
Normal file
99
src/gui/attributetable/qgsfeatureselectionmodel.h
Normal 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
|
@ -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 ) );
|
||||
|
||||
|
@ -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() ) );
|
||||
|
||||
|
@ -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 )
|
||||
|
Loading…
x
Reference in New Issue
Block a user