QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 
30 bool QgsLayerTreeUtils::readOldLegend( QgsLayerTreeGroup* root, const QDomElement& legendElem )
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 
172 QDomElement QgsLayerTreeUtils::writeOldLegend( QDomDocument& doc, QgsLayerTreeGroup* root, bool hasCustomOrder, const QStringList& order )
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 
183 QString QgsLayerTreeUtils::checkStateToXml( Qt::CheckState state )
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 
193 Qt::CheckState QgsLayerTreeUtils::checkStateFromXml( QString txt )
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 
261 bool QgsLayerTreeUtils::layersEditable( const QList<QgsLayerTreeLayer*>& layerNodes )
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 
275 bool QgsLayerTreeUtils::layersModified( const QList<QgsLayerTreeLayer*>& layerNodes )
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  foreach ( QgsLayerTreeNode* child, group->children() )
310  {
311  if ( QgsLayerTree::isGroup( child ) )
312  {
313  if ( child->customProperty( "embedded" ).toInt() )
315  else
317  }
318  }
319 }
320 
321 
323 {
324  foreach ( QgsLayerTreeNode* node, group->children() )
325  {
326  if ( !node->customProperty( "embedded_project" ).toString().isEmpty() )
327  {
328  // may change from absolute path to relative path
329  QString newPath = QgsProject::instance()->writePath( node->customProperty( "embedded_project" ).toString() );
330  node->setCustomProperty( "embedded_project", newPath );
331  }
332 
333  if ( QgsLayerTree::isGroup( node ) )
334  {
336  }
337  }
338 }
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.
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.
static QString checkStateToXml(Qt::CheckState state)
Convert Qt::CheckState to QString.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayer * layer() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
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.
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
Qt::CheckState isVisible() const
static void _readOldLegendLayer(const QDomElement &layerElem, QgsLayerTreeGroup *parent)
static QDomElement _writeOldLegendGroup(QDomDocument &doc, QgsLayerTreeGroup *nodeGroup, bool hasCustomOrder, const QStringList &order)
static QDomElement writeOldLegend(QDomDocument &doc, QgsLayerTreeGroup *root, bool hasCustomOrder, const QStringList &order)
Return.
static void _readOldLegendGroup(const QDomElement &groupElem, QgsLayerTreeGroup *parent)
QString writePath(QString filename) const
prepare a filename to save it to the project file
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.
static void removeChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups. Useful when saving layer tree.
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:362
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)
QString layerName() const
virtual bool isEditable() const
Returns true if the provider is in editing mode.
Represents a vector layer which manages a vector based data sets.
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
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
Layer tree node points to a map layer.