Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslayertreegroup.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 "qgslayertreegroup.h"
17 : :
18 : : #include "qgslayertree.h"
19 : : #include "qgslayertreeutils.h"
20 : : #include "qgsmaplayer.h"
21 : :
22 : : #include <QDomElement>
23 : : #include <QStringList>
24 : :
25 : :
26 : 5 : QgsLayerTreeGroup::QgsLayerTreeGroup( const QString &name, bool checked )
27 : 5 : : QgsLayerTreeNode( NodeGroup, checked )
28 : 5 : , mName( name )
29 : 10 : {
30 : 5 : connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged );
31 : 5 : }
32 : :
33 : 0 : QgsLayerTreeGroup::QgsLayerTreeGroup( const QgsLayerTreeGroup &other )
34 : 0 : : QgsLayerTreeNode( other )
35 : 0 : , mName( other.mName )
36 : 0 : , mChangingChildVisibility( other.mChangingChildVisibility )
37 : 0 : , mMutuallyExclusive( other.mMutuallyExclusive )
38 : 0 : , mMutuallyExclusiveChildIndex( other.mMutuallyExclusiveChildIndex )
39 : 0 : {
40 : 0 : connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged );
41 : 0 : }
42 : :
43 : 0 : QString QgsLayerTreeGroup::name() const
44 : : {
45 : 0 : return mName;
46 : : }
47 : :
48 : 0 : void QgsLayerTreeGroup::setName( const QString &n )
49 : : {
50 : 0 : if ( mName == n )
51 : 0 : return;
52 : :
53 : 0 : mName = n;
54 : 0 : emit nameChanged( this, n );
55 : 0 : }
56 : :
57 : :
58 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::insertGroup( int index, const QString &name )
59 : : {
60 : 0 : QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
61 : 0 : insertChildNode( index, grp );
62 : 0 : return grp;
63 : 0 : }
64 : :
65 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::addGroup( const QString &name )
66 : : {
67 : 0 : QgsLayerTreeGroup *grp = new QgsLayerTreeGroup( name );
68 : 0 : addChildNode( grp );
69 : 0 : return grp;
70 : 0 : }
71 : :
72 : 0 : QgsLayerTreeLayer *QgsLayerTreeGroup::insertLayer( int index, QgsMapLayer *layer )
73 : : {
74 : 0 : if ( !layer )
75 : 0 : return nullptr;
76 : :
77 : 0 : QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
78 : 0 : insertChildNode( index, ll );
79 : 0 : return ll;
80 : 0 : }
81 : :
82 : 0 : QgsLayerTreeLayer *QgsLayerTreeGroup::addLayer( QgsMapLayer *layer )
83 : : {
84 : 0 : if ( !layer )
85 : 0 : return nullptr;
86 : :
87 : 0 : QgsLayerTreeLayer *ll = new QgsLayerTreeLayer( layer );
88 : 0 : addChildNode( ll );
89 : 0 : return ll;
90 : 0 : }
91 : :
92 : 0 : void QgsLayerTreeGroup::insertChildNode( int index, QgsLayerTreeNode *node )
93 : : {
94 : 0 : QList<QgsLayerTreeNode *> nodes;
95 : 0 : nodes << node;
96 : 0 : insertChildNodes( index, nodes );
97 : 0 : }
98 : :
99 : 1 : void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNode *> &nodes )
100 : : {
101 : 1 : QgsLayerTreeNode *meChild = nullptr;
102 : 1 : if ( mMutuallyExclusive && mMutuallyExclusiveChildIndex >= 0 && mMutuallyExclusiveChildIndex < mChildren.count() )
103 : 0 : meChild = mChildren.at( mMutuallyExclusiveChildIndex );
104 : :
105 : : // low-level insert
106 : 1 : insertChildrenPrivate( index, nodes );
107 : :
108 : 1 : if ( mMutuallyExclusive )
109 : : {
110 : 0 : if ( meChild )
111 : : {
112 : : // the child could have change its index - or the new children may have been also set as visible
113 : 0 : mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
114 : 0 : }
115 : 0 : else if ( mChecked )
116 : : {
117 : : // we have not picked a child index yet, but we should pick one now
118 : : // ... so pick the first one from the newly added
119 : 0 : if ( index == -1 )
120 : 0 : index = mChildren.count() - nodes.count(); // get real insertion index
121 : 0 : mMutuallyExclusiveChildIndex = index;
122 : 0 : }
123 : 0 : updateChildVisibilityMutuallyExclusive();
124 : 0 : }
125 : 1 : }
126 : :
127 : 0 : void QgsLayerTreeGroup::addChildNode( QgsLayerTreeNode *node )
128 : : {
129 : 0 : insertChildNode( -1, node );
130 : 0 : }
131 : :
132 : 0 : void QgsLayerTreeGroup::removeChildNode( QgsLayerTreeNode *node )
133 : : {
134 : 0 : int i = mChildren.indexOf( node );
135 : 0 : if ( i >= 0 )
136 : 0 : removeChildren( i, 1 );
137 : 0 : }
138 : :
139 : 0 : void QgsLayerTreeGroup::removeLayer( QgsMapLayer *layer )
140 : : {
141 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
142 : : {
143 : 0 : if ( QgsLayerTree::isLayer( child ) )
144 : : {
145 : 0 : QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
146 : 0 : if ( childLayer->layer() == layer )
147 : : {
148 : 0 : removeChildren( mChildren.indexOf( child ), 1 );
149 : 0 : break;
150 : : }
151 : 0 : }
152 : : }
153 : 0 : }
154 : :
155 : 8 : void QgsLayerTreeGroup::removeChildren( int from, int count )
156 : : {
157 : 8 : QgsLayerTreeNode *meChild = nullptr;
158 : 8 : if ( mMutuallyExclusive && mMutuallyExclusiveChildIndex >= 0 && mMutuallyExclusiveChildIndex < mChildren.count() )
159 : 0 : meChild = mChildren.at( mMutuallyExclusiveChildIndex );
160 : :
161 : 8 : removeChildrenPrivate( from, count );
162 : :
163 : 8 : if ( meChild )
164 : : {
165 : : // the child could have change its index - or may have been removed completely
166 : 0 : mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
167 : : // we need to uncheck this group
168 : : //if ( mMutuallyExclusiveChildIndex == -1 )
169 : : // setItemVisibilityChecked( false );
170 : 0 : }
171 : 8 : }
172 : :
173 : 0 : void QgsLayerTreeGroup::removeChildrenGroupWithoutLayers()
174 : : {
175 : : // clean the layer tree by removing empty group
176 : 0 : const auto childNodes = children();
177 : 0 : for ( QgsLayerTreeNode *treeNode : childNodes )
178 : : {
179 : 0 : if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup )
180 : : {
181 : 0 : QgsLayerTreeGroup *treeGroup = qobject_cast<QgsLayerTreeGroup *>( treeNode );
182 : 0 : if ( treeGroup->findLayerIds().isEmpty() )
183 : 0 : removeChildNode( treeNode );
184 : : else
185 : 0 : treeGroup->removeChildrenGroupWithoutLayers();
186 : 0 : }
187 : : }
188 : 0 : }
189 : :
190 : 8 : void QgsLayerTreeGroup::removeAllChildren()
191 : : {
192 : 8 : removeChildren( 0, mChildren.count() );
193 : 8 : }
194 : :
195 : 0 : QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( QgsMapLayer *layer ) const
196 : : {
197 : 0 : if ( !layer )
198 : 0 : return nullptr;
199 : :
200 : 0 : return findLayer( layer->id() );
201 : 0 : }
202 : :
203 : 1 : QgsLayerTreeLayer *QgsLayerTreeGroup::findLayer( const QString &layerId ) const
204 : : {
205 : 1 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
206 : : {
207 : 0 : if ( QgsLayerTree::isLayer( child ) )
208 : : {
209 : 0 : QgsLayerTreeLayer *childLayer = QgsLayerTree::toLayer( child );
210 : 0 : if ( childLayer->layerId() == layerId )
211 : 0 : return childLayer;
212 : 0 : }
213 : 0 : else if ( QgsLayerTree::isGroup( child ) )
214 : : {
215 : 0 : QgsLayerTreeLayer *res = QgsLayerTree::toGroup( child )->findLayer( layerId );
216 : 0 : if ( res )
217 : 0 : return res;
218 : 0 : }
219 : : }
220 : 1 : return nullptr;
221 : 6 : }
222 : :
223 : 0 : QList<QgsLayerTreeLayer *> QgsLayerTreeGroup::findLayers() const
224 : 5 : {
225 : 0 : QList<QgsLayerTreeLayer *> list;
226 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
227 : : {
228 : 0 : if ( QgsLayerTree::isLayer( child ) )
229 : 0 : list << QgsLayerTree::toLayer( child );
230 : 5 : else if ( QgsLayerTree::isGroup( child ) )
231 : 0 : list << QgsLayerTree::toGroup( child )->findLayers();
232 : : }
233 : 0 : return list;
234 : 0 : }
235 : :
236 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::findGroup( const QString &name )
237 : : {
238 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
239 : : {
240 : 0 : if ( QgsLayerTree::isGroup( child ) )
241 : : {
242 : 0 : QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
243 : 0 : if ( childGroup->name() == name )
244 : 0 : return childGroup;
245 : : else
246 : : {
247 : 0 : QgsLayerTreeGroup *grp = childGroup->findGroup( name );
248 : 0 : if ( grp )
249 : 0 : return grp;
250 : : }
251 : 0 : }
252 : : }
253 : 0 : return nullptr;
254 : 0 : }
255 : :
256 : 0 : QList<QgsLayerTreeGroup *> QgsLayerTreeGroup::findGroups( bool recursive ) const
257 : : {
258 : 0 : QList<QgsLayerTreeGroup *> list;
259 : :
260 : 0 : for ( QgsLayerTreeNode *child : mChildren )
261 : : {
262 : 0 : if ( QgsLayerTree::isGroup( child ) )
263 : : {
264 : 0 : QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
265 : 0 : list << childGroup;
266 : 0 : if ( recursive )
267 : 0 : list << childGroup->findGroups( recursive );
268 : 0 : }
269 : : }
270 : 0 : return list;
271 : 0 : }
272 : :
273 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsReadWriteContext &context )
274 : : {
275 : 0 : if ( element.tagName() != QLatin1String( "layer-tree-group" ) )
276 : 0 : return nullptr;
277 : :
278 : 0 : QString name = context.projectTranslator()->translate( QStringLiteral( "project:layergroups" ), element.attribute( QStringLiteral( "name" ) ) );
279 : 0 : bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
280 : 0 : bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
281 : 0 : bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
282 : 0 : int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
283 : :
284 : 0 : QgsLayerTreeGroup *groupNode = new QgsLayerTreeGroup( name, checked );
285 : 0 : groupNode->setExpanded( isExpanded );
286 : :
287 : 0 : groupNode->readCommonXml( element );
288 : :
289 : 0 : groupNode->readChildrenFromXml( element, context );
290 : :
291 : 0 : groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );
292 : :
293 : 0 : return groupNode;
294 : 0 : }
295 : :
296 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::readXml( QDomElement &element, const QgsProject *project, const QgsReadWriteContext &context )
297 : : {
298 : 0 : QgsLayerTreeGroup *node = readXml( element, context );
299 : 0 : if ( node )
300 : 0 : node->resolveReferences( project );
301 : 0 : return node;
302 : : }
303 : :
304 : 0 : void QgsLayerTreeGroup::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context )
305 : : {
306 : 0 : QDomDocument doc = parentElement.ownerDocument();
307 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
308 : 0 : elem.setAttribute( QStringLiteral( "name" ), mName );
309 : 0 : elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
310 : 0 : elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
311 : 0 : if ( mMutuallyExclusive )
312 : : {
313 : 0 : elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
314 : 0 : elem.setAttribute( QStringLiteral( "mutually-exclusive-child" ), mMutuallyExclusiveChildIndex );
315 : 0 : }
316 : :
317 : 0 : writeCommonXml( elem );
318 : :
319 : 0 : for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
320 : 0 : node->writeXml( elem, context );
321 : :
322 : 0 : parentElement.appendChild( elem );
323 : 0 : }
324 : :
325 : 0 : void QgsLayerTreeGroup::readChildrenFromXml( QDomElement &element, const QgsReadWriteContext &context )
326 : : {
327 : 0 : QList<QgsLayerTreeNode *> nodes;
328 : 0 : QDomElement childElem = element.firstChildElement();
329 : 0 : while ( !childElem.isNull() )
330 : : {
331 : 0 : QgsLayerTreeNode *newNode = QgsLayerTreeNode::readXml( childElem, context );
332 : 0 : if ( newNode )
333 : 0 : nodes << newNode;
334 : :
335 : 0 : childElem = childElem.nextSiblingElement();
336 : : }
337 : :
338 : 0 : insertChildNodes( -1, nodes );
339 : 0 : }
340 : :
341 : 0 : QString QgsLayerTreeGroup::dump() const
342 : : {
343 : 0 : QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
344 : 0 : QStringList childrenDump;
345 : 0 : for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
346 : 0 : childrenDump << node->dump().split( '\n' );
347 : 0 : for ( int i = 0; i < childrenDump.count(); ++i )
348 : 0 : childrenDump[i].prepend( " " );
349 : 0 : return header + childrenDump.join( QLatin1Char( '\n' ) );
350 : 0 : }
351 : :
352 : 0 : QgsLayerTreeGroup *QgsLayerTreeGroup::clone() const
353 : : {
354 : 0 : return new QgsLayerTreeGroup( *this );
355 : 0 : }
356 : :
357 : 0 : void QgsLayerTreeGroup::resolveReferences( const QgsProject *project, bool looseMatching )
358 : : {
359 : 0 : for ( QgsLayerTreeNode *node : std::as_const( mChildren ) )
360 : 0 : node->resolveReferences( project, looseMatching );
361 : 0 : }
362 : :
363 : 0 : static bool _nodeIsChecked( QgsLayerTreeNode *node )
364 : : {
365 : 0 : return node->itemVisibilityChecked();
366 : : }
367 : :
368 : :
369 : 0 : bool QgsLayerTreeGroup::isMutuallyExclusive() const
370 : : {
371 : 0 : return mMutuallyExclusive;
372 : : }
373 : :
374 : 0 : void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIndex )
375 : : {
376 : 0 : mMutuallyExclusive = enabled;
377 : 0 : mMutuallyExclusiveChildIndex = initialChildIndex;
378 : :
379 : 0 : if ( !enabled )
380 : : {
381 : 0 : return;
382 : : }
383 : :
384 : 0 : if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
385 : : {
386 : : // try to use first checked index
387 : 0 : int index = 0;
388 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
389 : : {
390 : 0 : if ( _nodeIsChecked( child ) )
391 : : {
392 : 0 : mMutuallyExclusiveChildIndex = index;
393 : 0 : break;
394 : : }
395 : 0 : index++;
396 : : }
397 : 0 : }
398 : :
399 : 0 : updateChildVisibilityMutuallyExclusive();
400 : 0 : }
401 : :
402 : 0 : QStringList QgsLayerTreeGroup::findLayerIds() const
403 : : {
404 : 0 : QStringList lst;
405 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
406 : : {
407 : 0 : if ( QgsLayerTree::isGroup( child ) )
408 : 0 : lst << QgsLayerTree::toGroup( child )->findLayerIds();
409 : 0 : else if ( QgsLayerTree::isLayer( child ) )
410 : 0 : lst << QgsLayerTree::toLayer( child )->layerId();
411 : : }
412 : 0 : return lst;
413 : 0 : }
414 : :
415 : 0 : void QgsLayerTreeGroup::nodeVisibilityChanged( QgsLayerTreeNode *node )
416 : : {
417 : 0 : int childIndex = mChildren.indexOf( node );
418 : 0 : if ( childIndex == -1 )
419 : 0 : return; // not a direct child - ignore
420 : :
421 : 0 : if ( mMutuallyExclusive )
422 : : {
423 : 0 : if ( _nodeIsChecked( node ) )
424 : 0 : mMutuallyExclusiveChildIndex = childIndex;
425 : 0 : else if ( mMutuallyExclusiveChildIndex == childIndex )
426 : 0 : mMutuallyExclusiveChildIndex = -1;
427 : :
428 : : // we need to make sure there is only one child node checked
429 : 0 : updateChildVisibilityMutuallyExclusive();
430 : 0 : }
431 : 0 : }
432 : :
433 : 0 : void QgsLayerTreeGroup::updateChildVisibilityMutuallyExclusive()
434 : : {
435 : 0 : if ( mChildren.isEmpty() )
436 : 0 : return;
437 : :
438 : 0 : mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
439 : :
440 : 0 : int index = 0;
441 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
442 : : {
443 : 0 : child->setItemVisibilityChecked( index == mMutuallyExclusiveChildIndex );
444 : 0 : ++index;
445 : : }
446 : :
447 : 0 : mChangingChildVisibility = false;
448 : 0 : }
449 : :
450 : 0 : void QgsLayerTreeGroup::makeOrphan()
451 : : {
452 : 0 : QgsLayerTreeNode::makeOrphan();
453 : : // Reconnect internal signals
454 : 0 : connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged );
455 : 0 : }
456 : :
457 : 0 : void QgsLayerTreeGroup::setItemVisibilityCheckedRecursive( bool checked )
458 : : {
459 : 0 : QgsLayerTreeNode::setItemVisibilityChecked( checked );
460 : :
461 : 0 : mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
462 : :
463 : 0 : int index = 0;
464 : 0 : for ( QgsLayerTreeNode *child : std::as_const( mChildren ) )
465 : : {
466 : 0 : child->setItemVisibilityCheckedRecursive( checked && ( mMutuallyExclusiveChildIndex < 0 || index == mMutuallyExclusiveChildIndex ) );
467 : 0 : ++index;
468 : : }
469 : :
470 : 0 : mChangingChildVisibility = false;
471 : 0 : }
|