19 #include <QMessageBox> 
   20 #include <QProgressDialog> 
   22 #include <QInputDialog> 
   50   : QStackedWidget( parent )
 
   59   mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
 
   60   connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, 
this, &QgsDualView::showViewHeaderMenu );
 
   63   mConditionalFormatWidgetStack->hide();
 
   65   mConditionalFormatWidgetStack->setMainPanel( mConditionalFormatWidget );
 
   69   mConditionalSplitter->restoreState( settings.
value( QStringLiteral( 
"/qgis/attributeTable/splitterState" ), QByteArray() ).toByteArray() );
 
   71   mPreviewColumnsMenu = 
new QMenu( 
this );
 
   72   mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
 
   78   connect( mActionExpressionPreview, &QAction::triggered, 
this, &QgsDualView::previewExpressionBuilder );
 
   87   auto createShortcuts = [ = ]( 
const QString & objectName, void ( 
QgsFeatureListView::* slot )() )
 
   93       connect( sc, &QShortcut::activated, mFeatureListView, slot );
 
  100   QButtonGroup *buttonGroup = 
new QButtonGroup( 
this );
 
  101   buttonGroup->setExclusive( 
false );
 
  105   QAbstractButton *bt = buttonGroup->button( 
static_cast<int>( action ) );
 
  107     bt->setChecked( 
true );
 
  108   connect( buttonGroup, qgis::overload< QAbstractButton *, bool >::of( &QButtonGroup::buttonToggled ), 
this, &QgsDualView::panZoomGroupButtonToggled );
 
  109   mFlashButton->setChecked( 
QgsSettings().value( QStringLiteral( 
"/qgis/attributeTable/featureListHighlightFeature" ), 
true ).toBool() );
 
  110   connect( mFlashButton, &QToolButton::clicked, 
this, &QgsDualView::flashButtonClicked );
 
  116   settings.
setValue( QStringLiteral( 
"/qgis/attributeTable/splitterState" ), mConditionalSplitter->saveState() );
 
  126   mEditorContext = context;
 
  129   initModels( mapCanvas, request, loadFeatures );
 
  131   mConditionalFormatWidget->
setLayer( mLayer );
 
  133   mTableView->setModel( mFilterModel );
 
  134   mFeatureListView->setModel( mFeatureListModel );
 
  135   delete mAttributeForm;
 
  136   mAttributeForm = 
new QgsAttributeForm( mLayer, mTempAttributeFormFeature, mEditorContext );
 
  141     mAttributeEditorScrollArea->setWidgetResizable( 
true );
 
  142     mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
 
  143     mAttributeEditorScrollArea->setWidget( mAttributeForm );
 
  147     mAttributeEditor->layout()->addWidget( mAttributeForm );
 
  157       QgsMapCanvasUtils::flashMatchingFeatures( canvas, mLayer, filter );
 
  164       QgsMapCanvasUtils::zoomToMatchingFeatures( canvas, mLayer, filter );
 
  171   if ( mFeatureListPreviewButton->defaultAction() )
 
  172     mFeatureListView->setDisplayExpression( mDisplayExpression );
 
  179   if ( mFeatureListModel->
rowCount( ) > 0 )
 
  180     mFeatureListView->setEditSelection( 
QgsFeatureIds() << mFeatureListModel->
data( mFeatureListModel->index( 0, 0 ), QgsFeatureListModel::Role::FeatureRole ).value<
QgsFeature>().
id() );
 
  184 void QgsDualView::columnBoxInit()
 
  187   QList<QgsField> fields = mLayer->fields().toList();
 
  189   QString defaultField;
 
  192   QString displayExpression = mLayer->displayExpression();
 
  194   if ( displayExpression.isEmpty() )
 
  197     displayExpression = QStringLiteral( 
"'[Please define preview text]'" );
 
  200   mFeatureListPreviewButton->addAction( mActionExpressionPreview );
 
  201   mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
 
  203   const auto constFields = fields;
 
  206     int fieldIndex = mLayer->fields().lookupField( 
field.
name() );
 
  207     if ( fieldIndex == -1 )
 
  213       QIcon icon = mLayer->fields().iconForField( fieldIndex );
 
  214       QString text = mLayer->attributeDisplayName( fieldIndex );
 
  217       QAction *previewAction = 
