QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsvectorlayerundopassthroughcommand.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerundopassthroughcommand.cpp
3  ---------------------
4  begin : June 2017
5  copyright : (C) 2017 by Vincent Mora
6  email : vincent dot mora at oslandia dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgsfeatureiterator.h"
19 #include "qgsgeometry.h"
20 #include "qgsfeature.h"
21 #include "qgsvectorlayer.h"
23 
24 #include "qgslogger.h"
25 #include "qgstransaction.h"
26 
27 #include <QUuid>
28 
29 // TODO use setObsolete instead of mHasError when upgrading qt version, this will allow auto removal of the command
30 // for the moment a errored command is left on the stack
31 
33  : QgsVectorLayerUndoCommand( buffer )
34  , mSavePointId( ( mBuffer->L->isEditCommandActive() && !mBuffer->L->dataProvider()->transaction()->savePoints().isEmpty() )
35  || !autocreate
36  ? mBuffer->L->dataProvider()->transaction()->savePoints().last()
37  : mBuffer->L->dataProvider()->transaction()->createSavepoint( mError ) )
38  , mHasError( !mError.isEmpty() )
39  , mRecreateSavePoint( mBuffer->L->isEditCommandActive()
40  ? !mBuffer->L->dataProvider()->transaction()->lastSavePointIsDirty()
41  : true )
42 {
43  // the first command in the undo stack macro will have a clean save point
44  // the first command is responsible to re-create the savepoint after undo
45  setText( text );
46 }
47 
48 
50 {
51  if ( !mHasError )
52  {
53  setText( text() + " " + QObject::tr( "failed" ) );
54  mHasError = true;
55  }
56 }
57 
58 void QgsVectorLayerUndoPassthroughCommand::setErrorMessage( const QString &errorMessage )
59 {
60  mError = errorMessage;
61 }
62 
64 {
65  return mError;
66 }
67 
68 bool QgsVectorLayerUndoPassthroughCommand::setSavePoint( const QString &savePointId )
69 {
70  if ( !hasError() )
71  {
72  if ( savePointId.isEmpty() )
73  {
74  // re-create savepoint only if mRecreateSavePoint and rollBackToSavePoint as occurred
75  if ( mRecreateSavePoint && mBuffer->L->dataProvider()->transaction()->savePoints().indexOf( mSavePointId ) == -1 )
76  {
77  mSavePointId = mBuffer->L->dataProvider()->transaction()->createSavepoint( mSavePointId, mError );
78  if ( mSavePointId.isEmpty() )
79  {
80  setError();
81  }
82  }
83  }
84  else
85  {
86  mSavePointId = savePointId;
87  }
88  }
89  return !hasError();
90 }
91 
93 {
94  // rollback only occurs for the last command in undo macro
95  if ( !hasError() && mBuffer->L->dataProvider()->transaction()->savePoints().indexOf( mSavePointId ) != -1 )
96  {
97  if ( !mBuffer->L->dataProvider()->transaction()->rollbackToSavepoint( mSavePointId, mError ) )
98  {
99  setError();
100  }
101  }
102  return !hasError();
103 }
104 
105 
107  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "add features" ) )
108 {
109  static int sAddedIdLowWaterMark = -1;
110  for ( const QgsFeature &f : qgis::as_const( features ) )
111  {
112  mInitialFeatures << f;
113  //assign a temporary id to the feature (use negative numbers)
114  sAddedIdLowWaterMark--;
115  mInitialFeatures.last().setId( sAddedIdLowWaterMark );
116  }
117  mFeatures = mInitialFeatures;
118 }
119 
121 {
122  if ( rollBackToSavePoint() )
123  {
124  for ( const QgsFeature &f : qgis::as_const( mFeatures ) )
125  {
126  emit mBuffer->featureDeleted( f.id() );
127  }
128  mFeatures = mInitialFeatures;
129  }
130 }
131 
133 {
134  mFeatures = mInitialFeatures;
135  if ( setSavePoint() && mBuffer->L->dataProvider()->addFeatures( mFeatures ) )
136  {
137  for ( const QgsFeature &f : qgis::as_const( mFeatures ) )
138  {
139  emit mBuffer->featureAdded( f.id() );
140  }
141  }
142  else
143  {
144  setError();
145  }
146 }
147 
149  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "delete features" ) )
150  , mFids( fids )
151 {
152 }
153 
155 {
156  if ( rollBackToSavePoint() )
157  {
158  for ( const QgsFeatureId &id : mFids )
159  {
160  emit mBuffer->featureAdded( id );
161  }
162  }
163 }
164 
166 {
167  if ( setSavePoint() && mBuffer->L->dataProvider()->deleteFeatures( mFids ) )
168  {
169  for ( const QgsFeatureId &id : mFids )
170  {
171  emit mBuffer->featureDeleted( id );
172  }
173  }
174  else
175  {
176  setError();
177  }
178 }
179 
181  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change geometry" ) )
182  , mFid( fid )
183  , mNewGeom( geom )
184  , mOldGeom( mBuffer->L->getFeature( mFid ).geometry() )
185 {
186 }
187 
189 {
190  if ( rollBackToSavePoint() )
191  {
192  emit mBuffer->geometryChanged( mFid, mOldGeom );
193  }
194 }
195 
197 {
198  QgsGeometryMap geomMap;
199  geomMap.insert( mFid, mNewGeom );
200  if ( setSavePoint() && mBuffer->L->dataProvider()->changeGeometryValues( geomMap ) )
201  {
202  emit mBuffer->geometryChanged( mFid, mNewGeom );
203  }
204  else
205  {
206  setError();
207  }
208 }
209 
211  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) )
212  , mFid( fid )
213  , mField( field )
214  , mNewValue( newValue )
215  , mOldValue( mBuffer->L->getFeature( mFid ).attribute( field ) )
216 {
217 }
218 
220 {
221  if ( rollBackToSavePoint() )
222  {
223  emit mBuffer->attributeValueChanged( mFid, mField, mOldValue );
224  }
225 }
226 
228 {
229  QgsAttributeMap map;
230  map.insert( mField, mNewValue );
231  QgsChangedAttributesMap attribMap;
232  attribMap.insert( mFid, map );
233  if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) )
234  {
235  emit mBuffer->attributeValueChanged( mFid, mField, mNewValue );
236  }
237  else
238  {
239  setError();
240  }
241 }
242 
244  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "add attribute" ) + " " + field.name() )
245  , mField( field )
246 {
247 }
248 
250 {
251  // note that the deleteAttribute here is only necessary to inform the provider that
252  // an attribute is removed after the rollBackToSavePoint
253  const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
255  {
257  emit mBuffer->attributeDeleted( attr );
258  }
259  else
260  {
261  setError();
262  }
263 }
264 
266 {
267  if ( setSavePoint() && mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) )
268  {
270  const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
271  emit mBuffer->attributeAdded( attr );
272  }
273  else
274  {
275  setError();
276  }
277 }
278 
280  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "delete attribute" ) )
281  , mField( mBuffer->L->fields()[ attr ] )
282 {
283 }
284 
286 {
287  // note that the addAttributes here is only necessary to inform the provider that
288  // an attribute is added back after the rollBackToSavePoint
289  if ( mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) && rollBackToSavePoint() )
290  {
292  const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
293  emit mBuffer->attributeAdded( attr );
294  }
295  else
296  {
297  setError();
298  }
299 }
300 
302 {
303  const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
304  if ( setSavePoint() && mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) )
305  {
307  emit mBuffer->attributeDeleted( attr );
308  }
309  else
310  {
311  setError();
312  }
313 }
314 
316  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "rename attribute" ) + " " + newName )
317  , mAttr( attr )
318  , mNewName( newName )
319  , mOldName( mBuffer->L->fields()[ mAttr ].name() )
320 {
321 }
322 
324 {
325  // note that the renameAttributes here is only necessary to inform the provider that
326  // an attribute is renamed after the rollBackToSavePoint
327  QgsFieldNameMap map;
328  map[ mAttr ] = mOldName;
330  {
332  emit mBuffer->attributeRenamed( mAttr, mOldName );
333  }
334  else
335  {
336  setError();
337  }
338 }
339 
341 {
342  QgsFieldNameMap map;
343  map[ mAttr ] = mNewName;
344  if ( setSavePoint() && mBuffer->L->dataProvider()->renameAttributes( map ) )
345  {
347  emit mBuffer->attributeRenamed( mAttr, mNewName );
348  }
349  else
350  {
351  setError();
352  }
353 }
354 
356  : QgsVectorLayerUndoPassthroughCommand( buffer, name.isEmpty() ? QObject::tr( "custom transaction" ) : name, false )
357  , mTransaction( transaction )
358  , mSql( sql )
359 {
360 }
361 
363 {
364  if ( rollBackToSavePoint() )
365  {
366  mUndone = true;
367  emit mBuffer->L->layerModified();
368  }
369  else
370  {
371  setError();
372  }
373 }
374 
376 {
377  // the first time that the sql query is execute is within QgsTransaction
378  // itself. So the redo has to be executed only after an undo action.
379  if ( mUndone )
380  {
381  QString errorMessage;
382 
383  QString savePointId = mTransaction->createSavepoint( errorMessage );
384 
385  if ( errorMessage.isEmpty() )
386  {
387  setSavePoint( savePointId );
388 
389  if ( mTransaction->executeSql( mSql, errorMessage ) )
390  {
391  mUndone = false;
392  }
393  else
394  {
396  setError();
397  }
398  }
399  else
400  {
402  setError();
403  }
404  }
405 }
406 
408  : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) )
409  , mFid( fid )
410  , mNewValues( newValues )
411  , mOldValues( oldValues )
412 {
413 }
414 
416 {
417  if ( rollBackToSavePoint() )
418  {
419  for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
420  {
421  emit mBuffer->attributeValueChanged( mFid, it.key(), it.value() );
422  }
423  }
424 }
425 
427 {
428  QgsChangedAttributesMap attribMap;
429  attribMap.insert( mFid, mNewValues );
430  if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) )
431  {
432  for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
433  {
434  emit mBuffer->attributeValueChanged( mFid, it.key(), it.value() );
435  }
436  }
437 }
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
This class allows including a set of layers in a database-side transaction, provided the layer data p...
QList< QString > savePoints() const
returns savepoints
bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
QString createSavepoint(QString &error)
creates a save point returns empty string on error returns the last created savepoint if it's not dir...
virtual bool changeGeometryValues(const QgsGeometryMap &geometry_map)
Changes geometries of existing features.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
virtual bool deleteFeatures(const QgsFeatureIds &id)
Deletes one or more features from the provider.
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
virtual bool renameAttributes(const QgsFieldNameMap &renamedAttributes)
Renames existing attributes.
virtual bool deleteAttributes(const QgsAttributeIds &attributes)
Deletes existing attributes from the provider.
void attributeRenamed(int idx, const QString &newName)
Emitted when an attribute has been renamed.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geom)
Emitted when a feature's geometry is changed.
void featureDeleted(QgsFeatureId fid)
void attributeAdded(int idx)
void attributeDeleted(int idx)
void featureAdded(QgsFeatureId fid)
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &)
Base class for undo commands within a QgsVectorLayerEditBuffer.
QgsVectorLayerEditBuffer * mBuffer
Associated edit buffer.
QgsVectorLayerUndoPassthroughCommandAddAttribute(QgsVectorLayerEditBuffer *buffer, const QgsField &field)
Constructor for QgsVectorLayerUndoPassthroughCommandAddAttribute.
QgsFeatureList features() const
List of features (added feaures can be modified by default values from database)
QgsVectorLayerUndoPassthroughCommandAddFeatures(QgsVectorLayerEditBuffer *buffer, QgsFeatureList &features)
Constructor for QgsVectorLayerUndoPassthroughCommandAddFeatures.
QgsVectorLayerUndoPassthroughCommandChangeAttribute(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, int field, const QVariant &newValue)
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttribute.
QgsVectorLayerUndoPassthroughCommandChangeAttributes(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttributes.
QgsVectorLayerUndoPassthroughCommandChangeGeometry(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsGeometry &geom)
Constructor for QgsVectorLayerUndoPassthroughCommandChangeGeometry.
QgsVectorLayerUndoPassthroughCommandDeleteAttribute(QgsVectorLayerEditBuffer *buffer, int attr)
Constructor for QgsVectorLayerUndoCommandDeleteAttribute.
QgsVectorLayerUndoPassthroughCommandDeleteFeatures(QgsVectorLayerEditBuffer *buffer, const QgsFeatureIds &fids)
Constructor for QgsVectorLayerUndoPassthroughCommandDeleteFeatures.
QgsVectorLayerUndoPassthroughCommandRenameAttribute(QgsVectorLayerEditBuffer *buffer, int attr, const QString &newName)
Constructor for QgsVectorLayerUndoCommandRenameAttribute.
QgsVectorLayerUndoPassthroughCommandUpdate(QgsVectorLayerEditBuffer *buffer, QgsTransaction *transaction, const QString &sql, const QString &name)
Constructor for QgsVectorLayerUndoCommandUpdate.
Undo command for vector layer in transaction group mode.
QgsVectorLayerUndoPassthroughCommand(QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate=true)
Constructor for QgsVectorLayerUndoPassthroughCommand.
QString errorMessage() const
Returns the error message or an empty string if there's none.
void setErrorMessage(const QString &errorMessage)
Sets the error message.
void setError()
Set error flag and append "failed" to text.
bool setSavePoint(const QString &savePointId=QString())
Set the command savepoint or set error status.
bool rollBackToSavePoint()
Rollback command, release savepoint or set error status save point must be set prior to call error sa...
void layerModified()
Emitted when modifications has been done on layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QMap< int, QString > QgsFieldNameMap
Definition: qgsattributes.h:44
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:609
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:600
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:614
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
const QgsField & field
Definition: qgsfield.h:472
QSet< int > QgsAttributeIds