QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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  if ( !layer )
197  return nullptr;
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 
255 QList<QgsLayerTreeGroup *> QgsLayerTreeGroup::findGroups() const
256 {
257  QList<QgsLayerTreeGroup *> list;
258 
259  for ( QgsLayerTreeNode *child : mChildren )
260  {
261  if ( QgsLayerTree::isGroup( child ) )
262  list << QgsLayerTree::toGroup( child );
263  }
264  return list;
265 }
266 
267 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsReadWriteContext &context )
268 {
269  if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
270  return nullptr;
271 
272  QString name = context.projectTranslator()->translate( QStringLiteral( "project:layergroups" ), element.attribute( QStringLiteral( "name" ) ) );
273  bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
274  bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
275  bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
276  int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
277 
278  QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
279  groupNode->setExpanded( isExpanded );
280 
281  groupNode->readCommonXml( element );
282 
283  groupNode->readChildrenFromXml( element, context );
284 
285  groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
286 
287  return groupNode;
288 }
289 
290 QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project, const QgsReadWriteContext &context )
291 {
292  QgsLayerTreeGroup *node = readXml( element, context );
293  if ( node )
294  node->resolveReferences( project );
295  return node;
296 }
297 
298 void QgsLayerTreeGroup::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
299 {
300  QDomDocument doc = parentElement.ownerDocument();
301  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
302  elem.setAttribute( QStringLiteral( "name" ), mName );
303  elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
304  elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
305  if ( mMutuallyExclusive )
306  {
307  elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
308  elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
309  }
310 
311  writeCommonXml( elem );
312 
313  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
314  node->writeXml( elem, context );
315 
316  parentElement.appendChild( elem );
317 }
318 
319 void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element, const QgsReadWriteContext &context )
320 {
321  QList<QgsLayerTreeNode *> nodes;
322  QDomElement childElem = element.firstChildElement();
323  while ( !childElem.isNull() )
324  {
325  QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem, context );
326  if ( newNode )
327  nodes << newNode;
328 
329  childElem = childElem.nextSiblingElement();
330  }
331 
332  insertChildNodes( -1, nodes );
333 }
334 
335 QString QgsLayerTreeGroup::dump() const
336 {
337  QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
338  QStringList childrenDump;
339  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
340  childrenDump << node->dump().split( '\n' );
341  for ( int i = 0; i < childrenDump.count(); ++i )
342  childrenDump[i].prepend( " " );
343  return header + childrenDump.join( QStringLiteral( "\n" ) );
344 }
345 
347 {
348  return new QgsLayerTreeGroup( *this );
349 }
350 
351 void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
352 {
353  Q_FOREACH ( QgsLayerTreeNode *node, mChildren )
354  node->resolveReferences( project, looseMatching );
355 }
356 
357 static bool _nodeIsChecked( QgsLayerTreeNode *node )
358 {
359  return node->itemVisibilityChecked();
360 }
361 
362 
364 {
365  return mMutuallyExclusive;
366 }
367 
368 void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
369 {
370  mMutuallyExclusive = enabled;
371  mMutuallyExclusiveChildIndex = initialChildIndex;
372 
373  if ( !enabled )
374  {
375  return;
376  }
377 
378  if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
379  {
380  // try to use first checked index
381  int index = 0;
382  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
383  {
384  if ( _nodeIsChecked( child ) )
385  {
387  break;
388  }
389  index++;
390  }
391  }
392 
394 }
395 
397 {
398  QStringList lst;
399  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
400  {
401  if ( QgsLayerTree::isGroup( child ) )
402  lst << QgsLayerTree::toGroup( child )->findLayerIds();
403  else if ( QgsLayerTree::isLayer( child ) )
404  lst << QgsLayerTree::toLayer( child )->layerId();
405  }
406  return lst;
407 }
408 
410 {
411  int childIndex = mChildren.indexOf( node );
412  if ( childIndex == -1 )
413  return; // not a direct child - ignore
414 
415  if ( mMutuallyExclusive )
416  {
417  if ( _nodeIsChecked( node ) )
418  mMutuallyExclusiveChildIndex = childIndex;
419  else if ( mMutuallyExclusiveChildIndex == childIndex )
421 
422  // we need to make sure there is only one child node checked
424  }
425 }
426 
428 {
429  if ( mChildren.isEmpty() )
430  return;
431 
432  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
433 
434  int index = 0;
435  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
436  {
438  ++index;
439  }
440 
441  mChangingChildVisibility = false;
442 }
443 
445 {
447 
448  mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
449 
450  int index = 0;
451  Q_FOREACH ( QgsLayerTreeNode *child, mChildren )
452  {
454  ++index;
455  }
456 
457  mChangingChildVisibility = false;
458 }
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:63
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
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).
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
QgsMapLayer * layer() const
Returns 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 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
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
virtual QString dump() const =0
Returns string with layer tree structure. For debug purposes only.
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.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
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) ...
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
QgsLayerTreeLayer * insertLayer(int index, QgsMapLayer *layer)
Insert a new layer node for given map layer at specified position.
void insertChildrenPrivate(int index, QList< QgsLayerTreeNode * > nodes)
Low-level insertion of children to the node. The children must not have any parent yet! ...
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
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.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
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 ...
QString layerId() const
Returns the ID for the map layer associated with this node.
Reads and writes project states.
Definition: qgsproject.h:89
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
int mMutuallyExclusiveChildIndex
Keeps track which child has been most recently selected (so if the whole group is unchecked and check...
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
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.
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.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
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.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
QList< QgsLayerTreeNode * > mChildren
list of children - node is responsible for their deletion
Container of other groups and layers.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
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.