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