new QAction( icon, text, mFeatureListPreviewButton );
 
  218       connect( previewAction, &QAction::triggered, 
this, [ = ] { previewColumnChanged( previewAction, fieldName ); } );
 
  219       mPreviewColumnsMenu->addAction( previewAction );
 
  221       if ( text == defaultField )
 
  223         mFeatureListPreviewButton->setDefaultAction( previewAction );
 
  228   QMenu *sortMenu = 
new QMenu( 
this );
 
  230   sortMenuAction->setMenu( sortMenu );
 
  232   QAction *sortByPreviewExpressionAsc = 
new QAction( 
QgsApplication::getThemeIcon( QStringLiteral( 
"sort.svg" ) ), tr( 
"By Preview Expression (ascending)" ), 
this );
 
  233   connect( sortByPreviewExpressionAsc, &QAction::triggered, 
this, [ = ]()
 
  237   sortMenu->addAction( sortByPreviewExpressionAsc );
 
  238   QAction *sortByPreviewExpressionDesc = 
new QAction( 
QgsApplication::getThemeIcon( QStringLiteral( 
"sort-reverse.svg" ) ), tr( 
"By Preview Expression (descending)" ), 
this );
 
  239   connect( sortByPreviewExpressionDesc, &QAction::triggered, 
this, [ = ]()
 
  243   sortMenu->addAction( sortByPreviewExpressionDesc );
 
  244   QAction *sortByPreviewExpressionCustom = 
new QAction( 
QgsApplication::getThemeIcon( QStringLiteral( 
"mIconExpressionPreview.svg" ) ), tr( 
"By Custom Expression" ), 
this );
 
  245   connect( sortByPreviewExpressionCustom, &QAction::triggered, 
this, [ = ]()
 
  250   sortMenu->addAction( sortByPreviewExpressionCustom );
 
  252   mFeatureListPreviewButton->addAction( sortMenuAction );
 
  254   QAction *separator = 
new QAction( mFeatureListPreviewButton );
 
  255   separator->setSeparator( 
true );
 
  256   mFeatureListPreviewButton->addAction( separator );
 
  257   restoreRecentDisplayExpressions();
 
  260   if ( !mFeatureListPreviewButton->defaultAction() )
 
  262     mFeatureListView->setDisplayExpression( displayExpression );
 
  263     mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
 
  264     setDisplayExpression( mFeatureListView->displayExpression() );
 
  268     mFeatureListPreviewButton->defaultAction()->trigger();
 
  274   setCurrentIndex( 
view );
 
  308                              || ( mMasterModel->
rowCount() == 0 ); 
 
  310   if ( !needsGeometry )
 
  347       setBrowsingAutoPanScaleAllowed( 
false );
 
  354       setBrowsingAutoPanScaleAllowed( 
true );
 
  358   if ( requiresTableReload )
 
  364     whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
 
  378 void QgsDualView::initLayerCache( 
bool cacheGeometry )
 
  382   int cacheSize = settings.
value( QStringLiteral( 
"qgis/attributeTableRowCache" ), 
"10000" ).toInt();
 
  388     rebuildFullLayerCache();
 
  394   delete mFeatureListModel;
 
  415   connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
 
  416   connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
 
  424 void QgsDualView::restoreRecentDisplayExpressions()
 
  426   const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( 
"dualview/previewExpressions" ) ).toList();
 
  428   for ( 
const QVariant &previewExpression : previewExpressions )
 
  429     insertRecentlyUsedDisplayExpression( previewExpression.toString() );
 
  432 void QgsDualView::saveRecentDisplayExpressions()
 const 
  438   QList<QAction *> actions = mFeatureListPreviewButton->actions();
 
  441   int index = actions.indexOf( mLastDisplayExpressionAction );
 
  444     QVariantList previewExpressions;
 
  445     for ( ; index < actions.length(); ++index )
 
  447       QAction *action = actions.at( index );
 
  448       previewExpressions << action->property( 
"previewExpression" );
 
  451     mLayer->setCustomProperty( QStringLiteral( 
"dualview/previewExpressions" ), previewExpressions );
 
  455 void QgsDualView::setDisplayExpression( 
const QString &expression )
 
  457   mDisplayExpression = expression;
 
  458   insertRecentlyUsedDisplayExpression( expression );
 
  461 void QgsDualView::insertRecentlyUsedDisplayExpression( 
const QString &expression )
 
  463   QList<QAction *> actions = mFeatureListPreviewButton->actions();
 
  466   int index = actions.indexOf( mLastDisplayExpressionAction );
 
  469     for ( 
int i = 0; index + i < actions.length(); ++i )
 
  471       QAction *action = actions.at( index );
 
  472       if ( action->text() == expression || i >= 9 )
 
  474         if ( action == mLastDisplayExpressionAction )
 
  475           mLastDisplayExpressionAction = 
nullptr;
 
  476         mFeatureListPreviewButton->removeAction( action );
 
  480         if ( !mLastDisplayExpressionAction )
 
  481           mLastDisplayExpressionAction = action;
 
  486   QString name = expression;
 
  488   if ( expression.startsWith( QLatin1String( 
"COALESCE( \"" ) ) && expression.endsWith( QLatin1String( 
", '<NULL>' )" ) ) )
 
  490     name = expression.mid( 11, expression.length() - 24 ); 
 
  492     int fieldIndex = mLayer->fields().indexOf( name );
 
  493     if ( fieldIndex != -1 )
 
  495       name = mLayer->attributeDisplayName( fieldIndex );
 
  496       icon = mLayer->fields().iconForField( fieldIndex );
 
  504   QAction *previewAction = 
new QAction( icon, name, mFeatureListPreviewButton );
 
  505   previewAction->setProperty( 
"previewExpression", expression );
 
  506   connect( previewAction, &QAction::triggered, 
this, [expression, 
this]( 
bool )
 
  508     setDisplayExpression( expression );
 
  509     mFeatureListPreviewButton->setText( expression );
 
  513   mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction );
 
  514   mLastDisplayExpressionAction = previewAction;
 
  517 void QgsDualView::updateEditSelectionProgress( 
int progress, 
int count )
 
  519   mProgressCount->setText( QStringLiteral( 
"%1 / %2" ).arg( progress + 1 ).arg( count ) );
 
  520   mPreviousFeatureButton->setEnabled( progress > 0 );
 
  521   mNextFeatureButton->setEnabled( progress + 1 < count );
 
  522   mFirstFeatureButton->setEnabled( progress > 0 );
 
  523   mLastFeatureButton->setEnabled( progress + 1 < count );
 
  526 void QgsDualView::panOrZoomToFeature( 
const QgsFeatureIds &featureset )
 
  531     if ( mBrowsingAutoPanScaleAllowed )
 
  533       if ( mAutoPanButton->isChecked() )
 
  534         QTimer::singleShot( 0, 
this, [ = ]()
 
  538       else if ( mAutoZoomButton->isChecked() )
 
  539         QTimer::singleShot( 0, 
this, [ = ]()
 
  544     if ( mFlashButton->isChecked() )
 
  545       QTimer::singleShot( 0, 
this, [ = ]()
 
  549     mLastFeatureSet = featureset;
 
  553 void QgsDualView::setBrowsingAutoPanScaleAllowed( 
bool allowed )
 
  555   if ( mBrowsingAutoPanScaleAllowed == allowed )
 
  558   mBrowsingAutoPanScaleAllowed = allowed;
 
  560   mAutoPanButton->setEnabled( allowed );
 
  561   mAutoZoomButton->setEnabled( allowed );
 
  563   QString disabledHint = tr( 
"(disabled when attribute table only shows features visible in the current map canvas extent)" );
 
  565   mAutoPanButton->setToolTip( tr( 
"Automatically pan to the current feature" ) + ( allowed ? QString() : QString( 
' ' ) + disabledHint ) );
 
  566   mAutoZoomButton->setToolTip( tr( 
"Automatically zoom to the current feature" ) + ( allowed ? QString() : QString( 
' ' ) + disabledHint ) );
 
  569 void QgsDualView::panZoomGroupButtonToggled( QAbstractButton *button, 
bool checked )
 
  571   if ( button == mAutoPanButton && checked )
 
  574     mAutoZoomButton->setChecked( 
false );
 
  576   else if ( button == mAutoZoomButton && checked )
 
  579     mAutoPanButton->setChecked( 
false );
 
  586   if ( checked && mLayer->isSpatial() )
 
  587     panOrZoomToFeature( mFeatureListView->currentEditSelection() );
 
  590 void QgsDualView::flashButtonClicked( 
bool clicked )
 
  592   QgsSettings().
setValue( QStringLiteral( 
"/qgis/attributeTable/featureListHighlightFeature" ), clicked );
 
  599     canvas->
flashFeatureIds( mLayer, mFeatureListView->currentEditSelection() );
 
  602 void QgsDualView::featureListAboutToChangeEditSelection( 
bool &ok )
 
  604   if ( mLayer->isEditable() && !mAttributeForm->
save() )
 
  608 void QgsDualView::featureListCurrentEditSelectionChanged( 
const QgsFeature &feat )
 
  610   if ( !mAttributeForm )
 
  612     mTempAttributeFormFeature = feat;
 
  614   else if ( !mLayer->isEditable() || mAttributeForm->
save() )
 
  618     featureset << feat.
id();
 
  621     if ( mLayer->isSpatial() )
 
  622       panOrZoomToFeature( featureset );
 
  633   mFeatureListView->setCurrentFeatureEdited( 
false );
 
  634   mFeatureListView->setEditSelection( fids );
 
  639   return mAttributeForm->
save();
 
  644   mConditionalFormatWidgetStack->setVisible( !mConditionalFormatWidgetStack->isVisible() );
 
  651     mPreviousView = 
view();
 
  673 void QgsDualView::previewExpressionBuilder()
 
  679   dlg.setWindowTitle( tr( 
"Expression Based Preview" ) );
 
  680   dlg.setExpressionText( mFeatureListView->displayExpression() );
 
  682   if ( dlg.exec() == QDialog::Accepted )
 
  684     mFeatureListView->setDisplayExpression( dlg.expressionText() );
 
  685     mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
 
  686     mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
 
  689   setDisplayExpression( mFeatureListView->displayExpression() );
 
  692 void QgsDualView::previewColumnChanged( QAction *previewAction, 
const QString &expression )
 
  694   if ( !mFeatureListView->setDisplayExpression( QStringLiteral( 
"COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
 
  696     QMessageBox::warning( 
this,
 
  697                           tr( 
"Column Preview" ),
 
  698                           tr( 
"Could not set column '%1' as preview column.\nParser error:\n%2" )
 
  699                           .arg( previewAction->text(), mFeatureListView->parserErrorString() )
 
  704     mFeatureListPreviewButton->setText( previewAction->text() );
 
  705     mFeatureListPreviewButton->setIcon( previewAction->icon() );
 
  706     mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
 
  709   setDisplayExpression( mFeatureListView->displayExpression() );
 
  719   return mFilterModel->rowCount();
 
  724   const QModelIndex currentIndex = mTableView->currentIndex();
 
  725   if ( !currentIndex.isValid() )
 
  730   QVariant var = mMasterModel->
data( currentIndex, Qt::DisplayRole );
 
  731   QApplication::clipboard()->setText( var.toString() );
 
  737     mProgressDlg->cancel();
 
  742   if ( mAttributeForm )
 
  751   saveRecentDisplayExpressions();
 
  754 void QgsDualView::viewWillShowContextMenu( QMenu *menu, 
const QModelIndex &masterIndex )
 
  761   QAction *copyContentAction = menu->addAction( tr( 
"Copy Cell Content" ) );
 
  762   menu->addAction( copyContentAction );
 
  763   connect( copyContentAction, &QAction::triggered, 
this, [masterIndex, 
this]
 
  765     QVariant var = mMasterModel->
data( masterIndex, Qt::DisplayRole );
 
  766     QApplication::clipboard()->setText( var.toString() );
 
  773     QAction *zoomToFeatureAction = menu->addAction( tr( 
"Zoom to Feature" ) );
 
  774     connect( zoomToFeatureAction, &QAction::triggered, 
this, &QgsDualView::zoomToCurrentFeature );
 
  776     QAction *panToFeatureAction = menu->addAction( tr( 
"Pan to Feature" ) );
 
  777     connect( panToFeatureAction, &QAction::triggered, 
this, &QgsDualView::panToCurrentFeature );
 
  779     QAction *flashFeatureAction = menu->addAction( tr( 
"Flash Feature" ) );
 
  780     connect( flashFeatureAction, &QAction::triggered, 
this, &QgsDualView::flashCurrentFeature );
 
  784   const QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( 
"Field" ) );
 
  785   if ( !actions.isEmpty() )
 
  787     QAction *a = menu->addAction( tr( 
"Run Layer Action" ) );
 
  788     a->setEnabled( 
false );
 
  790     for ( 
const QgsAction &action : actions )
 
  792       if ( !action.runable() )
 
  795       if ( vl && !vl->isEditable() && action.isEnabledOnlyWhenEditable() )
 
  802   QModelIndex rowSourceIndex = mMasterModel->index( masterIndex.row(), 0 );
 
  803   if ( ! rowSourceIndex.isValid() )
 
  810   if ( !registeredActions.isEmpty() )
 
  813     menu->addSeparator();
 
  825   if ( mLayer->selectedFeatureCount() > 1 && mLayer->selectedFeatureIds().contains( currentFid ) )
 
  828     if ( !registeredActions.isEmpty() )
 
  830       menu->addSeparator();
 
  831       QAction *action = menu->addAction( tr( 
"Actions on Selection (%1)" ).arg( mLayer->selectedFeatureCount() ) );
 
  832       action->setEnabled( 
false );
 
  836         menu->addAction( action->text(), action, [ = ]() {action->triggerForFeatures( mLayer, mLayer->selectedFeatures() );} );
 
  841   menu->addSeparator();
 
  847 void QgsDualView::widgetWillShowContextMenu( 
QgsActionMenu *menu, 
const QModelIndex &atIndex )
 
  853 void QgsDualView::showViewHeaderMenu( QPoint point )
 
  855   int col = mTableView->columnAt( point.x() );
 
  857   delete mHorizontalHeaderMenu;
 
  858   mHorizontalHeaderMenu = 
new QMenu( 
this );
 
  860   QAction *hide = 
new QAction( tr( 
"&Hide Column" ), mHorizontalHeaderMenu );
 
  861   connect( hide, &QAction::triggered, 
this, &QgsDualView::hideColumn );
 
  862   hide->setData( col );
 
  863   mHorizontalHeaderMenu->addAction( hide );
 
  864   QAction *setWidth = 
new QAction( tr( 
"&Set Width…" ), mHorizontalHeaderMenu );
 
  865   connect( setWidth, &QAction::triggered, 
this, &QgsDualView::resizeColumn );
 
  866   setWidth->setData( col );
 
  867   mHorizontalHeaderMenu->addAction( setWidth );
 
  868   QAction *optimizeWidth = 
new QAction( tr( 
"&Autosize" ), mHorizontalHeaderMenu );
 
  869   connect( optimizeWidth, &QAction::triggered, 
this, &QgsDualView::autosizeColumn );
 
  870   optimizeWidth->setData( col );
 
  871   mHorizontalHeaderMenu->addAction( optimizeWidth );
 
  873   mHorizontalHeaderMenu->addSeparator();
 
  874   QAction *organize = 
new QAction( tr( 
"&Organize Columns…" ), mHorizontalHeaderMenu );
 
  875   connect( organize, &QAction::triggered, 
this, &QgsDualView::organizeColumns );
 
  876   mHorizontalHeaderMenu->addAction( organize );
 
  877   QAction *sort = 
new QAction( tr( 
"&Sort…" ), mHorizontalHeaderMenu );
 
  878   connect( sort, &QAction::triggered, 
this, [ = ]() {modifySort();} );
 
  879   mHorizontalHeaderMenu->addAction( sort );
 
  881   mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
 
  884 void QgsDualView::organizeColumns()
 
  892   if ( dialog.exec() == QDialog::Accepted )
 
  899 void QgsDualView::tableColumnResized( 
int column, 
int width )
 
  903   if ( sourceCol >= 0 && config.
columnWidth( sourceCol ) != width )
 
  910 void QgsDualView::hideColumn()
 
  912   QAction *action = qobject_cast<QAction *>( sender() );
 
  913   int col = action->data().toInt();
 
  916   if ( sourceCol >= 0 )
 
  923 void QgsDualView::resizeColumn()
 
  925   QAction *action = qobject_cast<QAction *>( sender() );
 
  926   int col = action->data().toInt();
 
  932   if ( sourceCol >= 0 )
 
  935     int width = QInputDialog::getInt( 
this, tr( 
"Set column width" ), tr( 
"Enter column width" ),
 
  936                                       mTableView->columnWidth( col ),
 
  946 void QgsDualView::autosizeColumn()
 
  948   QAction *action = qobject_cast<QAction *>( sender() );
 
  949   int col = action->data().toInt();
 
  950   mTableView->resizeColumnToContents( col );
 
  953 bool QgsDualView::modifySort()
 
  961   orderByDlg.setWindowTitle( tr( 
"Configure Attribute Table Sort Order" ) );
 
  962   QDialogButtonBox *dialogButtonBox = 
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
 
  963   QGridLayout *layout = 
new QGridLayout();
 
  964   connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
 
  965   connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
 
  966   orderByDlg.setLayout( layout );
 
  968   QGroupBox *sortingGroupBox = 
new QGroupBox();
 
  969   sortingGroupBox->setTitle( tr( 
"Defined sort order in attribute table" ) );
 
  970   sortingGroupBox->setCheckable( 
true );
 
  972   layout->addWidget( sortingGroupBox );
 
  973   sortingGroupBox->setLayout( 
new QGridLayout() );
 
  978   expressionBuilder->
initWithLayer( mLayer, context, QStringLiteral( 
"generic" ) );
 
  981   sortingGroupBox->layout()->addWidget( expressionBuilder );
 
  983   QCheckBox *cbxSortAscending = 
new QCheckBox( tr( 
"Sort ascending" ) );
 
  984   cbxSortAscending->setChecked( config.
sortOrder() == Qt::AscendingOrder );
 
  985   sortingGroupBox->layout()->addWidget( cbxSortAscending );
 
  987   layout->addWidget( dialogButtonBox );
 
  988   if ( orderByDlg.exec() )
 
  990     Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
 
  991     if ( sortingGroupBox->isChecked() )
 
 1013 void QgsDualView::zoomToCurrentFeature()
 
 1015   QModelIndex currentIndex = mTableView->currentIndex();
 
 1016   if ( !currentIndex.isValid() )
 
 1022   ids.insert( mFilterModel->
rowToId( currentIndex ) );
 
 1030 void QgsDualView::panToCurrentFeature()
 
 1032   QModelIndex currentIndex = mTableView->currentIndex();
 
 1033   if ( !currentIndex.isValid() )
 
 1039   ids.insert( mFilterModel->
rowToId( currentIndex ) );
 
 1047 void QgsDualView::flashCurrentFeature()
 
 1049   QModelIndex currentIndex = mTableView->currentIndex();
 
 1050   if ( !currentIndex.isValid() )
 
 1056   ids.insert( mFilterModel->
rowToId( currentIndex ) );
 
 1064 void QgsDualView::rebuildFullLayerCache()
 
 1072 void QgsDualView::previewExpressionChanged( 
const QString &expression )
 
 1074   mLayer->setDisplayExpression( expression );
 
 1077 void QgsDualView::onSortColumnChanged()
 
 1081        cfg.
sortOrder() != mFilterModel->sortOrder() )
 
 1089 void QgsDualView::updateSelectedFeatures()
 
 1101 void QgsDualView::extentChanged()
 
 1114 void QgsDualView::featureFormAttributeChanged( 
const QString &attribute, 
const QVariant &value, 
bool attributeChanged )
 
 1116   Q_UNUSED( attribute )
 
 1118   if ( attributeChanged )
 
 1119     mFeatureListView->setCurrentFeatureEdited( 
true );
 
 1141   mTableView->setFeatureSelectionManager( featureSelectionManager );
 
 1142   mFeatureListView->setFeatureSelectionManager( featureSelectionManager );
 
 1144   if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == 
this )
 
 1145     delete mFeatureSelectionManager;
 
 1147   mFeatureSelectionManager = featureSelectionManager;
 
 1153   mConfig.
update( mLayer->fields() );
 
 1154   mLayer->setAttributeTableConfig( mConfig );
 
 1156   mTableView->setAttributeTableConfig( mConfig );
 
 1162     mFilterModel->
sort( -1 );
 
 1181 void QgsDualView::progress( 
int i, 
bool &cancel )
 
 1183   if ( !mProgressDlg )
 
 1185     mProgressDlg = 
new QProgressDialog( tr( 
"Loading features…" ), tr( 
"Abort" ), 0, 0, 
this );
 
 1186     mProgressDlg->setWindowTitle( tr( 
"Attribute Table" ) );
 
 1187     mProgressDlg->setWindowModality( Qt::WindowModal );
 
 1188     mProgressDlg->show();
 
 1191   mProgressDlg->setLabelText( tr( 
"%1 features loaded." ).arg( i ) );
 
 1192   QCoreApplication::processEvents();
 
 1194   cancel = mProgressDlg && mProgressDlg->wasCanceled();
 
 1197 void QgsDualView::finished()
 
 1199   delete mProgressDlg;
 
 1200   mProgressDlg = 
nullptr;