32#include <QDesktopServices>
40#include "moc_qgsattributetableview.cpp"
42using namespace Qt::StringLiterals;
48 restoreGeometry( settings.
value( u
"BetterAttributeTable/geometry"_s ).toByteArray() );
51 horizontalHeader()->setHighlightSections(
false );
55 setItemDelegate( mTableDelegate );
57 setEditTriggers( QAbstractItemView::AllEditTriggers );
59 setSelectionBehavior( QAbstractItemView::SelectRows );
60 setSelectionMode( QAbstractItemView::ExtendedSelection );
61 setSortingEnabled(
true );
62 horizontalHeader()->setSortIndicatorShown(
false );
64 setHorizontalScrollMode( QAbstractItemView::ScrollPerPixel );
66 verticalHeader()->viewport()->installEventFilter(
this );
68 connect( verticalHeader(), &QHeaderView::sectionPressed,
this, [
this](
int row ) {
selectRow( row,
true ); } );
70 connect( horizontalHeader(), &QHeaderView::sectionResized,
this, &QgsAttributeTableView::columnSizeChanged );
71 connect( horizontalHeader(), &QHeaderView::sortIndicatorChanged,
this, &QgsAttributeTableView::showHorizontalSortIndicator );
77 if (
object == verticalHeader()->viewport() )
79 switch ( event->type() )
81 case QEvent::MouseButtonPress:
82 mFeatureSelectionModel->enableSync(
false );
85 case QEvent::MouseButtonRelease:
86 mFeatureSelectionModel->enableSync(
true );
89 case QEvent::MouseButtonDblClick:
91 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
92 if ( mouseEvent->button() == Qt::LeftButton )
94 const int row = verticalHeader()->logicalIndexAt( mouseEvent->pos() );
95 if ( row >= 0 && mFilterModel )
97 const QModelIndex index = mFilterModel->index( row, 0 );
98 if ( index.isValid() )
100 const QgsFeatureId fid = mFilterModel->rowToId( index );
112 return QTableView::eventFilter(
object, event );
117 if ( mBlockColumnUpdates )
120 ScopedIntIncrementor blockSignals( &mBlockColumnUpdates );
122 const auto constColumns = config.
columns();
123 QMap<QString, int> columns;
126 if ( columnConfig.hidden )
129 if ( columnConfig.width >= 0 )
131 setColumnWidth( i, columnConfig.width );
135 setColumnWidth( i, horizontalHeader()->defaultSectionSize() );
137 columns.insert( columnConfig.name, i );
143 horizontalHeader()->setSortIndicatorShown(
false );
153 horizontalHeader()->setSortIndicatorShown(
true );
154 horizontalHeader()->setSortIndicator( columns.value( refCols.constFirst() ), config.
sortOrder() );
158 horizontalHeader()->setSortIndicatorShown(
false );
170 const QgsFeatureIds featureIds = mFeatureSelectionManager->selectedFeatureIds();
171 QModelIndexList indexList;
174 const QModelIndex index = mFilterModel->fidToIndex(
id );
178 std::sort( indexList.begin(), indexList.end() );
179 QList<QgsFeatureId> ids;
180 for (
const QModelIndex &index : indexList )
190 mFilterModel = filterModel;
191 QTableView::setModel( mFilterModel );
195 connect( mFilterModel, &QObject::destroyed,
this, &QgsAttributeTableView::modelDeleted );
199 delete mFeatureSelectionModel;
200 mFeatureSelectionModel =
nullptr;
204 if ( !mFeatureSelectionManager )
207 mFeatureSelectionManager = mOwnedFeatureSelectionManager;
210 mFeatureSelectionModel =
new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
211 setSelectionModel( mFeatureSelectionModel );
212 mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
214 mFeatureSelectionModel,
229 mFeatureSelectionManager = featureSelectionManager;
231 if ( mFeatureSelectionModel )
232 mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
235 if ( mOwnedFeatureSelectionManager )
237 mOwnedFeatureSelectionManager->deleteLater();
238 mOwnedFeatureSelectionManager =
nullptr;
242QWidget *QgsAttributeTableView::createActionWidget(
QgsFeatureId fid )
246 QToolButton *toolButton =
nullptr;
247 QWidget *container =
nullptr;
251 toolButton =
new QToolButton();
252 toolButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
253 toolButton->setPopupMode( QToolButton::MenuButtonPopup );
254 container = toolButton;
258 container =
new QWidget();
259 container->setLayout(
new QHBoxLayout() );
260 container->layout()->setContentsMargins( 0, 0, 0, 0 );
263 QList<QAction *> actionList;
264 QAction *defaultAction =
nullptr;
267 const QList<QgsAction> actions = mFilterModel->layer()->actions()->actions( u
"Feature"_s );
268 const auto constActions = actions;
269 for (
const QgsAction &action : constActions )
271 if ( !mFilterModel->layer()->isEditable() && action.isEnabledOnlyWhenEditable() )
274 const QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name() : QString();
275 QAction *act =
new QAction( action.icon(), actionTitle, container );
276 act->setToolTip( action.name() );
277 act->setData(
"user_action" );
278 act->setProperty(
"fid", fid );
279 act->setProperty(
"action_id", action.id() );
280 connect( act, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
283 if ( mFilterModel->layer()->actions()->defaultAction( u
"Feature"_s ).id() == action.id() )
287 QgsMapLayerActionContext context;
290 for ( QgsMapLayerAction *mapLayerAction : mapLayerActions )
292 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
293 action->setData(
"map_layer_action" );
294 action->setToolTip( mapLayerAction->text() );
295 action->setProperty(
"fid", fid );
296 action->setProperty(
"action", QVariant::fromValue( qobject_cast<QObject *>( mapLayerAction ) ) );
297 connect( action, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
298 actionList << action;
301 defaultAction = action;
304 if ( !defaultAction && !actionList.isEmpty() )
305 defaultAction = actionList.at( 0 );
307 const auto constActionList = actionList;
308 for ( QAction *act : constActionList )
312 toolButton->addAction( act );
314 if ( act == defaultAction )
315 toolButton->setDefaultAction( act );
317 container = toolButton;
321 QToolButton *btn =
new QToolButton;
322 btn->setDefaultAction( act );
323 container->layout()->addWidget( btn );
329 static_cast<QHBoxLayout *
>( container->layout() )->addStretch();
334 if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
335 toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
345 settings.
setValue( u
"BetterAttributeTable/geometry"_s, QVariant( saveGeometry() ) );
350 setSelectionMode( QAbstractItemView::NoSelection );
351 QTableView::mousePressEvent( event );
352 setSelectionMode( QAbstractItemView::ExtendedSelection );
357 setSelectionMode( QAbstractItemView::NoSelection );
358 QTableView::mouseReleaseEvent( event );
359 setSelectionMode( QAbstractItemView::ExtendedSelection );
360 if ( event->modifiers() == Qt::ControlModifier )
362 const QModelIndex index = indexAt( event->pos() );
363 const QVariant data = model()->data( index, Qt::DisplayRole );
364 if ( data.userType() == QMetaType::Type::QString )
366 const QString textVal = data.toString();
369 QDesktopServices::openUrl( QUrl( textVal ) );
377 setSelectionMode( QAbstractItemView::NoSelection );
378 QTableView::mouseMoveEvent( event );
379 setSelectionMode( QAbstractItemView::ExtendedSelection );
384 switch ( event->key() )
392 setSelectionMode( QAbstractItemView::NoSelection );
393 QTableView::keyPressEvent( event );
394 setSelectionMode( QAbstractItemView::ExtendedSelection );
398 QTableView::keyPressEvent( event );
405 const auto constIndexes = indexes;
406 for (
const QModelIndex &index : constIndexes )
414 setDirtyRegion( viewport()->rect() );
419 QItemSelection selection;
420 selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
421 mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
427 mActionPopup =
nullptr;
429 const QModelIndex idx = mFilterModel->mapToMaster( indexAt( event->pos() ) );
430 if ( !idx.isValid() )
439 mActionPopup =
new QMenu(
this );
441 QAction *selectAllAction = mActionPopup->addAction( tr(
"Select All" ) );
442 selectAllAction->setShortcut( QKeySequence::SelectAll );
448 if ( !mActionPopup->actions().isEmpty() )
450 mActionPopup->popup( event->globalPos() );
464void QgsAttributeTableView::modelDeleted()
466 mFilterModel =
nullptr;
467 mFeatureSelectionManager =
nullptr;
468 mFeatureSelectionModel =
nullptr;
473 if ( selectionBehavior() == QTableView::SelectColumns || ( selectionMode() == QTableView::SingleSelection && selectionBehavior() == QTableView::SelectItems ) )
476 if ( row >= 0 && row < model()->rowCount() )
478 const int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
479 const QModelIndex index = model()->index( row, column );
480 QItemSelectionModel::SelectionFlags command = selectionCommand( index );
481 selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
482 if ( ( anchor && !( command & QItemSelectionModel::Current ) ) || ( selectionMode() == QTableView::SingleSelection ) )
483 mRowSectionAnchor = row;
485 if ( selectionMode() != QTableView::SingleSelection && command.testFlag( QItemSelectionModel::Toggle ) )
488 mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index ) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
489 command &= ~QItemSelectionModel::Toggle;
490 command |= mCtrlDragSelectionFlag;
492 command |= QItemSelectionModel::Current;
495 const QModelIndex tl = model()->index( std::min( mRowSectionAnchor, row ), 0 );
496 const QModelIndex br = model()->index( std::max( mRowSectionAnchor, row ), model()->columnCount() - 1 );
497 if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
498 setSelection( visualRect( tl ) | visualRect( br ), command );
500 mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
504void QgsAttributeTableView::showHorizontalSortIndicator()
506 horizontalHeader()->setSortIndicatorShown(
true );
509void QgsAttributeTableView::actionTriggered()
511 QAction *action = qobject_cast<QAction *>( sender() );
512 const QgsFeatureId fid = action->property(
"fid" ).toLongLong();
515 mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
517 if ( action->data().toString() ==
"user_action"_L1 )
519 mFilterModel->layer()->actions()->doAction( action->property(
"action_id" ).toUuid(), f );
521 else if ( action->data().toString() ==
"map_layer_action"_L1 )
523 QObject *
object = action->property(
"action" ).value<QObject *>();
524 QgsMapLayerAction *layerAction = qobject_cast<QgsMapLayerAction *>(
object );
527 QgsMapLayerActionContext context;
529 layerAction->triggerForFeature( mFilterModel->layer(), f );
531 layerAction->triggerForFeature( mFilterModel->layer(), f, context );
536void QgsAttributeTableView::columnSizeChanged(
int index,
int oldWidth,
int newWidth )
538 if ( mBlockColumnUpdates )
543 ScopedIntIncrementor blockSignals( &mBlockColumnUpdates );
547void QgsAttributeTableView::onActionColumnItemPainted(
const QModelIndex &index )
549 if ( !indexWidget( index ) )
552 mActionWidgets.insert( index, widget );
553 setIndexWidget( index, widget );
557void QgsAttributeTableView::recreateActionWidgets()
559 QMap<QModelIndex, QWidget *>::const_iterator it = mActionWidgets.constBegin();
560 for ( ; it != mActionWidgets.constEnd(); ++it )
565 setIndexWidget( it.key(),
nullptr );
567 mActionWidgets.clear();
572 const QModelIndex index = mFilterModel->fidToIndex( fid );
574 if ( !index.isValid() )
579 const QModelIndex selectionIndex = index.sibling( index.row(), col );
581 if ( !selectionIndex.isValid() )
584 selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
589 QWidget *editor = indexWidget( currentIndex() );
590 commitData( editor );
591 closeEditor( editor, QAbstractItemDelegate::NoHint );
@ SingleFeature
Action targets a single feature from a layer.
A container for configuration of the attribute table.
Qt::SortOrder sortOrder() const
Gets the sort order.
QVector< QgsAttributeTableConfig::ColumnConfig > columns() const
Gets the list with all columns and their configuration.
@ DropDown
A tool button with a drop-down to select the current action.
@ ButtonList
A list of buttons.
ActionWidgetStyle actionWidgetStyle() const
Gets the style of the action widget.
QString sortExpression() const
Gets the expression used for sorting.
A delegate item class for QgsAttributeTable (see Qt documentation for QItemDelegate).
void actionColumnItemPainted(const QModelIndex &index) const
Emitted when an action column item is painted.
A proxy model for filtering an attribute table model.
@ FeatureId
Get the feature id of the feature in this row.
void willShowContextMenu(QMenu *menu, const QModelIndex &atIndex)
Emitted in order to provide a hook to add additional* menu entries to the context menu.
QList< QgsFeatureId > selectedFeaturesIds() const
Returns the selected features in the attribute table in table sorted order.
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
setFeatureSelectionManager
void mouseMoveEvent(QMouseEvent *event) override
Called for mouse move events on a table cell.
virtual void selectRow(int row)
QgsAttributeTableView(QWidget *parent=nullptr)
Constructor for QgsAttributeTableView.
void selectAll() override
void rowHeaderDoubleClicked(QgsFeatureId fid)
Emitted when a row header is double-clicked.
void scrollToFeature(const QgsFeatureId &fid, int column=-1)
Scroll to a feature with a given fid.
void mouseReleaseEvent(QMouseEvent *event) override
Called for mouse release events on a table cell.
void contextMenuEvent(QContextMenuEvent *event) override
Is called when the context menu will be shown.
virtual void _q_selectRow(int row)
void closeEvent(QCloseEvent *event) override
Saves geometry to the settings on close.
void mousePressEvent(QMouseEvent *event) override
Called for mouse press events on a table cell.
void closeCurrentEditor()
Closes the editor delegate for the current item, committing its changes to the model.
void keyPressEvent(QKeyEvent *event) override
Called for key press events Disables selection change by only pressing an arrow key.
void setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table config which should be used to control the appearance of the attribute table.
void columnResized(int column, int width)
Emitted when a column in the view has been resized.
void repaintRequested(const QModelIndexList &indexes)
bool eventFilter(QObject *object, QEvent *event) override
This event filter is installed on the verticalHeader to intercept mouse press and release events.
virtual void setModel(QgsAttributeTableFilterModel *filterModel)
Handles parsing and evaluation of expressions (formerly called "search strings").
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Item selection model for selecting features.
void requestRepaint(const QModelIndexList &indexes)
Request a repaint of a list of model indexes.
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Is an interface class to abstract feature selection handling.
void changed()
Triggered when an action is added or removed from the registry.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, Qgis::MapLayerActionTargets targets=Qgis::MapLayerActionTarget::AllActions, const QgsMapLayerActionContext &context=QgsMapLayerActionContext())
Returns the map layer actions which can run on the specified layer.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static bool isUrl(const QString &string)
Returns whether the string is a URL (http,https,ftp,file).
QgsTableView(QWidget *parent=nullptr)
Constructor for QgsTableView.
Manages vector layer selections.
Represents a vector layer which manages a vector based dataset.
void readOnlyChanged()
Emitted when the read only state of this layer is changed.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Defines the configuration of a column in the attribute table.