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 );
210 mFeatureSelectionModel,
225 mFeatureSelectionManager = featureSelectionManager;
227 if ( mFeatureSelectionModel )
228 mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
231 if ( mOwnedFeatureSelectionManager )
233 mOwnedFeatureSelectionManager->deleteLater();
234 mOwnedFeatureSelectionManager =
nullptr;
238QWidget *QgsAttributeTableView::createActionWidget(
QgsFeatureId fid )
242 QToolButton *toolButton =
nullptr;
243 QWidget *container =
nullptr;
247 toolButton =
new QToolButton();
248 toolButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
249 toolButton->setPopupMode( QToolButton::MenuButtonPopup );
250 container = toolButton;
254 container =
new QWidget();
255 container->setLayout(
new QHBoxLayout() );
256 container->layout()->setContentsMargins( 0, 0, 0, 0 );
259 QList<QAction *> actionList;
260 QAction *defaultAction =
nullptr;
263 const QList<QgsAction> actions = mFilterModel->layer()->actions()->actions( u
"Feature"_s );
264 const auto constActions = actions;
265 for (
const QgsAction &action : constActions )
267 if ( !mFilterModel->layer()->isEditable() && action.isEnabledOnlyWhenEditable() )
270 const QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name() : QString();
271 QAction *act =
new QAction( action.icon(), actionTitle, container );
272 act->setToolTip( action.name() );
273 act->setData(
"user_action" );
274 act->setProperty(
"fid", fid );
275 act->setProperty(
"action_id", action.id() );
276 connect( act, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
279 if ( mFilterModel->layer()->actions()->defaultAction( u
"Feature"_s ).id() == action.id() )
283 QgsMapLayerActionContext context;
286 for ( QgsMapLayerAction *mapLayerAction : mapLayerActions )
288 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
289 action->setData(
"map_layer_action" );
290 action->setToolTip( mapLayerAction->text() );
291 action->setProperty(
"fid", fid );
292 action->setProperty(
"action", QVariant::fromValue( qobject_cast<QObject *>( mapLayerAction ) ) );
293 connect( action, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
294 actionList << action;
297 defaultAction = action;
300 if ( !defaultAction && !actionList.isEmpty() )
301 defaultAction = actionList.at( 0 );
303 const auto constActionList = actionList;
304 for ( QAction *act : constActionList )
308 toolButton->addAction( act );
310 if ( act == defaultAction )
311 toolButton->setDefaultAction( act );
313 container = toolButton;
317 QToolButton *btn =
new QToolButton;
318 btn->setDefaultAction( act );
319 container->layout()->addWidget( btn );
325 static_cast<QHBoxLayout *
>( container->layout() )->addStretch();
330 if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
331 toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
341 settings.
setValue( u
"BetterAttributeTable/geometry"_s, QVariant( saveGeometry() ) );
346 setSelectionMode( QAbstractItemView::NoSelection );
347 QTableView::mousePressEvent( event );
348 setSelectionMode( QAbstractItemView::ExtendedSelection );
353 setSelectionMode( QAbstractItemView::NoSelection );
354 QTableView::mouseReleaseEvent( event );
355 setSelectionMode( QAbstractItemView::ExtendedSelection );
356 if ( event->modifiers() == Qt::ControlModifier )
358 const QModelIndex index = indexAt( event->pos() );
359 const QVariant data = model()->data( index, Qt::DisplayRole );
360 if ( data.userType() == QMetaType::Type::QString )
362 const QString textVal = data.toString();
365 QDesktopServices::openUrl( QUrl( textVal ) );
373 setSelectionMode( QAbstractItemView::NoSelection );
374 QTableView::mouseMoveEvent( event );
375 setSelectionMode( QAbstractItemView::ExtendedSelection );
380 switch ( event->key() )
388 setSelectionMode( QAbstractItemView::NoSelection );
389 QTableView::keyPressEvent( event );
390 setSelectionMode( QAbstractItemView::ExtendedSelection );
394 QTableView::keyPressEvent( event );
401 const auto constIndexes = indexes;
402 for (
const QModelIndex &index : constIndexes )
410 setDirtyRegion( viewport()->rect() );
415 QItemSelection selection;
416 selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
417 mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
423 mActionPopup =
nullptr;
425 const QModelIndex idx = mFilterModel->mapToMaster( indexAt( event->pos() ) );
426 if ( !idx.isValid() )
435 mActionPopup =
new QMenu(
this );
437 QAction *selectAllAction = mActionPopup->addAction( tr(
"Select All" ) );
438 selectAllAction->setShortcut( QKeySequence::SelectAll );
444 if ( !mActionPopup->actions().isEmpty() )
446 mActionPopup->popup( event->globalPos() );
460void QgsAttributeTableView::modelDeleted()
462 mFilterModel =
nullptr;
463 mFeatureSelectionManager =
nullptr;
464 mFeatureSelectionModel =
nullptr;
469 if ( selectionBehavior() == QTableView::SelectColumns || ( selectionMode() == QTableView::SingleSelection && selectionBehavior() == QTableView::SelectItems ) )
472 if ( row >= 0 && row < model()->rowCount() )
474 const int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
475 const QModelIndex index = model()->index( row, column );
476 QItemSelectionModel::SelectionFlags command = selectionCommand( index );
477 selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
478 if ( ( anchor && !( command & QItemSelectionModel::Current ) ) || ( selectionMode() == QTableView::SingleSelection ) )
479 mRowSectionAnchor = row;
481 if ( selectionMode() != QTableView::SingleSelection && command.testFlag( QItemSelectionModel::Toggle ) )
484 mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index ) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
485 command &= ~QItemSelectionModel::Toggle;
486 command |= mCtrlDragSelectionFlag;
488 command |= QItemSelectionModel::Current;
491 const QModelIndex tl = model()->index( std::min( mRowSectionAnchor, row ), 0 );
492 const QModelIndex br = model()->index( std::max( mRowSectionAnchor, row ), model()->columnCount() - 1 );
493 if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
494 setSelection( visualRect( tl ) | visualRect( br ), command );
496 mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
500void QgsAttributeTableView::showHorizontalSortIndicator()
502 horizontalHeader()->setSortIndicatorShown(
true );
505void QgsAttributeTableView::actionTriggered()
507 QAction *action = qobject_cast<QAction *>( sender() );
508 const QgsFeatureId fid = action->property(
"fid" ).toLongLong();
511 mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
513 if ( action->data().toString() ==
"user_action"_L1 )
515 mFilterModel->layer()->actions()->doAction( action->property(
"action_id" ).toUuid(), f );
517 else if ( action->data().toString() ==
"map_layer_action"_L1 )
519 QObject *
object = action->property(
"action" ).value<QObject *>();
520 QgsMapLayerAction *layerAction = qobject_cast<QgsMapLayerAction *>(
object );
523 QgsMapLayerActionContext context;
525 layerAction->triggerForFeature( mFilterModel->layer(), f );
527 layerAction->triggerForFeature( mFilterModel->layer(), f, context );
532void QgsAttributeTableView::columnSizeChanged(
int index,
int oldWidth,
int newWidth )
538void QgsAttributeTableView::onActionColumnItemPainted(
const QModelIndex &index )
540 if ( !indexWidget( index ) )
543 mActionWidgets.insert( index, widget );
544 setIndexWidget( index, widget );
548void QgsAttributeTableView::recreateActionWidgets()
550 QMap<QModelIndex, QWidget *>::const_iterator it = mActionWidgets.constBegin();
551 for ( ; it != mActionWidgets.constEnd(); ++it )
556 setIndexWidget( it.key(),
nullptr );
558 mActionWidgets.clear();
563 const QModelIndex index = mFilterModel->fidToIndex( fid );
565 if ( !index.isValid() )
570 const QModelIndex selectionIndex = index.sibling( index.row(), col );
572 if ( !selectionIndex.isValid() )
575 selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
580 QWidget *editor = indexWidget( currentIndex() );
581 commitData( editor );
582 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.