QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  for ( QgsLayerTreeNode *child : qgis::as_const( 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  const auto childNodes = children();
177  for ( QgsLayerTreeNode *treeNode : childNodes )
178  {
179  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
180  {
181  QgsLayerTreeGroup *treeGroup = qobject_cast<QgsLayerTreeGroup *>( treeNode );
182  if ( treeGroup->findLayerIds().isEmpty() )
183  removeChildNode( treeNode );
184  else
186  }
187  }
188 }
189 
191 {
192  removeChildren( 0, mChildren.count() );
193 }
194 
196 {
197  if ( !layer )
198  return nullptr;
199 
200  return findLayer( layer->id() );
201 }
202 
203 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString &layerId ) const
204 {
205  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
206  {
207  if ( QgsLayerTree::isLayer( child ) )
208  {
209  QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
210  if ( childLayer->layerId() == layerId )
211  return childLayer;
212  }
213  else if ( QgsLayerTree::isGroup( child ) )
214  {
215  QgsLayerTreeLayer *res = QgsLayerTree::toGroup( child )->findLayer( layerId );
216  if ( res )
217  return res;
218  }
219  }
220  return nullptr;
221 }
222 
223 QList<QgsLayerTreeLayer *> QgsLayerTreeGroup::findLayers() const
224 {
225  QList<QgsLayerTreeLayer *> list;
226  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
227  {
228  if ( QgsLayerTree::isLayer( child ) )
229  list << QgsLayerTree::toLayer( child );
230  else if ( QgsLayerTree::isGroup( child ) )
231  list << QgsLayerTree::toGroup( child )->findLayers();
232  }
233  return list;
234 }
235 
237 {
238  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
239  {
240  if ( QgsLayerTree::isGroup( child ) )
241  {
242  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
243  if ( childGroup->name() == name )
244  return childGroup;
245  else
246  {
247  QgsLayerTreeGroup *grp = childGroup->findGroup( name );
248  if ( grp )
249  return grp;
250  }
251  }
252  }
253  return nullptr;
254 }
255 
256 QList<QgsLayerTreeGroup *> QgsLayerTreeGroup::findGroups() const
257 {
258  QList<QgsLayerTreeGroup *> list;
259 
260  for ( QgsLayerTreeNode *child : mChildren )
261  {
262  if ( QgsLayerTree::isGroup( child ) )
263  list << QgsLayerTree::toGroup( child );
264  }
265  return list;
266 }
267 
268 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsReadWriteContext &context )
269 {
270  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
271  return nullptr;
272 
273  QString name = context.projectTranslator()->translate( QStringLiteral( "project:layergroups" ), element.attribute( QStringLiteral( "name" ) ) );
274  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
275  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
276  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
277  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
278 
279  QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
280  groupNode->setExpanded( isExpanded );
281 
282  groupNode->readCommonXml( element );
283 
284  groupNode->readChildrenFromXml( element, context );
285 
286  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
287 
288  return groupNode;
289 }
290 
291 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project, const QgsReadWriteContext &context )
292 {
293  QgsLayerTreeGroup *node = readXml( element, context );
294  if ( node )
295  node->resolveReferences( project );
296  return node;
297 }
298 
299 void QgsLayerTreeGroup::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
300 {
301  QDomDocument doc = parentElement.ownerDocument();
302  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
303  elem.setAttribute( QStringLiteral( "name" ), mName );
304  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
305  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
306  if ( mMutuallyExclusive )
307  {
308  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
309  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
310  }
311 
312  writeCommonXml( elem );
313 
314  for ( QgsLayerTreeNode *node : qgis::as_const( mChildren ) )
315  node->writeXml( elem, context );
316 
317  parentElement.appendChild( elem );
318 }
319 
320 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element, const QgsReadWriteContext &context )
321 {
322  QList<QgsLayerTreeNode *> nodes;
323  QDomElement childElem = element.firstChildElement();
324  while ( !childElem.isNull() )
325  {
326  QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem, context );
327  if ( newNode )
328  nodes << newNode;
329 
330  childElem = childElem.nextSiblingElement();
331  }
332 
333  insertChildNodes( -1, nodes );
334 }
335 
336 QString QgsLayerTreeGroup::dump() const
337 {
338  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
339  QStringList childrenDump;
340  for ( QgsLayerTreeNode *node : qgis::as_const( mChildren ) )
341  childrenDump << node->dump().split( '\n' );
342  for ( int i = 0; i < childrenDump.count(); ++i )
343  childrenDump[i].prepend( " " );
344  return header + childrenDump.join( QStringLiteral( "\n" ) );
345 }
346 
348 {
349  return new QgsLayerTreeGroup( *this );
350 }
351 
352 void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
353 {
354  for ( QgsLayerTreeNode *node : qgis::as_const( mChildren ) )
355  node->resolveReferences( project, looseMatching );
356 }
357 
358 static bool _nodeIsChecked( QgsLayerTreeNode *node )
359 {
360  return node->itemVisibilityChecked();
361 }
362 
363 
365 {
366  return mMutuallyExclusive;
367 }
368 
369 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
370 {
371  mMutuallyExclusive = enabled;
372  mMutuallyExclusiveChildIndex = initialChildIndex;
373 
374  if ( !enabled )
375  {
376  return;
377  }
378 
379  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
380  {
381  // try to use first checked index
382  int index = 0;
383  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
384  {
385  if ( _nodeIsChecked( child ) )
386  {
388  break;
389  }
390  index++;
391  }
392  }
393 
395 }
396 
398 {
399  QStringList lst;
400  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
401  {
402  if ( QgsLayerTree::isGroup( child ) )
403  lst << QgsLayerTree::toGroup( child )->findLayerIds();
404  else if ( QgsLayerTree::isLayer( child ) )
405  lst << QgsLayerTree::toLayer( child )->layerId();
406  }
407  return lst;
408 }
409 
411 {
412  int childIndex = mChildren.indexOf( node );
413  if ( childIndex == -1 )
414  return; // not a direct child - ignore
415 
416  if ( mMutuallyExclusive )
417  {
418  if ( _nodeIsChecked( node ) )
419  mMutuallyExclusiveChildIndex = childIndex;
420  else if ( mMutuallyExclusiveChildIndex == childIndex )
422 
423  // we need to make sure there is only one child node checked
425  }
426 }
427 
429 {
430  if ( mChildren.isEmpty() )
431  return;
432 
433  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
434 
435  int index = 0;
436  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
437  {
438  child->setItemVisibilityChecked( index == mMutuallyExclusiveChildIndex );
439  ++index;
440  }
441 
442  mChangingChildVisibility = false;
443 }
444 
446 {
448 
449  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
450 
451  int index = 0;
452  for ( QgsLayerTreeNode *child : qgis::as_const( mChildren ) )
453  {
454  child->setItemVisibilityCheckedRecursive( checked && ( mMutuallyExclusiveChildIndex < 0 || index == mMutuallyExclusiveChildIndex ) );
455  ++index;
456  }
457 
458  mChangingChildVisibility = false;
459 }
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:78
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! ...
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
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.
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
Returns whether the node should be shown as expanded or collapsed in GUI.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QString layerId() const
Returns the ID for the map layer associated with this node.
bool isMutuallyExclusive() const
Returns 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.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
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.
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.
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
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 nullptr ...
Reads and writes project states.
Definition: qgsproject.h:89
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...
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
The derived translate() translates with QTranslator and qm file the sourceText.
void removeChildrenPrivate(int from, int count, bool destroy=true)
Low-level removal of children from the node.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
void updateChildVisibilityMutuallyExclusive()
Set check state of children - if mutually exclusive.
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
QString dump() const override
Returns text representation of the tree.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
QgsLayerTreeGroup * clone() const override
Returns 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) ...
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.