QGIS API Documentation  2.99.0-Master (7fe5405)
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 #include "qgsproject.h"
22 
23 #include <QDomElement>
24 #include <QStringList>
25 
26 
27 QgsLayerTreeGroup::QgsLayerTreeGroup( const QString& name, bool checked )
28  : QgsLayerTreeNode( NodeGroup, checked )
29  , mName( name )
30  , mChangingChildVisibility( false )
31  , mMutuallyExclusive( false )
32  , mMutuallyExclusiveChildIndex( -1 )
33 {
35 }
36 
38  : QgsLayerTreeNode( other )
39  , mName( other.mName )
43 {
45 }
46 
47 QString QgsLayerTreeGroup::name() const
48 {
49  return mName;
50 }
51 
52 void QgsLayerTreeGroup::setName( const QString& n )
53 {
54  if ( mName == n )
55  return;
56 
57  mName = n;
58  emit nameChanged( this, n );
59 }
60 
61 
63 {
64  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
65  insertChildNode( index, grp );
66  return grp;
67 }
68 
70 {
71  QgsLayerTreeGroup* grp = new QgsLayerTreeGroup( name );
72  addChildNode( grp );
73  return grp;
74 }
75 
77 {
78  if ( !layer || QgsProject::instance()->mapLayer( layer->id() ) != layer )
79  return nullptr;
80 
81  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
82  insertChildNode( index, ll );
83  return ll;
84 }
85 
87 {
88  if ( !layer || QgsProject::instance()->mapLayer( layer->id() ) != layer )
89  return nullptr;
90 
91  QgsLayerTreeLayer* ll = new QgsLayerTreeLayer( layer );
92  addChildNode( ll );
93  return ll;
94 }
95 
97 {
98  QList<QgsLayerTreeNode*> nodes;
99  nodes << node;
100  insertChildNodes( index, nodes );
101 }
102 
103 void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode*>& nodes )
104 {
105  QgsLayerTreeNode* meChild = nullptr;
107  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
108 
109  // low-level insert
110  insertChildrenPrivate( index, nodes );
111 
112  if ( mMutuallyExclusive )
113  {
114  if ( meChild )
115  {
116  // the child could have change its index - or the new children may have been also set as visible
117  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
118  }
119  else if ( mChecked )
120  {
121  // we have not picked a child index yet, but we should pick one now
122  // ... so pick the first one from the newly added
123  if ( index == -1 )
124  index = mChildren.count() - nodes.count(); // get real insertion index
126  }
128  }
129 }
130 
132 {
133  insertChildNode( -1, node );
134 }
135 
137 {
138  int i = mChildren.indexOf( node );
139  if ( i >= 0 )
140  removeChildren( i, 1 );
141 }
142 
144 {
145  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
146  {
147  if ( QgsLayerTree::isLayer( child ) )
148  {
149  QgsLayerTreeLayer* childLayer = QgsLayerTree::toLayer( child );
150  if ( childLayer->layer() == layer )
151  {
152  removeChildren( mChildren.indexOf( child ), 1 );
153  break;
154  }
155  }
156  }
157 }
158 
159 void QgsLayerTreeGroup::removeChildren( int from, int count )
160 {
161  QgsLayerTreeNode* meChild = nullptr;
163  meChild = mChildren.at( mMutuallyExclusiveChildIndex );
164 
165  removeChildrenPrivate( from, count );
166 
167  if ( meChild )
168  {
169  // the child could have change its index - or may have been removed completely
170  mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
171  // we need to uncheck this group
172  //if ( mMutuallyExclusiveChildIndex == -1 )
173  // setItemVisibilityChecked( false );
174  }
175 }
176 
178 {
179  // clean the layer tree by removing empty group
180  Q_FOREACH ( QgsLayerTreeNode* treeNode, children() )
181  {
182  if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
183  {
184  QgsLayerTreeGroup* treeGroup = qobject_cast<QgsLayerTreeGroup*>( treeNode );
185  if ( treeGroup->findLayerIds().isEmpty() )
186  removeChildNode( treeNode );
187  else
189  }
190  }
191 }
192 
194 {
195  removeChildren( 0, mChildren.count() );
196 }
197 
199 {
200  return findLayer( layer->id() );
201 }
202 
203 QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString& layerId ) const
204 {
205  Q_FOREACH ( QgsLayerTreeNode* child, 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  Q_FOREACH ( QgsLayerTreeNode* child, 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  Q_FOREACH ( QgsLayerTreeNode* child, 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 
257 {
258  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
259  return nullptr;
260 
261  QString name = element.attribute( QStringLiteral( "name" ) );
262  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
263  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
264  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
265  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
266 
267  QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( name, checked );
268  groupNode->setExpanded( isExpanded );
269 
270  groupNode->readCommonXml( element );
271 
272  groupNode->readChildrenFromXml( element );
273 
274  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
275 
276  return groupNode;
277 }
278 
279 void QgsLayerTreeGroup::writeXml( QDomElement& parentElement )
280 {
281  QDomDocument doc = parentElement.ownerDocument();
282  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
283  elem.setAttribute( QStringLiteral( "name" ), mName );
284  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
285  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
286  if ( mMutuallyExclusive )
287  {
288  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
289  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
290  }
291 
292  writeCommonXml( elem );
293 
294  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
295  node->writeXml( elem );
296 
297  parentElement.appendChild( elem );
298 }
299 
300 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement& element )
301 {
302  QList<QgsLayerTreeNode*> nodes;
303  QDomElement childElem = element.firstChildElement();
304  while ( !childElem.isNull() )
305  {
306  QgsLayerTreeNode* newNode = QgsLayerTreeNode::readXml( childElem );
307  if ( newNode )
308  nodes << newNode;
309 
310  childElem = childElem.nextSiblingElement();
311  }
312 
313  insertChildNodes( -1, nodes );
314 }
315 
316 QString QgsLayerTreeGroup::dump() const
317 {
318  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
319  QStringList childrenDump;
320  Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
321  childrenDump << node->dump().split( '\n' );
322  for ( int i = 0; i < childrenDump.count(); ++i )
323  childrenDump[i].prepend( " " );
324  return header + childrenDump.join( QStringLiteral( "\n" ) );
325 }
326 
328 {
329  return new QgsLayerTreeGroup( *this );
330 }
331 
332 static bool _nodeIsChecked( QgsLayerTreeNode* node )
333 {
334  return node->itemVisibilityChecked();
335 }
336 
337 
339 {
340  return mMutuallyExclusive;
341 }
342 
343 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
344 {
345  mMutuallyExclusive = enabled;
346  mMutuallyExclusiveChildIndex = initialChildIndex;
347 
348  if ( !enabled )
349  {
350  return;
351  }
352 
353  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
354  {
355  // try to use first checked index
356  int index = 0;
357  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
358  {
359  if ( _nodeIsChecked( child ) )
360  {
362  break;
363  }
364  index++;
365  }
366  }
367 
369 }
370 
372 {
373  QStringList lst;
374  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
375  {
376  if ( QgsLayerTree::isGroup( child ) )
377  lst << QgsLayerTree::toGroup( child )->findLayerIds();
378  else if ( QgsLayerTree::isLayer( child ) )
379  lst << QgsLayerTree::toLayer( child )->layerId();
380  }
381  return lst;
382 }
383 
385 {
386  int childIndex = mChildren.indexOf( node );
387  if ( childIndex == -1 )
388  return; // not a direct child - ignore
389 
390  if ( mMutuallyExclusive )
391  {
392  if ( _nodeIsChecked( node ) )
393  mMutuallyExclusiveChildIndex = childIndex;
394  else if ( mMutuallyExclusiveChildIndex == childIndex )
396 
397  // we need to make sure there is only one child node checked
399  }
400 }
401 
403 {
404  if ( mChildren.isEmpty() )
405  return;
406 
407  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
408 
409  int index = 0;
410  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
411  {
413  ++index;
414  }
415 
416  mChangingChildVisibility = false;
417 }
418 
420 {
422 
423  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
424 
425  int index = 0;
426  Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
427  {
429  ++index;
430  }
431 
432  mChangingChildVisibility = false;
433 }
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". The nodes will be deleted.
static unsigned index
void removeChildrenGroupWithoutLayers()
Remove all child group nodes without layers. The groupnodes will be deleted.
Base class for all map layer types.
Definition: qgsmaplayer.h:50
QgsLayerTreeGroup * addGroup(const QString &name)
Append a new group node with given name. Newly created node is owned by this group.
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. The nodes will be deleted.
bool mExpanded
whether the node should be shown in GUI as expanded
NodeType nodeType()
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
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. The node will be deleted.
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.
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. The node will be deleted.
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer. Newly created node is owned by this group.
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.
QgsLayerTreeLayer * insertLayer(int index, QgsMapLayer *layer)
Insert a new layer node for given map layer at specified position. Newly created node is owned by thi...
void writeCommonXml(QDomElement &element)
Write common XML elements.
void setName(const QString &n) override
Set 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. Returns new instance.
This class is a base class for nodes in a layer tree.
static Qt::CheckState checkStateFromXml(const QString &txt)
Convert QString to Qt::CheckState.
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position. The nodes must not have a parent yet. The nodes will be owned by this group.
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
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.
static bool _nodeIsChecked(QgsLayerTreeNode *node)
QgsLayerTreeGroup(const QString &name=QString(), bool checked=true)
Constructor.
virtual QString dump() const override
Return text representation of the tree. For debugging purposes only.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position. The node must not have a parent yet. The node will be own...
virtual QgsLayerTreeGroup * clone() const override
Return a clone of the group. The children are cloned too.
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
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. Searches recursively the whole sub-tree.
bool mMutuallyExclusive
Whether the group is mutually exclusive (i.e. only one child can be checked at a time) ...
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:350
void addChildNode(QgsLayerTreeNode *node)
Append an existing node. The node must not have a parent yet. The node will be owned by this group...
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QString name() const override
Get group&#39;s name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
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.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QgsLayerTreeGroup * insertGroup(int index, const QString &name)
Insert a new group node with given name at specified position. Newly created node is owned by this gr...
Layer tree node points to a map layer.