Showing with 121 additions and 22 deletions.
  1. +1 −0 src/app/qgsattributetabledialog.cpp
  2. +4 −2 src/core/qgsvectorlayercache.cpp
  3. +7 −1 src/core/qgsvectorlayercache.h
  4. +93 −17 src/gui/attributetable/qgsdualview.cpp
  5. +16 −2 src/gui/attributetable/qgsdualview.h
1 change: 1 addition & 0 deletions src/app/qgsattributetabledialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ void QgsAttributeTableDialog::on_mOpenFieldCalculator_clicked()
if ( col >= 0 )
{
masterModel->reload( masterModel->index( 0, col ), masterModel->index( masterModel->rowCount() - 1, col ) );
mMainView->reloadAttribute( col );
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/core/qgsvectorlayercache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer* layer, int cacheSize,

connect( mLayer, SIGNAL( attributeDeleted( int ) ), SLOT( attributeDeleted( int ) ) );
connect( mLayer, SIGNAL( updatedFields() ), SLOT( updatedFields() ) );
connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), SLOT( onAttributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
}

void QgsVectorLayerCache::setCacheSize( int cacheSize )
Expand Down Expand Up @@ -183,14 +183,16 @@ void QgsVectorLayerCache::featureRemoved( QgsFeatureId fid )
}
}

void QgsVectorLayerCache::attributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
{
QgsCachedFeature* cachedFeat = mCache[ fid ];

if ( NULL != cachedFeat )
{
cachedFeat->mFeature->setAttribute( field, value );
}

emit attributeValueChanged( fid, field, value );
}

void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
Expand Down
8 changes: 7 additions & 1 deletion src/core/qgsvectorlayercache.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,14 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
*/
void cachedLayerDeleted();

/**
* @brief Is emitted when an attribute is changed. Is re-emitted after the layer itself emits this signal.
* You should connect to this signal, to be sure, to not get a cached value if querying the cache.
*/
void attributeValueChanged( const QgsFeatureId& fid, const int& field, const QVariant &value );

private slots:
void attributeValueChanged( QgsFeatureId fid, int field, const QVariant& value );
void onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value );
void featureDeleted( QgsFeatureId fid );
void featureAdded( QgsFeatureId fid );
void attributeAdded( int field );
Expand Down
110 changes: 93 additions & 17 deletions src/gui/attributetable/qgsdualview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ void QgsDualView::init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QgsDista

connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );

connect( layer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( beforeCommitChanges() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( attributeAdded( int ) ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( attributeDeleted( int ) ), this, SLOT( editingToggled() ) );

initLayerCache( layer );
initModels( mapCanvas );

Expand All @@ -86,13 +80,19 @@ void QgsDualView::columnBoxInit()
// load fields
QList<QgsField> fields = mLayerCache->layer()->pendingFields().toList();

QString defaultField;

// default expression: saved value
QString displayExpression = mLayerCache->layer()->displayExpression();

// if no display expression is saved: use display field instead
if ( displayExpression == "" )
{
displayExpression = mLayerCache->layer()->displayField();
if ( mLayerCache->layer()->displayField() != "" )
{
defaultField = mLayerCache->layer()->displayField();
displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
}
}

// if neither diaplay expression nor display field is saved...
Expand All @@ -102,31 +102,37 @@ void QgsDualView::columnBoxInit()

if ( pkAttrs.size() > 0 )
{
if ( pkAttrs.size() == 1 )
defaultField = pkAttrs.at( 0 );

// ... If there are primary key(s) defined
QStringList pkFields;

foreach ( int attr, pkAttrs )
Q_FOREACH( int attr, pkAttrs )
{
pkFields.append( "\"" + fields[attr].name() + "\"" );
pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
}

displayExpression = pkFields.join( "||', '||" );
}
else if ( fields.size() > 0 )
{
if ( fields.size() == 1 )
defaultField = fields.at( 0 ).name();

// ... concat all fields
QStringList fieldNames;
foreach ( QgsField field, fields )
{
fieldNames.append( "\"" + field.name() + "\"" );
fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
}

displayExpression = fieldNames.join( "||', '||" );
}
else
{
// ... there isn't really much to display
displayExpression = "[Please define preview text]";
displayExpression = "'[Please define preview text]'";
}
}

Expand Down Expand Up @@ -157,7 +163,7 @@ void QgsDualView::columnBoxInit()
connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
mPreviewColumnsMenu->addAction( previewAction );

if ( text == displayExpression )
if ( text == defaultField )
{
mFeatureListPreviewButton->setDefaultAction( previewAction );
}
Expand Down Expand Up @@ -218,7 +224,12 @@ void QgsDualView::initLayerCache( QgsVectorLayer* layer )
mLayerCache->setFullCache( true );
}

