QGIS API Documentation  2.4.0-Chugiak
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsvectorlayereditbuffer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayereditbuffer.cpp
3  ---------------------
4  begin : Dezember 2012
5  copyright : (C) 2012 by Martin Dobias
6  email : wonder dot sk at gmail 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  ***************************************************************************/
16 
17 #include "qgsgeometry.h"
18 #include "qgslogger.h"
20 #include "qgsvectordataprovider.h"
21 #include "qgsvectorlayer.h"
22 
23 
25  : L( layer )
26 {
27  connect( L->undoStack(), SIGNAL( indexChanged( int ) ), this, SLOT( undoIndexChanged( int ) ) ); // TODO[MD]: queued?
28 }
29 
31 {
32 }
33 
34 
36 {
37  return !L->undoStack()->isClean();
38 }
39 
40 
42 {
43  QgsDebugMsg( QString( "undo index changed %1" ).arg( index ) );
44  Q_UNUSED( index );
45  emit layerModified();
46 }
47 
48 
50 {
51  // delete attributes from the higher indices to lower indices
52  for ( int i = mDeletedAttributeIds.count() - 1; i >= 0; --i )
53  {
54  fields.remove( mDeletedAttributeIds[i] );
55  }
56  // add new fields
57  for ( int i = 0; i < mAddedAttributes.count(); ++i )
58  {
60  }
61 }
62 
63 
65 {
66  if ( mChangedGeometries.contains( f.id() ) )
68 }
69 
70 
72 {
73  QgsAttributes& attrs = f.attributes();
74 
75  // remove all attributes that will disappear - from higher indices to lower
76  for ( int idx = mDeletedAttributeIds.count() - 1; idx >= 0; --idx )
77  {
78  attrs.remove( mDeletedAttributeIds[idx] );
79  }
80 
81  // adjust size to accommodate added attributes
82  attrs.resize( attrs.count() + mAddedAttributes.count() );
83 
84  // update changed attributes
85  if ( mChangedAttributeValues.contains( f.id() ) )
86  {
87  const QgsAttributeMap &map = mChangedAttributeValues[f.id()];
88  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
89  attrs[it.key()] = it.value();
90  }
91 }
92 
93 
94 
95 
97 {
99  {
100  return false;
101  }
102  if ( L->mUpdatedFields.count() != f.attributes().count() )
103  return false;
104 
105  // TODO: check correct geometry type
106 
107  L->undoStack()->push( new QgsVectorLayerUndoCommandAddFeature( this, f ) );
108  return true;
109 }
110 
111 
113 {
115  return false;
116 
117  for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
118  {
119  addFeature( *iter );
120  }
121 
122  L->updateExtents();
123  return true;
124 }
125 
126 
127 
129 {
131  return false;
132 
133  if ( FID_IS_NEW( fid ) )
134  {
135  if ( !mAddedFeatures.contains( fid ) )
136  return false;
137  }
138  else // existing feature
139  {
140  if ( mDeletedFeatureIds.contains( fid ) )
141  return false;
142  }
143 
144  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteFeature( this, fid ) );
145  return true;
146 }
147 
148 
150 {
152  return false;
153 
154  if ( !L->hasGeometryType() )
155  {
156  return false;
157  }
158 
159  if ( FID_IS_NEW( fid ) )
160  {
161  if ( !mAddedFeatures.contains( fid ) )
162  return false;
163  }
164 
165  // TODO: check compatible geometry
166 
167  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeGeometry( this, fid, geom ) );
168  return true;
169 }
170 
171 
172 bool QgsVectorLayerEditBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
173 {
175  return false;
176 
177  if ( FID_IS_NEW( fid ) )
178  {
179  if ( !mAddedFeatures.contains( fid ) )
180  return false;
181  }
182 
183  if ( field < 0 || field >= L->pendingFields().count() ||
185  return false;
186 
187  L->undoStack()->push( new QgsVectorLayerUndoCommandChangeAttribute( this, fid, field, newValue, oldValue ) );
188  return true;
189 }
190 
191 
193 {
195  return false;
196 
197  if ( field.name().isEmpty() )
198  return false;
199 
200  const QgsFields& updatedFields = L->pendingFields();
201  for ( int idx = 0; idx < updatedFields.count(); ++idx )
202  {
203  if ( updatedFields[idx].name() == field.name() )
204  return false;
205  }
206 
207  if ( !L->dataProvider()->supportedType( field ) )
208  return false;
209 
210  L->undoStack()->push( new QgsVectorLayerUndoCommandAddAttribute( this, field ) );
211  return true;
212 }
213 
214 
216 {
218  return false;
219 
220  if ( index < 0 || index >= L->pendingFields().count() )
221  return false;
222 
223  // find out source of the field
224  QgsFields::FieldOrigin origin = L->pendingFields().fieldOrigin( index );
225  int originIndex = L->pendingFields().fieldOriginIndex( index );
226 
227  if ( origin == QgsFields::OriginProvider && mDeletedAttributeIds.contains( originIndex ) )
228  return false;
229 
230  if ( origin == QgsFields::OriginJoin )
231  return false;
232 
233  L->undoStack()->push( new QgsVectorLayerUndoCommandDeleteAttribute( this, index ) );
234  return true;
235 }
236 
237 
238 bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors )
239 {
240  QgsVectorDataProvider* provider = L->dataProvider();
241  commitErrors.clear();
242 
243  int cap = provider->capabilities();
244  bool success = true;
245 
246  QgsFields oldFields = L->pendingFields();
247 
248  //
249  // delete attributes
250  //
251  bool attributesChanged = false;
252  if ( !mDeletedAttributeIds.isEmpty() )
253  {
255  {
256  commitErrors << tr( "SUCCESS: %n attribute(s) deleted.", "deleted attributes count", mDeletedAttributeIds.size() );
257 
259 
260  mDeletedAttributeIds.clear();
261  attributesChanged = true;
262  }
263  else
264  {
265  commitErrors << tr( "ERROR: %n attribute(s) not deleted.", "not deleted attributes count", mDeletedAttributeIds.size() );
266 #if 0
267  QString list = "ERROR: Pending attribute deletes:";
268  foreach ( int idx, mDeletedAttributeIds )
269  {
270  list.append( " " + L->pendingFields()[idx].name() );
271  }
272  commitErrors << list;
273 #endif
274  success = false;
275  }
276  }
277 
278  //
279  // add attributes
280  //
281  if ( !mAddedAttributes.isEmpty() )
282  {
284  {
285  commitErrors << tr( "SUCCESS: %n attribute(s) added.", "added attributes count", mAddedAttributes.size() );
286 
288 
289  mAddedAttributes.clear();
290  attributesChanged = true;
291  }
292  else
293  {
294  commitErrors << tr( "ERROR: %n new attribute(s) not added", "not added attributes count", mAddedAttributes.size() );
295 #if 0
296  QString list = "ERROR: Pending adds:";
297  foreach ( QgsField f, mAddedAttributes )
298  {
299  list.append( " " + f.name() );
300  }
301  commitErrors << list;
302 #endif
303  success = false;
304  }
305  }
306 
307  //
308  // check that addition/removal went as expected
309  //
310  bool attributeChangesOk = true;
311  if ( attributesChanged )
312  {
313  L->updateFields();
314  QgsFields newFields = L->pendingFields();
315 
316  if ( oldFields.count() != newFields.count() )
317  {
318  commitErrors << tr( "ERROR: the count of fields is incorrect after addition/removal of fields!" );
319  attributeChangesOk = false; // don't try attribute updates - they'll fail.
320  }
321 
322  for ( int i = 0; i < qMin( oldFields.count(), newFields.count() ); ++i )
323  {
324  const QgsField& oldField = oldFields[i];
325  const QgsField& newField = newFields[i];
326  if ( attributeChangesOk && oldField != newField )
327  {
328  commitErrors
329  << tr( "ERROR: field with index %1 is not the same!" ).arg( i )
330  << tr( "Provider: %1" ).arg( L->providerType() )
331  << tr( "Storage: %1" ).arg( L->storageType() )
332  << QString( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
333  .arg( tr( "expected field" ) )
334  .arg( oldField.name() )
335  .arg( QVariant::typeToName( oldField.type() ) )
336  .arg( oldField.typeName() )
337  .arg( oldField.length() )
338  .arg( oldField.precision() )
339  << QString( "%1: name=%2 type=%3 typeName=%4 len=%5 precision=%6" )
340  .arg( tr( "retrieved field" ) )
341  .arg( newField.name() )
342  .arg( QVariant::typeToName( newField.type() ) )
343  .arg( newField.typeName() )
344  .arg( newField.length() )
345  .arg( newField.precision() );
346  attributeChangesOk = false; // don't try attribute updates - they'll fail.
347  }
348  }
349  }
350 
351  if ( attributeChangesOk )
352  {
353  //
354  // change attributes
355  //
356  if ( !mChangedAttributeValues.isEmpty() )
357  {
359  {
360  commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
361 
363 
364  mChangedAttributeValues.clear();
365  }
366  else
367  {
368  commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
369 #if 0
370  QString list = "ERROR: pending changes:";
371  foreach ( QgsFeatureId id, mChangedAttributeValues.keys() )
372  {
373  list.append( "\n " + FID_TO_STRING( id ) + "[" );
374  foreach ( int idx, mChangedAttributeValues[ id ].keys() )
375  {
376  list.append( QString( " %1:%2" ).arg( L->pendingFields()[idx].name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
377  }
378  list.append( " ]" );
379  }
380  commitErrors << list;
381 #endif
382  success = false;
383  }
384  }
385 
386  //
387  // delete features
388  //
389  if ( success && !mDeletedFeatureIds.isEmpty() )
390  {
392  {
393  commitErrors << tr( "SUCCESS: %n feature(s) deleted.", "deleted features count", mDeletedFeatureIds.size() );
394  // TODO[MD]: we should not need this here
395  for ( QgsFeatureIds::const_iterator it = mDeletedFeatureIds.begin(); it != mDeletedFeatureIds.end(); ++it )
396  {
397  mChangedAttributeValues.remove( *it );
398  mChangedGeometries.remove( *it );
399  }
400 
402 
403  mDeletedFeatureIds.clear();
404  }
405  else
406  {
407  commitErrors << tr( "ERROR: %n feature(s) not deleted.", "not deleted features count", mDeletedFeatureIds.size() );
408 #if 0
409  QString list = "ERROR: pending deletes:";
410  foreach ( QgsFeatureId id, mDeletedFeatureIds )
411  {
412  list.append( " " + FID_TO_STRING( id ) );
413  }
414  commitErrors << list;
415 #endif
416  success = false;
417  }
418  }
419 
420  //
421  // add features
422  //
423  if ( success && !mAddedFeatures.isEmpty() )
424  {
426  {
427  QList<QgsFeatureId> ids = mAddedFeatures.keys();
428  QgsFeatureList featuresToAdd = mAddedFeatures.values();
429 
430  if ( provider->addFeatures( featuresToAdd ) )
431  {
432  commitErrors << tr( "SUCCESS: %n feature(s) added.", "added features count", featuresToAdd.size() );
433 
434  emit committedFeaturesAdded( L->id(), featuresToAdd );
435 
436  // notify everyone that the features with temporary ids were updated with permanent ids
437  for ( int i = 0; i < featuresToAdd.count(); ++i )
438  {
439  if ( featuresToAdd[i].id() != ids[i] )
440  {
441  //update selection
442  if ( L->mSelectedFeatureIds.contains( ids[i] ) )
443  {
444  L->mSelectedFeatureIds.remove( ids[i] );
445  L->mSelectedFeatureIds.insert( featuresToAdd[i].id() );
446  }
447  emit featureDeleted( ids[i] );
448  emit featureAdded( featuresToAdd[i].id() );
449  }
450  }
451 
452  mAddedFeatures.clear();
453  }
454  else
455  {
456  commitErrors << tr( "ERROR: %n feature(s) not added.", "not added features count", mAddedFeatures.size() );
457 #if 0
458  QString list = "ERROR: pending adds:";
459  foreach ( QgsFeature f, mAddedFeatures )
460  {
461  list.append( " " + FID_TO_STRING( f.id() ) + "[" );
462  for ( int i = 0; i < L->pendingFields().size(); i++ )
463  {
464  list.append( QString( " %1:%2" ).arg( L->pendingFields()[i].name() ).arg( f.attributes()[i].toString() ) );
465  }
466  list.append( " ]" );
467  }
468  commitErrors << list;
469 #endif
470  success = false;
471  }
472  }
473  else
474  {
475  commitErrors << tr( "ERROR: %n feature(s) not added - provider doesn't support adding features.", "not added features count", mAddedFeatures.size() );
476  success = false;
477  }
478  }
479  }
480  else
481  {
482  success = false;
483  }
484 
485  //
486  // update geometries
487  //
488  if ( success && !mChangedGeometries.isEmpty() )
489  {
491  {
492  commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );
493 
495 
496  mChangedGeometries.clear();
497  }
498  else
499  {
500  commitErrors << tr( "ERROR: %n geometries not changed.", "not changed geometries count", mChangedGeometries.size() );
501  success = false;
502  }
503  }
504 
505  if ( !success && provider->hasErrors() )
506  {
507  commitErrors << tr( "\n Provider errors:" );
508  foreach ( QString e, provider->errors() )
509  {
510  commitErrors << " " + e.replace( "\n", "\n " );
511  }
512  provider->clearErrors();
513  }
514 
515  return success;
516 }
517 
518 
520 {
521  if ( !isModified() )
522  return;
523 
524  // limit canvas redraws to one by jumping to beginning of stack
525  // see QgsUndoWidget::indexChanged
526  L->undoStack()->setIndex( 0 );
527 
528  Q_ASSERT( mAddedAttributes.isEmpty() );
529  Q_ASSERT( mDeletedAttributeIds.isEmpty() );
530  Q_ASSERT( mChangedAttributeValues.isEmpty() );
531  Q_ASSERT( mChangedGeometries.isEmpty() );
532  Q_ASSERT( mAddedFeatures.isEmpty() );
533 }
534 
535 #if 0
536 QString QgsVectorLayerEditBuffer::dumpEditBuffer()
537 {
538  QString msg;
539  if ( !mChangedGeometries.isEmpty() )
540  {
541  msg += "CHANGED GEOMETRIES:\n";
542  for ( QgsGeometryMap::const_iterator it = mChangedGeometries.begin(); it != mChangedGeometries.end(); ++it )
543  {
544  // QgsFeatureId, QgsGeometry
545  msg += QString( "- FID %1: %2" ).arg( it.key() ).arg( it.value().to );
546  }
547  }
548  return msg;
549 }
550 #endif
551 
553 {
554  // go through the changed attributes map and adapt indices
555  for ( int i = 0; i < mChangedAttributeValues.size(); ++i )
556  {
558  }
559 
560  // go through added features and adapt attributes
561  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
562  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
563  {
564  QgsAttributes& attrs = featureIt->attributes();
565  attrs.insert( index, QVariant() );
566  }
567 }
568 
570 {
571  // go through the changed attributes map and adapt indices
572  for ( int i = 0; i < mChangedAttributeValues.size(); ++i )
573  {
575  // remove the attribute
576  if ( attrMap.contains( index ) )
577  attrMap.remove( index );
578 
579  // update attribute indices
580  updateAttributeMapIndex( attrMap, index, -1 );
581  }
582 
583  // go through added features and adapt attributes
584  QgsFeatureMap::iterator featureIt = mAddedFeatures.begin();
585  for ( ; featureIt != mAddedFeatures.end(); ++featureIt )
586  {
587  QgsAttributes& attrs = featureIt->attributes();
588  attrs.remove( index );
589  }
590 }
591 
592 
593 
595 {
596  QgsAttributeMap updatedMap;
597  for ( QgsAttributeMap::const_iterator it = map.begin(); it != map.end(); ++it )
598  {
599  int attrIndex = it.key();
600  updatedMap.insert( attrIndex < index ? attrIndex : attrIndex + offset, it.value() );
601  }
602  map = updatedMap;
603 }
604 
605 
606 
608 {
609  L->updateFields();
610 }
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:100
void updateFields()
Assembles mUpdatedFields considering provider fields, joined fields and added fields.
void updateChangedAttributes(QgsFeature &f)
Update feature with uncommited attribute updates.
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:58
static unsigned index
void handleAttributeDeleted(int index)
update added and changed features after removal of an attribute
field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfield.h:169
bool addAttribute(const QgsField &field)
add an attribute field (but does not commit it) returns true if the field was added ...
void committedAttributesDeleted(const QString &layerId, const QgsAttributeList &deletedAttributes)
Signals emitted after committing changes.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsfeature.h:98
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes.
bool addFeatures(QgsFeatureList &features)
Insert a copy of the given features into the layer (but does not commit it)
bool addFeature(QgsFeature &f)
Adds a feature.
field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfield.h:170
void committedAttributesAdded(const QString &layerId, const QList< QgsField > &addedAttributes)
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:330
virtual bool deleteFeatures(const QgsFeatureIds &id)
Deletes one or more features.
int precision() const
Gets the precision of the field.
Definition: qgsfield.cpp:78
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:83
friend class QgsVectorLayerUndoCommandChangeGeometry
Container of fields for a vector layer.
Definition: qgsfield.h:161
void rollBack()
Stop editing and discard the edits.
QStringList errors()
Get recorded errors.
friend class QgsVectorLayerUndoCommandAddAttribute
QgsChangedAttributesMap mChangedAttributeValues
Changed attributes values which are not commited.
void updateFeatureGeometry(QgsFeature &f)
Update feature with uncommited geometry updates.
allows deletion of attributes (fields)
field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfield.h:168
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
friend class QgsVectorLayerUndoCommandDeleteAttribute
QgsVectorLayerEditBuffer(QgsVectorLayer *layer)
virtual void updateExtents()
Update the extents for the layer.
void featureAdded(QgsFeatureId fid)
void setGeometry(const QgsGeometry &geom)
Set this feature's geometry from another QgsGeometry object (deep copy)
Definition: qgsfeature.cpp:134
void committedGeometriesChanges(const QString &layerId, const QgsGeometryMap &changedGeometries)
bool supportedType(const QgsField &field) const
check if provider supports type of field
allows addition of new attributes (fields)
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
bool hasErrors()
Provider has errors to report.
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
int fieldOriginIndex(int fieldIdx) const
Get field's origin index (its meaning is specific to each type of origin)
Definition: qgsfield.h:217
QgsGeometryMap mChangedGeometries
Changed geometries which are not commited.
void handleAttributeAdded(int index)
update added and changed features after addition of an attribute
const QgsAttributes & attributes() const
Definition: qgsfeature.h:142
allows modifications of geometries
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfield.cpp:140
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Definition: qgsmaplayer.cpp:92
void clearErrors()
Clear recorded errors.
int count() const
Return number of items.
Definition: qgsfield.h:195
QgsFeatureIds mDeletedFeatureIds
Deleted feature IDs which are not commited.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:31
void remove(int fieldIdx)
Remove a field with the given index.
Definition: qgsfield.cpp:153
bool commitChanges(QStringList &commitErrors)
Attempts to commit any changes to disk.
bool deleteAttribute(int attr)
delete an attribute field (but does not commit it)
friend class QgsVectorLayerUndoCommandDeleteFeature
virtual bool changeGeometryValues(QgsGeometryMap &geometry_map)
Changes geometries of existing features.
QString providerType() const
Return the provider type for this layer.
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QList< QgsField > mAddedAttributes
added attributes fields which are not commited
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
FieldOrigin fieldOrigin(int fieldIdx) const
Get field's origin (value from an enumeration)
Definition: qgsfield.h:215
void committedAttributeValuesChanges(const QString &layerId, const QgsChangedAttributesMap &changedAttributesValues)
const QString & typeName() const
Gets the field type.
Definition: qgsfield.cpp:68
int length() const
Gets the length of the field.
Definition: qgsfield.cpp:73
int size() const
Return number of items.
Definition: qgsfield.h:197
virtual bool deleteAttributes(const QgsAttributeIds &attributes)
Deletes existing attributes.
bool deleteFeature(QgsFeatureId fid)
delete a feature from the layer (but does not commit it)
qint64 QgsFeatureId
Definition: qgsfeature.h:30
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
changed an attribute value (but does not commit it)
#define FID_IS_NEW(fid)
Definition: qgsfeature.h:81
QUndoStack * undoStack()
Return pointer to layer's undo stack.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
void committedFeaturesRemoved(const QString &layerId, const QgsFeatureIds &deletedFeatureIds)
QgsVectorDataProvider * dataProvider()
Returns the data provider.
void committedFeaturesAdded(const QString &layerId, const QgsFeatureList &addedFeatures)
This is the base class for vector data providers.
QgsFields mUpdatedFields
field map to commit
QgsFeatureMap mAddedFeatures
New features which are not commited.
void updateFields(QgsFields &fields)
void layerModified()
This signal is emitted when modifications has been done on layer.
Represents a vector layer which manages a vector based data sets.
bool isModified() const
Returns true if the provider has been modified since the last commit.
QgsFeatureIds mSelectedFeatureIds
Set holding the feature IDs that are activated.
QgsAttributeList mDeletedAttributeIds
deleted attributes fields which are not commited.
allows modification of attribute values
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) ...
bool changeGeometry(QgsFeatureId fid, QgsGeometry *geom)
change feature's geometry
void featureDeleted(QgsFeatureId fid)
friend class QgsVectorLayerUndoCommandChangeAttribute
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:63
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#define tr(sourceText)