28#include "moc_qgsvectorlayereditbuffer.cpp"
30using namespace Qt::StringLiterals;
33template <
class Key,
class T>
void mapToReversedLists(
const QMap< Key, T > &map, QList<Key> &ks, QList<T> &vs )
35 ks.reserve( map.size() );
36 vs.reserve( map.size() );
37 typename QMap<Key, T>::const_iterator i = map.constEnd();
38 while ( i != map.constBegin() )
42 vs.append( i.value() );
55 return !
L->undoStack()->isClean();
83 fields.
rename( renameIt.key(), renameIt.value() );
127 for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
128 attrs[it.key()] = it.value();
165 bool anyAdded =
false;
166 for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
168 const bool thisFeatureResult =
addFeature( *iter );
169 result = result && thisFeatureResult;
170 anyAdded |= thisFeatureResult;
191 QgsDebugError( u
"Cannot delete features (missing DeleteFeature capability)"_s );
199 QgsDebugError( u
"Cannot delete features (in the list of added features)"_s );
207 QgsDebugError( u
"Cannot delete features (in the list of deleted features)"_s );
220 QgsDebugError( u
"Cannot delete features (missing DeleteFeatures capability)"_s );
241 if ( !
L->isSpatial() )
267 for (
auto it = newValues.constBegin() ; it != newValues.constEnd(); ++it )
269 const int field = it.key();
270 const QVariant newValue = it.value();
273 if ( oldValues.contains( field ) )
274 oldValue = oldValues[field];
298 if ( field < 0 || field >=
L->fields().count() ||
315 if ( field.
name().isEmpty() )
319 for (
const QgsField &updatedField : fields )
321 if ( updatedField.name() == field.
name() )
325 if ( !
L->dataProvider()->supportedType( field ) )
338 if ( index < 0 || index >=
L->fields().count() )
343 int originIndex =
L->fields().fieldOriginIndex( index );
360 if ( newName.isEmpty() )
363 if ( index < 0 || index >=
L->fields().count() )
367 for (
const QgsField &updatedField : fields )
369 if ( updatedField.name() == newName )
380 commitErrors.clear();
392 success &= commitChangesCheckGeometryTypeCompatibility( commitErrors );
399 bool attributesChanged =
false;
402 bool attributesDeleted =
false;
403 success &= commitChangesDeleteAttributes( attributesDeleted, commitErrors );
404 attributesChanged |= attributesDeleted;
410 bool attributesRenamed =
false;
411 success &= commitChangesRenameAttributes( attributesRenamed, commitErrors );
412 attributesChanged |= attributesRenamed;
420 bool attributesAdded =
false;
421 success &= commitChangesAddAttributes( attributesAdded, commitErrors );
422 attributesChanged |= attributesAdded;
428 if ( success && attributesChanged )
429 success &= commitChangesCheckAttributesModifications( oldFields, commitErrors );
436 bool attributesChanged;
437 success &= commitChangesChangeAttributes( attributesChanged, commitErrors );
445 bool featuresDeleted;
446 success &= commitChangesDeleteFeatures( featuresDeleted, commitErrors );
455 success &= commitChangesAddFeatures( featuresAdded, commitErrors );
461 commitErrors << tr(
"\n Provider errors:" );
462 const auto constErrors = provider->
errors();
463 for ( QString e : constErrors )
465 commitErrors <<
" " + e.replace(
'\n',
"\n "_L1 );
481 L->undoStack()->setIndex( 0 );
496QString QgsVectorLayerEditBuffer::dumpEditBuffer()
501 msg +=
"CHANGED GEOMETRIES:\n";
505 msg += QString(
"- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
526 attrs.insert( index, QVariant() );
527 featureIt->setAttributes( attrs );
529 const QgsFields oldFields = featureIt->fields();
530 for (
int i = 0; i < oldFields.
size(); i++ )
534 fields.
append( field,
L->fields().fieldOrigin(
L->fields().indexFromName( field.
name() ) ) );
538 if ( index == oldFields.
size() )
540 fields.
append( field,
L->fields().fieldOrigin(
L->fields().indexFromName( field.
name() ) ) );
542 featureIt->setFields( fields,
false );
548 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end(), std::greater< int >() );
549 const auto constSortedRenamedIndices = sortedRenamedIndices;
550 for (
int renameIndex : constSortedRenamedIndices )
552 if ( renameIndex >= index )
569 if ( attrMap.contains( index ) )
570 attrMap.remove( index );
581 attrs.remove( index );
582 featureIt->setAttributes( attrs );
585 featureIt->setFields( fields,
false );
591 std::sort( sortedRenamedIndices.begin(), sortedRenamedIndices.end() );
594 const auto constSortedRenamedIndices = sortedRenamedIndices;
595 for (
int renameIndex : constSortedRenamedIndices )
597 if ( renameIndex > index )
613 for ( QgsAttributeMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
615 int attrIndex = it.key();
616 updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
628bool QgsVectorLayerEditBuffer::commitChangesCheckGeometryTypeCompatibility( QStringList &commitErrors )
639 if ( ( ! f.hasGeometry() ) ||
645 commitErrors << tr(
"ERROR: %n feature(s) not added - geometry type is not compatible with the current layer.",
"not added features count",
mAddedFeatures.size() );
653 commitErrors << tr(
"ERROR: %n feature(s) not added - provider doesn't support adding features.",
"not added features count",
mAddedFeatures.size() );
660bool QgsVectorLayerEditBuffer::commitChangesDeleteAttributes(
bool &attributesDeleted, QStringList &commitErrors )
662 attributesDeleted =
false;
669 commitErrors << tr(
"SUCCESS: %n attribute(s) deleted.",
"deleted attributes count",
mDeletedAttributeIds.size() );
674 attributesDeleted =
true;
678 commitErrors << tr(
"ERROR: %n attribute(s) not deleted.",
"not deleted attributes count",
mDeletedAttributeIds.size() );
680 QString list =
"ERROR: Pending attribute deletes:";
682 for (
int idx : constMDeletedAttributeIds )
684 list.append(
' ' +
L->fields().at( idx ).name() );
686 commitErrors << list;
694bool QgsVectorLayerEditBuffer::commitChangesRenameAttributes(
bool &attributesRenamed, QStringList &commitErrors )
696 attributesRenamed =
false;
703 commitErrors << tr(
"SUCCESS: %n attribute(s) renamed.",
"renamed attributes count",
mRenamedAttributes.size() );
708 attributesRenamed =
true;
712 commitErrors << tr(
"ERROR: %n attribute(s) not renamed",
"not renamed attributes count",
mRenamedAttributes.size() );
719bool QgsVectorLayerEditBuffer::commitChangesAddAttributes(
bool &attributesAdded, QStringList &commitErrors )
721 attributesAdded =
false;
728 commitErrors << tr(
"SUCCESS: %n attribute(s) added.",
"added attributes count",
mAddedAttributes.size() );
731 attributesAdded =
true;
735 commitErrors << tr(
"ERROR: %n new attribute(s) not added",
"not added attributes count",
mAddedAttributes.size() );
737 QString list =
"ERROR: Pending adds:";
739 for ( QgsField f : constMAddedAttributes )
741 list.append(
' ' + f.name() );
743 commitErrors << list;
751bool QgsVectorLayerEditBuffer::commitChangesCheckAttributesModifications(
const QgsFields oldFields, QStringList &commitErrors )
754 QgsFields newFields =
L->fields();
758 commitErrors << tr(
"ERROR: the count of fields is incorrect after addition/removal of fields!" );
762 for (
int i = 0; i < std::min( oldFields.
count(), newFields.
count() ); ++i )
764 QgsField oldField = oldFields.
at( i );
765 QgsField newField = newFields.
at( i );
766 if ( oldField != newField )
769 << tr(
"ERROR: field with index %1 is not the same!" ).arg( i )
770 << tr(
"Provider: %1" ).arg(
L->providerType() )
771 << tr(
"Storage: %1" ).arg(
L->storageType() )
772 << u
"%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6"_s
773 .arg( tr(
"expected field" ),
775 QVariant::typeToName( oldField.
type() ),
779 << u
"%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6"_s
780 .arg( tr(
"retrieved field" ),
782 QVariant::typeToName( newField.
type() ),
793bool QgsVectorLayerEditBuffer::commitChangesChangeAttributes(
bool &attributesChanged, QStringList &commitErrors )
795 attributesChanged =
false;
805 attributesChanged =
true;
824 commitErrors << tr(
"ERROR: %1 geometries not changed. Data provider '%2' does not have ChangeFeatures or ChangeGeometries capabilities",
"not changed geometries count" )
826 .arg(
L->dataProvider()->name() );
832 commitErrors << tr(
"SUCCESS: %n geometries were changed.",
"changed geometries count",
mChangedGeometries.size() );
833 attributesChanged =
true;
839 commitErrors << tr(
"ERROR: %n geometries not changed.",
"not changed geometries count",
mChangedGeometries.size() );
849 commitErrors << tr(
"ERROR: %1 attribute value change(s) not applied. Data provider '%2' does not have ChangeFeatures or ChangeAttributeValues capabilities",
"not changed attribute values count" )
851 .arg(
L->dataProvider()->name() );
857 commitErrors << tr(
"SUCCESS: %n attribute value(s) changed.",
"changed attribute values count",
mChangedAttributeValues.size() );
858 attributesChanged =
true;
864 commitErrors << tr(
"ERROR: %n attribute value change(s) not applied.",
"not changed attribute values count",
mChangedAttributeValues.size() );
866 QString list =
"ERROR: pending changes:";
872 for (
int idx : constKeys )
874 list.append( QString(
" %1:%2" ).arg(
L->fields().at( idx ).name() ).arg(
mChangedAttributeValues[
id][idx].toString() ) );
878 commitErrors << list;
887bool QgsVectorLayerEditBuffer::commitChangesDeleteFeatures(
bool &featuresDeleted, QStringList &commitErrors )
889 featuresDeleted =
false;
896 commitErrors << tr(
"SUCCESS: %n feature(s) deleted.",
"deleted features count",
mDeletedFeatureIds.size() );
897 featuresDeleted =
true;
911 commitErrors << tr(
"ERROR: %n feature(s) not deleted.",
"not deleted features count",
mDeletedFeatureIds.size() );
913 QString list =
"ERROR: pending deletes:";
919 commitErrors << list;
927bool QgsVectorLayerEditBuffer::commitChangesAddFeatures(
bool &featuresAdded, QStringList &commitErrors )
929 featuresAdded =
false;
936 QList<QgsFeatureId> ids;
944 for (
int i = 0; i < featuresToAdd.count(); ++i )
952 commitErrors << tr(
"SUCCESS: %n feature(s) added.",
"added features count", featuresToAdd.size() );
953 featuresAdded =
true;
957 for (
int i = 0; i < featuresToAdd.count(); ++i )
959 if ( featuresToAdd[i].
id() != ids[i] )
962 if (
L->mSelectedFeatureIds.contains( ids[i] ) )
964 L->mSelectedFeatureIds.remove( ids[i] );
965 L->mSelectedFeatureIds.insert( featuresToAdd[i].
id() );
976 commitErrors << tr(
"ERROR: %n feature(s) not added.",
"not added features count",
mAddedFeatures.size() );
978 QString list =
"ERROR: pending adds:";
980 for ( QgsFeature f : constMAddedFeatures )
983 for (
int i = 0; i <
L->fields().size(); i++ )
985 list.append( QString(
" %1:%2" ).arg(
L->fields().at( i ).name() ).arg( f.attributes()[i].toString() ) );
989 commitErrors << list;
996 commitErrors << tr(
"ERROR: %n feature(s) not added - provider doesn't support adding features.",
"not added features count",
mAddedFeatures.size() );
@ ChangeFeatures
Supports joint updates for attributes and geometry. Providers supporting this should still define Cha...
@ AddFeatures
Allows adding features.
@ ChangeGeometries
Allows modifications of geometries.
@ AddAttributes
Allows addition of new attributes (fields).
@ RenameAttributes
Supports renaming attributes (fields).
@ DeleteFeatures
Allows deletion of features.
@ DeleteAttributes
Allows deletion of attributes (fields).
@ ChangeAttributeValues
Allows modification of attribute values.
@ Provider
Field originates from the underlying data provider of the vector layer.
@ Edit
Field has been temporarily added in editing mode.
@ Expression
Field is calculated from an expression.
@ Join
Field originates from a joined layer.
@ RollBackOnErrors
Roll back the whole transaction if a single add feature operation fails.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
int attributeCount() const
Returns the number of attributes attached to the feature.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Encapsulate a field in an attribute table or data source.
QString typeName() const
Gets the field type.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
void remove(int fieldIdx)
Removes the field with the given index.
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
A geometry is the spatial representation of a feature.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Base class for vector data providers.
void clearErrors()
Clear recorded errors.
QStringList errors() const
Gets recorded errors.
virtual Q_INVOKABLE Qgis::VectorProviderCapabilities capabilities() const
Returns flags containing the supported capabilities.
virtual bool doesStrictFeatureTypeCheck() const
Returns true if the provider is strict about the type of inserted features (e.g.
QgsGeometry convertToProviderType(const QgsGeometry &geom) const
Converts the geometry to the provider type if possible / necessary.
Qgis::WkbType wkbType() const override=0
Returns the geometry type which is returned by this layer.
bool hasErrors() const
Provider has errors to report.
void committedAttributesDeleted(const QString &layerId, const QgsAttributeList &deletedAttributes)
Emitted after attribute deletion has been committed to the layer.
virtual bool deleteFeature(QgsFeatureId fid)
Delete a feature from the layer (but does not commit it).
QgsFeatureMap mAddedFeatures
New features which are not committed.
void setEditBufferGroup(QgsVectorLayerEditBufferGroup *editBufferGroup)
Set the parent edit buffer group for this edit buffer.
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
Emitted after feature attribute value changes have been committed to the layer.
friend class QgsVectorLayerUndoCommandAddFeature
virtual bool renameAttribute(int attr, const QString &newName)
Renames an attribute field (but does not commit it).
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommitted geometry updates.
virtual bool deleteFeatures(const QgsFeatureIds &fid)
Deletes a set of features from the layer (but does not commit it).
void handleAttributeDeleted(int index)
Update added and changed features after removal of an attribute.
virtual bool addAttribute(const QgsField &field)
Adds an attribute field (but does not commit it) returns true if the field was added.
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
Emitted after attribute addition has been committed to the layer.
virtual bool addFeatures(QgsFeatureList &features)
Insert a copy of the given features into the layer (but does not commit it).
QgsVectorLayerEditBuffer()=default
QgsFieldNameMap mRenamedAttributes
Renamed attributes which are not committed.
QgsFeatureIds allAddedOrEditedFeatures() const
Returns a list of the features IDs for all newly added or edited features in the buffer.
QgsGeometryMap mChangedGeometries
Changed geometries which are not committed.
void committedAttributesRenamed(const QString &layerId, const QgsFieldNameMap &renamedAttributes)
Emitted after committing an attribute rename.
virtual bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues)
Changes values of attributes (but does not commit it).
QgsAttributeList mDeletedAttributeIds
Deleted attributes fields which are not committed. The list is kept sorted.
QgsFeatureIds mDeletedFeatureIds
Deleted feature IDs which are not committed.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
void updateFields(QgsFields &fields)
Updates fields.
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
Emitted after feature addition has been committed to the layer.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature was deleted from the buffer.
QgsVectorLayerEditBufferGroup * editBufferGroup() const
Returns the parent edit buffer group for this edit buffer, or nullptr if not part of a group.
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
Emitted after feature geometry changes have been committed to the layer.
virtual bool addFeature(QgsFeature &f)
Adds a feature.
virtual void rollBack()
Stop editing and discard the edits.
friend class QgsVectorLayerUndoCommandRenameAttribute
void handleAttributeAdded(int index, const QgsField &field)
Update added and changed features after addition of an attribute.
friend class QgsVectorLayerUndoCommandChangeGeometry
void undoIndexChanged(int index)
QgsVectorLayerEditBufferGroup * mEditBufferGroup
friend class QgsVectorLayerUndoCommandDeleteAttribute
void featureAdded(QgsFeatureId fid)
Emitted when a feature has been added to the buffer.
QgsChangedAttributesMap mChangedAttributeValues
Changed attributes values which are not committed.
int mBlockModifiedSignals
virtual bool commitChanges(QStringList &commitErrors)
Attempts to commit any changes to disk.
friend class QgsVectorLayerUndoCommandChangeAttribute
friend class QgsVectorLayerUndoCommandAddAttribute
virtual bool deleteAttribute(int attr)
Deletes an attribute field (but does not commit it).
virtual bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changed an attribute value (but does not commit it).
void updateAttributeMapIndex(QgsAttributeMap &attrs, int index, int offset) const
Updates an index in an attribute map to a new value (for updates of changed attributes).
QList< QgsField > mAddedAttributes
Added attributes fields which are not committed.
virtual bool changeGeometry(QgsFeatureId fid, const QgsGeometry &geom)
Change feature's geometry.
void layerModified()
Emitted when modifications has been done on layer.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommitted attribute updates.
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
Emitted after feature removal has been committed to the layer.
friend class QgsVectorLayerUndoCommandDeleteFeature
friend class QgsVectorLayerEditBufferGroup
static void matchAttributesToFields(QgsFeature &feature, const QgsFields &fields)
Matches the attributes in feature to the specified fields.
Represents a vector layer which manages a vector based dataset.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
QMap< int, QVariant > QgsAttributeMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
#define FID_TO_STRING(fid)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
void mapToReversedLists(const QMap< Key, T > &map, QList< Key > &ks, QList< T > &vs)
populate two lists (ks, vs) from map - in reverse order