QGIS/src/core/qgsvectorlayerundocommand.cpp
Juergen E. Fischer 9d7ef65a2d vector layer: fix attributeAdded signal
don't assume new attributes are added at end, because in case of joins
they are added after the provider fields and before the joined fields.

field calculator: prepare expression again after attribute was added
(fixes #9320)
2014-02-12 00:54:44 +01:00

424 lines
12 KiB
C++

/***************************************************************************
qgsvectorlayerundocommand.cpp
---------------------
begin : June 2009
copyright : (C) 2009 by Martin Dobias
email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsvectorlayerundocommand.h"
#include "qgsgeometry.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgsgeometrycache.h"
#include "qgsvectorlayereditbuffer.h"
#include "qgslogger.h"
QgsVectorLayerUndoCommandAddFeature::QgsVectorLayerUndoCommandAddFeature( QgsVectorLayerEditBuffer* buffer, QgsFeature& f )
: QgsVectorLayerUndoCommand( buffer )
{
static int addedIdLowWaterMark = -1;
//assign a temporary id to the feature (use negative numbers)
addedIdLowWaterMark--;
QgsDebugMsg( "Assigned feature id " + QString::number( addedIdLowWaterMark ) );
// Force a feature ID (to keep other functions in QGIS happy,
// providers will use their own new feature ID when we commit the new feature)
// and add to the known added features.
f.setFeatureId( addedIdLowWaterMark );
mFeature = f;
}
void QgsVectorLayerUndoCommandAddFeature::undo()
{
#ifdef QGISDEBUG
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.find( mFeature.id() );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
#endif
mBuffer->mAddedFeatures.remove( mFeature.id() );
if ( mFeature.geometry() )
cache()->removeGeometry( mFeature.id() );
emit mBuffer->featureDeleted( mFeature.id() );
}
void QgsVectorLayerUndoCommandAddFeature::redo()
{
mBuffer->mAddedFeatures.insert( mFeature.id(), mFeature );
if ( mFeature.geometry() )
cache()->cacheGeometry( mFeature.id(), *mFeature.geometry() );
emit mBuffer->featureAdded( mFeature.id() );
}
QgsVectorLayerUndoCommandDeleteFeature::QgsVectorLayerUndoCommandDeleteFeature( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid )
: QgsVectorLayerUndoCommand( buffer )
{
mFid = fid;
if ( FID_IS_NEW( mFid ) )
{
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
mOldAddedFeature = it.value();
}
}
void QgsVectorLayerUndoCommandDeleteFeature::undo()
{
if ( FID_IS_NEW( mFid ) )
{
mBuffer->mAddedFeatures.insert( mOldAddedFeature.id(), mOldAddedFeature );
}
else
{
mBuffer->mDeletedFeatureIds.remove( mFid );
}
emit mBuffer->featureAdded( mFid );
}
void QgsVectorLayerUndoCommandDeleteFeature::redo()
{
if ( FID_IS_NEW( mFid ) )
{
mBuffer->mAddedFeatures.remove( mFid );
}
else
{
mBuffer->mDeletedFeatureIds.insert( mFid );
}
emit mBuffer->featureDeleted( mFid );
}
QgsVectorLayerUndoCommandChangeGeometry::QgsVectorLayerUndoCommandChangeGeometry( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid, QgsGeometry* newGeom )
: QgsVectorLayerUndoCommand( buffer )
, mFid( fid )
{
if ( FID_IS_NEW( mFid ) )
{
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
mOldGeom = new QgsGeometry( *it.value().geometry() );
}
else
{
bool changedAlready = mBuffer->mChangedGeometries.contains( mFid );
QgsGeometry geom;
bool cachedGeom = cache()->geometry( mFid, geom );
mOldGeom = ( changedAlready && cachedGeom ) ? new QgsGeometry( geom ) : 0;
}
mNewGeom = new QgsGeometry( *newGeom );
}
int QgsVectorLayerUndoCommandChangeGeometry::id() const
{
return 1;
}
bool QgsVectorLayerUndoCommandChangeGeometry::mergeWith( const QUndoCommand *other )
{
if ( other->id() != id() )
return false;
const QgsVectorLayerUndoCommandChangeGeometry *merge = dynamic_cast<const QgsVectorLayerUndoCommandChangeGeometry *>( other );
if ( !merge )
return false;
if ( merge->mFid != mFid )
return false;
delete mNewGeom;
mNewGeom = merge->mNewGeom;
merge->mNewGeom = 0;
return true;
}
QgsVectorLayerUndoCommandChangeGeometry::~QgsVectorLayerUndoCommandChangeGeometry()
{
delete mOldGeom;
delete mNewGeom;
}
void QgsVectorLayerUndoCommandChangeGeometry::undo()
{
if ( FID_IS_NEW( mFid ) )
{
// modify added features
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
it.value().setGeometry( *mOldGeom );
cache()->cacheGeometry( mFid, *mOldGeom );
emit mBuffer->geometryChanged( mFid, *mOldGeom );
}
else
{
// existing feature
if ( !mOldGeom )
{
mBuffer->mChangedGeometries.remove( mFid );
QgsFeature f;
if ( layer()->getFeatures( QgsFeatureRequest().setFilterFid( mFid ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) && f.geometry() )
{
cache()->cacheGeometry( mFid, *f.geometry() );
emit mBuffer->geometryChanged( mFid, *f.geometry() );
}
}
else
{
mBuffer->mChangedGeometries[mFid] = *mOldGeom;
cache()->cacheGeometry( mFid, *mOldGeom );
emit mBuffer->geometryChanged( mFid, *mOldGeom );
}
}
}
void QgsVectorLayerUndoCommandChangeGeometry::redo()
{
if ( FID_IS_NEW( mFid ) )
{
// modify added features
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
it.value().setGeometry( *mNewGeom );
}
else
{
mBuffer->mChangedGeometries[ mFid ] = *mNewGeom;
}
cache()->cacheGeometry( mFid, *mNewGeom );
emit mBuffer->geometryChanged( mFid, *mNewGeom );
}
QgsVectorLayerUndoCommandChangeAttribute::QgsVectorLayerUndoCommandChangeAttribute( QgsVectorLayerEditBuffer* buffer, QgsFeatureId fid, int fieldIndex, const QVariant &newValue, const QVariant &oldValue )
: QgsVectorLayerUndoCommand( buffer )
, mFid( fid )
, mFieldIndex( fieldIndex )
, mOldValue( oldValue )
, mNewValue( newValue )
, mFirstChange( true )
{
if ( FID_IS_NEW( mFid ) )
{
// work with added feature
QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
if ( it.value().attribute( mFieldIndex ).isValid() )
{
mOldValue = it.value().attribute( mFieldIndex );
mFirstChange = false;
}
}
else if ( mBuffer->mChangedAttributeValues.contains( mFid ) && mBuffer->mChangedAttributeValues[mFid].contains( mFieldIndex ) )
{
mOldValue = mBuffer->mChangedAttributeValues[mFid][mFieldIndex];
mFirstChange = false;
}
}
void QgsVectorLayerUndoCommandChangeAttribute::undo()
{
QVariant original = mOldValue;
if ( FID_IS_NEW( mFid ) )
{
// added feature
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
it.value().setAttribute( mFieldIndex, mOldValue );
}
else if ( mFirstChange )
{
// existing feature
mBuffer->mChangedAttributeValues[mFid].remove( mFieldIndex );
if ( mBuffer->mChangedAttributeValues[mFid].isEmpty() )
mBuffer->mChangedAttributeValues.remove( mFid );
if ( !mOldValue.isValid() )
{
// get old value from provider
QgsFeature tmp;
QgsFeatureRequest request;
request.setFilterFid( mFid );
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setSubsetOfAttributes( QgsAttributeList() << mFieldIndex );
QgsFeatureIterator fi = layer()->getFeatures( request );
if ( fi.nextFeature( tmp ) )
original = tmp.attribute( mFieldIndex );
}
}
else
{
mBuffer->mChangedAttributeValues[mFid][mFieldIndex] = mOldValue;
}
emit mBuffer->attributeValueChanged( mFid, mFieldIndex, original );
}
void QgsVectorLayerUndoCommandChangeAttribute::redo()
{
if ( FID_IS_NEW( mFid ) )
{
// updated added feature
QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
it.value().setAttribute( mFieldIndex, mNewValue );
}
else
{
// changed attribute of existing feature
if ( !mBuffer->mChangedAttributeValues.contains( mFid ) )
{
mBuffer->mChangedAttributeValues.insert( mFid, QgsAttributeMap() );
}
mBuffer->mChangedAttributeValues[mFid].insert( mFieldIndex, mNewValue );
}
emit mBuffer->attributeValueChanged( mFid, mFieldIndex, mNewValue );
}
QgsVectorLayerUndoCommandAddAttribute::QgsVectorLayerUndoCommandAddAttribute( QgsVectorLayerEditBuffer* buffer, const QgsField& field )
: QgsVectorLayerUndoCommand( buffer )
, mField( field )
{
const QgsFields &fields = layer()->pendingFields();
int i;
for ( i = 0; i < fields.count() && fields.fieldOrigin( i ) != QgsFields::OriginJoin; i++ )
;
mFieldIndex = i;
}
void QgsVectorLayerUndoCommandAddAttribute::undo()
{
int index = layer()->pendingFields().fieldOriginIndex( mFieldIndex );
mBuffer->mAddedAttributes.removeAt( index );
mBuffer->updateLayerFields();
mBuffer->handleAttributeDeleted( mFieldIndex );
emit mBuffer->attributeDeleted( mFieldIndex );
}
void QgsVectorLayerUndoCommandAddAttribute::redo()
{
mBuffer->mAddedAttributes.append( mField );
mBuffer->updateLayerFields();
mBuffer->handleAttributeAdded( mFieldIndex );
emit mBuffer->attributeAdded( mFieldIndex );
}
QgsVectorLayerUndoCommandDeleteAttribute::QgsVectorLayerUndoCommandDeleteAttribute( QgsVectorLayerEditBuffer* buffer, int fieldIndex )
: QgsVectorLayerUndoCommand( buffer )
, mFieldIndex( fieldIndex )
{
const QgsFields& fields = layer()->pendingFields();
QgsFields::FieldOrigin origin = fields.fieldOrigin( mFieldIndex );
mOriginIndex = fields.fieldOriginIndex( mFieldIndex );
mProviderField = ( origin == QgsFields::OriginProvider );
if ( !mProviderField )
{
// need to store the field definition
mOldField = mBuffer->mAddedAttributes[mOriginIndex];
}
// save values of new features
for ( QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.begin(); it != mBuffer->mAddedFeatures.end(); ++it )
{
const QgsFeature& f = it.value();
mDeletedValues.insert( f.id(), f.attribute( mFieldIndex ) );
}
// save changed values
for ( QgsChangedAttributesMap::const_iterator it = mBuffer->mChangedAttributeValues.begin(); it != mBuffer->mChangedAttributeValues.end(); ++it )
{
const QgsAttributeMap& attrs = it.value();
if ( attrs.contains( mFieldIndex ) )
mDeletedValues.insert( it.key(), attrs[mFieldIndex] );
}
}
void QgsVectorLayerUndoCommandDeleteAttribute::undo()
{
if ( mProviderField )
{
mBuffer->mDeletedAttributeIds.removeOne( mOriginIndex );
}
else
{
// newly added attribute
mBuffer->mAddedAttributes.insert( mOriginIndex, mOldField );
}
mBuffer->updateLayerFields();
mBuffer->handleAttributeAdded( mFieldIndex ); // update changed attributes + new features
// set previously used attributes of new features
for ( QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.begin(); it != mBuffer->mAddedFeatures.end(); ++it )
{
QgsFeature& f = it.value();
f.setAttribute( mFieldIndex, mDeletedValues.value( f.id() ) );
}
// set previously used changed attributes
for ( QMap<QgsFeatureId, QVariant>::const_iterator it = mDeletedValues.begin(); it != mDeletedValues.end(); ++it )
{
if ( !FID_IS_NEW( it.key() ) )
{
QgsAttributeMap& attrs = mBuffer->mChangedAttributeValues[it.key()]; // also adds record if nonexistant
attrs.insert( mFieldIndex, it.value() );
}
}
emit mBuffer->attributeAdded( mFieldIndex );
}
void QgsVectorLayerUndoCommandDeleteAttribute::redo()
{
if ( mProviderField )
{
mBuffer->mDeletedAttributeIds.append( mOriginIndex );
qSort( mBuffer->mDeletedAttributeIds ); // keep it sorted
}
else
{
// newly added attribute
mBuffer->mAddedAttributes.removeAt( mOriginIndex ); // removing temporary attribute
}
mBuffer->updateLayerFields();
mBuffer->handleAttributeDeleted( mFieldIndex ); // update changed attributes + new features
emit mBuffer->attributeDeleted( mFieldIndex );
}