connect( layer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( beforeCommitChanges() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) );
connect( layer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
connect( layer, SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
connect( mLayerCache, SIGNAL( attributeValueChanged( QgsFeatureId, int, QVariant ) ), this, SLOT( onAttributeValueChanged( QgsFeatureId, int, QVariant ) ) );
}

void QgsDualView::initModels( QgsMapCanvas* mapCanvas )
Expand All @@ -237,7 +248,7 @@ void QgsDualView::initModels( QgsMapCanvas* mapCanvas )
mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
}

void QgsDualView::on_mFeatureList_currentEditSelectionChanged( QgsFeature &feat )
void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
{
if ( !feat.isValid() )
return;
Expand All @@ -251,9 +262,6 @@ void QgsDualView::on_mFeatureList_currentEditSelectionChanged( QgsFeature &feat
mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );
}

if ( feat.attributes().count() != mLayerCache->layer()->pendingFields().count() )
mLayerCache->featureAtId( feat.id(), feat );

mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( feat ), true, mDistanceArea, this, false );
mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );
mAttributeDialog->dialog()->setVisible( true );
Expand Down Expand Up @@ -319,7 +327,7 @@ void QgsDualView::previewColumnChanged( QObject* action )

if ( previewAction )
{
if ( !mFeatureList->setDisplayExpression( previewAction->text() ) )
if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
{
QMessageBox::warning( this
, tr( "Could not set preview column" )
Expand Down Expand Up @@ -410,6 +418,74 @@ void QgsDualView::attributeDeleted( int attribute )
}
}

void QgsDualView::attributeAdded( int attribute )
{
if ( mAttributeDialog && mAttributeDialog->dialog() )
{
// Let the dialog write the edited widget values to it's feature
mAttributeDialog->accept();
// Get the edited feature
QgsFeature* feat = mAttributeDialog->feature();

// Get the feature including the newly added attribute
QgsFeature newFeat;
mLayerCache->featureAtId( feat->id(), newFeat );

int offset = 0;
for ( int idx = 0; idx < newFeat.attributes().count(); ++idx )
{
if ( idx == attribute )
{
offset = 1;
}
else
{
newFeat.setAttribute( idx, feat->attribute( idx - offset ) );
}
}

*feat = newFeat;

// Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
QgsAttributeDialog* oldDialog = mAttributeDialog;

mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );

mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( *feat ), true, mDistanceArea, this, false );
mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );

delete oldDialog;
}
}

void QgsDualView::reloadAttribute( const int& idx )
{
if ( mAttributeDialog && mAttributeDialog->dialog() )
{
// Let the dialog write the edited widget values to it's feature
mAttributeDialog->accept();
// Get the edited feature
QgsFeature* feat = mAttributeDialog->feature();

// Get the feature including the changed attribute
QgsFeature newFeat;
mLayerCache->featureAtId( feat->id(), newFeat );

// Update the attribute
feat->setAttribute( idx, newFeat.attribute( idx ) );

// Backup old dialog and delete only after creating the new dialog, so we can "hot-swap" the contained QgsFeature
QgsAttributeDialog* oldDialog = mAttributeDialog;

mAttributeEditorLayout->removeWidget( mAttributeDialog->dialog() );

mAttributeDialog = new QgsAttributeDialog( mLayerCache->layer(), new QgsFeature( *feat ), true, mDistanceArea, this, false );
mAttributeEditorLayout->addWidget( mAttributeDialog->dialog() );

delete oldDialog;
}
}

void QgsDualView::setFilteredFeatures( QgsFeatureIds filteredFeatures )
{
mFilterModel->setFilteredFeatures( filteredFeatures );
Expand Down
18 changes: 16 additions & 2 deletions src/gui/attributetable/qgsdualview.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,14 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
/**
* @brief saveEditChanges
*/

void saveEditChanges();

/**
* Update the shown feature if an attribute changed
*/
void reloadAttribute( const int& attribute );


signals:
/**
* Is emitted, whenever the display expression is successfully changed
Expand All @@ -171,7 +176,7 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*
* @param feat The newly visible feature
*/
void on_mFeatureList_currentEditSelectionChanged( QgsFeature& feat );
void on_mFeatureList_currentEditSelectionChanged( const QgsFeature& feat );

void previewExpressionBuilder();

Expand All @@ -192,6 +197,15 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
void attributeDeleted( int attribute );

/**
* If an attribute on this layer is added, add the field also for open
* attribute dialogs.
* (as long as the attribute dialog is not able to handle this problem)
*
* @param attribute The attribute being added
*/
void attributeAdded( int attribute );

/**
* Will be called periodically, when loading layers from slow data providers.
*
Expand Down