QGIS API Documentation  2.99.0-Master (dcec6bb)
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  , mChangingChildVisibility( false )
30  , mMutuallyExclusive( false )
31  , mMutuallyExclusiveChildIndex( -1 )
32 {
34 }
35 
37  : QgsLayerTreeNode( other )
38  , mName( other.mName )
42 {
44 }
45 
46 QString QgsLayerTreeGroup::name() const
47 {
48  return mName;
49 }
50 
51 void QgsLayerTreeGroup::setName( const QString &n )
52 {
53  if ( mName == n )
54  return;
55 
56  mName = n;
57  emit nameChanged( this, n );
58 }
59 
60 
62 {
63  QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
64  insertChildNode( index, grp );
65  return grp;
66 }
67 
69 {
70  QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
71  addChildNode( grp );
72  return grp;
73 }
74 
76 {
77  if ( !layer )
78  return nullptr;
79 
80  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
81  insertChildNode( index, ll );
82  return ll;
83 }
84 
86 {
87  if ( !layer )
88  return nullptr;
89 
90  QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
91  addChildNode( ll );
92  return ll;
93 }
94 
96 {
97  QList<QgsLayerTreeNode *> nodes;
98  nodes << node;
99  insertChildNodes( index, nodes );
100 }
101 
102 void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode *> &nodes )
103 {
104  QgsLayerTreeNode *meChild = nullptr;
106  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
107 
108  // low-level insert
109  insertChildrenPrivate( index, nodes );
110 
111  if ( mMutuallyExclusive )
112  {
113  if ( meChild )
114  {
115  // the child could have change its index - or the new children may have been also set as visible
116  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
117  }
118  else if ( mChecked )
119  {
120  // we have not picked a child index yet, but we should pick one now
121  // ... so pick the first one from the newly added
122  if ( index == -1 )
123  index = mChildren.count() - nodes.count(); // get real insertion index
125  }
127  }
128 }
129 
131 {
132  insertChildNode( -1, node );
133 }
134 
136 {
137  int i = mChildren.indexOf( node );
138  if ( i >= 0 )
139  removeChildren( i, 1 );
140 }
141 
143 {
144  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
145  {
146  if ( QgsLayerTree::isLayer( child ) )
147  {
148  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
149  if ( childLayer->layer() == layer )
150  {
151  removeChildren( mChildren.indexOf( child ), 1 );
152  break;
153  }
154  }
155  }
156 }
157 
158 void QgsLayerTreeGroup::removeChildren( int from, int count )
159 {
160  QgsLayerTreeNode *meChild = nullptr;
162  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
163 
164  removeChildrenPrivate( from, count );
165 
166  if ( meChild )
167  {
168  // the child could have change its index - or may have been removed completely
169  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
170  // we need to uncheck this group
171  //if ( mMutuallyExclusiveChildIndex == -1 )
172  // setItemVisibilityChecked( false );
173  }
174 }
175 
177 {
178  // clean the layer tree by removing empty group
179  Q_FOREACH ( QgsLayerTreeNode *treeNode, children() )
180  {
181  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
182  {
183  QgsLayerTreeGroup *treeGroup = qobject_cast<QgsLayerTreeGroup *>( treeNode );
184  if ( treeGroup->findLayerIds().isEmpty() )
185  removeChildNode( treeNode );
186  else
188  }
189  }
190 }
191 
193 {
194  removeChildren( 0, mChildren.count() );
195 }
196 
198 {
199  return findLayer( layer->id() );
200 }
201 
202 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString &layerId ) const
203 {
204  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
205  {
206  if ( QgsLayerTree::isLayer( child ) )
207  {
208  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
209  if ( childLayer->layerId() == layerId )
210  return childLayer;
211  }
212  else if ( QgsLayerTree::isGroup( child ) )
213  {
214  QgsLayerTreeLayer *res = QgsLayerTree::toGroup( child )->findLayer( layerId );
215  if ( res )
216  return res;
217  }
218  }
219  return nullptr;
220 }
221 
222 QList<QgsLayerTreeLayer *> QgsLayerTreeGroup::findLayers() const
223 {
224  QList<QgsLayerTreeLayer *> list;
225  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
226  {
227  if ( QgsLayerTree::isLayer( child ) )
228  list << QgsLayerTree::toLayer( child );
229  else if ( QgsLayerTree::isGroup( child ) )
230  list << QgsLayerTree::toGroup( child )->findLayers();
231  }
232  return list;
233 }
234 
236 {
237  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
238  {
239  if ( QgsLayerTree::isGroup( child ) )
240  {
241  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
242  if ( childGroup->name() == name )
243  return childGroup;
244  else
245  {
246  QgsLayerTreeGroup *grp = childGroup->findGroup( name );
247  if ( grp )
248  return grp;
249  }
250  }
251  }
252  return nullptr;
253 }
254 
256 {
257  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
258  return nullptr;
259 
260  QString name = element.attribute( QStringLiteral( "name" ) );
261  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
262  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
263  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
264  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
265 
266  QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
267  groupNode->setExpanded( isExpanded );
268 
269  groupNode->readCommonXml( element );
270 
271  groupNode->readChildrenFromXml( element );
272 
273  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
274 
275  return groupNode;
276 }
277 
278 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project )
279 {
280  QgsLayerTreeGroup *node = readXml( element );
281  if ( node )
282  node->resolveReferences( project );
283  return node;
284 }
285 
286 void QgsLayerTreeGroup::writeXml( QDomElement &parentElement )
287 {
288  QDomDocument doc = parentElement.ownerDocument();
289  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
290  elem.setAttribute( QStringLiteral( "name" ), mName );
291  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
292  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
293  if ( mMutuallyExclusive )
294  {
295  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
296  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
297  }
298 
299  writeCommonXml( elem );
300 
301  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
302  node->writeXml( elem );
303 
304  parentElement.appendChild( elem );
305 }
306 
307 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element )
308 {
309  QList<QgsLayerTreeNode *> nodes;
310  QDomElement childElem = element.firstChildElement();
311  while ( !childElem.isNull() )
312  {
313  QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem );
314  if ( newNode )
315  nodes << newNode;
316 
317  childElem = childElem.nextSiblingElement();
318  }
319 
320  insertChildNodes( -1, nodes );
321 }
322 
323 QString QgsLayerTreeGroup::dump() const
324 {
325  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
326  QStringList childrenDump;
327  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
328  childrenDump << node->dump().split( '\n' );
329  for ( int i = 0; i < childrenDump.count(); ++i )
330  childrenDump[i].prepend( " " );
331  return header + childrenDump.join( QStringLiteral( "\n" ) );
332 }
333 
335 {
336  return new QgsLayerTreeGroup( *this );
337 }
338 
339 void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
340 {
341  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
342  node->resolveReferences( project, looseMatching );
343 }
344 
345 static bool _nodeIsChecked( QgsLayerTreeNode *node )
346 {
347  return node->itemVisibilityChecked();
348 }
349 
350 
352 {
353  return mMutuallyExclusive;
354 }
355 
356 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
357 {
358  mMutuallyExclusive = enabled;
359  mMutuallyExclusiveChildIndex = initialChildIndex;
360 
361  if ( !enabled )
362  {
363  return;
364  }
365 
366  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
367  {
368  // try to use first checked index
369  int index = 0;
370  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
371  {
372  if ( _nodeIsChecked( child ) )
373  {
375  break;
376  }
377  index++;
378  }
379  }
380 
382 }
383 
385 {
386  QStringList lst;
387  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
388  {
389  if ( QgsLayerTree::isGroup( child ) )
390  lst << QgsLayerTree::toGroup( child )->findLayerIds();
391  else if ( QgsLayerTree::isLayer( child ) )
392  lst << QgsLayerTree::toLayer( child )->layerId();
393  }
394  return lst;
395 }
396 
398 {
399  int childIndex = mChildren.indexOf( node );
400  if ( childIndex == -1 )
401  return; // not a direct child - ignore
402 
403  if ( mMutuallyExclusive )
404  {
405  if ( _nodeIsChecked( node ) )
406  mMutuallyExclusiveChildIndex = childIndex;
407  else if ( mMutuallyExclusiveChildIndex == childIndex )
409 
410  // we need to make sure there is only one child node checked
412  }
413 }
414 
416 {
417  if ( mChildren.isEmpty() )
418  return;
419 
420  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
421 
422  int index = 0;
423  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
424  {
426  ++index;
427  }
428 
429  mChangingChildVisibility = false;
430 }
431 
433 {
435 
436  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
437 
438  int index = 0;
439  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
440  {
442  ++index;
443  }
444 
445  mChangingChildVisibility = false;
446 }
void nodeVisibilityChanged(QgsLayerTreeNode *node)
Layer tree group node serves as a container for layers and further groups.
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:74
Base class for all map layer types.
Definition: qgsmaplayer.h:54
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:42
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 readChildrenFromXml(QDomElement &element)
Read children from XML and append them to the group.
void insertChildrenPrivate(int index, QList< QgsLayerTreeNode *> nodes)
Low-level insertion of children to the node. The children must not have any parent yet! ...
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:63
virtual void writeXml(QDomElement &parentElement) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element...
virtual QString dump() const =0
Return string with layer tree structure. For debug purposes only.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
static QgsLayerTreeGroup * readXml(QDomElement &element)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on ...
virtual void setItemVisibilityCheckedRecursive(bool checked) override
Check or uncheck a node and all its children (taking into account exclusion rules) ...
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.
virtual void writeXml(QDomElement &parentElement)=0
Write layer tree to XML.
static QgsLayerTreeNode * readXml(QDomElement &element)
Read layer tree from XML.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:52
This class is a base class for nodes in a layer tree.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
Reads and writes project states.
Definition: qgsproject.h:78
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.