1 /***************************************************************************
2 qgslayertreeregistrybridge.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 "qgslayertreeregistrybridge.h"
17
18 #include "qgslayertree.h"
19
20 #include "qgsproject.h"
21 #include "qgslogger.h"
22
QgsLayerTreeRegistryBridge(QgsLayerTreeGroup * root,QgsProject * project,QObject * parent)23 QgsLayerTreeRegistryBridge::QgsLayerTreeRegistryBridge( QgsLayerTreeGroup *root, QgsProject *project, QObject *parent )
24 : QObject( parent )
25 , mRoot( root )
26 , mProject( project )
27 , mRegistryRemovingLayers( false )
28 , mEnabled( true )
29 , mNewLayersVisible( true )
30 , mInsertionPoint( root, 0 )
31 {
32 connect( mProject, &QgsProject::legendLayersAdded, this, &QgsLayerTreeRegistryBridge::layersAdded );
33 connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsLayerTreeRegistryBridge::layersWillBeRemoved );
34
35 connect( mRoot, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeRegistryBridge::groupWillRemoveChildren );
36 connect( mRoot, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeRegistryBridge::groupRemovedChildren );
37 }
38
setLayerInsertionPoint(QgsLayerTreeGroup * parentGroup,int index)39 void QgsLayerTreeRegistryBridge::setLayerInsertionPoint( QgsLayerTreeGroup *parentGroup, int index )
40 {
41 mInsertionPoint.group = parentGroup;
42 mInsertionPoint.position = index;
43 }
44
setLayerInsertionPoint(const InsertionPoint & insertionPoint)45 void QgsLayerTreeRegistryBridge::setLayerInsertionPoint( const InsertionPoint &insertionPoint )
46 {
47 mInsertionPoint = insertionPoint;
48 }
49
layersAdded(const QList<QgsMapLayer * > & layers)50 void QgsLayerTreeRegistryBridge::layersAdded( const QList<QgsMapLayer *> &layers )
51 {
52 if ( !mEnabled )
53 return;
54
55 QList<QgsLayerTreeNode *> nodes;
56 const auto constLayers = layers;
57 for ( QgsMapLayer *layer : constLayers )
58 {
59 QgsLayerTreeLayer *nodeLayer = new QgsLayerTreeLayer( layer );
60 nodeLayer->setItemVisibilityChecked( mNewLayersVisible );
61
62 nodes << nodeLayer;
63
64 // check whether the layer is marked as embedded
65 QString projectFile = mProject->layerIsEmbedded( nodeLayer->layerId() );
66 if ( !projectFile.isEmpty() )
67 {
68 nodeLayer->setCustomProperty( QStringLiteral( "embedded" ), 1 );
69 nodeLayer->setCustomProperty( QStringLiteral( "embedded_project" ), projectFile );
70 }
71 }
72
73 // add new layers to the right place
74 mInsertionPoint.group->insertChildNodes( mInsertionPoint.position, nodes );
75
76 // tell other components that layers have been added - this signal is used in QGIS to auto-select the first layer
77 emit addedLayersToLayerTree( layers );
78 }
79
layersWillBeRemoved(const QStringList & layerIds)80 void QgsLayerTreeRegistryBridge::layersWillBeRemoved( const QStringList &layerIds )
81 {
82 QgsDebugMsgLevel( QStringLiteral( "%1 layers will be removed, enabled:%2" ).arg( layerIds.count() ).arg( mEnabled ), 4 );
83
84 if ( !mEnabled )
85 return;
86
87 // when we start removing child nodes, the bridge would try to remove those layers from
88 // the registry _again_ in groupRemovedChildren() - this prevents it
89 mRegistryRemovingLayers = true;
90
91 const auto constLayerIds = layerIds;
92 for ( const QString &layerId : constLayerIds )
93 {
94 QgsLayerTreeLayer *nodeLayer = mRoot->findLayer( layerId );
95 if ( nodeLayer )
96 qobject_cast<QgsLayerTreeGroup *>( nodeLayer->parent() )->removeChildNode( nodeLayer );
97 }
98
99 mRegistryRemovingLayers = false;
100 }
101
102
_collectLayerIdsInGroup(QgsLayerTreeGroup * group,int indexFrom,int indexTo,QStringList & lst)103 static void _collectLayerIdsInGroup( QgsLayerTreeGroup *group, int indexFrom, int indexTo, QStringList &lst )
104 {
105 for ( int i = indexFrom; i <= indexTo; ++i )
106 {
107 QgsLayerTreeNode *child = group->children().at( i );
108 if ( QgsLayerTree::isLayer( child ) )
109 {
110 lst << QgsLayerTree::toLayer( child )->layerId();
111 }
112 else if ( QgsLayerTree::isGroup( child ) )
113 {
114 _collectLayerIdsInGroup( QgsLayerTree::toGroup( child ), 0, child->children().count() - 1, lst );
115 }
116 }
117 }
118
groupWillRemoveChildren(QgsLayerTreeNode * node,int indexFrom,int indexTo)119 void QgsLayerTreeRegistryBridge::groupWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
120 {
121 if ( mRegistryRemovingLayers )
122 return; // do not try to remove those layers again
123
124 Q_ASSERT( mLayerIdsForRemoval.isEmpty() );
125
126 Q_ASSERT( QgsLayerTree::isGroup( node ) );
127 QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
128
129 _collectLayerIdsInGroup( group, indexFrom, indexTo, mLayerIdsForRemoval );
130 }
131
groupRemovedChildren()132 void QgsLayerTreeRegistryBridge::groupRemovedChildren()
133 {
134 if ( mRegistryRemovingLayers )
135 return; // do not try to remove those layers again
136
137 // remove only those that really do not exist in the tree
138 // (ignores layers that were dragged'n'dropped: 1. drop new 2. remove old)
139 QStringList toRemove;
140 const auto constMLayerIdsForRemoval = mLayerIdsForRemoval;
141 for ( const QString &layerId : constMLayerIdsForRemoval )
142 if ( !mRoot->findLayer( layerId ) )
143 toRemove << layerId;
144 mLayerIdsForRemoval.clear();
145
146 QgsDebugMsgLevel( QStringLiteral( "%1 layers will be removed" ).arg( toRemove.count() ), 4 );
147
148 // delay the removal of layers from the registry. There may be other slots connected to map layer registry's signals
149 // that might disrupt the execution flow - e.g. a processEvents() call may force update of layer tree view with
150 // semi-broken tree model
151 QMetaObject::invokeMethod( this, "removeLayersFromRegistry", Qt::QueuedConnection, Q_ARG( QStringList, toRemove ) );
152 }
153
removeLayersFromRegistry(const QStringList & layerIds)154 void QgsLayerTreeRegistryBridge::removeLayersFromRegistry( const QStringList &layerIds )
155 {
156 mProject->removeMapLayers( layerIds );
157 }
158