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