QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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  foreach ( 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  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: return "Qt::Unchecked";
188  case Qt::PartiallyChecked: return "Qt::PartiallyChecked";
189  case Qt::Checked: default: return "Qt::Checked";
190  }
191 }
192 
194 {
195  if ( txt == "Qt::Unchecked" )
196  return Qt::Unchecked;
197  else if ( txt == "Qt::PartiallyChecked" )
198  return Qt::PartiallyChecked;
199  else // "Qt::Checked"
200  return Qt::Checked;
201 }
202 
203 
204 
205 static void _readOldLegendGroup( const QDomElement& groupElem, QgsLayerTreeGroup* parent )
206 {
207  QDomNodeList groupChildren = groupElem.childNodes();
208 
209  QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( groupElem.attribute( "name" ) );
210 
211  groupNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( groupElem.attribute( "checked" ) ) );
212  groupNode->setExpanded( groupElem.attribute( "open" ) == "true" );
213 
214  if ( groupElem.attribute( "embedded" ) == "1" )
215  {
216  groupNode->setCustomProperty( "embedded", 1 );
217  groupNode->setCustomProperty( "embedded_project", groupElem.attribute( "project" ) );
218  }
219 
220  for ( int i = 0; i < groupChildren.size(); ++i )
221  {
222  QDomElement currentChildElem = groupChildren.at( i ).toElement();
223  if ( currentChildElem.tagName() == "legendlayer" )
224  {
225  _readOldLegendLayer( currentChildElem, groupNode );
226  }
227  else if ( currentChildElem.tagName() == "legendgroup" )
228  {
229  _readOldLegendGroup( currentChildElem, groupNode );
230  }
231  }
232 
233  parent->addChildNode( groupNode );
234 }
235 
236 static void _readOldLegendLayer( const QDomElement& layerElem, QgsLayerTreeGroup* parent )
237 {
238  QDomElement layerFileElem = layerElem.firstChildElement( "filegroup" ).firstChildElement( "legendlayerfile" );
239  QString layerId = layerFileElem.attribute( "layerid" );
240  QgsLayerTreeLayer* layerNode = new QgsLayerTreeLayer( layerId, layerElem.attribute( "name" ) );
241 
242  layerNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( layerElem.attribute( "checked" ) ) );
243  layerNode->setExpanded( layerElem.attribute( "open" ) == "true" );
244 
245  if ( layerFileElem.attribute( "isInOverview" ) == "1" )
246  layerNode->setCustomProperty( "overview", 1 );
247 
248  if ( layerElem.attribute( "embedded" ) == "1" )
249  layerNode->setCustomProperty( "embedded", 1 );
250 
251  if ( layerElem.attribute( "showFeatureCount" ) == "1" )
252  layerNode->setCustomProperty( "showFeatureCount", 1 );
253 
254  // drawing order is handled by readOldLegendLayerOrder()
255 
256  parent->addChildNode( layerNode );
257 }
258 
259 
260 
262 {
263  foreach ( QgsLayerTreeLayer* layerNode, layerNodes )
264  {
265  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer*>( layerNode->layer() );
266  if ( !vl )
267  continue;
268 
269  if ( vl->isEditable() )
270  return true;
271  }
272  return false;
273 }
274 
276 {
277  foreach ( QgsLayerTreeLayer* layerNode, layerNodes )
278  {
279  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer*>( layerNode->layer() );
280  if ( !vl )
281  continue;
282 
283  if ( vl->isEditable() && vl->isModified() )
284  return true;
285  }
286  return false;
287 }
288 
290 {
291  QList<QgsLayerTreeNode*> nodesToRemove;
292  foreach ( QgsLayerTreeNode* node, group->children() )
293  {
294  if ( QgsLayerTree::isGroup( node ) )
296  else if ( QgsLayerTree::isLayer( node ) )
297  {
298  if ( !QgsLayerTree::toLayer( node )->layer() )
299  nodesToRemove << node;
300  }
301  }
302 
303  foreach ( QgsLayerTreeNode* node, nodesToRemove )
304  group->removeChildNode( node );
305 }
306 
308 {
309  QStringList list;
310 
311  if ( QgsLayerTree::isGroup( node ) )
312  {
313  foreach ( QgsLayerTreeNode *child, QgsLayerTree::toGroup( node )->children() )
314  {
315  list << invisibleLayerList( child );
316  }
317  }
318  else if ( QgsLayerTree::isLayer( node ) )
319  {
320  QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
321 
322  if ( !layer->isVisible() )
323  list << layer->layerId();
324  }
325 
326  return list;
327 }
328 
330 {
331  foreach ( QgsLayerTreeNode* child, group->children() )
332  {
333  if ( QgsLayerTree::isGroup( child ) )
334  {
335  if ( child->customProperty( "embedded" ).toInt() )
336  {
337  child->setCustomProperty( "embedded-invisible-layers", invisibleLayerList( child ) );
339  }
340  else
341  {
343  }
344  }
345  }
346 }
347 
348 
350 {
351  foreach ( QgsLayerTreeNode* node, group->children() )
352  {
353  if ( !node->customProperty( "embedded_project" ).toString().isEmpty() )
354  {
355  // may change from absolute path to relative path
356  QString newPath = QgsProject::instance()->writePath( node->customProperty( "embedded_project" ).toString() );
357  node->setCustomProperty( "embedded_project", newPath );
358  }
359 
360  if ( QgsLayerTree::isGroup( node ) )
361  {
363  }
364  }
365 }
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)
static Qt::CheckState checkStateFromXml(QString txt)
Convert QString to Qt::CheckState.
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QString writePath(QString filename, 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.
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)
int toInt(bool *ok, int base) const
static QDomElement _writeOldLegendGroup(QDomDocument &doc, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QStringList &order)
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.
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's name.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
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
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:351
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...
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
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