30#include <QApplication>
31#include <QContextMenuEvent>
38#include "moc_qgslayertreeview.cpp"
40using namespace Qt::StringLiterals;
42#ifdef ENABLE_MODELTEST
43#include <QAbstractItemModelTester>
52 , mBlockDoubleClickTimer( new QTimer( this ) )
54 mBlockDoubleClickTimer->setSingleShot(
true );
55 mBlockDoubleClickTimer->setInterval( QApplication::doubleClickInterval() );
57 setHeaderHidden(
true );
59 setDragEnabled(
true );
60 setAcceptDrops(
true );
61 setDropIndicatorShown(
true );
62 setEditTriggers( EditKeyPressed );
63 setExpandsOnDoubleClick(
false );
66 header()->setStretchLastSection(
false );
67 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
70 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
72 setSelectionMode( ExtendedSelection );
73 setDefaultDropAction( Qt::MoveAction );
84 if ( mBlockDoubleClickTimer->isActive() )
87 QTreeView::mouseDoubleClickEvent( event );
92 if ( mLayerTreeModel )
96 disconnect( mLayerTreeModel, &QAbstractItemModel::dataChanged,
this, &QgsLayerTreeViewBase::onDataChanged );
99 mLayerTreeModel = model;
108 connect( mLayerTreeModel, &QAbstractItemModel::dataChanged,
this, &QgsLayerTreeViewBase::onDataChanged );
115 return mLayerTreeModel;
123 const auto constChildren = node->
children();
131 if ( model() == mLayerTreeModel )
134 if ( QAbstractProxyModel *proxy = qobject_cast< QAbstractProxyModel *>( model() ) )
136 return proxy->mapToSource( index );
139 return QModelIndex();
145 if ( model() == mLayerTreeModel )
148 if ( QAbstractProxyModel *proxy = qobject_cast< QAbstractProxyModel *>( model() ) )
150 return proxy->mapFromSource( index );
153 return QModelIndex();
160 node->setExpanded( isExpanded( index ) );
165 QStringList lst = node->layerNode()->customProperty( u
"expandedLegendNodes"_s ).toStringList();
166 const bool expanded = isExpanded( index );
167 const bool isInList = lst.contains( ruleKey );
168 if ( expanded && !isInList )
170 lst.append( ruleKey );
171 node->layerNode()->setCustomProperty( u
"expandedLegendNodes"_s, lst );
173 else if ( !expanded && isInList )
175 lst.removeAll( ruleKey );
176 node->layerNode()->setCustomProperty( u
"expandedLegendNodes"_s, lst );
184 if ( isExpanded( idx ) != expanded )
185 setExpanded( idx, expanded );
190 if ( !mLayerTreeModel )
196void QgsLayerTreeViewBase::onDataChanged(
const QModelIndex &,
const QModelIndex &,
const QVector<int> & )
198 mBlockDoubleClickTimer->start();
203 if ( mLayerTreeModel )
216 if ( mLayerTreeModel )
217 return mLayerTreeModel->node2index( node );
219 return QModelIndex();
229 if ( QItemSelectionModel *selectModel = selectionModel() )
230 return index2node( selectModel->currentIndex() );
237 if ( !mLayerTreeModel )
240 QModelIndexList mapped;
241 if ( QItemSelectionModel *selectModel = selectionModel() )
243 const QModelIndexList selected = selectModel->selectedIndexes();
244 mapped.reserve( selected.size() );
245 for (
const QModelIndex &index : selected )
249 return mLayerTreeModel->indexes2nodes( mapped, skipInternal );
261 if ( mLayerTreeModel && index.isValid() )
291 if ( QItemSelectionModel *selectModel = selectionModel() )
306 QList<QgsLayerTreeLayer *> layerNodes;
307 const QList<QgsLayerTreeNode *> constSelectedNodes =
selectedNodes();
308 layerNodes.reserve( constSelectedNodes.size() );
319 QList<QgsMapLayer *> list;
321 list.reserve( constSelectedLayerNodes.size() );
325 list << node->layer();
337 if ( mLayerTreeModel )
338 return mLayerTreeModel->legendNode2index( legendNode );
339 return QModelIndex();
346 setCurrentIndex( QModelIndex() );
355 if ( !layer || !mLayerTreeModel )
357 setCurrentIndex( QModelIndex() );
370 if ( QItemSelectionModel *selectModel = selectionModel() )
378 QList<QgsLayerTreeModelLegendNode *> res;
379 QItemSelectionModel *selectModel = selectionModel();
380 if ( !mLayerTreeModel || !selectModel )
383 const QModelIndexList selected = selectModel->selectedIndexes();
384 res.reserve( selected.size() );
385 for (
const QModelIndex &index : selected )
390 res.push_back( node );
399 QModelIndexList mapped;
400 QItemSelectionModel *selectModel = selectionModel();
401 if ( !mLayerTreeModel || !selectModel )
402 return QList<QgsMapLayer *>();
404 const QModelIndexList selected = selectModel->selectedIndexes();
405 mapped.reserve( selected.size() );
406 for (
const QModelIndex &index : selected )
411 const QList<QgsLayerTreeNode *> nodes = mLayerTreeModel->indexes2nodes( mapped,
false );
413 return qgis::setToList( layersSet );
423 const auto constLayerLegendNodes = model->
layerLegendNodes( nodeLayer,
true );
427 if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
436 const auto constChildren = parent->
children();
439 node->setExpanded( expanded );
483 setStyle(
new QgsLayerTreeViewProxyStyle(
this ) );
487 connect( horizontalScrollBar(), &QScrollBar::valueChanged,
this, &QgsLayerTreeView::onHorizontalScroll );
500 proxyModel->setShowPrivateLayers( mShowPrivateLayers );
501 proxyModel->setHideValidLayers( mHideValidLayers );
513 mMessageBar->pushMessage( message, level );
520#ifdef ENABLE_MODELTEST
521 new QAbstractItemModelTester( mProxyModel, QAbstractItemModelTester::FailureReportingMode::Fatal,
this );
524 QTreeView::setModel( mProxyModel );
531 connect( treeModel, &QAbstractItemModel::dataChanged,
this, &QgsLayerTreeView::onDataChanged );
544 if ( !layer || !layerModel )
557 const QModelIndex idx = indexAt( event->pos() );
558 if ( !idx.isValid() )
559 setCurrentIndex( QModelIndex() );
566 if ( menu->actions().count() != 0 )
567 menu->exec( mapToGlobal( event->pos() ) );
577 if ( !parentNode || !layerModel )
586 const int widgetsCount = layer->customProperty( u
"embeddedWidgets/count"_s, 0 ).toInt();
587 QList<QgsLayerTreeModelLegendNode *> legendNodes = layerModel->
layerLegendNodes( nodeLayer,
true );
588 for (
int i = 0; i < widgetsCount; ++i )
590 const QString providerId = layer->customProperty( u
"embeddedWidgets/%1/id"_s.arg( i ) ).toString();
594 QWidget *wdgt = provider->createWidget( layer, i );
611 setIndexWidget( index, wdgt );
621 const QStringList expandedNodeKeys = parentNode->
customProperty( u
"expandedLegendNodes"_s ).toStringList();
622 if ( expandedNodeKeys.isEmpty() )
629 if ( expandedNodeKeys.contains( ruleKey ) )
635 QList<QgsLayerTreeNode *> children = parentNode->
children();
636 for (
int i = start; i <= end && i < children.count(); ++i )
658 const QString layerCurrentID = layerCurrent ? layerCurrent->
id() : QString();
663 QModelIndex proxyModelNodeLayerIndex;
668 proxyModelNodeLayerIndex =
node2index( nodeLayer );
671 if ( !proxyModelNodeLayerIndex.isValid() )
679 layerModel->
setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
698void QgsLayerTreeView::onCustomPropertyChanged(
QgsLayerTreeNode *node,
const QString &key )
704 const QSet<QString> expandedLegendNodes = qgis::listToSet( node->
customProperty( u
"expandedLegendNodes"_s ).toStringList() );
710 if ( !key.isEmpty() )
711 setExpanded(
legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
722 viewport()->repaint();
725 viewport()->repaint();
741QStringList QgsLayerTreeView::viewOnlyCustomProperties()
743 return QStringList() << u
"expandedLegendNodes"_s;
760 if ( mMessageBar == messageBar )
763 mMessageBar = messageBar;
771 mMessageBar->pushMessage( message, level );
780 mShowPrivateLayers = showPrivate;
781 mProxyModel->setShowPrivateLayers( showPrivate );
789 mHideValidLayers = hideValid;
790 mProxyModel->setHideValidLayers( mHideValidLayers );
795 return mShowPrivateLayers;
800 return mHideValidLayers;
814 if ( event->modifiers() & Qt::ControlModifier )
818 QTreeView::mouseReleaseEvent( event );
824 if ( event->key() == Qt::Key_Space )
826 const QList<QgsLayerTreeNode *> constSelectedNodes =
selectedNodes();
828 if ( !constSelectedNodes.isEmpty() )
830 const bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
845 if ( event->modifiers() & Qt::ControlModifier )
849 QTreeView::keyPressEvent( event );
855 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( u
"application/x-vnd.qgis.qgis.uri"_s ) )
858 if ( !event->mimeData()->hasFormat( u
"application/qgis.layertreemodeldata"_s ) )
864 QTreeView::dragEnterEvent( event );
869 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( u
"application/x-vnd.qgis.qgis.uri"_s ) )
872 if ( !event->mimeData()->hasFormat( u
"application/qgis.layertreemodeldata"_s ) )
878 QTreeView::dragMoveEvent( event );
883 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( u
"application/x-vnd.qgis.qgis.uri"_s ) )
886 if ( !event->mimeData()->hasFormat( u
"application/qgis.layertreemodeldata"_s ) )
890 QModelIndex index = indexAt( event->pos() );
891 if ( index.isValid() )
893 setCurrentIndex( index );
900 if ( event->keyboardModifiers() & Qt::AltModifier || event->keyboardModifiers() & Qt::ControlModifier )
904 QTreeView::dropEvent( event );
917 header()->setMinimumSectionSize( viewport()->width() );
918 QTreeView::resizeEvent( event );
921void QgsLayerTreeView::onHorizontalScroll(
int value )
924 viewport()->update();
927void QgsLayerTreeView::onDataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QVector<int> &roles )
930 Q_UNUSED( bottomRight )
936 if ( roles.contains( Qt::SizeHintRole ) )
937 viewport()->update();
944void QgsLayerTreeView::checkModel()
946 std::function<void( QgsLayerTreeNode *,
int )> debug;
947 debug = [ & ]( QgsLayerTreeNode * node,
int depth )
950 qDebug() <<
"----------------------------------------------";
957 for (
int i = 0; i < mProxyModel->rowCount(
node2index( node ) ); i++ )
961 debug( childNode, depth + 1 );
963 qDebug() <<
"Warning no child node!";
976 : QSortFilterProxyModel( parent )
977 , mLayerTreeModel( treeModel )
979 setSourceModel( treeModel );
984 if ( filterText == mFilterText )
987 mFilterText = filterText;
993 QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
1009 if ( !mFilterText.isEmpty() && !layer->
name().contains( mFilterText, Qt::CaseInsensitive ) )
1015 if ( mHideValidLayers && layer->
isValid() )
1029 return mShowPrivateLayers;
1034 if ( showPrivate == mShowPrivateLayers )
1037 mShowPrivateLayers = showPrivate;
1043 return mHideValidLayers;
1048 if ( hideValid == mHideValidLayers )
1051 mHideValidLayers = hideValid;
MessageLevel
Level for messages This will be used both for message log and message bar in application.
@ Info
Information message.
static const double UI_SCALE_FACTOR
UI scaling factor.
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
An abstract interface for legend items returned from QgsMapLayerLegend implementation.
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ ParentRuleKey
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKey
Rule key of the node (QString).
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
A model representing the layer tree, including layers and groups of layers.
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
Qt::ItemFlags flags(const QModelIndex &index) const override
void setCurrentIndex(const QModelIndex ¤tIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
void setTargetScreenProperties(const QSet< QgsScreenProperties > &properties)
Sets all target screen properties to use when generating icons for Qt::DecorationRole data.
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
bool testFlag(Flag f) const
Check whether a flag is enabled.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node).
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
Base class for nodes in a layer tree.
@ NodeCustom
Leaf node pointing to a custom object.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
virtual QString name() const =0
Returns name of the node.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children).
void expandedChanged(QgsLayerTreeNode *node, bool expanded)
Emitted when the collapsed/expanded state of a node within the tree has been changed.
A proxy model for QgsLayerTreeModel, supporting private layers and text filtering.
bool showPrivateLayers() const
Returns if private layers are shown.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
void setFilterText(const QString &filterText=QString())
Sets filter to filterText.
bool hideValidLayers() const
Returns if valid layers should be hidden (i.e.
void setHideValidLayers(bool hideValid)
Sets whether valid layers should be hidden (i.e.
virtual bool nodeShown(QgsLayerTreeNode *node) const
Returns true if the specified node should be shown.
void setShowPrivateLayers(bool showPrivate)
Determines if private layers are shown.
QgsLayerTreeProxyModel(QgsLayerTreeModel *treeModel, QObject *parent)
Constructs QgsLayerTreeProxyModel with source model treeModel and a parent.
static QSet< QgsMapLayer * > collectMapLayersRecursive(const QList< QgsLayerTreeNode * > &nodes)
Returns map layers from the given list of layer tree nodes.
void updateExpandedStateToNode(const QModelIndex &index)
Stores the expanded state to a node with matching index.
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Returns the list of selected layer tree nodes.
QgsLayerTreeNode * currentNode() const
Returns the current node.
QgsLayerTreeGroup * currentGroupNode() const
Returns the current group node.
QList< QgsMapLayer * > selectedLayers() const
Returns the list of selected layers.
QModelIndex node2sourceIndex(QgsLayerTreeNode *node) const
Returns the layer tree source model index for a given node.
QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index) const
Returns legend node for given view index.
void mouseDoubleClickEvent(QMouseEvent *event) override
void setLayerTreeModel(QgsLayerTreeModel *model)
Associates a layer tree model with the view.
QgsMapLayer * layerForIndex(const QModelIndex &index) const
Returns the map layer corresponding to a view index.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns the view index for a given legend node.
~QgsLayerTreeViewBase() override
void setCurrentNode(QgsLayerTreeNode *node)
Sets the currently selected node.
void onExpandedChanged(QgsLayerTreeNode *node, bool expanded)
Called when the expanded state changes for a node.
QModelIndex layerTreeModelIndexToViewIndex(const QModelIndex &index) const
Returns the layer tree model index corresponding with a view index.
QModelIndex legendNode2sourceIndex(QgsLayerTreeModelLegendNode *legendNode)
Returns the layer tree source model index for a given legend node.
QList< QgsLayerTreeModelLegendNode * > selectedLegendNodes() const
Returns the list of selected legend nodes.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns the view model index for a given node.
void onModelReset()
Called when the model is reset.
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
Updates the expanded state from a node.
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Returns the list of selected nodes filtered to just layer nodes (QgsLayerTreeLayer).
QModelIndex viewIndexToLayerTreeModelIndex(const QModelIndex &index) const
Returns the view index corresponding with a layer tree model index.
QgsLayerTreeViewBase(QWidget *parent=nullptr)
Constructor for QgsLayerTreeViewBase.
QgsLayerTreeViewDefaultActions * defaultActions()
Gets access to the default actions that may be used with the tree view.
void setCurrentLayer(QgsMapLayer *layer)
Sets the currently selected layer.
void collapseAllNodes()
Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes.
QList< QgsMapLayer * > selectedLayersRecursive() const
Gets list of selected layers, including those that are not directly selected, but their ancestor grou...
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns the layer tree node for given view index.
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
QgsLayerTreeModelLegendNode * currentLegendNode() const
Gets current legend node.
void expandAllNodes()
Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes.
QgsMapLayer * currentLayer() const
Returns the currently selected layer, or nullptr if no layers is selected.
QgsLayerTreeModel * layerTreeModel() const
Returns the associated layer tree model.
Serves as a factory of actions that can be used together with a layer tree view.
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
void changed()
Emitted when the indicator changes state (e.g.
QList< QgsLayerTreeViewIndicator * > indicators(QgsLayerTreeNode *node) const
Returns list of indicators associated with a particular layer tree node.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
void onDoubleClicked(const QModelIndex &index)
Expands layer groups only.
void contextMenuAboutToShow(QMenu *menu)
Emitted when the context menu is about to show.
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer).
void datasetsDropped(QDropEvent *event)
Emitted when datasets are dropped onto the layer tree view.
void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class.
void removeIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Removes a previously added indicator to a layer tree node.
QHash< QgsLayerTreeNode *, QList< QgsLayerTreeViewIndicator * > > mIndicators
Storage of indicators used with the tree view.
void refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitore...
void dropEvent(QDropEvent *event) override
QgsLayerTreeView(QWidget *parent=nullptr)
Constructor for QgsLayerTreeView.
QgsLayerTreeViewMenuProvider * menuProvider() const
Returns pointer to the context menu provider. May be nullptr.
void resizeEvent(QResizeEvent *event) override
void mouseReleaseEvent(QMouseEvent *event) override
bool hideValidLayers() const
Returns if valid layers should be hidden (i.e.
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Sets provider for context menu. Takes ownership of the instance.
void setLayerVisible(QgsMapLayer *layer, bool visible)
Convenience methods which sets the visible state of the specified map layer.
void dragMoveEvent(QDragMoveEvent *event) override
QPoint mLastReleaseMousePos
Used by the item delegate for identification of which indicator has been clicked.
friend class QgsLayerTreeViewItemDelegate
QgsLayerTreeProxyModel * proxyModel() const
Returns the proxy model used by the view.
void contextMenuEvent(QContextMenuEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void setMessageBar(QgsMessageBar *messageBar)
Set the message bar to display messages from the layer tree.
void addIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Adds an indicator to the given layer tree node.
void setHideValidLayers(bool hideValid)
Sets whether valid layers should be hidden (i.e.
std::unique_ptr< QgsLayerTreeViewMenuProvider > mMenuProvider
Context menu provider. Owned by the view.
void modelRowsInserted(const QModelIndex &index, int start, int end)
void setLayerMarkWidth(int width)
Set width of contextual menu mark, at right of layer node items.
void setShowPrivateLayers(bool showPrivate)
Set the show private layers to showPrivate.
~QgsLayerTreeView() override
void dragEnterEvent(QDragEnterEvent *event) override
bool showPrivateLayers() const
Returns the show private layers status.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Base class for all map layer types.
QgsMapLayer::LayerFlags flags
@ Private
Determines if the layer is meant to be exposed to the GUI, i.e. visible in the layer legend tree.
A bar for displaying non-blocking messages to the user.
A utility class for dynamic handling of changes to screen properties.
void screenDpiChanged(double dpi)
Emitted whenever the screen dpi associated with the widget is changed.
Stores properties relating to a screen.