32#include <QDesktopServices>
39#include "moc_qgsattributetableview.cpp"
45 restoreGeometry( settings.
value( QStringLiteral(
"BetterAttributeTable/geometry" ) ).toByteArray() );
48 horizontalHeader()->setHighlightSections(
false );
52 setItemDelegate( mTableDelegate );
54 setEditTriggers( QAbstractItemView::AllEditTriggers );
56 setSelectionBehavior( QAbstractItemView::SelectRows );
57 setSelectionMode( QAbstractItemView::ExtendedSelection );
58 setSortingEnabled(
true );
59 horizontalHeader()->setSortIndicatorShown(
false );
61 setHorizontalScrollMode( QAbstractItemView::ScrollPerPixel );
63 verticalHeader()->viewport()->installEventFilter(
this );
65 connect( verticalHeader(), &QHeaderView::sectionPressed,
this, [
this](
int row ) {
selectRow( row,
true ); } );
67 connect( horizontalHeader(), &QHeaderView::sectionResized,
this, &QgsAttributeTableView::columnSizeChanged );
68 connect( horizontalHeader(), &QHeaderView::sortIndicatorChanged,
this, &QgsAttributeTableView::showHorizontalSortIndicator );
74 if (
object == verticalHeader()->viewport() )
76 switch ( event->type() )
78 case QEvent::MouseButtonPress:
79 mFeatureSelectionModel->enableSync(
false );
82 case QEvent::MouseButtonRelease:
83 mFeatureSelectionModel->enableSync(
true );
86 case QEvent::MouseButtonDblClick:
88 QMouseEvent *mouseEvent =
static_cast<QMouseEvent *
>( event );
89 if ( mouseEvent->button() == Qt::LeftButton )
91 const int row = verticalHeader()->logicalIndexAt( mouseEvent->pos() );
92 if ( row >= 0 && mFilterModel )
94 const QModelIndex index = mFilterModel->index( row, 0 );
95 if ( index.isValid() )
109 return QTableView::eventFilter(
object, event );
115 const auto constColumns = config.
columns();
116 QMap<QString, int> columns;
119 if ( columnConfig.hidden )
122 if ( columnConfig.width >= 0 )
124 setColumnWidth( i, columnConfig.width );
128 setColumnWidth( i, horizontalHeader()->defaultSectionSize() );
130 columns.insert( columnConfig.name, i );
136 horizontalHeader()->setSortIndicatorShown(
false );
146 horizontalHeader()->setSortIndicatorShown(
true );
147 horizontalHeader()->setSortIndicator( columns.value( refCols.constFirst() ), config.
sortOrder() );
151 horizontalHeader()->setSortIndicatorShown(
false );
163 const QgsFeatureIds featureIds = mFeatureSelectionManager->selectedFeatureIds();
164 QModelIndexList indexList;
167 const QModelIndex index = mFilterModel->fidToIndex(
id );
171 std::sort( indexList.begin(), indexList.end() );
172 QList<QgsFeatureId> ids;
173 for (
const QModelIndex &index : indexList )
183 mFilterModel = filterModel;
184 QTableView::setModel( mFilterModel );
188 connect( mFilterModel, &QObject::destroyed,
this, &QgsAttributeTableView::modelDeleted );
192 delete mFeatureSelectionModel;
193 mFeatureSelectionModel =
nullptr;
197 if ( !mFeatureSelectionManager )
200 mFeatureSelectionManager = mOwnedFeatureSelectionManager;
203 mFeatureSelectionModel =
new QgsFeatureSelectionModel( mFilterModel, mFilterModel, mFeatureSelectionManager, mFilterModel );
204 setSelectionModel( mFeatureSelectionModel );
205 mTableDelegate->setFeatureSelectionModel( mFeatureSelectionModel );
217 mFeatureSelectionManager = featureSelectionManager;
219 if ( mFeatureSelectionModel )
220 mFeatureSelectionModel->setFeatureSelectionManager( mFeatureSelectionManager );
223 if ( mOwnedFeatureSelectionManager )
225 mOwnedFeatureSelectionManager->deleteLater();
226 mOwnedFeatureSelectionManager =
nullptr;
230QWidget *QgsAttributeTableView::createActionWidget(
QgsFeatureId fid )
234 QToolButton *toolButton =
nullptr;
235 QWidget *container =
nullptr;
239 toolButton =
new QToolButton();
240 toolButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
241 toolButton->setPopupMode( QToolButton::MenuButtonPopup );
242 container = toolButton;
246 container =
new QWidget();
247 container->setLayout(
new QHBoxLayout() );
248 container->layout()->setContentsMargins( 0, 0, 0, 0 );
251 QList<QAction *> actionList;
252 QAction *defaultAction =
nullptr;
255 const QList<QgsAction> actions = mFilterModel->layer()->actions()->actions( QStringLiteral(
"Feature" ) );
256 const auto constActions = actions;
257 for (
const QgsAction &action : constActions )
259 if ( !mFilterModel->layer()->isEditable() && action.isEnabledOnlyWhenEditable() )
262 const QString actionTitle = !action.shortTitle().isEmpty() ? action.shortTitle() : action.icon().isNull() ? action.name()
264 QAction *act =
new QAction( action.icon(), actionTitle, container );
265 act->setToolTip( action.name() );
266 act->setData(
"user_action" );
267 act->setProperty(
"fid", fid );
268 act->setProperty(
"action_id", action.id() );
269 connect( act, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
272 if ( mFilterModel->layer()->actions()->defaultAction( QStringLiteral(
"Feature" ) ).
id() == action.id() )
276 QgsMapLayerActionContext context;
279 for ( QgsMapLayerAction *mapLayerAction : mapLayerActions )
281 QAction *action =
new QAction( mapLayerAction->icon(), mapLayerAction->text(), container );
282 action->setData(
"map_layer_action" );
283 action->setToolTip( mapLayerAction->text() );
284 action->setProperty(
"fid", fid );
285 action->setProperty(
"action", QVariant::fromValue( qobject_cast<QObject *>( mapLayerAction ) ) );
286 connect( action, &QAction::triggered,
this, &QgsAttributeTableView::actionTriggered );
287 actionList << action;
290 defaultAction = action;
293 if ( !defaultAction && !actionList.isEmpty() )
294 defaultAction = actionList.at( 0 );
296 const auto constActionList = actionList;
297 for ( QAction *act : constActionList )
301 toolButton->addAction( act );
303 if ( act == defaultAction )
304 toolButton->setDefaultAction( act );
306 container = toolButton;
310 QToolButton *btn =
new QToolButton;
311 btn->setDefaultAction( act );
312 container->layout()->addWidget( btn );
318 static_cast<QHBoxLayout *
>( container->layout() )->addStretch();
323 if ( toolButton && !toolButton->actions().isEmpty() && actions->defaultAction() == -1 )
324 toolButton->setDefaultAction( toolButton->actions().at( 0 ) );
334 settings.
setValue( QStringLiteral(
"BetterAttributeTable/geometry" ), QVariant( saveGeometry() ) );
339 setSelectionMode( QAbstractItemView::NoSelection );
340 QTableView::mousePressEvent( event );
341 setSelectionMode( QAbstractItemView::ExtendedSelection );
346 setSelectionMode( QAbstractItemView::NoSelection );
347 QTableView::mouseReleaseEvent( event );
348 setSelectionMode( QAbstractItemView::ExtendedSelection );
349 if ( event->modifiers() == Qt::ControlModifier )
351 const QModelIndex index = indexAt( event->pos() );
352 const QVariant data = model()->data( index, Qt::DisplayRole );
353 if ( data.userType() == QMetaType::Type::QString )
355 const QString textVal = data.toString();
358 QDesktopServices::openUrl( QUrl( textVal ) );
366 setSelectionMode( QAbstractItemView::NoSelection );
367 QTableView::mouseMoveEvent( event );
368 setSelectionMode( QAbstractItemView::ExtendedSelection );
373 switch ( event->key() )
381 setSelectionMode( QAbstractItemView::NoSelection );
382 QTableView::keyPressEvent( event );
383 setSelectionMode( QAbstractItemView::ExtendedSelection );
387 QTableView::keyPressEvent( event );
394 const auto constIndexes = indexes;
395 for (
const QModelIndex &index : constIndexes )
403 setDirtyRegion( viewport()->rect() );
408 QItemSelection selection;
409 selection.append( QItemSelectionRange( mFilterModel->index( 0, 0 ), mFilterModel->index( mFilterModel->rowCount() - 1, 0 ) ) );
410 mFeatureSelectionModel->selectFeatures( selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
416 mActionPopup =
nullptr;
418 const QModelIndex idx = mFilterModel->mapToMaster( indexAt( event->pos() ) );
419 if ( !idx.isValid() )
428 mActionPopup =
new QMenu(
this );
430 QAction *selectAllAction = mActionPopup->addAction( tr(
"Select All" ) );
431 selectAllAction->setShortcut( QKeySequence::SelectAll );
437 if ( !mActionPopup->actions().isEmpty() )
439 mActionPopup->popup( event->globalPos() );
453void QgsAttributeTableView::modelDeleted()
455 mFilterModel =
nullptr;
456 mFeatureSelectionManager =
nullptr;
457 mFeatureSelectionModel =
nullptr;
462 if ( selectionBehavior() == QTableView::SelectColumns
463 || ( selectionMode() == QTableView::SingleSelection && selectionBehavior() == QTableView::SelectItems ) )
466 if ( row >= 0 && row < model()->rowCount() )
468 const int column = horizontalHeader()->logicalIndexAt( isRightToLeft() ? viewport()->width() : 0 );
469 const QModelIndex index = model()->index( row, column );
470 QItemSelectionModel::SelectionFlags command = selectionCommand( index );
471 selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate );
472 if ( ( anchor && !( command & QItemSelectionModel::Current ) )
473 || ( selectionMode() == QTableView::SingleSelection ) )
474 mRowSectionAnchor = row;
476 if ( selectionMode() != QTableView::SingleSelection
477 && command.testFlag( QItemSelectionModel::Toggle ) )
480 mCtrlDragSelectionFlag = mFeatureSelectionModel->isSelected( index )
481 ? QItemSelectionModel::Deselect
482 : QItemSelectionModel::Select;
483 command &= ~QItemSelectionModel::Toggle;
484 command |= mCtrlDragSelectionFlag;
486 command |= QItemSelectionModel::Current;
489 const QModelIndex tl = model()->index( std::min( mRowSectionAnchor, row ), 0 );
490 const QModelIndex br = model()->index( std::max( mRowSectionAnchor, row ), model()->columnCount() - 1 );
491 if ( verticalHeader()->sectionsMoved() && tl.row() != br.row() )
492 setSelection( visualRect( tl ) | visualRect( br ), command );
494 mFeatureSelectionModel->selectFeatures( QItemSelection( tl, br ), command );
498void QgsAttributeTableView::showHorizontalSortIndicator()
500 horizontalHeader()->setSortIndicatorShown(
true );
503void QgsAttributeTableView::actionTriggered()
505 QAction *action = qobject_cast<QAction *>( sender() );
506 const QgsFeatureId fid = action->property(
"fid" ).toLongLong();
509 mFilterModel->layerCache()->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f );
511 if ( action->data().toString() == QLatin1String(
"user_action" ) )
513 mFilterModel->layer()->actions()->doAction( action->property(
"action_id" ).toUuid(), f );
515 else if ( action->data().toString() == QLatin1String(
"map_layer_action" ) )
517 QObject *
object = action->property(
"action" ).value<QObject *>();
518 QgsMapLayerAction *layerAction = qobject_cast<QgsMapLayerAction *>(
object );
521 QgsMapLayerActionContext context;
523 layerAction->triggerForFeature( mFilterModel->layer(), f );
525 layerAction->triggerForFeature( mFilterModel->layer(), f, context );
530void QgsAttributeTableView::columnSizeChanged(
int index,
int oldWidth,
int newWidth )
536void QgsAttributeTableView::onActionColumnItemPainted(
const QModelIndex &index )
538 if ( !indexWidget( index ) )
541 mActionWidgets.insert( index, widget );
542 setIndexWidget( index, widget );
546void QgsAttributeTableView::recreateActionWidgets()
548 QMap<QModelIndex, QWidget *>::const_iterator it = mActionWidgets.constBegin();
549 for ( ; it != mActionWidgets.constEnd(); ++it )
554 setIndexWidget( it.key(),
nullptr );
556 mActionWidgets.clear();
561 const QModelIndex index = mFilterModel->fidToIndex( fid );
563 if ( !index.isValid() )
568 const QModelIndex selectionIndex = index.sibling( index.row(), col );
570 if ( !selectionIndex.isValid() )
573 selectionModel()->setCurrentIndex( index, QItemSelectionModel::SelectCurrent );
578 QWidget *editor = indexWidget( currentIndex() );
579 commitData( editor );
580 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.