Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgslayertree 3 : : --------------------- 4 : : begin : 22.3.2017 5 : : copyright : (C) 2017 by Matthias Kuhn 6 : : email : matthias@opengis.ch 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 "qgslayertree.h" 17 : : #include "qgsmaplayerlistutils.h" 18 : : #include "qgsvectorlayer.h" 19 : : 20 : 5 : QgsLayerTree::QgsLayerTree() 21 : 5 : { 22 : 5 : connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren ); 23 : 5 : connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren ); 24 : 5 : } 25 : : 26 : 0 : QgsLayerTree::QgsLayerTree( const QgsLayerTree &other ) 27 : 0 : : QgsLayerTreeGroup( other ) 28 : 0 : , mCustomLayerOrder( other.mCustomLayerOrder ) 29 : 0 : , mHasCustomLayerOrder( other.mHasCustomLayerOrder ) 30 : 0 : { 31 : 0 : connect( this, &QgsLayerTree::addedChildren, this, &QgsLayerTree::nodeAddedChildren ); 32 : 0 : connect( this, &QgsLayerTree::removedChildren, this, &QgsLayerTree::nodeRemovedChildren ); 33 : 0 : } 34 : : 35 : 1 : QList<QgsMapLayer *> QgsLayerTree::customLayerOrder() const 36 : : { 37 : 1 : return _qgis_listQPointerToRaw( mCustomLayerOrder ); 38 : : } 39 : : 40 : 9 : void QgsLayerTree::setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder ) 41 : : { 42 : 9 : QgsWeakMapLayerPointerList newOrder = _qgis_listRawToQPointer( customLayerOrder ); 43 : : 44 : 9 : if ( newOrder == mCustomLayerOrder ) 45 : 8 : return; 46 : : 47 : 1 : mCustomLayerOrder = newOrder; 48 : 1 : emit customLayerOrderChanged(); 49 : : 50 : 1 : if ( mHasCustomLayerOrder ) 51 : 0 : emit layerOrderChanged(); 52 : 9 : } 53 : : 54 : 8 : void QgsLayerTree::setCustomLayerOrder( const QStringList &customLayerOrder ) 55 : : { 56 : 8 : QList<QgsMapLayer *> layers; 57 : : 58 : 8 : for ( const auto &layerId : customLayerOrder ) 59 : : { 60 : 0 : QgsLayerTreeLayer *nodeLayer = findLayer( layerId ); 61 : 0 : if ( nodeLayer ) 62 : : { 63 : : // configuration from 2.x projects might have non spatial layers 64 : 0 : QgsMapLayer *layer = nodeLayer->layer(); 65 : 0 : if ( !layer || !layer->isSpatial() ) 66 : : { 67 : 0 : continue; 68 : : } 69 : 0 : layers.append( layer ); 70 : 0 : } 71 : : } 72 : 8 : setCustomLayerOrder( layers ); 73 : 8 : } 74 : : 75 : 0 : QList<QgsMapLayer *> QgsLayerTree::layerOrder() const 76 : : { 77 : 0 : if ( mHasCustomLayerOrder ) 78 : : { 79 : 0 : return customLayerOrder(); 80 : : } 81 : : else 82 : : { 83 : 0 : QList<QgsMapLayer *> layers; 84 : 0 : const QList< QgsLayerTreeLayer * > foundLayers = findLayers(); 85 : 0 : for ( const auto &treeLayer : foundLayers ) 86 : : { 87 : 0 : QgsMapLayer *layer = treeLayer->layer(); 88 : 0 : if ( !layer || !layer->isSpatial() ) 89 : : { 90 : 0 : continue; 91 : : } 92 : 0 : layers.append( layer ); 93 : : } 94 : 0 : return layers; 95 : 0 : } 96 : 0 : } 97 : : 98 : 0 : bool QgsLayerTree::hasCustomLayerOrder() const 99 : : { 100 : 0 : return mHasCustomLayerOrder; 101 : : } 102 : : 103 : 8 : void QgsLayerTree::setHasCustomLayerOrder( bool hasCustomLayerOrder ) 104 : : { 105 : 8 : if ( hasCustomLayerOrder == mHasCustomLayerOrder ) 106 : 8 : return; 107 : : 108 : 0 : mHasCustomLayerOrder = hasCustomLayerOrder; 109 : : 110 : 0 : emit hasCustomLayerOrderChanged( hasCustomLayerOrder ); 111 : 0 : emit layerOrderChanged(); 112 : 8 : } 113 : : 114 : 0 : QgsLayerTree *QgsLayerTree::readXml( QDomElement &element, const QgsReadWriteContext &context ) 115 : : { 116 : 0 : QgsLayerTree *tree = new QgsLayerTree(); 117 : : 118 : 0 : tree->readCommonXml( element ); 119 : : 120 : 0 : tree->readChildrenFromXml( element, context ); 121 : : 122 : 0 : return tree; 123 : 0 : } 124 : : 125 : 0 : void QgsLayerTree::writeXml( QDomElement &parentElement, const QgsReadWriteContext &context ) 126 : : { 127 : 0 : QDomDocument doc = parentElement.ownerDocument(); 128 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) ); 129 : : 130 : 0 : writeCommonXml( elem ); 131 : : 132 : 0 : for ( QgsLayerTreeNode *node : std::as_const( mChildren ) ) 133 : 0 : node->writeXml( elem, context ); 134 : : 135 : 0 : QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) ); 136 : 0 : customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 ); 137 : 0 : elem.appendChild( customOrderElem ); 138 : : 139 : 0 : for ( QgsMapLayer *layer : std::as_const( mCustomLayerOrder ) ) 140 : : { 141 : : // Safety belt, see https://github.com/qgis/QGIS/issues/26975 142 : : // Crash when deleting an item from the layout legend 143 : 0 : if ( ! layer ) 144 : 0 : continue; 145 : 0 : QDomElement layerElem = doc.createElement( QStringLiteral( "item" ) ); 146 : 0 : layerElem.appendChild( doc.createTextNode( layer->id() ) ); 147 : 0 : customOrderElem.appendChild( layerElem ); 148 : 0 : } 149 : : 150 : 0 : elem.appendChild( customOrderElem ); 151 : : 152 : 0 : parentElement.appendChild( elem ); 153 : 0 : } 154 : : 155 : 0 : QgsLayerTree *QgsLayerTree::clone() const 156 : : { 157 : 0 : return new QgsLayerTree( *this ); 158 : 0 : } 159 : : 160 : 8 : void QgsLayerTree::clear() 161 : : { 162 : 8 : removeAllChildren(); 163 : 8 : setHasCustomLayerOrder( false ); 164 : 8 : setCustomLayerOrder( QStringList() ); 165 : 8 : } 166 : : 167 : 1 : void QgsLayerTree::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo ) 168 : : { 169 : : Q_ASSERT( node ); 170 : : 171 : : // collect layer IDs that have been added in order to put them into custom layer order 172 : 1 : QList<QgsMapLayer *> layers; 173 : : 174 : 1 : QList<QgsLayerTreeNode *> children = node->children(); 175 : 2 : for ( int i = indexFrom; i <= indexTo; ++i ) 176 : : { 177 : 1 : QgsLayerTreeNode *child = children.at( i ); 178 : 1 : if ( QgsLayerTree::isLayer( child ) ) 179 : : { 180 : 1 : layers << QgsLayerTree::toLayer( child )->layer(); 181 : 1 : } 182 : 0 : else if ( QgsLayerTree::isGroup( child ) ) 183 : : { 184 : 0 : const auto nodeLayers = QgsLayerTree::toGroup( child )->findLayers(); 185 : 0 : for ( QgsLayerTreeLayer *nodeL : nodeLayers ) 186 : 0 : layers << nodeL->layer(); 187 : 0 : } 188 : 1 : } 189 : : 190 : 2 : for ( QgsMapLayer *layer : std::as_const( layers ) ) 191 : : { 192 : 1 : if ( !mCustomLayerOrder.contains( layer ) && layer ) 193 : 1 : mCustomLayerOrder.append( layer ); 194 : : } 195 : : 196 : 1 : emit customLayerOrderChanged(); 197 : 1 : emit layerOrderChanged(); 198 : 1 : } 199 : : 200 : 1 : void QgsLayerTree::nodeRemovedChildren() 201 : : { 202 : 1 : QList<QgsMapLayer *> layers = customLayerOrder(); 203 : 1 : auto layer = layers.begin(); 204 : : 205 : 1 : while ( layer != layers.end() ) 206 : : { 207 : 0 : if ( !findLayer( *layer ) ) 208 : 0 : layer = layers.erase( layer ); 209 : : else 210 : 0 : ++layer; 211 : : } 212 : : 213 : : // we need to ensure that the customLayerOrderChanged signal is ALWAYS raised 214 : : // here, since that order HAS changed due to removal of the child! 215 : : // setCustomLayerOrder will only emit this signal when the layers list 216 : : // at this stage is different to the stored customer layer order. If this 217 : : // isn't the case (i.e. the lists ARE the same) then manually emit the 218 : : // signal 219 : 1 : const bool emitSignal = _qgis_listRawToQPointer( layers ) == mCustomLayerOrder; 220 : : 221 : 1 : setCustomLayerOrder( layers ); 222 : 1 : if ( emitSignal ) 223 : 0 : emit customLayerOrderChanged(); 224 : : 225 : 1 : emit layerOrderChanged(); 226 : 1 : } 227 : : 228 : 0 : void QgsLayerTree::addMissingLayers() 229 : : { 230 : 5 : bool changed = false; 231 : : 232 : 0 : const QList< QgsLayerTreeLayer * > layers = findLayers(); 233 : 0 : for ( const auto layer : layers ) 234 : : { 235 : 0 : if ( !mCustomLayerOrder.contains( layer->layer() ) && 236 : 0 : layer->layer() && layer->layer()->isSpatial() ) 237 : : { 238 : 0 : mCustomLayerOrder.append( layer->layer() ); 239 : 0 : changed = true; 240 : 0 : } 241 : : } 242 : : 243 : 0 : if ( changed ) 244 : : { 245 : 0 : emit customLayerOrderChanged(); 246 : 0 : if ( mHasCustomLayerOrder ) 247 : 0 : emit layerOrderChanged(); 248 : 0 : } 249 : 0 : } 250 : : 251 : 0 : void QgsLayerTree::readLayerOrderFromXml( const QDomElement &elem ) 252 : : { 253 : 0 : QStringList order; 254 : : 255 : 0 : QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) ); 256 : 0 : if ( !customOrderElem.isNull() ) 257 : : { 258 : 0 : setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ) ).toInt() ); 259 : : 260 : 0 : QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) ); 261 : 0 : while ( !itemElem.isNull() ) 262 : : { 263 : 0 : order.append( itemElem.text() ); 264 : 0 : itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) ); 265 : : } 266 : 0 : } 267 : : 268 : 0 : setCustomLayerOrder( order ); 269 : 0 : addMissingLayers(); 270 : 0 : }