QGIS API Documentation
qgslayertreeutils.cpp
Go to the documentation of this file.
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 
18 #include "qgslayertree.h"
19 
20 #include "qgsvectorlayer.h"
21 
22 #include "qgsproject.h"
23 
24 #include <QDomElement>
25 
26 
27 static void _readOldLegendGroup( const QDomElement& groupElem, QgsLayerTreeGroup* parent );
28 static void _readOldLegendLayer( const QDomElement& layerElem, QgsLayerTreeGroup* parent );
29 
31 {
32  if ( legendElem.isNull() )
33  return false;
34 
35  QDomNodeList legendChildren = legendElem.childNodes();
36 
37  for ( int i = 0; i < legendChildren.size(); ++i )
38  {
39  QDomElement currentChildElem = legendChildren.at( i ).toElement();
40  if ( currentChildElem.tagName() == "legendlayer" )
41  {
42  _readOldLegendLayer( currentChildElem, root );
43  }
44  else if ( currentChildElem.tagName() == "legendgroup" )
45  {
46  _readOldLegendGroup( currentChildElem, root );
47  }
48  }
49 
50  return true;
51 }
52 
53 
54 
55 static bool _readOldLegendLayerOrderGroup( const QDomElement& groupElem, QMap<int, QString>& layerIndexes )
56 {
57  QDomNodeList legendChildren = groupElem.childNodes();
58 
59  for ( int i = 0; i < legendChildren.size(); ++i )
60  {
61  QDomElement currentChildElem = legendChildren.at( i ).toElement();
62  if ( currentChildElem.tagName() == "legendlayer" )
63  {
64  QDomElement layerFileElem = currentChildElem.firstChildElement( "filegroup" ).firstChildElement( "legendlayerfile" );
65 
66  int layerIndex = currentChildElem.attribute( "drawingOrder" ).toInt();
67  if ( layerIndex == -1 )
68  return false; // order undefined
69  layerIndexes.insert( layerIndex, layerFileElem.attribute( "layerid" ) );
70  }
71  else if ( currentChildElem.tagName() == "legendgroup" )
72  {
73  if ( !_readOldLegendLayerOrderGroup( currentChildElem, layerIndexes ) )
74  return false;
75  }
76  }
77 
78  return true;
79 }
80 
81 
82 bool QgsLayerTreeUtils::readOldLegendLayerOrder( const QDomElement& legendElem, bool& hasCustomOrder, QStringList& order )
83 {
84  if ( legendElem.isNull() )
85  return false;
86 
87  hasCustomOrder = legendElem.attribute( "updateDrawingOrder" ) == "false";
88  order.clear();
89 
90  QMap<int, QString> layerIndexes;
91 
92  // try to read the order. may be undefined (order = -1) for some or all items
93  bool res = _readOldLegendLayerOrderGroup( legendElem, layerIndexes );
94 
95  if ( !res && hasCustomOrder )
96  return false; // invalid state
97 
98  Q_FOREACH ( const QString& layerId, layerIndexes )
99  {
100  QgsDebugMsg( layerId );
101  order.append( layerId );
102  }
103 
104  return true;
105 }
106 
107 
108 static QDomElement _writeOldLegendLayer( QDomDocument& doc, QgsLayerTreeLayer* nodeLayer, bool hasCustomOrder, const QStringList& order )
109 {
110  int drawingOrder = -1;
111  if ( hasCustomOrder )
112  drawingOrder = order.indexOf( nodeLayer->layerId() );
113 
114  QDomElement layerElem = doc.createElement( "legendlayer" );
115  layerElem.setAttribute( "drawingOrder", drawingOrder );
116  layerElem.setAttribute( "open", nodeLayer->isExpanded() ? "true" : "false" );
117  layerElem.setAttribute( "checked", QgsLayerTreeUtils::checkStateToXml( nodeLayer->isVisible() ) );
118  layerElem.setAttribute( "name", nodeLayer->layerName() );
119  layerElem.setAttribute( "showFeatureCount", nodeLayer->customProperty( "showFeatureCount" ).toInt() );
120 
121  QDomElement fileGroupElem = doc.createElement( "filegroup" );
122  fileGroupElem.setAttribute( "open", nodeLayer->isExpanded() ? "true" : "false" );
123  fileGroupElem.setAttribute( "hidden", "false" );
124 
125  QDomElement layerFileElem = doc.createElement( "legendlayerfile" );
126  layerFileElem.setAttribute( "isInOverview", nodeLayer->customProperty( "overview" ).toInt() );
127  layerFileElem.setAttribute( "layerid", nodeLayer->layerId() );
128  layerFileElem.setAttribute( "visible", nodeLayer->isVisible() == Qt::Checked ? 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 QStringList& order );
137 
138 static QDomElement _writeOldLegendGroup( QDomDocument& doc, QgsLayerTreeGroup* nodeGroup, bool hasCustomOrder, const QStringList& order )
139 {
140  QDomElement groupElem = doc.createElement( "legendgroup" );
141  groupElem.setAttribute( "open", nodeGroup->isExpanded() ? "true" : "false" );
142  groupElem.setAttribute( "name", nodeGroup->name() );
143  groupElem.setAttribute( "checked", QgsLayerTreeUtils::checkStateToXml( nodeGroup->isVisible() ) );
144 
145  if ( nodeGroup->customProperty( "embedded" ).toInt() )
146  {
147  groupElem.setAttribute( "embedded", 1 );
148  groupElem.setAttribute( "project", nodeGroup->customProperty( "embedded_project" ).toString() );
149  }
150 
151  _writeOldLegendGroupChildren( doc, groupElem, nodeGroup, hasCustomOrder, order );
152  return groupElem;
153 }
154 
155 
156 static void _writeOldLegendGroupChildren( QDomDocument& doc, QDomElement& groupElem, QgsLayerTreeGroup* nodeGroup, bool hasCustomOrder, const QStringList& order )
157 {
158  Q_FOREACH ( QgsLayerTreeNode* node, nodeGroup->children() )
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 
173 {
174  QDomElement legendElem = doc.createElement( "legend" );
175  legendElem.setAttribute( "updateDrawingOrder", hasCustomOrder ? "false" : "true" );
176 
177  _writeOldLegendGroupChildren( doc, legendElem, root, hasCustomOrder, order );
178 
179  return legendElem;
180 }
181 
182 
184 {
185  switch ( state )
186  {
187  case Qt::Unchecked:
188  return "Qt::Unchecked";
189  case Qt::PartiallyChecked:
190  return "Qt::PartiallyChecked";
191  case Qt::Checked:
192  default:
193  return "Qt::Checked";
194  }
195 }
196 
197 Qt::CheckState QgsLayerTreeUtils::checkStateFromXml( const QString& txt )
198 {
199  if ( txt == "Qt::Unchecked" )
200  return Qt::Unchecked;
201  else if ( txt == "Qt::PartiallyChecked" )
202  return Qt::PartiallyChecked;
203  else // "Qt::Checked"
204  return Qt::Checked;
205 }
206 
207 
208 
209 static void _readOldLegendGroup( const QDomElement& groupElem, QgsLayerTreeGroup* parent )
210 {
211  QDomNodeList groupChildren = groupElem.childNodes();
212 
213  QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( groupElem.attribute( "name" ) );
214 
215  groupNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( groupElem.attribute( "checked" ) ) );
216  groupNode->setExpanded( groupElem.attribute( "open" ) == "true" );
217 
218  if ( groupElem.attribute( "embedded" ) == "1" )
219  {
220  groupNode->setCustomProperty( "embedded", 1 );
221  groupNode->setCustomProperty( "embedded_project", groupElem.attribute( "project" ) );
222  }
223 
224  for ( int i = 0; i < groupChildren.size(); ++i )
225  {
226  QDomElement currentChildElem = groupChildren.at( i ).toElement();
227  if ( currentChildElem.tagName() == "legendlayer" )
228  {
229  _readOldLegendLayer( currentChildElem, groupNode );
230  }
231  else if ( currentChildElem.tagName() == "legendgroup" )
232  {
233  _readOldLegendGroup( currentChildElem, groupNode );
234  }
235  }
236 
237  parent->addChildNode( groupNode );
238 }
239 
240 static void _readOldLegendLayer( const QDomElement& layerElem, QgsLayerTreeGroup* parent )
241 {
242  QDomElement layerFileElem = layerElem.firstChildElement( "filegroup" ).firstChildElement( "legendlayerfile" );
243  QString layerId = layerFileElem.attribute( "layerid" );
244  QgsLayerTreeLayer* layerNode = new QgsLayerTreeLayer( layerId, layerElem.attribute( "name" ) );
245 
246  layerNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( layerElem.attribute( "checked" ) ) );
247  layerNode->setExpanded( layerElem.attribute( "open" ) == "true" );
248 
249  if ( layerFileElem.attribute( "isInOverview" ) == "1" )
250  layerNode->setCustomProperty( "overview", 1 );
251 
252  if ( layerElem.attribute( "embedded" ) == "1" )
253  layerNode->setCustomProperty( "embedded", 1 );
254 
255  if ( layerElem.attribute( "showFeatureCount" ) == "1" )
256  layerNode->setCustomProperty( "showFeatureCount", 1 );
257 
258  // drawing order is handled by readOldLegendLayerOrder()
259 
260  parent->addChildNode( layerNode );
261 }
262 
263 
264 
266 {
267  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
268  {
269  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer*>( layerNode->layer() );
270  if ( !vl )
271  continue;
272 
273  if ( vl->isEditable() )
274  return true;
275  }
276  return false;
277 }
278 
280 {
281  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
282  {
283  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer*>( layerNode->layer() );
284  if ( !vl )
285  continue;
286 
287  if ( vl->isEditable() && vl->isModified() )
288  return true;
289  }
290  return false;
291 }
292 
294 {
295  QList<QgsLayerTreeNode*> nodesToRemove;
296  Q_FOREACH ( QgsLayerTreeNode* node, group->children() )
297  {
298  if ( QgsLayerTree::isGroup( node ) )
300  else if ( QgsLayerTree::isLayer( node ) )
301  {
302  if ( !QgsLayerTree::toLayer( node )->layer() )
303  nodesToRemove << node;
304  }
305  }
306 
307  Q_FOREACH ( QgsLayerTreeNode* node, nodesToRemove )
308  group->removeChildNode( node );
309 }
310 
312 {
313  QStringList list;
314 
315  if ( QgsLayerTree::isGroup( node ) )
316  {
317  Q_FOREACH ( QgsLayerTreeNode *child, QgsLayerTree::toGroup( node )->children() )
318  {
319  list << invisibleLayerList( child );
320  }
321  }
322  else if ( QgsLayerTree::isLayer( node ) )
323  {
324  QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
325 
326  if ( !layer->isVisible() )
327  list << layer->layerId();
328  }
329 
330  return list;
331 }
332 
334 {
335  Q_FOREACH ( QgsLayerTreeNode* child, group->children() )
336  {
337  if ( QgsLayerTree::isGroup( child ) )
338  {
339  if ( child->customProperty( "embedded" ).toInt() )
340  {
341  child->setCustomProperty( "embedded-invisible-layers", invisibleLayerList( child ) );
343  }
344  else
345  {
347  }
348  }
349  }
350 }
351 
352 
354 {
355  Q_FOREACH ( QgsLayerTreeNode* node, group->children() )
356  {
357  if ( !node->customProperty( "embedded_project" ).toString().isEmpty() )
358  {
359  // may change from absolute path to relative path
360  QString newPath = QgsProject::instance()->writePath( node->customProperty( "embedded_project" ).toString() );
361  node->setCustomProperty( "embedded_project", newPath );
362  }
363 
364  if ( QgsLayerTree::isGroup( node ) )
365  {
367  }
368  }
369 }
370 
372 {
373  layer.setCustomProperty( "legend/expressionFilter", expr );
374  layer.setCustomProperty( "legend/expressionFilterEnabled", enabled );
375 }
376 
378 {
379  if ( enabled )
380  *enabled = layer.customProperty( "legend/expressionFilterEnabled", "" ).toBool();
381  return layer.customProperty( "legend/expressionFilter", "" ).toString();
382 }
383 
385 {
386  Q_FOREACH ( QgsLayerTreeLayer* l, group.findLayers() )
387  {
388  bool exprEnabled;
389  QString expr = legendFilterByExpression( *l, &exprEnabled );
390  if ( exprEnabled && !expr.isEmpty() )
391  {
392  return true;
393  }
394  }
395  return false;
396 }
397 
399 {
400  // get the index of the reflayer
401  QgsLayerTreeLayer* inTree = group->findLayer( refLayer->id() );
402  if ( !inTree )
403  return nullptr;
404 
405  int idx = 0;
406  Q_FOREACH ( QgsLayerTreeNode* vl, inTree->parent()->children() )
407  {
408  if ( vl->nodeType() == QgsLayerTreeNode::NodeLayer && static_cast<QgsLayerTreeLayer*>( vl )->layer() == refLayer )
409  {
410  break;
411  }
412  idx++;
413  }
414  // insert the new layer
415  QgsLayerTreeGroup* parent = static_cast<QgsLayerTreeGroup*>( inTree->parent() ) ? static_cast<QgsLayerTreeGroup*>( inTree->parent() ) : group;
416  return parent->insertLayer( idx, layerToInsert );
417 }
static QDomElement _writeOldLegendLayer(QDomDocument &doc, QgsLayerTreeLayer *nodeLayer, bool hasCustomOrder, const QStringList &order)
static bool readOldLegendLayerOrder(const QDomElement &legendElem, bool &hasCustomOrder, QStringList &order)
Try to load custom layer order from.
Layer tree group node serves as a container for layers and further groups.
void clear()
static void _writeOldLegendGroupChildren(QDomDocument &doc, QDomElement &groupElem, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QStringList &order)
Base class for all map layer types.
Definition: qgsmaplayer.h:49
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
static QString checkStateToXml(Qt::CheckState state)
Convert Qt::CheckState to QString.
QDomNode appendChild(const QDomNode &newChild)
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString attribute(const QString &name, const QString &defValue) const
QgsMapLayer * layer() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void removeAllChildren()
Remove all child nodes. The nodes will be deleted.
NodeType nodeType()
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group. The node will be deleted.
QDomNodeList childNodes() const
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
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
QDomElement toElement() const
Qt::CheckState isVisible() const
void append(const T &value)
int toInt(bool *ok) const
static void _readOldLegendLayer(const QDomElement &layerElem, QgsLayerTreeGroup *parent)
void setAttribute(const QString &name, const QString &value)
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...
int toInt(bool *ok, int base) const
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
static QDomElement _writeOldLegendGroup(QDomDocument &doc, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QStringList &order)
QgsLayerTreeNode * parent()
Get pointer to the parent. If parent is a null pointer, the node is a root node.
bool isEmpty() const
static QDomElement writeOldLegend(QDomDocument &doc, QgsLayerTreeGroup *root, bool hasCustomOrder, const QStringList &order)
Return.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void _readOldLegendGroup(const QDomElement &groupElem, QgsLayerTreeGroup *parent)
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
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
QString layerId() const
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group)
void setVisible(Qt::CheckState visible)
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
QString name() const
Get group&#39;s name.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
static bool layersModified(const QList< QgsLayerTreeLayer * > &layerNodes)
Return true if any of the layers is modified.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
Qt::CheckState isVisible() const
Return the check state of the group node.
static bool layersEditable(const QList< QgsLayerTreeLayer * > &layerNodes)
Return true if any of the layers is editable.
void setExpanded(bool expanded)
Set whether the node should be shown as expanded or collapsed in GUI.
bool isNull() const
leaf node pointing to a layer
static QgsLayerTreeLayer * insertLayerBelow(QgsLayerTreeGroup *group, const QgsMapLayer *refLayer, QgsMapLayer *layerToInsert)
Insert a QgsMapLayer just below another one.
static void setLegendFilterByExpression(QgsLayerTreeLayer &layer, const QString &expr, bool enabled=true)
Set the expression filter of a legend layer.
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
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:388
QDomElement firstChildElement(const QString &tagName) const
void addChildNode(QgsLayerTreeNode *node)
Append an existing node. The node must not have a parent yet. The node will be owned by this group...
bool toBool() const
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
static bool _readOldLegendLayerOrderGroup(const QDomElement &groupElem, QMap< int, QString > &layerIndexes)
int indexOf(const QRegExp &rx, int from) const
iterator insert(const Key &key, const T &value)
QString tagName() const
int size() const
QDomElement createElement(const QString &tagName)
QString layerName() const
Represents a vector layer which manages a vector based data sets.
static QStringList invisibleLayerList(QgsLayerTreeNode *node)
get invisible layers
void setVisible(Qt::CheckState state)
Set check state of the group node - will also update children.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QString toString() const
static QString legendFilterByExpression(const QgsLayerTreeLayer &layer, bool *enabled=nullptr)
Return the expression filter of a legend layer.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
Layer tree node points to a map layer.
QDomNode at(int index) const