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 
16 #include "qgsvectorlayerundopassthroughcommand.h"
17 
18 #include "qgsfeatureiterator.h"
19 #include "qgsgeometry.h"
20 #include "qgsfeature.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsvectorlayereditbuffer.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 
QgsVectorLayerUndoPassthroughCommand(QgsVectorLayerEditBuffer * buffer,const QString & text,bool autocreate)32 QgsVectorLayerUndoPassthroughCommand::QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate )
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 
setError()49 void QgsVectorLayerUndoPassthroughCommand::setError()
50 {
51   if ( !mHasError )
52   {
53     setText( text() + " " + QObject::tr( "failed" ) );
54     mHasError = true;
55   }
56 }
57 
setErrorMessage(const QString & errorMessage)58 void QgsVectorLayerUndoPassthroughCommand::setErrorMessage( const QString &errorMessage )
59 {
60   mError = errorMessage;
61 }
62 
errorMessage() const63 QString QgsVectorLayerUndoPassthroughCommand::errorMessage() const
64 {
65   return mError;
66 }
67 
setSavePoint(const QString & savePointId)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 
rollBackToSavePoint()92 bool QgsVectorLayerUndoPassthroughCommand::rollBackToSavePoint()
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 
QgsVectorLayerUndoPassthroughCommandAddFeatures(QgsVectorLayerEditBuffer * buffer,QgsFeatureList & features)106 QgsVectorLayerUndoPassthroughCommandAddFeatures::QgsVectorLayerUndoPassthroughCommandAddFeatures( QgsVectorLayerEditBuffer *buffer, QgsFeatureList &features )
107   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "add features" ) )
108 {
109   static int sAddedIdLowWaterMark = -1;
110   for ( const QgsFeature &f : std::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 }
120 
undo()121 void QgsVectorLayerUndoPassthroughCommandAddFeatures::undo()
122 {
123   if ( rollBackToSavePoint() )
124   {
125     for ( const QgsFeature &f : std::as_const( mFeatures ) )
126     {
127       mBuffer->mAddedFeatures.remove( f.id() );
128       emit mBuffer->featureDeleted( f.id() );
129     }
130     mFeatures = mInitialFeatures;
131   }
132 }
133 
redo()134 void QgsVectorLayerUndoPassthroughCommandAddFeatures::redo()
135 {
136   mFeatures = mInitialFeatures;
137   mBuffer->L->dataProvider()->clearErrors();
138   if ( setSavePoint() && mBuffer->L->dataProvider()->addFeatures( mFeatures ) && ! mBuffer->L->dataProvider()->hasErrors() )
139   {
140     for ( const QgsFeature &f : std::as_const( mFeatures ) )
141     {
142       mBuffer->mAddedFeatures.insert( f.id(), f );
143       emit mBuffer->featureAdded( f.id() );
144     }
145   }
146   else
147   {
148     setError();
149   }
150 }
151 
QgsVectorLayerUndoPassthroughCommandDeleteFeatures(QgsVectorLayerEditBuffer * buffer,const QgsFeatureIds & fids)152 QgsVectorLayerUndoPassthroughCommandDeleteFeatures::QgsVectorLayerUndoPassthroughCommandDeleteFeatures( QgsVectorLayerEditBuffer *buffer, const QgsFeatureIds &fids )
153   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "delete features" ) )
154   , mFids( fids )
155 {
156 }
157 
undo()158 void QgsVectorLayerUndoPassthroughCommandDeleteFeatures::undo()
159 {
160   if ( rollBackToSavePoint() )
161   {
162     for ( const QgsFeatureId &fid : mFids )
163     {
164       mBuffer->mDeletedFeatureIds.remove( fid );
165       if ( mDeletedNewFeatures.contains( fid ) )
166       {
167         mBuffer->mAddedFeatures.insert( fid, mDeletedNewFeatures.value( fid ) );
168       }
169       emit mBuffer->featureAdded( fid );
170     }
171   }
172 }
173 
redo()174 void QgsVectorLayerUndoPassthroughCommandDeleteFeatures::redo()
175 {
176   mBuffer->L->dataProvider()->clearErrors();
177   if ( setSavePoint() && mBuffer->L->dataProvider()->deleteFeatures( mFids ) && ! mBuffer->L->dataProvider()->hasErrors() )
178   {
179     mDeletedNewFeatures.clear();
180     for ( const QgsFeatureId &fid : mFids )
181     {
182       if ( mBuffer->mAddedFeatures.contains( fid ) )
183       {
184         mDeletedNewFeatures.insert( fid, mBuffer->mAddedFeatures[ fid ] );
185         mBuffer->mAddedFeatures.remove( fid );
186       }
187       else
188       {
189         mBuffer->mDeletedFeatureIds.insert( fid );
190       }
191       emit mBuffer->featureDeleted( fid );
192     }
193   }
194   else
195   {
196     setError();
197   }
198 }
199 
QgsVectorLayerUndoPassthroughCommandChangeGeometry(QgsVectorLayerEditBuffer * buffer,QgsFeatureId fid,const QgsGeometry & geom)200 QgsVectorLayerUndoPassthroughCommandChangeGeometry::QgsVectorLayerUndoPassthroughCommandChangeGeometry( QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsGeometry &geom )
201   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change geometry" ) )
202   , mFid( fid )
203   , mNewGeom( geom )
204   , mOldGeom( mBuffer->L->getFeature( mFid ).geometry() )
205   , mFirstChange( true )
206 {
207   if ( mBuffer->mAddedFeatures.contains( mFid ) )
208   {
209     mFirstChange = false;
210   }
211   else if ( mBuffer->mChangedGeometries.contains( mFid ) )
212   {
213     mFirstChange = false;
214     mOldGeom = mBuffer->mChangedGeometries[mFid];
215   }
216 }
217 
undo()218 void QgsVectorLayerUndoPassthroughCommandChangeGeometry::undo()
219 {
220   if ( rollBackToSavePoint() )
221   {
222     if ( mBuffer->mAddedFeatures.contains( mFid ) )
223     {
224       mBuffer->mAddedFeatures[ mFid ].setGeometry( mOldGeom );
225     }
226     else if ( mFirstChange )
227     {
228       mBuffer->mChangedGeometries.remove( mFid );
229     }
230     else
231     {
232       mBuffer->mChangedGeometries[mFid] = mOldGeom;
233     }
234     emit mBuffer->geometryChanged( mFid,  mOldGeom );
235   }
236 }
237 
redo()238 void QgsVectorLayerUndoPassthroughCommandChangeGeometry::redo()
239 {
240   QgsGeometryMap geomMap;
241   geomMap.insert( mFid, mNewGeom );
242   mBuffer->L->dataProvider()->clearErrors();
243   if ( setSavePoint() && mBuffer->L->dataProvider()->changeGeometryValues( geomMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
244   {
245     if ( mBuffer->mAddedFeatures.contains( mFid ) )
246     {
247       mBuffer->mAddedFeatures[ mFid ].setGeometry( mNewGeom );
248     }
249     else
250     {
251       mBuffer->mChangedGeometries[ mFid ] = mNewGeom;
252     }
253     emit mBuffer->geometryChanged( mFid, mNewGeom );
254   }
255   else
256   {
257     setError();
258   }
259 }
260 
mergeWith(const QUndoCommand * other)261 bool QgsVectorLayerUndoPassthroughCommandChangeGeometry::mergeWith( const QUndoCommand *other )
262 {
263   if ( other->id() != id() )
264     return false;
265 
266   const QgsVectorLayerUndoPassthroughCommandChangeGeometry *merge = dynamic_cast<const QgsVectorLayerUndoPassthroughCommandChangeGeometry *>( other );
267   if ( !merge )
268     return false;
269 
270   if ( merge->mFid != mFid )
271     return false;
272 
273   mNewGeom = merge->mNewGeom;
274   merge->mNewGeom = QgsGeometry();
275 
276   return true;
277 }
278 
279 
280 
QgsVectorLayerUndoPassthroughCommandChangeAttribute(QgsVectorLayerEditBuffer * buffer,QgsFeatureId fid,int field,const QVariant & newValue)281 QgsVectorLayerUndoPassthroughCommandChangeAttribute::QgsVectorLayerUndoPassthroughCommandChangeAttribute( QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, int field, const QVariant &newValue )
282   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) )
283   , mFid( fid )
284   , mFieldIndex( field )
285   , mNewValue( newValue )
286   , mOldValue( mBuffer->L->getFeature( mFid ).attribute( field ) )
287   , mFirstChange( true )
288 {
289 
290   if ( mBuffer->mAddedFeatures.contains( mFid ) )
291   {
292     // work with added feature
293     QgsFeatureMap::const_iterator it = mBuffer->mAddedFeatures.constFind( mFid );
294     Q_ASSERT( it != mBuffer->mAddedFeatures.constEnd() );
295     if ( it.value().attribute( mFieldIndex ).isValid() )
296     {
297       mOldValue = it.value().attribute( mFieldIndex );
298       mFirstChange = false;
299     }
300   }
301   else if ( mBuffer->mChangedAttributeValues.contains( mFid ) && mBuffer->mChangedAttributeValues[mFid].contains( mFieldIndex ) )
302   {
303     mOldValue = mBuffer->mChangedAttributeValues[mFid][mFieldIndex];
304     mFirstChange = false;
305   }
306 }
307 
undo()308 void QgsVectorLayerUndoPassthroughCommandChangeAttribute::undo()
309 {
310   if ( rollBackToSavePoint() )
311   {
312     QVariant original = mOldValue;
313 
314     if ( mBuffer->mAddedFeatures.contains( mFid ) )
315     {
316       // added feature
317       QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
318       Q_ASSERT( it != mBuffer->mAddedFeatures.end() );
319       it.value().setAttribute( mFieldIndex, mOldValue );
320     }
321     else if ( mFirstChange )
322     {
323       // existing feature
324       mBuffer->mChangedAttributeValues[mFid].remove( mFieldIndex );
325       if ( mBuffer->mChangedAttributeValues[mFid].isEmpty() )
326         mBuffer->mChangedAttributeValues.remove( mFid );
327 
328       if ( !mOldValue.isValid() )
329       {
330         // get old value from provider
331         QgsFeature tmp;
332         QgsFeatureRequest request;
333         request.setFilterFid( mFid );
334         request.setFlags( QgsFeatureRequest::NoGeometry );
335         request.setSubsetOfAttributes( QgsAttributeList() << mFieldIndex );
336         std::unique_ptr<QgsVectorLayer> layerClone( layer()->clone() );
337         QgsFeatureIterator fi = layerClone->getFeatures( request );
338         if ( fi.nextFeature( tmp ) )
339           original = tmp.attribute( mFieldIndex );
340       }
341     }
342     else
343     {
344       mBuffer->mChangedAttributeValues[mFid][mFieldIndex] = mOldValue;
345     }
346     emit mBuffer->attributeValueChanged( mFid, mFieldIndex, original );
347   }
348 }
349 
redo()350 void QgsVectorLayerUndoPassthroughCommandChangeAttribute::redo()
351 {
352   QgsAttributeMap map;
353   map.insert( mFieldIndex, mNewValue );
354   QgsChangedAttributesMap attribMap;
355   attribMap.insert( mFid, map );
356   mBuffer->L->dataProvider()->clearErrors();
357   if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
358   {
359     // Update existing feature
360     QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid );
361     if ( it != mBuffer->mAddedFeatures.end() )
362     {
363       it.value().setAttribute( mFieldIndex, mNewValue );
364     }
365     else
366     {
367       // changed attribute of existing feature
368       if ( !mBuffer->mChangedAttributeValues.contains( mFid ) )
369       {
370         mBuffer->mChangedAttributeValues.insert( mFid, QgsAttributeMap() );
371       }
372 
373       mBuffer->mChangedAttributeValues[mFid].insert( mFieldIndex, mNewValue );
374     }
375     emit mBuffer->attributeValueChanged( mFid, mFieldIndex, mNewValue );
376   }
377   else
378   {
379     setError();
380   }
381 }
382 
QgsVectorLayerUndoPassthroughCommandAddAttribute(QgsVectorLayerEditBuffer * buffer,const QgsField & field)383 QgsVectorLayerUndoPassthroughCommandAddAttribute::QgsVectorLayerUndoPassthroughCommandAddAttribute( QgsVectorLayerEditBuffer *buffer, const QgsField &field )
384   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "add attribute" ) + " " + field.name() )
385   , mField( field )
386 {
387 }
388 
undo()389 void QgsVectorLayerUndoPassthroughCommandAddAttribute::undo()
390 {
391   // note that the deleteAttribute here is only necessary to inform the provider that
392   // an attribute is removed after the rollBackToSavePoint
393   const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
394   if ( rollBackToSavePoint() )
395   {
396     mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr );
397     mBuffer->mAddedAttributes.removeAll( mField );
398     mBuffer->updateLayerFields();
399     emit mBuffer->attributeDeleted( attr );
400   }
401   else
402   {
403     setError();
404   }
405 }
406 
redo()407 void QgsVectorLayerUndoPassthroughCommandAddAttribute::redo()
408 {
409   mBuffer->L->dataProvider()->clearErrors();
410   if ( setSavePoint() && mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField ) && ! mBuffer->L->dataProvider()->hasErrors() )
411   {
412     mBuffer->updateLayerFields();
413     const int attr = mBuffer->L->dataProvider()->fieldNameIndex( mField.name() );
414     mBuffer->mAddedAttributes.append( mField );
415     emit mBuffer->attributeAdded( attr );
416   }
417   else
418   {
419     setError();
420   }
421 }
422 
QgsVectorLayerUndoPassthroughCommandDeleteAttribute(QgsVectorLayerEditBuffer * buffer,int attr)423 QgsVectorLayerUndoPassthroughCommandDeleteAttribute::QgsVectorLayerUndoPassthroughCommandDeleteAttribute( QgsVectorLayerEditBuffer *buffer, int attr )
424   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "delete attribute" ) )
425   , mField( mBuffer->L->fields()[ attr ] )
426   , mOriginalFieldIndex( attr )
427 {
428 }
429 
undo()430 void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::undo()
431 {
432   // note that the addAttributes here is only necessary to inform the provider that
433   // an attribute is added back after the rollBackToSavePoint
434   mBuffer->L->dataProvider()->clearErrors();
435   if ( mBuffer->L->dataProvider()->addAttributes( QList<QgsField>() << mField )  && rollBackToSavePoint() && ! mBuffer->L->dataProvider()->hasErrors() )
436   {
437     mBuffer->mDeletedAttributeIds.removeOne( mOriginalFieldIndex );
438     mBuffer->updateLayerFields();
439     emit mBuffer->attributeAdded( mOriginalFieldIndex );
440   }
441   else
442   {
443     setError();
444   }
445 }
446 
redo()447 void QgsVectorLayerUndoPassthroughCommandDeleteAttribute::redo()
448 {
449   mBuffer->L->dataProvider()->clearErrors();
450   if ( setSavePoint() && mBuffer->L->dataProvider()->deleteAttributes( QgsAttributeIds() << mOriginalFieldIndex ) && ! mBuffer->L->dataProvider()->hasErrors() )
451   {
452     mBuffer->mDeletedAttributeIds.append( mOriginalFieldIndex );
453     mBuffer->updateLayerFields();
454     emit mBuffer->attributeDeleted( mOriginalFieldIndex );
455   }
456   else
457   {
458     setError();
459   }
460 }
461 
QgsVectorLayerUndoPassthroughCommandRenameAttribute(QgsVectorLayerEditBuffer * buffer,int attr,const QString & newName)462 QgsVectorLayerUndoPassthroughCommandRenameAttribute::QgsVectorLayerUndoPassthroughCommandRenameAttribute( QgsVectorLayerEditBuffer *buffer, int attr, const QString &newName )
463   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "rename attribute" ) + " " + newName )
464   , mAttr( attr )
465   , mNewName( newName )
466   , mOldName( mBuffer->L->fields()[ mAttr ].name() )
467 {
468 }
469 
undo()470 void QgsVectorLayerUndoPassthroughCommandRenameAttribute::undo()
471 {
472   // note that the renameAttributes here is only necessary to inform the provider that
473   // an attribute is renamed after the rollBackToSavePoint
474   QgsFieldNameMap map;
475   map[ mAttr ] = mOldName;
476   mBuffer->L->dataProvider()->clearErrors();
477   if ( mBuffer->L->dataProvider()->renameAttributes( map ) && rollBackToSavePoint() && ! mBuffer->L->dataProvider()->hasErrors() )
478   {
479     mBuffer->updateLayerFields();
480     emit mBuffer->attributeRenamed( mAttr, mOldName );
481   }
482   else
483   {
484     setError();
485   }
486 }
487 
redo()488 void QgsVectorLayerUndoPassthroughCommandRenameAttribute::redo()
489 {
490   QgsFieldNameMap map;
491   map[ mAttr ] = mNewName;
492   mBuffer->L->dataProvider()->clearErrors();
493   if ( setSavePoint() && mBuffer->L->dataProvider()->renameAttributes( map ) && ! mBuffer->L->dataProvider()->hasErrors() )
494   {
495     mBuffer->updateLayerFields();
496     emit mBuffer->attributeRenamed( mAttr, mNewName );
497   }
498   else
499   {
500     setError();
501   }
502 }
503 
QgsVectorLayerUndoPassthroughCommandUpdate(QgsVectorLayerEditBuffer * buffer,QgsTransaction * transaction,const QString & sql,const QString & name)504 QgsVectorLayerUndoPassthroughCommandUpdate::QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer, QgsTransaction *transaction, const QString &sql, const QString &name )
505   : QgsVectorLayerUndoPassthroughCommand( buffer, name.isEmpty() ? QObject::tr( "custom transaction" ) : name, false )
506   , mTransaction( transaction )
507   , mSql( sql )
508 {
509 }
510 
undo()511 void QgsVectorLayerUndoPassthroughCommandUpdate::undo()
512 {
513   if ( rollBackToSavePoint() )
514   {
515     mUndone = true;
516     emit mBuffer->L->layerModified();
517   }
518   else
519   {
520     setError();
521   }
522 }
523 
redo()524 void QgsVectorLayerUndoPassthroughCommandUpdate::redo()
525 {
526   // the first time that the sql query is execute is within QgsTransaction
527   // itself. So the redo has to be executed only after an undo action.
528   if ( mUndone )
529   {
530     QString errorMessage;
531 
532     QString savePointId = mTransaction->createSavepoint( errorMessage );
533 
534     if ( errorMessage.isEmpty() )
535     {
536       setSavePoint( savePointId );
537 
538       if ( mTransaction->executeSql( mSql, errorMessage ) )
539       {
540         mUndone = false;
541       }
542       else
543       {
544         setErrorMessage( errorMessage );
545         setError();
546       }
547     }
548     else
549     {
550       setErrorMessage( errorMessage );
551       setError();
552     }
553   }
554 }
555 
QgsVectorLayerUndoPassthroughCommandChangeAttributes(QgsVectorLayerEditBuffer * buffer,QgsFeatureId fid,const QgsAttributeMap & newValues,const QgsAttributeMap & oldValues)556 QgsVectorLayerUndoPassthroughCommandChangeAttributes::QgsVectorLayerUndoPassthroughCommandChangeAttributes( QgsVectorLayerEditBuffer *buffer, QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
557   : QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "change attribute value" ) )
558   , mFid( fid )
559   , mNewValues( newValues )
560   , mOldValues( oldValues )
561 {
562   if ( mOldValues.isEmpty() )
563   {
564     const auto oldAttrs( mBuffer->L->getFeature( mFid ).attributes() );
565     for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
566     {
567       mOldValues[ it.key() ] = oldAttrs[ it.key() ];
568     }
569   }
570   const bool isAdded { mBuffer->mAddedFeatures.contains( mFid ) };
571   for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
572   {
573     if ( isAdded && mBuffer->mAddedFeatures[ mFid ].attribute( it.key() ).isValid() )
574     {
575       mFirstChanges[ it.key() ] = false;
576     }
577     else if ( mBuffer->mChangedAttributeValues.contains( mFid ) && mBuffer->mChangedAttributeValues[mFid].contains( it.key() ) )
578     {
579       mFirstChanges[ it.key() ] = false;
580     }
581     else
582     {
583       mFirstChanges[ it.key() ] = true;
584     }
585   }
586 }
587 
undo()588 void QgsVectorLayerUndoPassthroughCommandChangeAttributes::undo()
589 {
590   if ( rollBackToSavePoint() )
591   {
592     QgsFeatureMap::iterator addedIt = mBuffer->mAddedFeatures.find( mFid );
593     for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
594     {
595       const auto fieldIndex { it.key() };
596       if ( addedIt != mBuffer->mAddedFeatures.end() )
597       {
598         addedIt.value().setAttribute( fieldIndex, mOldValues[ it.key() ] );
599       }
600       else if ( mFirstChanges.contains( fieldIndex ) && mFirstChanges[ fieldIndex ] )
601       {
602         // existing feature
603         mBuffer->mChangedAttributeValues[mFid].remove( fieldIndex );
604       }
605       else
606       {
607         // changed attribute of existing feature
608         if ( !mBuffer->mChangedAttributeValues.contains( mFid ) )
609         {
610           mBuffer->mChangedAttributeValues.insert( mFid, QgsAttributeMap() );
611         }
612         mBuffer->mChangedAttributeValues[mFid].insert( fieldIndex, mOldValues[ it.key() ] );
613       }
614       emit mBuffer->attributeValueChanged( mFid, it.key(), mOldValues[ it.key() ] );
615     }
616     if ( mBuffer->mChangedAttributeValues[mFid].isEmpty() )
617       mBuffer->mChangedAttributeValues.remove( mFid );
618   }
619 }
620 
redo()621 void QgsVectorLayerUndoPassthroughCommandChangeAttributes::redo()
622 {
623   QgsChangedAttributesMap attribMap;
624   attribMap.insert( mFid, mNewValues );
625   mBuffer->L->dataProvider()->clearErrors();
626   if ( setSavePoint() && mBuffer->L->dataProvider()->changeAttributeValues( attribMap ) && ! mBuffer->L->dataProvider()->hasErrors() )
627   {
628     QgsFeatureMap::iterator addedIt = mBuffer->mAddedFeatures.find( mFid );
629     for ( auto it = mNewValues.constBegin(); it != mNewValues.constEnd(); ++it )
630     {
631       const auto fieldIndex { it.key() };
632       // Update existing feature
633       if ( addedIt != mBuffer->mAddedFeatures.end() )
634       {
635         addedIt.value().setAttribute( fieldIndex, it.value() );
636       }
637       else
638       {
639         // changed attribute of existing feature
640         if ( !mBuffer->mChangedAttributeValues.contains( mFid ) )
641         {
642           mBuffer->mChangedAttributeValues.insert( mFid, QgsAttributeMap() );
643         }
644         mBuffer->mChangedAttributeValues[mFid].insert( fieldIndex, it.value() );
645       }
646       emit mBuffer->attributeValueChanged( mFid, it.key(), it.value() );
647     }
648   }
649 }
650