QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayertreefilterproxymodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreefilterproxymodel.cpp
3
4 ---------------------
5 begin : 05.06.2020
6 copyright : (C) 2020 by Denis Rouzaud
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgslayertree.h"
20#include "qgslayertreemodel.h"
21#include "qgssymbol.h"
22
24 : QSortFilterProxyModel( parent )
25{
26 connect( QgsProject::instance(), &QgsProject::readProject, this, [this]
27 {
28 beginResetModel();
29 endResetModel();
30 } );
31}
32
33void QgsLayerTreeFilterProxyModel::setCheckedLayers( const QList<QgsMapLayer *> layers )
34{
35 // do not use invalidate() since it's not the filter which changes but the data
36 beginResetModel();
37 mCheckedLayers = layers;
38 endResetModel();
39}
40
41int QgsLayerTreeFilterProxyModel::columnCount( const QModelIndex &parent ) const
42{
43 Q_UNUSED( parent )
44 return 1;
45}
46
47Qt::ItemFlags QgsLayerTreeFilterProxyModel::flags( const QModelIndex &idx ) const
48{
49 if ( idx.column() == 0 )
50 {
51 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
52 }
53 return Qt::NoItemFlags;
54}
55
56QModelIndex QgsLayerTreeFilterProxyModel::index( int row, int column, const QModelIndex &parent ) const
57{
58 QModelIndex newIndex = QSortFilterProxyModel::index( row, 0, parent );
59 if ( column == 0 )
60 return newIndex;
61
62 return createIndex( row, column, newIndex.internalId() );
63}
64
65QModelIndex QgsLayerTreeFilterProxyModel::parent( const QModelIndex &child ) const
66{
67 return QSortFilterProxyModel::parent( createIndex( child.row(), 0, child.internalId() ) );
68}
69
70QModelIndex QgsLayerTreeFilterProxyModel::sibling( int row, int column, const QModelIndex &idx ) const
71{
72 const QModelIndex parent = idx.parent();
73 return index( row, column, parent );
74}
75
77{
78 QgsLayerTreeNode *node = nullptr;
79 if ( idx.column() == 0 )
80 {
81 node = mLayerTreeModel->index2node( mapToSource( idx ) );
82 }
83
84 if ( !node || !QgsLayerTree::isLayer( node ) )
85 return nullptr;
86
87 return QgsLayerTree::toLayer( node )->layer();
88}
89
90void QgsLayerTreeFilterProxyModel::setFilterText( const QString &filterText )
91{
92 if ( filterText == mFilterText )
93 return;
94
95 mFilterText = filterText;
96 invalidateFilter();
97}
98
100{
101 return mLayerTreeModel;
102}
103
105{
106 mLayerTreeModel = layerTreeModel;
107 QSortFilterProxyModel::setSourceModel( layerTreeModel );
108}
109
111{
112 mFilters = filters;
113 invalidateFilter();
114}
115
116bool QgsLayerTreeFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
117{
118 QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
119 return nodeShown( node );
120}
121
123{
124 return mCheckedLayers.contains( layer );
125}
126
128{
129 if ( checked )
130 {
131 mCheckedLayers << layer;
132 }
133 else
134 {
135 mCheckedLayers.removeAll( layer );
136 }
137}
138
139void QgsLayerTreeFilterProxyModel::setLayerCheckedPrivate( QgsMapLayer *layer, bool checked )
140{
141 if ( checked && isLayerChecked( layer ) )
142 return;
143 if ( !checked && !isLayerChecked( layer ) )
144 return;
145
146 QgsLayerTreeNode *node = mLayerTreeModel->rootGroup()->findLayer( layer );
147 const QModelIndex index = mapFromSource( mLayerTreeModel->node2index( node ) );
148
149 setLayerChecked( layer, checked );
150
151 emit dataChanged( index, index );
152}
153
154bool QgsLayerTreeFilterProxyModel::layerShown( QgsMapLayer *layer ) const
155{
156 Q_UNUSED( layer )
157 return true;
158}
159
160bool QgsLayerTreeFilterProxyModel::nodeShown( QgsLayerTreeNode *node ) const
161{
162 if ( !node )
163 return false;
164 if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
165 {
166 const auto constChildren = node->children();
167 for ( QgsLayerTreeNode *child : constChildren )
168 {
169 if ( nodeShown( child ) )
170 {
171 return true;
172 }
173 }
174 return false;
175 }
176 else
177 {
178 QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
179 if ( !layer )
180 return false;
181 if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
182 return false;
183 if ( !QgsMapLayerProxyModel::layerMatchesFilters( layer, mFilters ) )
184 return false;
185
186 return layerShown( layer );
187 }
188}
189
190QVariant QgsLayerTreeFilterProxyModel::data( const QModelIndex &idx, int role ) const
191{
192 if ( idx.column() == 0 )
193 {
194 if ( role == Qt::CheckStateRole )
195 {
196 QgsMapLayer *layer = mapLayer( idx );
197 if ( layer )
198 {
199 if ( isLayerChecked( layer ) )
200 {
201 return Qt::Checked;
202 }
203 else
204 {
205 return Qt::Unchecked;
206 }
207 }
208 else
209 {
210 // i.e. this is a group, analyze its children
211 bool hasChecked = false, hasUnchecked = false;
212 int n;
213 for ( n = 0; !hasChecked || !hasUnchecked; n++ )
214 {
215 const QVariant v = data( index( n, 0, idx ), role );
216 if ( !v.isValid() )
217 break;
218
219 switch ( v.toInt() )
220 {
221 case Qt::PartiallyChecked:
222 // parent of partially checked child shared state
223 return Qt::PartiallyChecked;
224
225 case Qt::Checked:
226 hasChecked = true;
227 break;
228
229 case Qt::Unchecked:
230 hasUnchecked = true;
231 break;
232 }
233 }
234
235 // unchecked leaf
236 if ( n == 0 )
237 return Qt::Unchecked;
238
239 // both
240 if ( hasChecked && hasUnchecked )
241 return Qt::PartiallyChecked;
242
243 if ( hasChecked )
244 return Qt::Checked;
245
246 Q_ASSERT( hasUnchecked );
247 return Qt::Unchecked;
248 }
249 }
250 else
251 {
252 return mLayerTreeModel->data( mapToSource( idx ), role );
253 }
254 }
255 return QVariant();
256}
257
258bool QgsLayerTreeFilterProxyModel::setData( const QModelIndex &index, const QVariant &value, int role )
259{
260 if ( index.column() == 0 )
261 {
262 if ( role == Qt::CheckStateRole )
263 {
264 int i = 0;
265 for ( i = 0; ; i++ )
266 {
267 const QModelIndex child = QgsLayerTreeFilterProxyModel::index( i, 0, index );
268 if ( !child.isValid() )
269 break;
270
271 setData( child, value, role );
272 }
273
274 if ( i == 0 )
275 {
276 QgsMapLayer *layer = mapLayer( index );
277 if ( !layer )
278 {
279 return false;
280 }
281 if ( value.toInt() == Qt::Checked )
282 setLayerCheckedPrivate( layer, true );
283 else if ( value.toInt() == Qt::Unchecked )
284 setLayerCheckedPrivate( layer, false );
285 else
286 Q_ASSERT( false ); // expected checked or unchecked
287 }
288 emit dataChanged( index, index );
289 return true;
290 }
291
292 return mLayerTreeModel->setData( mapToSource( index ), value, role );
293 }
294
295 return false;
296}
QFlags< LayerFilter > LayerFilters
Definition: qgis.h:151
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
virtual void setLayerChecked(QgsMapLayer *layer, bool checked)
This will set if the layer is checked or not.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
void setFilters(Qgis::LayerFilters filters)
Defines the type layers (vector, raster, etc) shown in the tree If the list is empty,...
virtual int columnCount(const QModelIndex &parent) const override
QgsMapLayer * mapLayer(const QModelIndex &idx) const
Returns the map layer at a given index.
void setLayerTreeModel(QgsLayerTreeModel *layerTreeModel)
Sets the layer tree model.
virtual bool isLayerChecked(QgsMapLayer *layer) const
Returns if the layer is checked or not.
virtual void setFilterText(const QString &filterText=QString())
Sets the filter text to search for a layer in the tree.
void setCheckedLayers(const QList< QgsMapLayer * > layers)
Initialize the list of checked layers.
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
QModelIndex parent(const QModelIndex &child) const override
virtual QVariant data(const QModelIndex &index, int role) const override
QgsLayerTreeFilterProxyModel(QObject *parent=nullptr)
Constructor.
virtual Qt::ItemFlags flags(const QModelIndex &idx) const override
QgsLayerTreeModel * layerTreeModel() const
Rerturns the layer tree model.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
static bool layerMatchesFilters(const QgsMapLayer *layer, const Qgis::LayerFilters &filters)
Returns if the layer matches the given filters.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
QString name
Definition: qgsmaplayer.h:78
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
void readProject(const QDomDocument &)
Emitted when a project is being read.