QGIS API Documentation  2.9.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  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 }