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;