QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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
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.
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.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
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.
This class is a base class for nodes in a layer tree.
void readCommonXml(QDomElement &element)
Read common XML elements.
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
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:76
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
The class is used as a container of context for various read/write operations on other objects.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.