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 );
118 const auto constColumns = config.
columns();
119 QMap<QString, int> columns;
122 if ( columnConfig.hidden )
125 if ( columnConfig.width >= 0 )
127 setColumnWidth( i, columnConfig.width );
131 setColumnWidth( i, horizontalHeader()->defaultSectionSize() );
133 columns.insert( columnConfig.name, i );
139 horizontalHeader()->setSortIndicatorShown(
false );
149 horizontalHeader()->setSortIndicatorShown(
true );
150 horizontalHeader()->setSortIndicator( columns.value( refCols.constFirst() ), config.
sortOrder() );
154 horizontalHeader()->setSortIndicatorShown(
false );
166 const QgsFeatureIds featureIds = mFeatureSelectionManager->selectedFeatureIds();
167 QModelIndexList indexList;
170 const QModelIndex index = mFilterModel->fidToIndex(
id );
174 std::sort( indexList.begin(), indexList.end() );
175 QList<QgsFeatureId> ids;
176 for (
const QModelIndex &index : indexList )
186 mFilterModel = filterModel;
187 QTableView::setModel( mFilterModel );
191 connect( mFilterModel, &QObject::destroyed,
this, &QgsAttributeTableView::modelDeleted );
195 delete mFeatureSelectionModel;
196 mFeatureSelectionModel =
nullptr;
200 if ( !mFeatureSelectionManager )
203 mFeatureSelectionManager = mOwnedFeatureSelectionManager;
206 mFeatureSelectionModel =
new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
207 setSelectionModel( mFeatureSelectionModel );
208 mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
220 mFeatureSelectionManager = featureSelectionManager;
222 if ( mFeatureSelectionModel )
223 mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
226 if ( mOwnedFeatureSelectionManager )
228 mOwnedFeatureSelectionManager->deleteLater();
229 mOwnedFeatureSelectionManager =
nullptr;
233QWidget *QgsAttributeTableView::createActionWidget(
QgsFeatureId fid )
237 QToolButton *toolButton =
nullptr;
238 QWidget *container =
nullptr;
242 toolButton =
new QToolButton();
243 toolButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
244 toolButton->setPopupMode( QToolButton::MenuButtonPopup );
245 container = toolButton;
249 container =
new QWidget();
250 container->setLayout(
new QHBoxLayout() );
251 container->layout()->setContentsMargins( 0, 0, 0, 0 );
254 QList<QAction *> actionList;
255 QAction *defaultAction =
nullptr;
258 const QList<QgsAction> actions = mFilterModel->layer()->actions()->actions( u
"Feature"_s );
259 const auto constActions = actions;
260 for (
const QgsAction &action : constActions )
262 if ( !mFilterModel->layer()->isEditable() && action.isEnabledOnlyWhenEditable() )
265 const QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name()
267 QAction *act =
new QAction( action.icon(), actionTitle, container );
268 act->setToolTip( action.name() );
269 act->setData(
"user_action" );
270 act->setProperty(
"fid", fid );
271 act->setProperty(
"action_id", action.id() );
272 connect( act, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
275 if ( mFilterModel->layer()->actions()->defaultAction( u
"Feature"_s ).id() == action.id() )
279 QgsMapLayerActionContext context;
282 for ( QgsMapLayerAction *mapLayerAction : mapLayerActions )
284 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
285 action->setData(
"map_layer_action" );
286 action->setToolTip( mapLayerAction->text() );
287 action->setProperty(
"fid", fid );
288 action->setProperty(
"action", QVariant::fromValue( qobject_cast<QObject *>( mapLayerAction ) ) );
289 connect( action, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
290 actionList << action;
293 defaultAction = action;
296 if ( !defaultAction && !actionList.isEmpty() )
297 defaultAction = actionList.at( 0 );
299 const auto constActionList = actionList;
300 for ( QAction *act : constActionList )
304 toolButton->addAction( act );
306 if ( act == defaultAction )
307 toolButton->setDefaultAction( act );
309 container = toolButton;
313 QToolButton *btn =
new QToolButton;
314 btn->setDefaultAction( act );
315 container->layout()->addWidget( btn );
321 static_cast<QHBoxLayout *
>( container->layout() )->addStretch();
326 if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
327 toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
337 settings.
setValue( u
"BetterAttributeTable/geometry"_s, QVariant( saveGeometry() ) );
342 setSelectionMode( QAbstractItemView::NoSelection );
343 QTableView::mousePressEvent( event );
344 setSelectionMode( QAbstractItemView::ExtendedSelection );
349 setSelectionMode( QAbstractItemView::NoSelection );
350 QTableView::mouseReleaseEvent( event );
351 setSelectionMode( QAbstractItemView::ExtendedSelection );
352 if ( event->modifiers() == Qt::ControlModifier )
354 const QModelIndex index = indexAt( event->pos() );
355 const QVariant data = model()->data( index, Qt::DisplayRole );
356 if ( data.userType() == QMetaType::Type::QString )
358 const QString textVal = data.toString();
361 QDesktopServices::openUrl( QUrl( textVal ) );
369 setSelectionMode( QAbstractItemView::NoSelection );
370 QTableView::mouseMoveEvent( event );
371 setSelectionMode( QAbstractItemView::ExtendedSelection );
376 switch ( event->key() )
384 setSelectionMode( QAbstractItemView::NoSelection );
385 QTableView::keyPressEvent( event );
386 setSelectionMode( QAbstractItemView::ExtendedSelection );
390 QTableView::keyPressEvent( event );
397 const auto constIndexes = indexes;
398 for (
const QModelIndex &index : constIndexes )
406 setDirtyRegion( viewport()->rect() );
411 QItemSelection selection;
412 selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
413 mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
419 mActionPopup =
nullptr;
421 const QModelIndex idx = mFilterModel->mapToMaster( indexAt( event->pos() ) );
422 if ( !idx.isValid() )
431 mActionPopup =
new QMenu(
this );
433 QAction *selectAllAction = mActionPopup->addAction( tr(
"Select All" ) );
434 selectAllAction->setShortcut( QKeySequence::SelectAll );
440 if ( !mActionPopup->actions().isEmpty() )
442 mActionPopup->popup( event->globalPos() );
456void QgsAttributeTableView::modelDeleted()
458 mFilterModel =
nullptr;
459 mFeatureSelectionManager =
nullptr;
460 mFeatureSelectionModel =
nullptr;
465 if ( selectionBehavior() == QTableView::SelectColumns
466 || ( selectionMode() == QTableView::SingleSelection && selectionBehavior() == QTableView::SelectItems ) )
469 if ( row >= 0 && row < model()->rowCount() )
471 const int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
472 const QModelIndex index = model()->index( row, column );
473 QItemSelectionModel::SelectionFlags command = selectionCommand( index );
474 selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
475 if ( ( anchor && !( command & QItemSelectionModel::Current ) )
476 || ( selectionMode() == QTableView::SingleSelection ) )
477 mRowSectionAnchor = row;
479 if ( selectionMode() != QTableView::SingleSelection
480 && command.testFlag( QItemSelectionModel::Toggle ) )
483 mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
484 ? QItemSelectionModel::Deselect
485 : QItemSelectionModel::Select;
486 command &= ~QItemSelectionModel::Toggle;
487 command |= mCtrlDragSelectionFlag;
489 command |= QItemSelectionModel::Current;
492 const QModelIndex tl = model()->index( std::min( mRowSectionAnchor, row ), 0 );
493 const QModelIndex br = model()->index( std::max( mRowSectionAnchor, row ), model()->columnCount() - 1 );
494 if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
495 setSelection( visualRect( tl ) | visualRect( br ), command );
497 mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
501void QgsAttributeTableView::showHorizontalSortIndicator()
503 horizontalHeader()->setSortIndicatorShown(
true );
506void QgsAttributeTableView::actionTriggered()
508 QAction *action = qobject_cast<QAction *>( sender() );
509 const QgsFeatureId fid = action->property(
"fid" ).toLongLong();
512 mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
514 if ( action->data().toString() ==
"user_action"_L1 )
516 mFilterModel->layer()->actions()->doAction( action->property(
"action_id" ).toUuid(), f );
518 else if ( action->data().toString() ==
"map_layer_action"_L1 )
520 QObject *
object = action->property(
"action" ).value<QObject *>();
521 QgsMapLayerAction *layerAction = qobject_cast<QgsMapLayerAction *>(
object );
524 QgsMapLayerActionContext context;
526 layerAction->triggerForFeature( mFilterModel->layer(), f );
528 layerAction->triggerForFeature( mFilterModel->layer(), f, context );
533void QgsAttributeTableView::columnSizeChanged(
int index,
int oldWidth,
int newWidth )
539void QgsAttributeTableView::onActionColumnItemPainted(
const QModelIndex &index )
541 if ( !indexWidget( index ) )
544 mActionWidgets.insert( index, widget );
545 setIndexWidget( index, widget );
549void QgsAttributeTableView::recreateActionWidgets()
551 QMap<QModelIndex, QWidget *>::const_iterator it = mActionWidgets.constBegin();
552 for ( ; it != mActionWidgets.constEnd(); ++it )
557 setIndexWidget( it.key(),
nullptr );
559 mActionWidgets.clear();
564 const QModelIndex index = mFilterModel->fidToIndex( fid );
566 if ( !index.isValid() )
571 const QModelIndex selectionIndex = index.sibling( index.row(), col );
573 if ( !selectionIndex.isValid() )
576 selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
581 QWidget *editor = indexWidget( currentIndex() );
582 commitData( editor );
583 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.