QGIS API Documentation  3.17.0-Master (df2c9ff931)
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 
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  {
395  setErrorMessage( errorMessage );
396  setError();
397  }
398  }
399  else
400  {
401  setErrorMessage( errorMessage );
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 }
QgsVectorLayerUndoPassthroughCommandAddAttribute(QgsVectorLayerEditBuffer *buffer, const QgsField &field)
Constructor for QgsVectorLayerUndoPassthroughCommandAddAttribute.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:609
void setErrorMessage(const QString &errorMessage)
Sets the error message.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geom)
Emitted when a feature&#39;s geometry is changed.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QString name
Definition: qgsfield.h:59
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
void attributeDeleted(int idx)
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:614
void setError()
Set error flag and append "failed" to text.
bool setSavePoint(const QString &savePointId=QString())
Set the command savepoint or set error status.
QgsVectorLayerUndoPassthroughCommandDeleteAttribute(QgsVectorLayerEditBuffer *buffer, int attr)
Constructor for QgsVectorLayerUndoCommandDeleteAttribute.
virtual bool deleteFeatures(const QgsFeatureIds &id)
Deletes one or more features from the provider.
QgsVectorLayerEditBuffer * mBuffer
Associated edit buffer.
virtual bool renameAttributes(const QgsFieldNameMap &renamedAttributes)
Renames existing attributes.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features ...
Definition: qgsfeatureid.h:28
Base class for undo commands within a QgsVectorLayerEditBuffer.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:123
Undo command for vector layer in transaction group mode.
bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
QgsVectorLayerUndoPassthroughCommand(QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate=true)
Constructor for QgsVectorLayerUndoPassthroughCommand.
void featureAdded(QgsFeatureId fid)
QSet< int > QgsAttributeIds
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
QgsVectorLayerUndoPassthroughCommandAddFeatures(QgsVectorLayerEditBuffer *buffer, QgsFeatureList &features)
Constructor for QgsVectorLayerUndoPassthroughCommandAddFeatures.
void attributeAdded(int idx)
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
bool rollBackToSavePoint()
Rollback command, release savepoint or set error status save point must be set prior to call error sa...
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:49
QgsVectorLayerUndoPassthroughCommandChangeGeometry(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsGeometry &geom)
Constructor for QgsVectorLayerUndoPassthroughCommandChangeGeometry.
virtual bool changeGeometryValues(const QgsGeometryMap &geometry_map)
Changes geometries of existing features.
void attributeRenamed(int idx, const QString &newName)
Emitted when an attribute has been renamed.
QMap< int, QString > QgsFieldNameMap
Definition: qgsattributes.h:44
QgsVectorLayerUndoPassthroughCommandUpdate(QgsVectorLayerEditBuffer *buffer, QgsTransaction *transaction, const QString &sql, const QString &name)
Constructor for QgsVectorLayerUndoCommandUpdate.
QgsVectorLayerUndoPassthroughCommandDeleteFeatures(QgsVectorLayerEditBuffer *buffer, const QgsFeatureIds &fids)
Constructor for QgsVectorLayerUndoPassthroughCommandDeleteFeatures.
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:600
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &)
This class allows including a set of layers in a database-side transaction, provided the layer data p...
QgsVectorLayerUndoPassthroughCommandChangeAttribute(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, int field, const QVariant &newValue)
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttribute.
virtual bool deleteAttributes(const QgsAttributeIds &attributes)
Deletes existing attributes from the provider.
QgsVectorLayerUndoPassthroughCommandRenameAttribute(QgsVectorLayerEditBuffer *buffer, int attr, const QString &newName)
Constructor for QgsVectorLayerUndoCommandRenameAttribute.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QString createSavepoint(QString &error)
creates a save point returns empty string on error returns the last created savepoint if it&#39;s not dir...
QgsVectorLayerUndoPassthroughCommandChangeAttributes(QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Constructor for QgsVectorLayerUndoPassthroughCommandChangeAttributes.
QString errorMessage() const
Returns the error message or an empty string if there&#39;s none.
void layerModified()
Emitted when modifications has been done on layer.
QList< QString > savePoints() const
returns savepoints
const QgsField & field
Definition: qgsfield.h:471
void featureDeleted(QgsFeatureId fid)