QGIS API Documentation  3.27.0-Master (0e23467727)
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 : [email protected]
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 "qgsmaplayerlistutils_p.h"
18 #include "qgsvectorlayer.h"
19 
21 {
22  init();
23 }
24 
26  : QgsLayerTreeGroup( other )
27  , mCustomLayerOrder( other.mCustomLayerOrder )
28  , mHasCustomLayerOrder( other.mHasCustomLayerOrder )
29 {
30  init();
31 }
32 
33 void QgsLayerTree::init()
34 {
35  connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
36  connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
37 }
38 
39 QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const
40 {
41  return _qgis_listQPointerToRaw( mCustomLayerOrder );
42 }
43 
44 void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder )
45 {
46  QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder );
47 
48  if ( newOrder == mCustomLayerOrder )
49  return;
50 
51  mCustomLayerOrder = newOrder;
53 
54  if ( mHasCustomLayerOrder )
55  emit layerOrderChanged();
56 }
57 
58 void QgsLayerTree::setCustomLayerOrder( const QStringList &customLayerOrder )
59 {
60  QList<QgsMapLayer *> layers;
61 
62  for ( const auto &layerId : customLayerOrder )
63  {
64  QgsLayerTreeLayer *nodeLayer = findLayer( layerId );
65  if ( nodeLayer )
66  {
67  // configuration from 2.x projects might have non spatial layers
68  QgsMapLayer *layer = nodeLayer->layer();
69  if ( !layer || !layer->isSpatial() )
70  {
71  continue;
72  }
73  layers.append( layer );
74  }
75  }
76  setCustomLayerOrder( layers );
77 }
78 
79 QList<QgsMapLayer *> QgsLayerTree::layerOrder() const
80 {
81  if ( mHasCustomLayerOrder )
82  {
83  return customLayerOrder();
84  }
85  else
86  {
88  }
89 }
90 
92 {
93  return mHasCustomLayerOrder;
94 }
95 
96 void QgsLayerTree::setHasCustomLayerOrder( bool hasCustomLayerOrder )
97 {
98  if ( hasCustomLayerOrder == mHasCustomLayerOrder )
99  return;
100 
101  mHasCustomLayerOrder = hasCustomLayerOrder;
102 
104  emit layerOrderChanged();
105 }
106 
107 QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context )
108 {
109  QgsLayerTree *tree = new QgsLayerTree();
110 
111  tree->readCommonXml( element );
112 
113  tree->readChildrenFromXml( element, context );
114 
115  return tree;
116 }
117 
118 void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
119 {
120  QDomDocument doc = parentElement.ownerDocument();
121  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
122 
123  writeCommonXml( elem );
124 
125  for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
126  node->writeXml( elem, context );
127 
128  QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
129  customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
130  elem.appendChild( customOrderElem );
131 
132  for ( QgsMapLayer *layer : std::as_const( mCustomLayerOrder ) )
133  {
134  // Safety belt, see https://github.com/qgis/QGIS/issues/26975
135  // Crash when deleting an item from the layout legend
136  if ( ! layer )
137  continue;
138  QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) );
139  layerElem.appendChild( doc.createTextNode( layer->id() ) );
140  customOrderElem.appendChild( layerElem );
141  }
142 
143  elem.appendChild( customOrderElem );
144 
145  parentElement.appendChild( elem );
146 }
147 
149 {
150  return new QgsLayerTree( *this );
151 }
152 
154 {
156  setHasCustomLayerOrder( false );
157  setCustomLayerOrder( QStringList() );
158 }
159 
160 void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
161 {
162  Q_ASSERT( node );
163 
164  // collect layer IDs that have been added in order to put them into custom layer order
165  QList<QgsMapLayer *> layers;
166 
167  QList<QgsLayerTreeNode *> children = node->children();
168  for ( int i = indexFrom; i <= indexTo; ++i )
169  {
170  QgsLayerTreeNode *child = children.at( i );
171  if ( QgsLayerTree::isLayer( child ) )
172  {
173  layers << QgsLayerTree::toLayer( child )->layer();
174  }
175  else if ( QgsLayerTree::isGroup( child ) )
176  {
177  const auto nodeLayers = QgsLayerTree::toGroup( child )->findLayers();
178  for ( QgsLayerTreeLayer *nodeL : nodeLayers )
179  layers << nodeL->layer();
180  }
181  }
182 
183  for ( QgsMapLayer *layer : std::as_const( layers ) )
184  {
185  if ( !mCustomLayerOrder.contains( layer ) && layer )
186  mCustomLayerOrder.append( layer );
187  }
188 
190  emit layerOrderChanged();
191 }
192 
193 void QgsLayerTree::nodeRemovedChildren()
194 {
195  QList<QgsMapLayer *> layers = customLayerOrder();
196  auto layer = layers.begin();
197 
198  while ( layer != layers.end() )
199  {
200  if ( !findLayer( *layer ) )
201  layer = layers.erase( layer );
202  else
203  ++layer;
204  }
205 
206  // we need to ensure that the customLayerOrderChanged signal is ALWAYS raised
207  // here, since that order HAS changed due to removal of the child!
208  // setCustomLayerOrder will only emit this signal when the layers list
209  // at this stage is different to the stored customer layer order. If this
210  // isn't the case (i.e. the lists ARE the same) then manually emit the
211  // signal
212  const bool emitSignal = _qgis_listRawToQPointer( layers ) == mCustomLayerOrder;
213 
214  setCustomLayerOrder( layers );
215  if ( emitSignal )
217 
218  emit layerOrderChanged();
219 }
220 
221 void QgsLayerTree::addMissingLayers()
222 {
223  bool changed = false;
224 
225  const QList< QgsLayerTreeLayer * > layers = findLayers();
226  for ( const auto layer : layers )
227  {
228  if ( !mCustomLayerOrder.contains( layer->layer() ) &&
229  layer->layer() && layer->layer()->isSpatial() )
230  {
231  mCustomLayerOrder.append( layer->layer() );
232  changed = true;
233  }
234  }
235 
236  if ( changed )
237  {
239  if ( mHasCustomLayerOrder )
240  emit layerOrderChanged();
241  }
242 }
243 
244 void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem )
245 {
246  QStringList order;
247 
248  QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
249  if ( !customOrderElem.isNull() )
250  {
251  setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() );
252 
253  QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
254  while ( !itemElem.isNull() )
255  {
256  order.append( itemElem.text() );
257  itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
258  }
259  }
260 
261  setCustomLayerOrder( order );
262  addMissingLayers();
263 }
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.
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 * > children()
Gets list of children of the node. Children are owned by the parent.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
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.
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.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
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.
Definition: qgslayertree.h:43
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.
void layerOrderChanged()
Emitted when the layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
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.
Definition: qgsmaplayer.h:2153