QGIS API Documentation  2.99.0-Master (19b062c)
qgslayertreegroup.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreegroup.cpp
3  --------------------------------------
4  Date : May 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
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 "qgslayertreegroup.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreeutils.h"
20 #include "qgsmaplayer.h"
21 
22 #include <QDomElement>
23 #include <QStringList>
24 
25 
26 QgsLayerTreeGroup::QgsLayerTreeGroup( const QString &name, bool checked )
27  : QgsLayerTreeNode( NodeGroup, checked )
28  , mName( name )
29 {
31 }
32 
34  : QgsLayerTreeNode( other )
35  , mName( other.mName )
39 {
41 }
42 
43 QString QgsLayerTreeGroup::name() const
44 {
45  return mName;
46 }
47 
48 void QgsLayerTreeGroup::setName( const QString &n )
49 {
50  if ( mName == n )
51  return;
52 
53  mName = n;
54  emit nameChanged( this, n );
55 }
56 
57 
59 {
60  QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
61  insertChildNode( index, grp );
62  return grp;
63 }
64 
66 {
67  QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
68  addChildNode( grp );
69  return grp;
70 }
71 
73 {
74  if ( !layer )
75  return nullptr;
76 
77  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
78  insertChildNode( index, ll );
79  return ll;
80 }
81 
83 {
84  if ( !layer )
85  return nullptr;
86 
87  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
88  addChildNode( ll );
89  return ll;
90 }
91 
93 {
94  QList<QgsLayerTreeNode *> nodes;
95  nodes << node;
96  insertChildNodes( index, nodes );
97 }
98 
99 void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode *> &nodes )
100 {
101  QgsLayerTreeNode *meChild = nullptr;
103  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
104 
105  // low-level insert
106  insertChildrenPrivate( index, nodes );
107 
108  if ( mMutuallyExclusive )
109  {
110  if ( meChild )
111  {
112  // the child could have change its index - or the new children may have been also set as visible
113  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
114  }
115  else if ( mChecked )
116  {
117  // we have not picked a child index yet, but we should pick one now
118  // ... so pick the first one from the newly added
119  if ( index == -1 )
120  index = mChildren.count() - nodes.count(); // get real insertion index
122  }
124  }
125 }
126 
128 {
129  insertChildNode( -1, node );
130 }
131 
133 {
134  int i = mChildren.indexOf( node );
135  if ( i >= 0 )
136  removeChildren( i, 1 );
137 }
138 
140 {
141  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
142  {
143  if ( QgsLayerTree::isLayer( child ) )
144  {
145  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
146  if ( childLayer->layer() == layer )
147  {
148  removeChildren( mChildren.indexOf( child ), 1 );
149  break;
150  }
151  }
152  }
153 }
154 
155 void QgsLayerTreeGroup::removeChildren( int from, int count )
156 {
157  QgsLayerTreeNode *meChild = nullptr;
159  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
160 
161  removeChildrenPrivate( from, count );
162 
163  if ( meChild )
164  {
165  // the child could have change its index - or may have been removed completely
166  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
167  // we need to uncheck this group
168  //if ( mMutuallyExclusiveChildIndex == -1 )
169  // setItemVisibilityChecked( false );
170  }
171 }
172 
174 {
175  // clean the layer tree by removing empty group
176  Q_FOREACH ( QgsLayerTreeNode *treeNode, children() )
177  {
178  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
179  {
180  QgsLayerTreeGroup *treeGroup = qobject_cast<QgsLayerTreeGroup *>( treeNode );
181  if ( treeGroup->findLayerIds().isEmpty() )
182  removeChildNode( treeNode );
183  else
185  }
186  }
187 }
188 
190 {
191  removeChildren( 0, mChildren.count() );
192 }
193 
195 {
196  return findLayer( layer->id() );
197 }
198 
199 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString &layerId ) const
200 {
201  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
202  {
203  if ( QgsLayerTree::isLayer( child ) )
204  {
205  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
206  if ( childLayer->layerId() == layerId )
207  return childLayer;
208  }
209  else if ( QgsLayerTree::isGroup( child ) )
210  {
211  QgsLayerTreeLayer *res = QgsLayerTree::toGroup( child )->findLayer( layerId );
212  if ( res )
213  return res;
214  }
215  }
216  return nullptr;
217 }
218 
219 QList<QgsLayerTreeLayer *> QgsLayerTreeGroup::findLayers() const
220 {
221  QList<QgsLayerTreeLayer *> list;
222  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
223  {
224  if ( QgsLayerTree::isLayer( child ) )
225  list << QgsLayerTree::toLayer( child );
226  else if ( QgsLayerTree::isGroup( child ) )
227  list << QgsLayerTree::toGroup( child )->findLayers();
228  }
229  return list;
230 }
231 
233 {
234  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
235  {
236  if ( QgsLayerTree::isGroup( child ) )
237  {
238  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
239  if ( childGroup->name() == name )
240  return childGroup;
241  else
242  {
243  QgsLayerTreeGroup *grp = childGroup->findGroup( name );
244  if ( grp )
245  return grp;
246  }
247  }
248  }
249  return nullptr;
250 }
251 
252 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsReadWriteContext &context )
253 {
254  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
255  return nullptr;
256 
257  QString name = element.attribute( QStringLiteral( "name" ) );
258  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
259  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
260  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
261  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
262 
263  QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
264  groupNode->setExpanded( isExpanded );
265 
266  groupNode->readCommonXml( element );
267 
268  groupNode->readChildrenFromXml( element, context );
269 
270  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
271 
272  return groupNode;
273 }
274 
275 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project, const QgsReadWriteContext &context )
276 {
277  QgsLayerTreeGroup *node = readXml( element, context );
278  if ( node )
279  node->resolveReferences( project );
280  return node;
281 }
282 
283 void QgsLayerTreeGroup::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
284 {
285  QDomDocument doc = parentElement.ownerDocument();
286  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
287  elem.setAttribute( QStringLiteral( "name" ), mName );
288  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
289  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
290  if ( mMutuallyExclusive )
291  {
292  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
293  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
294  }
295 
296  writeCommonXml( elem );
297 
298  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
299  node->writeXml( elem, context );
300 
301  parentElement.appendChild( elem );
302 }
303 
304 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element, const QgsReadWriteContext &context )
305 {
306  QList<QgsLayerTreeNode *> nodes;
307  QDomElement childElem = element.firstChildElement();
308  while ( !childElem.isNull() )
309  {
310  QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem, context );
311  if ( newNode )
312  nodes << newNode;
313 
314  childElem = childElem.nextSiblingElement();
315  }
316 
317  insertChildNodes( -1, nodes );
318 }
319 
320 QString QgsLayerTreeGroup::dump() const
321 {
322  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
323  QStringList childrenDump;
324  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
325  childrenDump << node->dump().split( '\n' );
326  for ( int i = 0; i < childrenDump.count(); ++i )
327  childrenDump[i].prepend( " " );
328  return header + childrenDump.join( QStringLiteral( "\n" ) );
329 }
330 
332 {
333  return new QgsLayerTreeGroup( *this );
334 }
335 
336 void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
337 {
338  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
339  node->resolveReferences( project, looseMatching );
340 }
341 
342 static bool _nodeIsChecked( QgsLayerTreeNode *node )
343 {
344  return node->itemVisibilityChecked();
345 }
346 
347 
349 {
350  return mMutuallyExclusive;
351 }
352 
353 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
354 {
355  mMutuallyExclusive = enabled;
356  mMutuallyExclusiveChildIndex = initialChildIndex;
357 
358  if ( !enabled )
359  {
360  return;
361  }
362 
363  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
364  {
365  // try to use first checked index
366  int index = 0;
367  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
368  {
369  if ( _nodeIsChecked( child ) )
370  {
372  break;
373  }
374  index++;
375  }
376  }
377 
379 }
380 
382 {
383  QStringList lst;
384  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
385  {
386  if ( QgsLayerTree::isGroup( child ) )
387  lst << QgsLayerTree::toGroup( child )->findLayerIds();
388  else if ( QgsLayerTree::isLayer( child ) )
389  lst << QgsLayerTree::toLayer( child )->layerId();
390  }
391  return lst;
392 }
393 
395 {
396  int childIndex = mChildren.indexOf( node );
397  if ( childIndex == -1 )
398  return; // not a direct child - ignore
399 
400  if ( mMutuallyExclusive )
401  {
402  if ( _nodeIsChecked( node ) )
403  mMutuallyExclusiveChildIndex = childIndex;
404  else if ( mMutuallyExclusiveChildIndex == childIndex )
406 
407  // we need to make sure there is only one child node checked
409  }
410 }
411 
413 {
414  if ( mChildren.isEmpty() )
415  return;
416 
417  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
418 
419  int index = 0;
420  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
421  {
423  ++index;
424  }
425 
426  mChangingChildVisibility = false;
427 }
428 
430 {
432 
433  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
434 
435  int index = 0;
436  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
437  {
439  ++index;
440  }
441 
442  mChangingChildVisibility = false;
443 }
void nodeVisibilityChanged(QgsLayerTreeNode *node)
Layer tree group node serves as a container for layers and further groups.
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.
void removeChildren(int from, int count)
Remove child nodes from index "from".
void removeChildrenGroupWithoutLayers()
Remove all child group nodes without layers.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:56
QgsLayerTreeGroup * addGroup(const QString &name)
Append a new group node with given name.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
void setIsMutuallyExclusive(bool enabled, int initialChildIndex=-1)
Set whether the group is mutually exclusive (only one child can be checked at a time).
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void insertChildrenPrivate(int index, QList< QgsLayerTreeNode *> nodes)
Low-level insertion of children to the node. The children must not have any parent yet! ...
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeAllChildren()
Remove all child nodes.
bool mExpanded
whether the node should be shown in GUI as expanded
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
virtual QString dump() const =0
Return string with layer tree structure. For debug purposes only.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element...
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
virtual void setItemVisibilityCheckedRecursive(bool checked) override
Check or uncheck a node and all its children (taking into account exclusion rules) ...
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
virtual void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QString layerId() const
bool isMutuallyExclusive() const
Return whether the group is mutually exclusive (only one child can be checked at a time) ...
void removeLayer(QgsMapLayer *layer)
Remove map layer&#39;s node from this group.
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
virtual void setItemVisibilityCheckedRecursive(bool checked)
Check or uncheck a node and all its children (taking into account exclusion rules) ...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
QgsLayerTreeLayer * insertLayer(int index, QgsMapLayer *layer)
Insert a new layer node for given map layer at specified position.
void writeCommonXml(QDomElement &element)
Write common XML elements.
void setName(const QString &n) override
Sets the group&#39;s name.
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.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
static QgsLayerTreeGroup * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on ...
Reads and writes project states.
Definition: qgsproject.h:81
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position.
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void setExpanded(bool expanded)
Set whether the node should be shown as expanded or collapsed in GUI.
void removeChildrenPrivate(int from, int count, bool destroy=true)
Low-level removal of children from the node.
QgsMapLayer * layer() const
void updateChildVisibilityMutuallyExclusive()
Set check state of children - if mutually exclusive.
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
virtual QString dump() const override
Return text representation of the tree.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
virtual QgsLayerTreeGroup * clone() const override
Return a clone of the group.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
void readCommonXml(QDomElement &element)
Read common XML elements.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
bool mMutuallyExclusive
Whether the group is mutually exclusive (i.e. only one child can be checked at a time) ...
virtual void resolveReferences(const QgsProject *project, bool looseMatching=false)=0
Turn textual references to layers into map layer object from project.
void addChildNode(QgsLayerTreeNode *node)
Append an existing node.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QString name() const override
Returns the group&#39;s name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
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.
Container of other groups and layers.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
QgsLayerTreeGroup * insertGroup(int index, const QString &name)
Insert a new group node with given name at specified position.
Layer tree node points to a map layer.