53   : QAbstractTableModel( parent )
 
   54   , mLayer( layerCache->layer() )
 
   55   , mLayerCache( layerCache )
 
   64   mFeat.
setId( std::numeric_limits<int>::min() );
 
   79     mIsCleaningUpAfterRollback = 
true;
 
   80     bulkEditCommandEnded();
 
   81     mIsCleaningUpAfterRollback = 
false;
 
   90 bool QgsAttributeTableModel::loadFeatureAtId( 
QgsFeatureId fid )
 const 
   94   if ( fid == std::numeric_limits<int>::min() )
 
  104   return mExtraColumns;
 
  113 void QgsAttributeTableModel::featuresDeleted( 
const QgsFeatureIds &fids )
 
  117   const auto constFids = fids;
 
  120     QgsDebugMsgLevel( QStringLiteral( 
"(%2) fid: %1, size: %3" ).arg( fid ).arg( mFeatureRequest.
filterType() ).arg( mIdRowMap.size() ), 4 );
 
  122     const int row = 
idToRow( fid );
 
  127   std::sort( rows.begin(), rows.end() );
 
  131   int currentRowCount = 0;
 
  135   const auto constRows = rows;
 
  136   for ( 
const int row : constRows )
 
  139     qDebug() << 
"Row: " << row << 
", begin " << beginRow << 
", last " << lastRow << 
", current " << currentRowCount << 
", removed " << removedRows;
 
  146     if ( row != lastRow + 1 && lastRow != -1 )
 
  148       if ( rows.count() > 100 && currentRowCount < 10 )
 
  153       removeRows( beginRow - removedRows, currentRowCount );
 
  156       removedRows += currentRowCount;
 
  166     removeRows( beginRow - removedRows, currentRowCount );
 
  174   if ( row < 0 || count < 1 )
 
  177   if ( !mResettingModel )
 
  179     beginRemoveRows( parent, row, row + count - 1 );
 
  184     QgsDebugMsgLevel( QStringLiteral( 
"remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
 
  187   if ( mBulkEditCommandRunning && !mResettingModel )
 
  189     for ( 
int i = row; i < row + count; i++ )
 
  192       mInsertedRowsChanges.removeOne( fid );
 
  197   for ( 
int i = row; i < row + count; i++ )
 
  199     for ( SortCache &cache : mSortCaches )
 
  200       cache.sortCache.remove( mRowIdMap[i] );
 
  201     mIdRowMap.remove( mRowIdMap[i] );
 
  202     mRowIdMap.remove( i );
 
  206   const int n = mRowIdMap.size() + count;
 
  207   for ( 
int i = row + count; i < n; i++ )
 
  210     mIdRowMap[id] -= count;
 
  211     mRowIdMap[i - count] = id;
 
  212     mRowIdMap.remove( i );
 
  218     QgsDebugMsgLevel( QStringLiteral( 
"after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
 
  220     for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
 
  224     for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
 
  229   Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
 
  231   if ( !mResettingModel )
 
  237 void QgsAttributeTableModel::featureAdded( 
QgsFeatureId fid )
 
  242   if ( mFeat.
id() != fid )
 
  243     featOk = loadFeatureAtId( fid );
 
  247     for ( SortCache &cache : mSortCaches )
 
  249       if ( cache.sortFieldIndex >= 0 )
 
  252         const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
 
  253         const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
 
  254         const QVariant sortValue = fieldFormatter->
sortValue( mLayer, cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.
attribute( cache.sortFieldIndex ) );
 
  255         cache.sortCache.insert( mFeat.
id(), sortValue );
 
  257       else if ( cache.sortCacheExpression.isValid() )
 
  260         cache.sortCache[mFeat.
id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
 
  265     if ( ! mIdRowMap.contains( fid ) )
 
  267       const int n = mRowIdMap.size();
 
  268       if ( !mResettingModel )
 
  269         beginInsertRows( QModelIndex(), n, n );
 
  270       mIdRowMap.insert( fid, n );
 
  271       mRowIdMap.insert( n, fid );
 
  272       if ( !mResettingModel )
 
  275       if ( mBulkEditCommandRunning && !mResettingModel )
 
  277         mInsertedRowsChanges.append( fid );
 
  283 void QgsAttributeTableModel::updatedFields()
 
  289 void QgsAttributeTableModel::editCommandEnded()
 
  293   bulkEditCommandEnded( );
 
  296 void QgsAttributeTableModel::attributeDeleted( 
int idx )
 
  299   for ( 
const SortCache &cache : mSortCaches )
 
  301     if ( cache.sortCacheAttributes.contains( idx ) )
 
  309 void QgsAttributeTableModel::layerDeleted()
 
  311   mLayerCache = 
nullptr;
 
  315   mAttributeWidgetCaches.clear();
 
  317   mWidgetFactories.clear();
 
  318   mWidgetConfigs.clear();
 
  319   mFieldFormatters.clear();
 
  322 void QgsAttributeTableModel::fieldFormatterRemoved( 
QgsFieldFormatter *fieldFormatter )
 
  324   for ( 
int i = 0; i < mFieldFormatters.size(); ++i )
 
  326     if ( mFieldFormatters.at( i ) == fieldFormatter )
 
  331 void QgsAttributeTableModel::attributeValueChanged( 
QgsFeatureId fid, 
int idx, 
const QVariant &value )
 
  334   if ( mBulkEditCommandRunning )
 
  336     mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
 
  339   QgsDebugMsgLevel( QStringLiteral( 
"(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.
filterType() ), 2 );
 
  341   for ( SortCache &cache : mSortCaches )
 
  343     if ( cache.sortCacheAttributes.contains( idx ) )
 
  345       if ( cache.sortFieldIndex == -1 )
 
  347         if ( loadFeatureAtId( fid ) )
 
  350           cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
 
  356         const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
 
  357         const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
 
  358         const QVariant sortValue = fieldFormatter->
representValue( mLayer, cache.sortFieldIndex, widgetConfig, widgetCache, value );
 
  359         cache.sortCache.insert( fid, sortValue );
 
  366     if ( loadFeatureAtId( fid ) )
 
  371     if ( loadFeatureAtId( fid ) )
 
  375         if ( !mIdRowMap.contains( fid ) )
 
  388         if ( mIdRowMap.contains( fid ) )
 
  399 void QgsAttributeTableModel::loadAttributes()
 
  406   bool ins = 
false, rm = 
false;
 
  411   mWidgetFactories.clear();
 
  412   mAttributeWidgetCaches.clear();
 
  413   mWidgetConfigs.clear();
 
  414   mFieldFormatters.clear();
 
  416   for ( 
int idx = 0; idx < fields.
count(); ++idx )
 
  422     mWidgetFactories.append( widgetFactory );
 
  423     mWidgetConfigs.append( setup.
config() );
 
  424     mAttributeWidgetCaches.append( fieldFormatter->
createCache( mLayer, idx, setup.
config() ) );
 
  425     mFieldFormatters.append( fieldFormatter );
 
  430   if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
 
  433     beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
 
  435   else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
 
  438     beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
 
  441   mFieldCount = attributes.size();
 
  442   mAttributes = attributes;
 
  444   for ( SortCache &cache : mSortCaches )
 
  446     if ( cache.sortFieldIndex >= mAttributes.count() )
 
  447       cache.sortFieldIndex = -1;
 
  469   mResettingModel = 
true;
 
  491       if ( t.elapsed() > 1000 )
 
  500       featureAdded( mFeat.
id() );
 
  509   mResettingModel = 
false;
 
  515   if ( fieldName.isNull() )
 
  517     mRowStylesMap.clear();
 
  523   if ( fieldIndex == -1 )
 
  527   const int col = 
fieldCol( fieldIndex );
 
  528   emit dataChanged( index( 0, col ), index( 
rowCount() - 1, col ) );
 
  541   mRowIdMap.remove( rowA );
 
  542   mRowIdMap.remove( rowB );
 
  543   mRowIdMap.insert( rowA, b );
 
  544   mRowIdMap.insert( rowB, a );
 
  546   mIdRowMap.remove( a );
 
  547   mIdRowMap.remove( b );
 
  548   mIdRowMap.insert( a, rowB );
 
  549   mIdRowMap.insert( b, rowA );
 
  550   Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
 
  558   if ( !mIdRowMap.contains( 
id ) )
 
  560     QgsDebugMsg( QStringLiteral( 
"idToRow: id %1 not in the map" ).arg( 
id ) );
 
  564   return mIdRowMap[id];
 
  569   return index( 
idToRow( 
id ), 0 );
 
  574   QModelIndexList indexes;
 
  578   indexes.reserve( columns );
 
  579   for ( 
int column = 0; column < columns; ++column )
 
  581     indexes.append( index( row, column ) );
 
  589   if ( !mRowIdMap.contains( row ) )
 
  591     QgsDebugMsg( QStringLiteral( 
"rowToId: row %1 not in the map" ).arg( row ) );
 
  593     return std::numeric_limits<int>::min();
 
  596   return mRowIdMap[row];
 
  601   return mAttributes[col];
 
  606   return mAttributes.indexOf( idx );
 
  612   return mRowIdMap.size();
 
  618   return std::max( 1, mFieldCount + mExtraColumns );  
 
  626   if ( role == Qt::DisplayRole )
 
  628     if ( orientation == Qt::Vertical ) 
 
  630       return QVariant( section );
 
  632     else if ( section >= 0 && section < mFieldCount )
 
  634       const QString attributeName = mLayer->
fields().
at( mAttributes.at( section ) ).
displayName();
 
  635       return QVariant( attributeName );
 
  639       return tr( 
"extra column" );
 
  642   else if ( role == Qt::ToolTipRole )
 
  644     if ( orientation == Qt::Vertical )
 
  647       return tr( 
"Feature ID: %1" ).arg( 
rowToId( section ) );
 
  663   if ( !index.isValid() || !mLayer ||
 
  664        ( role != Qt::TextAlignmentRole
 
  665          && role != Qt::DisplayRole
 
  666          && role != Qt::ToolTipRole
 
  667          && role != Qt::EditRole
 
  670 #
if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
 
  671          && role != Qt::BackgroundColorRole
 
  672          && role != Qt::TextColorRole
 
  674          && role != Qt::BackgroundRole
 
  675          && role != Qt::ForegroundRole
 
  677          && role != Qt::DecorationRole
 
  678          && role != Qt::FontRole
 
  689   if ( index.column() >= mFieldCount )
 
  692   const int fieldId = mAttributes.at( index.column() );
 
  699     const unsigned long cacheIndex = role - 
SortRole;
 
  700     if ( cacheIndex < mSortCaches.size() )
 
  701       return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
 
  708   if ( role == Qt::TextAlignmentRole )
 
  710     return static_cast<Qt::Alignment::Int
>( mFieldFormatters.at( index.column() )->alignmentFlag( mLayer, fieldId, mWidgetConfigs.at( index.column() ) ) | Qt::AlignVCenter );
 
  713   if ( mFeat.
id() != rowId || !mFeat.
isValid() )
 
  715     if ( !loadFeatureAtId( rowId ) )
 
  716       return QVariant( 
"ERROR" );
 
  718     if ( mFeat.
id() != rowId )
 
  719       return QVariant( 
"ERROR" );
 
  722   QVariant val = mFeat.
attribute( fieldId );
 
  726     case Qt::DisplayRole:
 
  727       return mFieldFormatters.at( index.column() )->representValue( mLayer,
 
  729              mWidgetConfigs.at( index.column() ),
 
  730              mAttributeWidgetCaches.at( index.column() ),
 
  732     case Qt::ToolTipRole:
 
  734       QString tooltip = mFieldFormatters.at( index.column() )->representValue( mLayer,
 
  736                         mWidgetConfigs.at( index.column() ),
 
  737                         mAttributeWidgetCaches.at( index.column() ),
 
  741         tooltip = tr( 
"%1 (Ctrl+click to open)" ).arg( tooltip );
 
  748     case Qt::BackgroundRole:
 
  749 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) 
  750     case Qt::TextColorRole:
 
  752     case Qt::ForegroundRole:
 
  754     case Qt::DecorationRole:
 
  758       QList<QgsConditionalStyle> styles;
 
  759       if ( mRowStylesMap.contains( mFeat.
id() ) )
 
  761         styles = mRowStylesMap[mFeat.
id()];
 
  766         mRowStylesMap.insert( mFeat.
id(), styles );
 
  772       styles.insert( 0, rowstyle );
 
  779 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) 
  782         if ( role == Qt::ForegroundRole )
 
  785         if ( role == Qt::DecorationRole )
 
  787         if ( role == Qt::FontRole )
 
  792         if ( role == Qt::ForegroundRole )
 
  794           return QColor( Qt::blue );
 
  796         else if ( role == Qt::FontRole )
 
  799           font.setUnderline( 
true );
 
  815   if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !mLayer->
isEditable() )
 
  818   mRowStylesMap.remove( mFeat.
id() );
 
  828   if ( !index.isValid() )
 
  829     return Qt::ItemIsEnabled;
 
  831   if ( index.column() >= mFieldCount || !mLayer )
 
  832     return Qt::NoItemFlags;
 
  834   Qt::ItemFlags 
flags = QAbstractTableModel::flags( index );
 
  836   const int fieldIndex = mAttributes[index.column()];
 
  840     flags |= Qt::ItemIsEditable;
 
  845 void QgsAttributeTableModel::bulkEditCommandStarted()
 
  847   mBulkEditCommandRunning = 
true;
 
  848   mAttributeValueChanges.clear();
 
  851 void QgsAttributeTableModel::bulkEditCommandEnded()
 
  853   mBulkEditCommandRunning = 
false;
 
  856   const int changeCount( std::max( mAttributeValueChanges.count(), mInsertedRowsChanges.count() ) );
 
  857   const bool fullModelUpdate = changeCount > mLayerCache->
cacheSize() ||
 
  860   QgsDebugMsgLevel( QStringLiteral( 
"Bulk edit command ended with %1 modified rows over (%4), cache size is %2, starting %3 update." )
 
  863                     .arg( fullModelUpdate ? QStringLiteral( 
"full" ) :  QStringLiteral( 
"incremental" ) )
 
  868   if ( mIsCleaningUpAfterRollback )
 
  870     for ( 
const int fid : std::as_const( mInsertedRowsChanges ) )
 
  872       const int row( 
idToRow( fid ) );
 
  882   if ( fullModelUpdate )
 
  895     const auto keys = mAttributeValueChanges.keys();
 
  896     for ( 
const auto &key : keys )
 
  898       attributeValueChanged( key.first, key.second, mAttributeValueChanges.value( key ) );
 
  899       const int row( 
idToRow( key.first ) );
 
  900       const int col( 
fieldCol( key.second ) );
 
  901       minRow = std::min<int>( row, minRow );
 
  902       minCol = std::min<int>( col, minCol );
 
  903       maxRow = std::max<int>( row, maxRow );
 
  904       maxCol = std::max<int>( col, maxCol );
 
  907     emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
 
  909   mAttributeValueChanges.clear();
 
  914   mFeat.
setId( std::numeric_limits<int>::min() );
 
  915   emit dataChanged( index1, index2 );
 
  936   for ( 
int i = 0; i < mAttributes.size(); i++ )
 
  938     f.
setAttribute( mAttributes[i], 
data( index( idx.row(), i ), Qt::EditRole ) );
 
  946   if ( column == -1 || column >= mAttributes.count() )
 
  958   if ( cacheIndex >= mSortCaches.size() )
 
  960     mSortCaches.resize( cacheIndex + 1 );
 
  962   SortCache &cache = mSortCaches[cacheIndex];
 
  963   cache.sortCache.clear();
 
  964   cache.sortCacheAttributes.clear();
 
  965   cache.sortFieldIndex = -1;
 
  966   if ( !expressionString.isEmpty() )
 
  967     cache.sortCacheExpression = 
QgsExpression( expressionString );
 
  976   QVariant widgetCache;
 
  977   QVariantMap widgetConfig;
 
  979   if ( cache.sortCacheExpression.isField() )
 
  985   if ( cache.sortFieldIndex == -1 )
 
  987     cache.sortCacheExpression.prepare( &mExpressionContext );
 
  989     const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
 
  991     for ( 
const QString &col : referencedColumns )
 
  998     cache.sortCacheAttributes.append( cache.sortFieldIndex );
 
 1000     widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
 
 1001     widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
 
 1002     fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
 
 1013     if ( cache.sortFieldIndex == -1 )
 
 1016       const QVariant cacheValue = cache.sortCacheExpression.evaluate( &mExpressionContext );
 
 1017       cache.sortCache.insert( f.
id(), cacheValue );
 
 1021       const QVariant sortValue = fieldFormatter->
sortValue( mLayer, cache.sortFieldIndex, widgetConfig, widgetCache, f.
attribute( cache.sortFieldIndex ) );
 
 1022       cache.sortCache.insert( f.
id(), sortValue );
 
 1029   QString expressionString;
 
 1031   if ( cacheIndex >= mSortCaches.size() )
 
 1032     return expressionString;
 
 1034   const QgsExpression &expression = mSortCaches[cacheIndex].sortCacheExpression;
 
 1039     expressionString = QString();
 
 1041   return expressionString;
 
 1053   return mFeatureRequest;