void QgsAttributeTableModel::featureAdded( QgsFeatureId fid, bool resettingModel )
{
  QgsDebugMsgLevel( QStringLiteral( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
  bool featOk = true;

  if ( mFeat.id() != fid )
    featOk = loadFeatureAtId( fid );

  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
  {
    for ( SortCache &cache : mSortCaches )
    {
      if ( cache.sortFieldIndex >= 0 )
      {
        QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
        const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
        const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
        QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
        cache.sortCache.insert( mFeat.id(), sortValue );
      }
      else if ( cache.sortCacheExpression.isValid() )
      {
        mExpressionContext.setFeature( mFeat );
        cache.sortCache[mFeat.id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
      }
    }

    // Skip if the fid is already in the map (do not add twice)!
    if ( ! mIdRowMap.contains( fid ) )
    {
      int n = mRowIdMap.size();
      if ( !resettingModel )
        beginInsertRows( QModelIndex(), n, n );
      mIdRowMap.insert( fid, n );
      mRowIdMap.insert( n, fid );
      if ( !resettingModel )
        endInsertRows();
      reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
    }
  }
}
void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
{
  QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
  // No filter request: skip all possibly heavy checks
  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
  {
    setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
  }
  else
  {
    if ( loadFeatureAtId( fid ) )
    {
      if ( mFeatureRequest.acceptFeature( mFeat ) )
      {
        if ( !mIdRowMap.contains( fid ) )
        {
          // Feature changed in such a way, it will be shown now
          featureAdded( fid );
        }
        else
        {
          if ( idx == mCachedField )
            mFieldCache[ fid ] = value;
          // Update representation
          setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
        }
      }
      else
      {
        if ( mIdRowMap.contains( fid ) )
        {
          // Feature changed such, that it is no longer shown
          featureDeleted( fid );
        }
        // else: we don't care
      }
    }
  }
}
void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
{
  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
  bool featOk = true;

  if ( mFeat.id() != fid )
    featOk = loadFeatureAtId( fid );

  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
  {
    mFieldCache[ fid ] = mFeat.attribute( mCachedField );

    int n = mRowIdMap.size();
    beginInsertRows( QModelIndex(), n, n );

    mIdRowMap.insert( fid, n );
    mRowIdMap.insert( n, fid );

    endInsertRows();

    reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
  }
}
QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
{
  if ( !index.isValid() ||
       ( role != Qt::TextAlignmentRole
         && role != Qt::DisplayRole
         && role != Qt::EditRole
         && role != SortRole
         && role != FeatureIdRole
         && role != FieldIndexRole
       )
     )
    return QVariant();

  QgsFeatureId rowId = rowToId( index.row() );

  if ( role == FeatureIdRole )
    return rowId;

  if ( index.column() >= mFieldCount )
    return role == Qt::DisplayRole ? rowId : QVariant();

  int fieldId = mAttributes[ index.column()];

  if ( role == FieldIndexRole )
    return fieldId;

  const QgsField& field = layer()->pendingFields()[ fieldId ];

  QVariant::Type fldType = field.type();
  bool fldNumeric = ( fldType == QVariant::Int || fldType == QVariant::Double || fldType == QVariant::LongLong );

  if ( role == Qt::TextAlignmentRole )
  {
    if ( fldNumeric )
      return QVariant( Qt::AlignRight );
    else
      return QVariant( Qt::AlignLeft );
  }

  QVariant val;

  // if we don't have the row in current cache, load it from layer first
  if ( mCachedField == fieldId )
  {
    val = mFieldCache[ rowId ];
  }
  else
  {
    if ( mFeat.id() != rowId || !mFeat.isValid() )
    {
      if ( !loadFeatureAtId( rowId ) )
        return QVariant( "ERROR" );

      if ( mFeat.id() != rowId )
        return QVariant( "ERROR" );
    }

    val = mFeat.attribute( fieldId );
  }

  if ( role == Qt::DisplayRole )
  {
    return mWidgetFactories[ fieldId ]->representValue( layer(), fieldId, mWidgetConfigs[ fieldId ], mAttributeWidgetCaches[ fieldId ], val );
  }

  return val;
}
void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
{
  // Defer all updates if a bulk edit/rollback command is running
  if ( mBulkEditCommandRunning )
  {
    mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
    return;
  }
  QgsDebugMsgLevel( QStringLiteral( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 2 );

  for ( SortCache &cache : mSortCaches )
  {
    if ( cache.sortCacheAttributes.contains( idx ) )
    {
      if ( cache.sortFieldIndex == -1 )
      {
        if ( loadFeatureAtId( fid ) )
        {
          mExpressionContext.setFeature( mFeat );
          cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
        }
      }
      else
      {
        QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
        const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
        const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
        QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
        cache.sortCache.insert( fid, sortValue );
      }
    }
  }
  // No filter request: skip all possibly heavy checks
  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
  {
    if ( loadFeatureAtId( fid ) )
      setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
  }
  else
  {
    if ( loadFeatureAtId( fid ) )
    {
      if ( mFeatureRequest.acceptFeature( mFeat ) )
      {
        if ( !mIdRowMap.contains( fid ) )
        {
          // Feature changed in such a way, it will be shown now
          featureAdded( fid );
        }
        else
        {
          // Update representation
          setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
        }
      }
      else
      {
        if ( mIdRowMap.contains( fid ) )
        {
          // Feature changed such, that it is no longer shown
          featuresDeleted( QgsFeatureIds() << fid );
        }
        // else: we don't care
      }
    }
  }
}