1 /***************************************************************************
2   qgslayertreeutils.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 "qgslayertreeutils.h"
17 #include "qgslayertree.h"
18 #include "qgsvectorlayer.h"
19 #include "qgsproject.h"
20 #include "qgslogger.h"
21 
22 #include <QDomElement>
23 
24 
25 static void _readOldLegendGroup( const QDomElement &groupElem, QgsLayerTreeGroup *parent );
26 static void _readOldLegendLayer( const QDomElement &layerElem, QgsLayerTreeGroup *parent );
27 
readOldLegend(QgsLayerTreeGroup * root,const QDomElement & legendElem)28 bool QgsLayerTreeUtils::readOldLegend( QgsLayerTreeGroup *root, const QDomElement &legendElem )
29 {
30   if ( legendElem.isNull() )
31     return false;
32 
33   QDomNodeList legendChildren = legendElem.childNodes();
34 
35   for ( int i = 0; i < legendChildren.size(); ++i )
36   {
37     QDomElement currentChildElem = legendChildren.at( i ).toElement();
38     if ( currentChildElem.tagName() == QLatin1String( "legendlayer" ) )
39     {
40       _readOldLegendLayer( currentChildElem, root );
41     }
42     else if ( currentChildElem.tagName() == QLatin1String( "legendgroup" ) )
43     {
44       _readOldLegendGroup( currentChildElem, root );
45     }
46   }
47 
48   return true;
49 }
50 
51 
52 
_readOldLegendLayerOrderGroup(const QDomElement & groupElem,QMap<int,QString> & layerIndexes)53 static bool _readOldLegendLayerOrderGroup( const QDomElement &groupElem, QMap<int, QString> &layerIndexes )
54 {
55   QDomNodeList legendChildren = groupElem.childNodes();
56 
57   for ( int i = 0; i < legendChildren.size(); ++i )
58   {
59     QDomElement currentChildElem = legendChildren.at( i ).toElement();
60     if ( currentChildElem.tagName() == QLatin1String( "legendlayer" ) )
61     {
62       QDomElement layerFileElem = currentChildElem.firstChildElement( QStringLiteral( "filegroup" ) ).firstChildElement( QStringLiteral( "legendlayerfile" ) );
63 
64       int layerIndex = currentChildElem.attribute( QStringLiteral( "drawingOrder" ) ).toInt();
65       if ( layerIndex == -1 )
66         return false; // order undefined
67       layerIndexes.insert( layerIndex, layerFileElem.attribute( QStringLiteral( "layerid" ) ) );
68     }
69     else if ( currentChildElem.tagName() == QLatin1String( "legendgroup" ) )
70     {
71       if ( !_readOldLegendLayerOrderGroup( currentChildElem, layerIndexes ) )
72         return false;
73     }
74   }
75 
76   return true;
77 }
78 
79 
readOldLegendLayerOrder(const QDomElement & legendElem,bool & hasCustomOrder,QStringList & order)80 bool QgsLayerTreeUtils::readOldLegendLayerOrder( const QDomElement &legendElem, bool &hasCustomOrder, QStringList &order )
81 {
82   if ( legendElem.isNull() )
83     return false;
84 
85   hasCustomOrder = legendElem.attribute( QStringLiteral( "updateDrawingOrder" ) ) == QLatin1String( "false" );
86   order.clear();
87 
88   QMap<int, QString> layerIndexes;
89 
90   // try to read the order. may be undefined (order = -1) for some or all items
91   bool res = _readOldLegendLayerOrderGroup( legendElem, layerIndexes );
92 
93   if ( !res && hasCustomOrder )
94     return false; // invalid state
95 
96   const auto constLayerIndexes = layerIndexes;
97   for ( const QString &layerId : constLayerIndexes )
98   {
99     QgsDebugMsg( layerId );
100     order.append( layerId );
101   }
102 
103   return true;
104 }
105 
106 
_writeOldLegendLayer(QDomDocument & doc,QgsLayerTreeLayer * nodeLayer,bool hasCustomOrder,const QList<QgsMapLayer * > & order)107 static QDomElement _writeOldLegendLayer( QDomDocument &doc, QgsLayerTreeLayer *nodeLayer, bool hasCustomOrder, const QList<QgsMapLayer *> &order )
108 {
109   int drawingOrder = -1;
110   if ( hasCustomOrder )
111     drawingOrder = order.indexOf( nodeLayer->layer() );
112 
113   QDomElement layerElem = doc.createElement( QStringLiteral( "legendlayer" ) );
114   layerElem.setAttribute( QStringLiteral( "drawingOrder" ), drawingOrder );
115   layerElem.setAttribute( QStringLiteral( "open" ), nodeLayer->isExpanded() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
116   layerElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked ) );
117   layerElem.setAttribute( QStringLiteral( "name" ), nodeLayer->name() );
118   layerElem.setAttribute( QStringLiteral( "showFeatureCount" ), nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() );
119 
120   QDomElement fileGroupElem = doc.createElement( QStringLiteral( "filegroup" ) );
121   fileGroupElem.setAttribute( QStringLiteral( "open" ), nodeLayer->isExpanded() ? "true" : "false" );
122   fileGroupElem.setAttribute( QStringLiteral( "hidden" ), QStringLiteral( "false" ) );
123 
124   QDomElement layerFileElem = doc.createElement( QStringLiteral( "legendlayerfile" ) );
125   layerFileElem.setAttribute( QStringLiteral( "isInOverview" ), nodeLayer->customProperty( QStringLiteral( "overview" ) ).toInt() );
126   layerFileElem.setAttribute( QStringLiteral( "layerid" ), nodeLayer->layerId() );
127   layerFileElem.setAttribute( QStringLiteral( "visible" ), nodeLayer->isVisible() ? 1 : 0 );
128 
129   layerElem.appendChild( fileGroupElem );
130   fileGroupElem.appendChild( layerFileElem );
131   return layerElem;
132 }
133 
134 // need forward declaration as write[..]Group and write[..]GroupChildren call each other
135 static void _writeOldLegendGroupChildren( QDomDocument &doc, QDomElement &groupElem, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QList<QgsMapLayer *> &order );
136 
_writeOldLegendGroup(QDomDocument & doc,QgsLayerTreeGroup * nodeGroup,bool hasCustomOrder,const QList<QgsMapLayer * > & order)137 static QDomElement _writeOldLegendGroup( QDomDocument &doc, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QList<QgsMapLayer *> &order )
138 {
139   QDomElement groupElem = doc.createElement( QStringLiteral( "legendgroup" ) );
140   groupElem.setAttribute( QStringLiteral( "open" ), nodeGroup->isExpanded() ? "true" : "false" );
141   groupElem.setAttribute( QStringLiteral( "name" ), nodeGroup->name() );
142   groupElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked ) );
143 
144   if ( nodeGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
145   {
146     groupElem.setAttribute( QStringLiteral( "embedded" ), 1 );
147     groupElem.setAttribute( QStringLiteral( "project" ), nodeGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
148   }
149 
150   _writeOldLegendGroupChildren( doc, groupElem, nodeGroup, hasCustomOrder, order );
151   return groupElem;
152 }
153 
154 
_writeOldLegendGroupChildren(QDomDocument & doc,QDomElement & groupElem,QgsLayerTreeGroup * nodeGroup,bool hasCustomOrder,const QList<QgsMapLayer * > & order)155 static void _writeOldLegendGroupChildren( QDomDocument &doc, QDomElement &groupElem, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QList<QgsMapLayer *> &order )
156 {
157   const auto constChildren = nodeGroup->children();
158   for ( QgsLayerTreeNode *node : constChildren )
159   {
160     if ( QgsLayerTree::isGroup( node ) )
161     {
162       groupElem.appendChild( _writeOldLegendGroup( doc, QgsLayerTree::toGroup( node ), hasCustomOrder, order ) );
163     }
164     else if ( QgsLayerTree::isLayer( node ) )
165     {
166       groupElem.appendChild( _writeOldLegendLayer( doc, QgsLayerTree::toLayer( node ), hasCustomOrder, order ) );
167     }
168   }
169 }
170 
171 
writeOldLegend(QDomDocument & doc,QgsLayerTreeGroup * root,bool hasCustomOrder,const QList<QgsMapLayer * > & order)172 QDomElement QgsLayerTreeUtils::writeOldLegend( QDomDocument &doc, QgsLayerTreeGroup *root, bool hasCustomOrder, const QList<QgsMapLayer *> &order )
173 {
174   QDomElement legendElem = doc.createElement( QStringLiteral( "legend" ) );
175   legendElem.setAttribute( QStringLiteral( "updateDrawingOrder" ), hasCustomOrder ? QStringLiteral( "false" ) : QStringLiteral( "true" ) );
176 
177   _writeOldLegendGroupChildren( doc, legendElem, root, hasCustomOrder, order );
178 
179   return legendElem;
180 }
181 
182 
checkStateToXml(Qt::CheckState state)183 QString QgsLayerTreeUtils::checkStateToXml( Qt::CheckState state )
184 {
185   switch ( state )
186   {
187     case Qt::Unchecked:
188       return QStringLiteral( "Qt::Unchecked" );
189     case Qt::PartiallyChecked:
190       return QStringLiteral( "Qt::PartiallyChecked" );
191     case Qt::Checked:
192     default:
193       return QStringLiteral( "Qt::Checked" );
194   }
195 }
196 
checkStateFromXml(const QString & txt)197 Qt::CheckState QgsLayerTreeUtils::checkStateFromXml( const QString &txt )
198 {
199   if ( txt == QLatin1String( "Qt::Unchecked" ) )
200     return Qt::Unchecked;
201   else if ( txt == QLatin1String( "Qt::PartiallyChecked" ) )
202     return Qt::PartiallyChecked;
203   else // "Qt::Checked"
204     return Qt::Checked;
205 }
206 
207 
208 
_readOldLegendGroup(const QDomElement & groupElem,QgsLayerTreeGroup * parent)209 static void _readOldLegendGroup( const QDomElement &groupElem, QgsLayerTreeGroup *parent )
210 {
211   QDomNodeList groupChildren = groupElem.childNodes();
212 
213   QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( groupElem.attribute( QStringLiteral( "name" ) ) );
214 
215   groupNode->setItemVisibilityChecked( QgsLayerTreeUtils::checkStateFromXml( groupElem.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked );
216   groupNode->setExpanded( groupElem.attribute( QStringLiteral( "open" ) ) == QLatin1String( "true" ) );
217 
218   if ( groupElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
219   {
220     groupNode->setCustomProperty( QStringLiteral( "embedded" ), 1 );
221     groupNode->setCustomProperty( QStringLiteral( "embedded_project" ), groupElem.attribute( QStringLiteral( "project" ) ) );
222   }
223 
224   for ( int i = 0; i < groupChildren.size(); ++i )
225   {
226     QDomElement currentChildElem = groupChildren.at( i ).toElement();
227     if ( currentChildElem.tagName() == QLatin1String( "legendlayer" ) )
228     {
229       _readOldLegendLayer( currentChildElem, groupNode );
230     }
231     else if ( currentChildElem.tagName() == QLatin1String( "legendgroup" ) )
232     {
233       _readOldLegendGroup( currentChildElem, groupNode );
234     }
235   }
236 
237   parent->addChildNode( groupNode );
238 }
239 
_readOldLegendLayer(const QDomElement & layerElem,QgsLayerTreeGroup * parent)240 static void _readOldLegendLayer( const QDomElement &layerElem, QgsLayerTreeGroup *parent )
241 {
242   QDomElement layerFileElem = layerElem.firstChildElement( QStringLiteral( "filegroup" ) ).firstChildElement( QStringLiteral( "legendlayerfile" ) );
243   QString layerId = layerFileElem.attribute( QStringLiteral( "layerid" ) );
244   QgsLayerTreeLayer *layerNode = new QgsLayerTreeLayer( layerId, layerElem.attribute( QStringLiteral( "name" ) ) );
245 
246   layerNode->setItemVisibilityChecked( QgsLayerTreeUtils::checkStateFromXml( layerElem.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked );
247   layerNode->setExpanded( layerElem.attribute( QStringLiteral( "open" ) ) == QLatin1String( "true" ) );
248 
249   if ( layerFileElem.attribute( QStringLiteral( "isInOverview" ) ) == QLatin1String( "1" ) )
250     layerNode->setCustomProperty( QStringLiteral( "overview" ), 1 );
251 
252   if ( layerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
253     layerNode->setCustomProperty( QStringLiteral( "embedded" ), 1 );
254 
255   if ( layerElem.attribute( QStringLiteral( "showFeatureCount" ) ) == QLatin1String( "1" ) )
256     layerNode->setCustomProperty( QStringLiteral( "showFeatureCount" ), 1 );
257 
258   // drawing order is handled by readOldLegendLayerOrder()
259 
260   parent->addChildNode( layerNode );
261 }
262 
263 
264 
layersEditable(const QList<QgsLayerTreeLayer * > & layerNodes)265 bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer *> &layerNodes )
266 {
267   const auto constLayerNodes = layerNodes;
268   for ( QgsLayerTreeLayer *layerNode : constLayerNodes )
269   {
270     QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );
271     if ( !vl )
272       continue;
273 
274     if ( vl->isEditable() )
275       return true;
276   }
277   return false;
278 }
279 
layersModified(const QList<QgsLayerTreeLayer * > & layerNodes)280 bool QgsLayerTreeUtils::layersModified( const QList<QgsLayerTreeLayer *> &layerNodes )
281 {
282   const auto constLayerNodes = layerNodes;
283   for ( QgsLayerTreeLayer *layerNode : constLayerNodes )
284   {
285     QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );
286     if ( !vl )
287       continue;
288 
289     if ( vl->isEditable() && vl->isModified() )
290       return true;
291   }
292   return false;
293 }
294 
removeInvalidLayers(QgsLayerTreeGroup * group)295 void QgsLayerTreeUtils::removeInvalidLayers( QgsLayerTreeGroup *group )
296 {
297   QList<QgsLayerTreeNode *> nodesToRemove;
298   const auto constChildren = group->children();
299   for ( QgsLayerTreeNode *node : constChildren )
300   {
301     if ( QgsLayerTree::isGroup( node ) )
302       removeInvalidLayers( QgsLayerTree::toGroup( node ) );
303     else if ( QgsLayerTree::isLayer( node ) )
304     {
305       if ( !QgsLayerTree::toLayer( node )->layer() )
306         nodesToRemove << node;
307     }
308   }
309 
310   const auto constNodesToRemove = nodesToRemove;
311   for ( QgsLayerTreeNode *node : constNodesToRemove )
312     group->removeChildNode( node );
313 }
314 
storeOriginalLayersProperties(QgsLayerTreeGroup * group,const QDomDocument * doc)315 void QgsLayerTreeUtils::storeOriginalLayersProperties( QgsLayerTreeGroup *group,  const QDomDocument *doc )
316 {
317 
318   const QDomElement projectLayersElement { doc->documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ) };
319 
320   std::function<void ( QgsLayerTreeNode * )> _store = [ & ]( QgsLayerTreeNode * node )
321   {
322     if ( QgsLayerTree::isLayer( node ) )
323     {
324       QgsMapLayer *l( QgsLayerTree::toLayer( node )->layer() );
325       if ( l )
326       {
327         QDomElement layerElement { projectLayersElement.firstChildElement( QStringLiteral( "maplayer" ) ) };
328         while ( ! layerElement.isNull() )
329         {
330           const QString id( layerElement.firstChildElement( QStringLiteral( "id" ) ).firstChild().nodeValue() );
331           if ( id == l->id() )
332           {
333             QString str;
334             QTextStream stream( &str );
335             layerElement.save( stream, 4 /*indent*/ );
336             l->setOriginalXmlProperties( QStringLiteral( "<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>\n%1" ).arg( str ) );
337             break;
338           }
339           layerElement = layerElement.nextSiblingElement( );
340         }
341       }
342     }
343     else if ( QgsLayerTree::isGroup( node ) )
344     {
345       const QList<QgsLayerTreeNode *> constChildren( node->children( ) );
346       for ( const auto &childNode : constChildren )
347       {
348         _store( childNode );
349       }
350     }
351   };
352 
353   for ( QgsLayerTreeNode *node : group->children() )
354   {
355     _store( node );
356   }
357 }
358 
invisibleLayerList(QgsLayerTreeNode * node)359 QStringList QgsLayerTreeUtils::invisibleLayerList( QgsLayerTreeNode *node )
360 {
361   QStringList list;
362 
363   if ( QgsLayerTree::isGroup( node ) )
364   {
365     const auto constChildren = QgsLayerTree::toGroup( node )->children();
366     for ( QgsLayerTreeNode *child : constChildren )
367     {
368       if ( child->itemVisibilityChecked() == Qt::Unchecked )
369       {
370         list << invisibleLayerList( child );
371       }
372     }
373   }
374   else if ( QgsLayerTree::isLayer( node ) )
375   {
376     QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
377 
378     if ( !layer->isVisible() )
379       list << layer->layerId();
380   }
381 
382   return list;
383 }
384 
replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup * group)385 void QgsLayerTreeUtils::replaceChildrenOfEmbeddedGroups( QgsLayerTreeGroup *group )
386 {
387   const auto constChildren = group->children();
388   for ( QgsLayerTreeNode *child : constChildren )
389   {
390     if ( QgsLayerTree::isGroup( child ) )
391     {
392       if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
393       {
394         child->setCustomProperty( QStringLiteral( "embedded-invisible-layers" ), invisibleLayerList( child ) );
395         QgsLayerTree::toGroup( child )->removeAllChildren();
396       }
397       else
398       {
399         replaceChildrenOfEmbeddedGroups( QgsLayerTree::toGroup( child ) );
400       }
401     }
402   }
403 }
404 
405 
updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup * group,const QgsProject * project)406 void QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTreeGroup *group, const QgsProject *project )
407 {
408   const auto constChildren = group->children();
409   for ( QgsLayerTreeNode *node : constChildren )
410   {
411     if ( !node->customProperty( QStringLiteral( "embedded_project" ) ).toString().isEmpty() )
412     {
413       // may change from absolute path to relative path
414       QString newPath = project->writePath( node->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
415       node->setCustomProperty( QStringLiteral( "embedded_project" ), newPath );
416     }
417 
418     if ( QgsLayerTree::isGroup( node ) )
419     {
420       updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( node ), project );
421     }
422   }
423 }
424 
setLegendFilterByExpression(QgsLayerTreeLayer & layer,const QString & expr,bool enabled)425 void QgsLayerTreeUtils::setLegendFilterByExpression( QgsLayerTreeLayer &layer, const QString &expr, bool enabled )
426 {
427   layer.setCustomProperty( QStringLiteral( "legend/expressionFilter" ), expr );
428   layer.setCustomProperty( QStringLiteral( "legend/expressionFilterEnabled" ), enabled && !expr.isEmpty() );
429 }
430 
legendFilterByExpression(const QgsLayerTreeLayer & layer,bool * enabled)431 QString QgsLayerTreeUtils::legendFilterByExpression( const QgsLayerTreeLayer &layer, bool *enabled )
432 {
433   const QString expression = layer.customProperty( QStringLiteral( "legend/expressionFilter" ), QString() ).toString();
434   if ( enabled )
435     *enabled = !expression.isEmpty() && layer.customProperty( QStringLiteral( "legend/expressionFilterEnabled" ), QString() ).toBool();
436   return expression;
437 }
438 
hasLegendFilterExpression(const QgsLayerTreeGroup & group)439 bool QgsLayerTreeUtils::hasLegendFilterExpression( const QgsLayerTreeGroup &group )
440 {
441   const auto constFindLayers = group.findLayers();
442   for ( QgsLayerTreeLayer *l : constFindLayers )
443   {
444     bool exprEnabled;
445     QString expr = legendFilterByExpression( *l, &exprEnabled );
446     if ( exprEnabled && !expr.isEmpty() )
447     {
448       return true;
449     }
450   }
451   return false;
452 }
453 
insertLayerBelow(QgsLayerTreeGroup * group,const QgsMapLayer * refLayer,QgsMapLayer * layerToInsert)454 QgsLayerTreeLayer *QgsLayerTreeUtils::insertLayerBelow( QgsLayerTreeGroup *group, const QgsMapLayer *refLayer, QgsMapLayer *layerToInsert )
455 {
456   // get the index of the reflayer
457   QgsLayerTreeLayer *inTree = group->findLayer( refLayer->id() );
458   if ( !inTree )
459     return nullptr;
460 
461   int idx = 0;
462   const auto constChildren = inTree->parent()->children();
463   for ( QgsLayerTreeNode *vl : constChildren )
464   {
465     if ( vl->nodeType() == QgsLayerTreeNode::NodeLayer && static_cast<QgsLayerTreeLayer *>( vl )->layer() == refLayer )
466     {
467       break;
468     }
469     idx++;
470   }
471   // insert the new layer
472   QgsLayerTreeGroup *parent = static_cast<QgsLayerTreeGroup *>( inTree->parent() ) ? static_cast<QgsLayerTreeGroup *>( inTree->parent() ) : group;
473   return parent->insertLayer( idx, layerToInsert );
474 }
475 
_collectMapLayers(const QList<QgsLayerTreeNode * > & nodes,QSet<QgsMapLayer * > & layersSet)476 static void _collectMapLayers( const QList<QgsLayerTreeNode *> &nodes, QSet<QgsMapLayer *> &layersSet )
477 {
478   for ( QgsLayerTreeNode *node : nodes )
479   {
480     if ( QgsLayerTree::isLayer( node ) )
481     {
482       QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
483       if ( nodeLayer->layer() )
484         layersSet << nodeLayer->layer();
485     }
486     else if ( QgsLayerTree::isGroup( node ) )
487     {
488       _collectMapLayers( QgsLayerTree::toGroup( node )->children(), layersSet );
489     }
490   }
491 }
492 
collectMapLayersRecursive(const QList<QgsLayerTreeNode * > & nodes)493 QSet<QgsMapLayer *> QgsLayerTreeUtils::collectMapLayersRecursive( const QList<QgsLayerTreeNode *> &nodes )
494 {
495   QSet<QgsMapLayer *> layersSet;
496   _collectMapLayers( nodes, layersSet );
497   return layersSet;
498 }
499 
countMapLayerInTree(QgsLayerTreeNode * tree,QgsMapLayer * layer)500 int QgsLayerTreeUtils::countMapLayerInTree( QgsLayerTreeNode *tree, QgsMapLayer *layer )
501 {
502   if ( QgsLayerTree::isLayer( tree ) )
503   {
504     if ( QgsLayerTree::toLayer( tree )->layer() == layer )
505       return 1;
506     return 0;
507   }
508 
509   int cnt = 0;
510   const QList<QgsLayerTreeNode *> children = tree->children();
511   for ( QgsLayerTreeNode *child : children )
512     cnt += countMapLayerInTree( child, layer );
513   return cnt;
514 }
515 
firstGroupWithoutCustomProperty(QgsLayerTreeGroup * group,const QString & property)516 QgsLayerTreeGroup *QgsLayerTreeUtils::firstGroupWithoutCustomProperty( QgsLayerTreeGroup *group, const QString &property )
517 {
518   // if the group is embedded go to the first non-embedded group, at worst the top level item
519   while ( group->customProperty( property ).toInt() )
520   {
521     if ( !group->parent() )
522       break;
523 
524     if ( QgsLayerTree::isGroup( group->parent() ) )
525       group = QgsLayerTree::toGroup( group->parent() );
526     else
527       Q_ASSERT( false );
528   }
529   return group;
530 }
531