QGIS API Documentation  3.2.0-Bonn (bc43194)
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.h"
18 #include "qgsvectorlayer.h"
19 
21 {
22  connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
23  connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
24 }
25 
27  : QgsLayerTreeGroup( other )
28  , mCustomLayerOrder( other.mCustomLayerOrder )
29  , mHasCustomLayerOrder( other.mHasCustomLayerOrder )
30 {
31  connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren );
32  connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren );
33 }
34 
35 QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const
36 {
37  return _qgis_listQPointerToRaw( mCustomLayerOrder );
38 }
39 
40 void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder )
41 {
42  QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder );
43 
44  if ( newOrder == mCustomLayerOrder )
45  return;
46 
47  mCustomLayerOrder = newOrder;
49 
50  if ( mHasCustomLayerOrder )
51  emit layerOrderChanged();
52 }
53 
55 {
56  QList<QgsMapLayer *> layers;
57 
58  for ( const auto &layerId : customLayerOrder )
59  {
60  QgsLayerTreeLayer *nodeLayer = findLayer( layerId );
61  if ( nodeLayer )
62  {
63  // configuration from 2.x projects might have non spatial layers
64  QgsMapLayer *layer = nodeLayer->layer();
65  if ( !layer || !layer->isSpatial() )
66  {
67  continue;
68  }
69  layers.append( layer );
70  }
71  }
72  setCustomLayerOrder( layers );
73 }
74 
75 QList<QgsMapLayer *> QgsLayerTree::layerOrder() const
76 {
77  if ( mHasCustomLayerOrder )
78  {
79  return customLayerOrder();
80  }
81  else
82  {
83  QList<QgsMapLayer *> layers;
84  const QList< QgsLayerTreeLayer * > foundLayers = findLayers();
85  for ( const auto &treeLayer : foundLayers )
86  {
87  QgsMapLayer *layer = treeLayer->layer();
88  if ( !layer || !layer->isSpatial() )
89  {
90  continue;
91  }
92  layers.append( layer );
93  }
94  return layers;
95  }
96 }
97 
99 {
100  return mHasCustomLayerOrder;
101 }
102 
104 {
105  if ( hasCustomLayerOrder == mHasCustomLayerOrder )
106  return;
107 
108  mHasCustomLayerOrder = hasCustomLayerOrder;
109 
110  emit hasCustomLayerOrderChanged( hasCustomLayerOrder );
111  emit layerOrderChanged();
112 }
113 
114 QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context )
115 {
116  QgsLayerTree *tree = new QgsLayerTree();
117 
118  tree->readCommonXml( element );
119 
120  tree->readChildrenFromXml( element, context );
121 
122  return tree;
123 }
124 
125 void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
126 {
127  QDomDocument doc = parentElement.ownerDocument();
128  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
129 
130  writeCommonXml( elem );
131 
132  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
133  node->writeXml( elem, context );
134 
135  QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
136  customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
137  elem.appendChild( customOrderElem );
138 
139  Q_FOREACH ( QgsMapLayer *layer, mCustomLayerOrder )
140  {
141  // Safety belt, see https://issues.qgis.org/issues/19145
142  // Crash when deleting an item from the layout legend
143  if ( ! layer )
144  continue;
145  QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) );
146  layerElem.appendChild( doc.createTextNode( layer->id() ) );
147  customOrderElem.appendChild( layerElem );
148  }
149 
150  elem.appendChild( customOrderElem );
151 
152  parentElement.appendChild( elem );
153 }
154 
156 {
157  return new QgsLayerTree( *this );
158 }
159 
161 {
163  setHasCustomLayerOrder( false );
164  setCustomLayerOrder( QStringList() );
165 }
166 
167 void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
168 {
169  Q_ASSERT( node );
170 
171  // collect layer IDs that have been added in order to put them into custom layer order
172  QList<QgsMapLayer *> layers;
173 
174  QList<QgsLayerTreeNode *> children = node->children();
175  for ( int i = indexFrom; i <= indexTo; ++i )
176  {
177  QgsLayerTreeNode *child = children.at( i );
178  if ( QgsLayerTree::isLayer( child ) )
179  {
180  layers << QgsLayerTree::toLayer( child )->layer();
181  }
182  else if ( QgsLayerTree::isGroup( child ) )
183  {
184  Q_FOREACH ( QgsLayerTreeLayer *nodeL, QgsLayerTree::toGroup( child )->findLayers() )
185  layers << nodeL->layer();
186  }
187  }
188 
189  Q_FOREACH ( QgsMapLayer *layer, layers )
190  {
191  if ( !mCustomLayerOrder.contains( layer ) && layer )
192  mCustomLayerOrder.append( layer );
193  }
194 
196  emit layerOrderChanged();
197 }
198 
199 void QgsLayerTree::nodeRemovedChildren()
200 {
201  QList<QgsMapLayer *> layers = customLayerOrder();
202  auto layer = layers.begin();
203 
204  while ( layer != layers.end() )
205  {
206  if ( !findLayer( *layer ) )
207  layer = layers.erase( layer );
208  else
209  ++layer;
210  }
211 
212  setCustomLayerOrder( layers );
213  emit layerOrderChanged();
214 }
215 
216 void QgsLayerTree::addMissingLayers()
217 {
218  bool changed = false;
219 
220  const QList< QgsLayerTreeLayer * > foundLayers = findLayers();
221  for ( const auto layer : foundLayers )
222  {
223  if ( !mCustomLayerOrder.contains( layer->layer() ) &&
224  layer->layer() && layer->layer()->isSpatial() )
225  {
226  mCustomLayerOrder.append( layer->layer() );
227  changed = true;
228  }
229  }
230 
231  if ( changed )
232  {
234  if ( mHasCustomLayerOrder )
235  emit layerOrderChanged();
236  }
237 }
238 
239 void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem )
240 {
241  QStringList order;
242 
243  QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
244  if ( !customOrderElem.isNull() )
245  {
246  setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() );
247 
248  QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
249  while ( !itemElem.isNull() )
250  {
251  order.append( itemElem.text() );
252  itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
253  }
254  }
255 
256  setCustomLayerOrder( order );
257  addMissingLayers();
258 }
The class is used as a container of context for various read/write operations on other objects...
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:61
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeAllChildren()
Remove all child nodes.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
void layerOrderChanged()
Emitted when the layer order has changed.
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
void writeCommonXml(QDomElement &element)
Write common XML elements.
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
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
This class is a base class for nodes in a layer tree.
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
QgsMapLayer * layer() const
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
void setCustomLayerOrder(const QList< QgsMapLayer *> &customLayerOrder)
The order in which layers will be rendered on the canvas.
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write layer tree to XML.
QList< QgsWeakMapLayerPointer > QgsWeakMapLayerPointerList
A list of weak pointers to QgsMapLayers.
Definition: qgsmaplayer.h:1392
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
void readCommonXml(QDomElement &element)
Read common XML elements.
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsLayerTree()
Create a new empty layer tree.
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Layer tree node points to a map layer.