QGIS API Documentation 3.43.0-Master (c67cf405802)
qgslayertree.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertree
3 ---------------------
4 begin : 22.3.2017
5 copyright : (C) 2017 by Matthias Kuhn
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgslayertree.h"
17#include "moc_qgslayertree.cpp"
19#include "qgsvectorlayer.h"
20
22{
23 init();
24}
25
27 : QgsLayerTreeGroup( other )
28 , mCustomLayerOrder( other.mCustomLayerOrder )
29 , mHasCustomLayerOrder( other.mHasCustomLayerOrder )
30{
31 init();
32}
33
34void QgsLayerTree::init()
35{
36 connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
37 connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
38}
39
40QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const
41{
42 return _qgis_listQPointerToRaw( mCustomLayerOrder );
43}
44
45void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder )
46{
47 QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder );
48
49 if ( newOrder == mCustomLayerOrder )
50 return;
51
52 mCustomLayerOrder = newOrder;
54
55 if ( mHasCustomLayerOrder )
56 emit layerOrderChanged();
57}
58
59void QgsLayerTree::setCustomLayerOrder( const QStringList &customLayerOrder )
60{
61 QList<QgsMapLayer *> layers;
62
63 for ( const auto &layerId : customLayerOrder )
64 {
65 QgsLayerTreeLayer *nodeLayer = findLayer( layerId );
66 if ( nodeLayer )
67 {
68 // configuration from 2.x projects might have non spatial layers
69 QgsMapLayer *layer = nodeLayer->layer();
70 if ( !layer || !layer->isSpatial() )
71 {
72 continue;
73 }
74 layers.append( layer );
75 }
76 }
77 setCustomLayerOrder( layers );
78}
79
80QList<QgsMapLayer *> QgsLayerTree::layerOrder() const
81{
82 if ( mHasCustomLayerOrder )
83 {
84 return customLayerOrder();
85 }
86 else
87 {
89 }
90}
91
93{
94 return mHasCustomLayerOrder;
95}
96
97void QgsLayerTree::setHasCustomLayerOrder( bool hasCustomLayerOrder )
98{
99 if ( hasCustomLayerOrder == mHasCustomLayerOrder )
100 return;
101
102 mHasCustomLayerOrder = hasCustomLayerOrder;
103
105 emit layerOrderChanged();
106}
107
108QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
109{
110 QgsLayerTree *tree = new QgsLayerTree();
111
112 tree->readCommonXml( element );
113
114 tree->readChildrenFromXml( element, context );
115
116 return tree;
117}
118
119void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
120{
121 QDomDocument doc = parentElement.ownerDocument();
122 QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
123
124 writeCommonXml( elem );
125
126 for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
127 node->writeXml( elem, context );
128
129 QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
130 customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
131 elem.appendChild( customOrderElem );
132
133 for ( QgsMapLayer *layer : std::as_const( mCustomLayerOrder ) )
134 {
135 // Safety belt, see https://github.com/qgis/QGIS/issues/26975
136 // Crash when deleting an item from the layout legend
137 if ( ! layer )
138 continue;
139 QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) );
140 layerElem.appendChild( doc.createTextNode( layer->id() ) );
141 customOrderElem.appendChild( layerElem );
142 }
143
144 elem.appendChild( customOrderElem );
145
146 parentElement.appendChild( elem );
147}
148
150{
151 return new QgsLayerTree( *this );
152}
153
155{
157 setHasCustomLayerOrder( false );
158 setCustomLayerOrder( QStringList() );
159}
160
161void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
162{
163 Q_ASSERT( node );
164
165 // collect layer IDs that have been added in order to put them into custom layer order
166 QList<QgsMapLayer *> layers;
167
168 QList<QgsLayerTreeNode *> children = node->children();
169 for ( int i = indexFrom; i <= indexTo; ++i )
170 {
171 QgsLayerTreeNode *child = children.at( i );
172 if ( QgsLayerTree::isLayer( child ) )
173 {
174 layers << QgsLayerTree::toLayer( child )->layer();
175 }
176 else if ( QgsLayerTree::isGroup( child ) )
177 {
178 const auto nodeLayers = QgsLayerTree::toGroup( child )->findLayers();
179 for ( QgsLayerTreeLayer *nodeL : nodeLayers )
180 layers << nodeL->layer();
181 }
182 }
183
184 for ( QgsMapLayer *layer : std::as_const( layers ) )
185 {
186 if ( !mCustomLayerOrder.contains( layer ) && layer )
187 mCustomLayerOrder.append( layer );
188 }
189
191 emit layerOrderChanged();
192}
193
194void QgsLayerTree::nodeRemovedChildren()
195{
196 QList<QgsMapLayer *> layers = customLayerOrder();
197 auto layer = layers.begin();
198
199 while ( layer != layers.end() )
200 {
201 if ( !findLayer( *layer ) )
202 layer = layers.erase( layer );
203 else
204 ++layer;
205 }
206
207 // we need to ensure that the customLayerOrderChanged signal is ALWAYS raised
208 // here, since that order HAS changed due to removal of the child!
209 // setCustomLayerOrder will only emit this signal when the layers list
210 // at this stage is different to the stored customer layer order. If this
211 // isn't the case (i.e. the lists ARE the same) then manually emit the
212 // signal
213 const bool emitSignal = _qgis_listRawToQPointer( layers ) == mCustomLayerOrder;
214
215 setCustomLayerOrder( layers );
216 if ( emitSignal )
218
219 emit layerOrderChanged();
220}
221
222void QgsLayerTree::addMissingLayers()
223{
224 bool changed = false;
225
226 const QList< QgsLayerTreeLayer * > layers = findLayers();
227 for ( const auto layer : layers )
228 {
229 if ( !mCustomLayerOrder.contains( layer->layer() ) &&
230 layer->layer() && layer->layer()->isSpatial() )
231 {
232 mCustomLayerOrder.append( layer->layer() );
233 changed = true;
234 }
235 }
236
237 if ( changed )
238 {
240 if ( mHasCustomLayerOrder )
241 emit layerOrderChanged();
242 }
243}
244
245void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem )
246{
247 QStringList order;
248
249 QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
250 if ( !customOrderElem.isNull() )
251 {
252 setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() );
253
254 QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
255 while ( !itemElem.isNull() )
256 {
257 order.append( itemElem.text() );
258 itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
259 }
260 }
261
262 setCustomLayerOrder( order );
263 addMissingLayers();
264}
Layer tree group node serves as a container for layers and further groups.
void readChildrenFromXml(const QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QList< QgsMapLayer * > layerOrderRespectingGroupLayers() const
Returns an ordered list of map layers in the group, ignoring any layers which are child layers of Qgs...
void removeAllChildren()
Remove all child nodes.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
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.
Base class for nodes in a layer tree.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void writeCommonXml(QDomElement &element)
Write common XML elements.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
void readCommonXml(const QDomElement &element)
Read common XML elements.
Namespace with helper functions for layer tree operations.
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write layer tree to XML.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree()
Create a new empty layer tree.
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
void setCustomLayerOrder(const QList< QgsMapLayer * > &customLayerOrder)
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
void layerOrderChanged()
Emitted when the layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Base class for all map layer types.
Definition qgsmaplayer.h:77
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
A container for the context for various read/write operations on objects.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.