QGIS API Documentation 3.29.0-Master (ade4f0cf0f)
qgslayertreemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreemodel.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 <QMimeData>
17#include <QTextStream>
18
19#include "qgslayertreemodel.h"
20
21#include "qgsapplication.h"
22#include "qgslayertree.h"
23#include "qgslayertreeutils.h"
25#include "qgsproject.h"
26#include "qgsmaphittest.h"
27#include "qgsmaplayer.h"
28#include "qgsmaplayerlegend.h"
30#include "qgsmeshlayer.h"
31#include "qgspluginlayer.h"
32#include "qgsrasterlayer.h"
33#include "qgsrenderer.h"
34#include "qgssymbollayerutils.h"
35#include "qgsvectorlayer.h"
36#include "qgslayerdefinition.h"
37#include "qgsiconutils.h"
38#include "qgsmimedatautils.h"
40
41#include <QPalette>
42
44 : QAbstractItemModel( parent )
45 , mRootNode( rootNode )
46 , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
47 , mAutoCollapseLegendNodesCount( -1 )
48 , mLegendFilterByScale( 0 )
49 , mLegendFilterUsesExtent( false )
50 , mLegendMapViewMupp( 0 )
51 , mLegendMapViewDpi( 0 )
52 , mLegendMapViewScale( 0 )
53{
55
56 mFontLayer.setBold( true );
57
59 mDeferLegendInvalidationTimer.setSingleShot( true );
60}
61
63{
65}
66
67QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
68{
69 if ( !index.isValid() )
70 return mRootNode;
71
72 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
73 return qobject_cast<QgsLayerTreeNode *>( obj );
74}
75
76
77int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
78{
80 return legendNodeRowCount( nodeLegend );
81
83 if ( !n )
84 return 0;
85
86 if ( QgsLayerTree::isLayer( n ) )
87 {
88 if ( !testFlag( ShowLegend ) )
89 return 0;
90
92 }
93
94 return n->children().count();
95}
96
97int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
98{
99 Q_UNUSED( parent )
100 return 1;
101}
102
103QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
104{
105 if ( column < 0 || column >= columnCount( parent ) ||
106 row < 0 || row >= rowCount( parent ) )
107 return QModelIndex();
108
110 return legendNodeIndex( row, column, nodeLegend );
111
113 if ( !n )
114 return QModelIndex(); // have no children
115
117 {
118 return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
119 }
120
121 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
122}
123
124
125QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
126{
127 if ( !child.isValid() )
128 return QModelIndex();
129
130 if ( QgsLayerTreeNode *n = index2node( child ) )
131 {
132 return indexOfParentLayerTreeNode( n->parent() ); // must not be null
133 }
135 {
136 return legendParent( legendNode );
137 }
138 else
139 {
140 Q_ASSERT( false ); // no other node types!
141 return QModelIndex();
142 }
143
144}
145
146
148{
149 Q_ASSERT( parentNode );
150
151 QgsLayerTreeNode *grandParentNode = parentNode->parent();
152 if ( !grandParentNode )
153 return QModelIndex(); // root node -> invalid index
154
155 int row = grandParentNode->children().indexOf( parentNode );
156 Q_ASSERT( row >= 0 );
157
158 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
159}
160
161
162QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
163{
164 if ( !index.isValid() || index.column() > 1 )
165 return QVariant();
166
168 return legendNodeData( sym, role );
169
171 if ( role == Qt::DisplayRole || role == Qt::EditRole )
172 {
173 if ( QgsLayerTree::isGroup( node ) )
174 return QgsLayerTree::toGroup( node )->name();
175
176 if ( QgsLayerTree::isLayer( node ) )
177 {
178 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
179 QString name = nodeLayer->name();
180 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
181 if ( vlayer && nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
182 {
183 const bool estimatedCount = QgsDataSourceUri( vlayer->dataProvider()->dataSourceUri() ).useEstimatedMetadata();
184 const qlonglong count = vlayer->featureCount();
185
186 // if you modify this line, please update QgsSymbolLegendNode::updateLabel
187 name += QStringLiteral( " [%1%2]" ).arg(
188 estimatedCount ? QStringLiteral( "≈" ) : QString(),
189 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
190 }
191 return name;
192 }
193 }
194 else if ( role == Qt::DecorationRole && index.column() == 0 )
195 {
196 if ( QgsLayerTree::isGroup( node ) )
197 return iconGroup();
198
199 if ( QgsLayerTree::isLayer( node ) )
200 {
201 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
202
203 QgsMapLayer *layer = nodeLayer->layer();
204 if ( !layer )
205 return QVariant();
206
207 // icons possibly overriding default icon
208 QIcon icon = QgsIconUtils::iconForLayer( layer );
209
210 // if there's just on legend entry that should be embedded in layer - do that!
211 if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
212 {
213 icon = legendIconEmbeddedInParent( nodeLayer );
214 }
215
216 if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
217 {
218 const int iconSize = scaleIconSize( 16 );
219 QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
220
221 QPainter painter( &pixmap );
222 painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
223 painter.end();
224
225 icon = QIcon( pixmap );
226 }
227
228 return icon;
229 }
230 }
231 else if ( role == Qt::CheckStateRole )
232 {
234 return QVariant();
235
236 if ( QgsLayerTree::isLayer( node ) )
237 {
238 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
239
240 if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
241 return QVariant(); // do not show checkbox for non-spatial tables
242
243 return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
244 }
245 else if ( QgsLayerTree::isGroup( node ) )
246 {
247 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
248 return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
249 }
250 }
251 else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
252 {
253 QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
254 if ( index == mCurrentIndex )
255 f.setUnderline( true );
256 if ( QgsLayerTree::isLayer( node ) )
257 {
258 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
259 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
260 {
261 f.setItalic( !f.italic() );
262 }
263 }
264 return f;
265 }
266 else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
267 {
268 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
269 if ( QgsLayerTree::isLayer( node ) )
270 {
271 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
272 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
273 {
274 QColor fadedTextColor = brush.color();
275 fadedTextColor.setAlpha( 128 );
276 brush.setColor( fadedTextColor );
277 }
278 }
279 return brush;
280 }
281 else if ( role == Qt::ToolTipRole )
282 {
283 if ( QgsLayerTree::isLayer( node ) )
284 {
285 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
286 {
287 QString title =
288 !layer->title().isEmpty() ? layer->title() :
289 !layer->shortName().isEmpty() ? layer->shortName() :
290 layer->name();
291
292 title = "<b>" + title.toHtmlEscaped() + "</b>";
293
294 if ( layer->isSpatial() && layer->crs().isValid() )
295 {
296 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
297 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
298 else
299 title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
300 }
301
302 QStringList parts;
303 parts << title;
304
305 if ( !layer->abstract().isEmpty() )
306 {
307 parts << QString();
308 const QStringList abstractLines = layer->abstract().split( '\n' );
309 for ( const auto &l : abstractLines )
310 {
311 parts << l.toHtmlEscaped();
312 }
313 parts << QString();
314 }
315
316 QString source( layer->publicSource() );
317 if ( source.size() > 1024 )
318 {
319 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
320 }
321
322 parts << "<i>" + source.toHtmlEscaped() + "</i>";
323
324 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
325 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
326 const bool estimatedCount = QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata();
327 if ( showFeatureCount && estimatedCount )
328 {
329 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
330 }
331
332 return parts.join( QLatin1String( "<br/>" ) );
333 }
334 }
335 }
336
337 return QVariant();
338}
339
340
341Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
342{
343 if ( !index.isValid() )
344 {
345 Qt::ItemFlags rootFlags = Qt::ItemFlags();
346 if ( testFlag( AllowNodeReorder ) )
347 rootFlags |= Qt::ItemIsDropEnabled;
348 return rootFlags;
349 }
350
352 return legendNodeFlags( symn );
353
354 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
355
356 if ( testFlag( AllowNodeRename ) )
357 f |= Qt::ItemIsEditable;
358
360 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
361
362 if ( testFlag( AllowNodeReorder ) )
363 {
364 // only root embedded nodes can be reordered
365 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
366 f |= Qt::ItemIsDragEnabled;
367 }
368
370 f |= Qt::ItemIsUserCheckable;
371
372 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
373 f |= Qt::ItemIsDropEnabled;
374
375 return f;
376}
377
378bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
379{
381 if ( sym )
382 {
383 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
384 return false;
385 bool res = sym->setData( value, role );
386 if ( res )
387 emit dataChanged( index, index );
388 return res;
389 }
390
392 if ( !node )
393 return QAbstractItemModel::setData( index, value, role );
394
395 if ( role == Qt::CheckStateRole )
396 {
398 return false;
399
400 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
401 if ( checked && node->children().isEmpty() )
402 {
404 }
405 else if ( testFlag( ActionHierarchical ) )
406 {
407 if ( node->children().isEmpty() )
409 else
410 node->setItemVisibilityCheckedRecursive( checked );
411 }
412 else
413 {
414 node->setItemVisibilityChecked( checked );
415 }
416
418
419 return true;
420 }
421 else if ( role == Qt::EditRole )
422 {
423 if ( !testFlag( AllowNodeRename ) )
424 return false;
425
426 if ( QgsLayerTree::isLayer( node ) )
427 {
429 layer->setName( value.toString() );
430 emit dataChanged( index, index );
431 }
432 else if ( QgsLayerTree::isGroup( node ) )
433 {
434 QgsLayerTree::toGroup( node )->setName( value.toString() );
435 emit dataChanged( index, index );
436 }
437 }
438
439 return QAbstractItemModel::setData( index, value, role );
440}
441
443{
444 if ( !node || !node->parent() )
445 return QModelIndex(); // this is the only root item -> invalid index
446
447 QModelIndex parentIndex = node2index( node->parent() );
448
449 int row = node->parent()->children().indexOf( node );
450 Q_ASSERT( row >= 0 );
451 return index( row, 0, parentIndex );
452}
453
454
455static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
456{
457 if ( !child->parent() )
458 return false;
459
460 if ( child->parent() == node )
461 return true;
462
463 return _isChildOfNode( child->parent(), node );
464}
465
466static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
467{
468 for ( QgsLayerTreeNode *n : nodes )
469 {
470 if ( _isChildOfNode( child, n ) )
471 return true;
472 }
473
474 return false;
475}
476
477
478QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
479{
480 QList<QgsLayerTreeNode *> nodes;
481 const auto constList = list;
482 for ( const QModelIndex &index : constList )
483 {
485 if ( !node )
486 continue;
487
488 nodes << node;
489 }
490
491 if ( !skipInternal )
492 return nodes;
493
494 // remove any children of nodes if both parent node and children are selected
495 QList<QgsLayerTreeNode *> nodesFinal;
496 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
497 {
498 if ( !_isChildOfNodes( node, nodes ) )
499 nodesFinal << node;
500 }
501
502 return nodesFinal;
503}
504
506{
507 return mRootNode;
508}
509
511{
512 beginResetModel();
513
515
516 Q_ASSERT( mLegend.isEmpty() );
517
518 mRootNode = newRootGroup;
519
520 endResetModel();
521
523}
524
526{
527 // update title
528 QModelIndex idx = node2index( nodeLayer );
529 emit dataChanged( idx, idx );
530
531 // update children
532 int oldNodeCount = rowCount( idx );
533 if ( oldNodeCount > 0 )
534 {
535 beginRemoveRows( idx, 0, oldNodeCount - 1 );
536 removeLegendFromLayer( nodeLayer );
537 endRemoveRows();
538 }
539
540 addLegendToLayer( nodeLayer );
541 int newNodeCount = rowCount( idx );
542
543 // automatic collapse of legend nodes - useful if a layer has many legend nodes
544 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
545 nodeLayer->setExpanded( false );
546}
547
549{
550 return mCurrentIndex;
551}
552
553void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
554{
556}
557
558
559void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
560{
561 if ( nodeType == QgsLayerTreeNode::NodeGroup )
562 {
563 if ( mFontGroup != font )
564 {
565 mFontGroup = font;
567 }
568 }
569 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
570 {
571 if ( mFontLayer != font )
572 {
573 mFontLayer = font;
575 }
576 }
577 else
578 {
579 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
580 }
581}
582
583
584QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
585{
586 if ( nodeType == QgsLayerTreeNode::NodeGroup )
587 return mFontGroup;
588 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
589 return mFontLayer;
590 else
591 {
592 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
593 return QFont();
594 }
595}
596
598{
599 mLegendFilterByScale = scale;
600
601 // this could be later done in more efficient way
602 // by just updating active legend nodes, without refreshing original legend nodes
603 const auto layers = mRootNode->findLayers();
604 for ( QgsLayerTreeLayer *nodeLayer : layers )
605 refreshLayerLegend( nodeLayer );
606}
607
609{
610 setLegendFilter( settings, /* useExtent = */ true );
611}
612
613void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
614{
615 if ( settings && settings->hasValidSettings() )
616 {
617 mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
618 mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
620 mLegendFilterUsesExtent = useExtent;
621 // collect expression filters
622 if ( useExpressions )
623 {
624 const auto layers = mRootNode->findLayers();
625 for ( QgsLayerTreeLayer *nodeLayer : layers )
626 {
627 bool enabled;
628 QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
629 if ( enabled && !expr.isEmpty() )
630 {
631 exprs[ nodeLayer->layerId()] = expr;
632 }
633 }
634 }
635 bool polygonValid = !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry;
636 if ( useExpressions && !useExtent && !polygonValid ) // only expressions
637 {
639 }
640 else
641 {
642 mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, polygon, exprs ) );
643 }
645 }
646 else
647 {
649 return; // no change
650
652 mLegendFilterHitTest.reset();
653 }
654
655 // temporarily disable autocollapse so that legend nodes stay visible
656 int bkAutoCollapse = autoCollapseLegendNodes();
658
659 // this could be later done in more efficient way
660 // by just updating active legend nodes, without refreshing original legend nodes
661 const auto layers = mRootNode->findLayers();
662 for ( QgsLayerTreeLayer *nodeLayer : layers )
663 refreshLayerLegend( nodeLayer );
664
665 setAutoCollapseLegendNodes( bkAutoCollapse );
666}
667
668void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
669{
670 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
671 return;
672
673 double previousScale = mLegendMapViewScale;
674 mLegendMapViewScale = scale;
675 mLegendMapViewMupp = mapUnitsPerPixel;
676 mLegendMapViewDpi = dpi;
677
678 // now invalidate legend nodes!
680
681 if ( scale != previousScale )
682 refreshScaleBasedLayers( QModelIndex(), previousScale );
683}
684
685void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
686{
687 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
688 if ( dpi ) *dpi = mLegendMapViewDpi;
689 if ( scale ) *scale = mLegendMapViewScale;
690}
691
692QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
693{
695}
696
697void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
698{
699 mLayerStyleOverrides = overrides;
700}
701
702int QgsLayerTreeModel::scaleIconSize( int standardSize )
703{
704 return QgsApplication::scaleIconSize( standardSize, true );
705}
706
707void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
708{
709 beginInsertRows( node2index( node ), indexFrom, indexTo );
710}
711
712static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
713{
714 QList<QgsLayerTreeNode *> children = node->children();
715 QList<QgsLayerTreeLayer *> newLayerNodes;
716 for ( int i = indexFrom; i <= indexTo; ++i )
717 {
718 QgsLayerTreeNode *child = children.at( i );
719 if ( QgsLayerTree::isLayer( child ) )
720 newLayerNodes << QgsLayerTree::toLayer( child );
721 else if ( QgsLayerTree::isGroup( child ) )
722 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
723 }
724 return newLayerNodes;
725}
726
727void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
728{
729 Q_ASSERT( node );
730
731 endInsertRows();
732
733 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
734 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
735 connectToLayer( newLayerNode );
736}
737
738void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
739{
740 Q_ASSERT( node );
741
742 beginRemoveRows( node2index( node ), indexFrom, indexTo );
743
744 // disconnect from layers and remove their legend
745 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
746 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
747 disconnectFromLayer( nodeLayer );
748}
749
751{
752 endRemoveRows();
753}
754
756{
757 Q_ASSERT( node );
758
759 const QModelIndex index = node2index( node );
760 emit dataChanged( index, index );
761}
762
763void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
764{
765 Q_UNUSED( name )
766 Q_ASSERT( node );
767
768 const QModelIndex index = node2index( node );
769 emit dataChanged( index, index );
770}
771
772
774{
775 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
777}
778
779
781{
782 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
783 if ( !nodeLayer )
784 return;
785
786 // deferred connection to the layer
787 connectToLayer( nodeLayer );
788}
789
791{
792 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
793 if ( !nodeLayer )
794 return;
795
796 disconnectFromLayer( nodeLayer );
797
798 // wait for the layer to appear again
800}
801
803{
804 if ( !mRootNode )
805 return;
806
807 if ( !testFlag( ShowLegend ) )
808 return;
809
810 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
811 if ( !layer )
812 return;
813
814 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
815 if ( !nodeLayer )
816 return;
817
818 refreshLayerLegend( nodeLayer );
819}
820
822{
823 if ( !mRootNode )
824 return;
825
826 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
827 if ( !layer )
828 return;
829
830 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
831 if ( !nodeLayer )
832 return;
833
834 const QModelIndex index = node2index( nodeLayer );
835 emit dataChanged( index, index );
836}
837
839{
840 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
841 if ( !layer )
842 return;
843
844 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
845 if ( !nodeLayer )
846 return;
847
848 QModelIndex index = node2index( nodeLayer );
849 emit dataChanged( index, index );
850
851 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
852 refreshLayerLegend( nodeLayer );
853}
854
855
857{
858 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
859 if ( !legendNode )
860 return;
861
862 QModelIndex index = legendNode2index( legendNode );
863 if ( index.isValid() )
864 emit dataChanged( index, index );
865}
866
867void QgsLayerTreeModel::legendNodeSizeChanged()
868{
869 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
870 if ( !legendNode )
871 return;
872
873 QModelIndex index = legendNode2index( legendNode );
874 if ( index.isValid() )
875 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
876}
877
878
880{
881 if ( !nodeLayer->layer() )
882 {
883 // in order to connect to layer, we need to have it loaded.
884 // keep an eye on the layer ID: once loaded, we will use it
885 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
886 return;
887 }
888
889 // watch if the layer is getting removed
890 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
891
892 if ( testFlag( ShowLegend ) )
893 {
894 addLegendToLayer( nodeLayer );
895
896 // if we aren't loading a layer from a project, setup some nice default settings
897 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
898 {
899 // automatic collapse of legend nodes - useful if a layer has many legend nodes
901 nodeLayer->setExpanded( false );
902
904 {
905 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
906 }
907 }
908 }
909
910 QgsMapLayer *layer = nodeLayer->layer();
911 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
912 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
913
914 // using unique connection because there may be temporarily more nodes for a layer than just one
915 // which would create multiple connections, however disconnect() would disconnect all multiple connections
916 // even if we wanted to disconnect just one connection in each call.
917 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
918 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
919 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
920
921 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
922}
923
924// try to find out if the layer ID is present in the tree multiple times
925static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
926{
927 int count = 0;
928 const auto constChildren = group->children();
929 for ( QgsLayerTreeNode *child : constChildren )
930 {
931 if ( QgsLayerTree::isLayer( child ) )
932 {
933 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
934 count++;
935 }
936 else if ( QgsLayerTree::isGroup( child ) )
937 {
938 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
939 }
940 }
941 return count;
942}
943
945{
946 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
947
948 if ( !nodeLayer->layer() )
949 return; // we were never connected
950
951 if ( testFlag( ShowLegend ) )
952 {
953 removeLegendFromLayer( nodeLayer );
954 }
955
956 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
957 {
958 // last instance of the layer in the tree: disconnect from all signals from layer!
959 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
960 }
961}
962
964{
965 const auto constChildren = parentGroup->children();
966 for ( QgsLayerTreeNode *node : constChildren )
967 {
968 if ( QgsLayerTree::isGroup( node ) )
970 else if ( QgsLayerTree::isLayer( node ) )
972 }
973}
974
976{
977 const auto constChildren = parentGroup->children();
978 for ( QgsLayerTreeNode *node : constChildren )
979 {
980 if ( QgsLayerTree::isGroup( node ) )
982 else if ( QgsLayerTree::isLayer( node ) )
984 }
985}
986
988{
989 Q_ASSERT( mRootNode );
990
991 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
992 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
993 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
994 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
995 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
996 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
997 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
998
1000}
1001
1003{
1004 disconnect( mRootNode, nullptr, this, nullptr );
1005
1007}
1008
1010{
1011 QgsLayerTreeNode *node = index2node( idx );
1012 if ( !node )
1013 return;
1014
1015 int count = node->children().count();
1016 if ( count == 0 )
1017 return;
1018 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1019 for ( int i = 0; i < count; ++i )
1020 recursivelyEmitDataChanged( index( i, 0, idx ) );
1021}
1022
1023void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1024{
1025 QgsLayerTreeNode *node = index2node( idx );
1026 if ( !node )
1027 return;
1028
1029 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1030 {
1031 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1032 if ( layer && layer->hasScaleBasedVisibility() )
1033 {
1034 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1035 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1036 }
1037 }
1038 int count = node->children().count();
1039 for ( int i = 0; i < count; ++i )
1040 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1041}
1042
1044{
1045 return Qt::CopyAction | Qt::MoveAction;
1046}
1047
1049{
1050 QStringList types;
1051 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1052 return types;
1053}
1054
1055
1056QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1057{
1058 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1059 QModelIndexList sortedIndexes = indexes;
1060 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1061
1062 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1063
1064 if ( nodesFinal.isEmpty() )
1065 return nullptr;
1066
1067 QMimeData *mimeData = new QMimeData();
1068
1069 QDomDocument layerTreeDoc;
1070 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1071
1072 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1073 {
1074 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1075 }
1076 layerTreeDoc.appendChild( rootLayerTreeElem );
1077
1078 QString errorMessage;
1079 QgsReadWriteContext readWriteContext;
1080 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1081 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1082
1083 QString txt = layerDefinitionsDoc.toString();
1084
1085 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1086 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1087 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1088 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1089 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1090
1091 return mimeData;
1092}
1093
1094bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1095{
1096 if ( action == Qt::IgnoreAction )
1097 return true;
1098
1099 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1100 return false;
1101
1102 if ( column >= columnCount( parent ) )
1103 return false;
1104
1105 // don't accept drops from some layer tree subclasses to non-matching subclasses
1106 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1107 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1108 return false;
1109
1110 QgsLayerTreeNode *nodeParent = index2node( parent );
1111 if ( !QgsLayerTree::isGroup( nodeParent ) )
1112 return false;
1113
1114 if ( parent.isValid() && row == -1 )
1115 row = 0; // if dropped directly onto group item, insert at first position
1116
1117 // if we are coming from another QGIS instance, we need to add the layers too
1118 bool ok = false;
1119 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1120 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1121
1122 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1123 {
1124 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1125 QDomDocument layerDefinitionDoc;
1126 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1127 return false;
1128 QgsReadWriteContext context;
1129 QString errorMessage;
1130 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1131 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1132 }
1133 else
1134 {
1135 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1136
1137 QDomDocument layerTreeDoc;
1138 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1139 return false;
1140
1141 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1142 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1143 return false;
1144
1145 QList<QgsLayerTreeNode *> nodes;
1146
1147 QDomElement elem = rootLayerTreeElem.firstChildElement();
1148 while ( !elem.isNull() )
1149 {
1151 if ( node )
1152 nodes << node;
1153
1154 elem = elem.nextSiblingElement();
1155 }
1156
1157 if ( nodes.isEmpty() )
1158 return false;
1159
1160 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1161 }
1162 return true;
1163}
1164
1165bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1166{
1167 QgsLayerTreeNode *parentNode = index2node( parent );
1168 if ( QgsLayerTree::isGroup( parentNode ) )
1169 {
1170 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1171 return true;
1172 }
1173 return false;
1174}
1175
1176void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1177{
1178 mFlags = f;
1179}
1180
1182{
1183 if ( on )
1184 mFlags |= f;
1185 else
1186 mFlags &= ~f;
1187}
1188
1189QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1190{
1191 return mFlags;
1192}
1193
1195{
1196 return mFlags.testFlag( f );
1197}
1198
1200{
1201 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1202}
1203
1204QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1205{
1206 QList<QgsLayerTreeModelLegendNode *> filtered;
1207
1208 if ( mLegendFilterByScale > 0 )
1209 {
1210 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1211 {
1212 if ( node->isScaleOK( mLegendFilterByScale ) )
1213 filtered << node;
1214 }
1215 }
1216 else if ( mLegendFilterMapSettings )
1217 {
1218 if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1219 {
1220 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1221 {
1223 {
1225 filtered << node;
1226 break;
1227
1235 {
1236 const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1237 bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1238 if ( checked )
1239 {
1240 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1241 {
1242 if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1243 filtered << node;
1244 }
1245 else
1246 {
1247 filtered << node;
1248 }
1249 }
1250 else // unknown node type or unchecked
1251 filtered << node;
1252 break;
1253 }
1254 }
1255 }
1256 }
1257 }
1258 else
1259 {
1260 return nodes;
1261 }
1262
1263 return filtered;
1264}
1265
1266
1267
1269// Legend nodes routines - start
1270
1272{
1273 const auto constMLegend = mLegend;
1274 for ( const LayerLegendData &data : constMLegend )
1275 {
1276 qDeleteAll( data.originalNodes );
1277 delete data.tree;
1278 }
1279 mLegend.clear();
1280}
1281
1282
1284{
1285 if ( mLegend.contains( nodeLayer ) )
1286 {
1287 qDeleteAll( mLegend[nodeLayer].originalNodes );
1288 delete mLegend[nodeLayer].tree;
1289 mLegend.remove( nodeLayer );
1290 }
1291}
1292
1293
1295{
1296 if ( !nodeL || !nodeL->layer() )
1297 return;
1298
1299 QgsMapLayer *ml = nodeL->layer();
1300
1301 QgsMapLayerStyleOverride styleOverride( ml );
1302 if ( mLayerStyleOverrides.contains( ml->id() ) )
1303 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1304
1305 QgsMapLayerLegend *layerLegend = ml->legend();
1306 if ( !layerLegend )
1307 return;
1308 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1309
1310 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1312
1314 {
1315 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1316 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1317 while ( widgetsCount > 0 )
1318 {
1319 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1320 --widgetsCount;
1321 }
1322 }
1323
1324 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1325
1326 const auto constLstNew = lstNew;
1327 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1328 {
1329 n->setParent( this );
1331 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1332 }
1333
1334 // See if we have an embedded node - if we do, we will not use it among active nodes.
1335 // Legend node embedded in parent does not have to be the first one,
1336 // there can be also nodes generated for embedded widgets
1337 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1338 const auto constFilteredLstNew = filteredLstNew;
1339 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1340 {
1342 {
1343 embeddedNode = legendNode;
1344 filteredLstNew.removeOne( legendNode );
1345 break;
1346 }
1347 }
1348
1349 LayerLegendTree *legendTree = nullptr;
1350
1351 // maybe the legend nodes form a tree - try to create a tree structure from the list
1352 if ( testFlag( ShowLegendAsTree ) )
1353 legendTree = tryBuildLegendTree( filteredLstNew );
1354
1355 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1356
1357 if ( !filteredLstNew.isEmpty() )
1358 {
1359 // Make sure it's clear
1360 const QModelIndex nodeIndex { node2index( nodeL ) };
1361 if ( rowCount( nodeIndex ) > 0 )
1362 {
1363 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1364 mLegend[nodeL] = LayerLegendData();
1365 endRemoveRows();
1366 }
1367 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1368 }
1369
1371 data.originalNodes = lstNew;
1372 data.activeNodes = filteredLstNew;
1373 data.embeddedNodeInParent = embeddedNode;
1374 data.tree = legendTree;
1375
1376 mLegend[nodeL] = data;
1377
1378 if ( !filteredLstNew.isEmpty() )
1379 {
1380 endInsertRows();
1381 }
1382
1383 // invalidate map based data even if the data is not map-based to make sure
1384 // the symbol sizes are computed at least once
1385 mInvalidatedNodes.insert( nodeL );
1387}
1388
1389
1390QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1391{
1392 // first check whether there are any legend nodes that are not top-level
1393 bool hasParentKeys = false;
1394 for ( QgsLayerTreeModelLegendNode *n : nodes )
1395 {
1396 if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1397 {
1398 hasParentKeys = true;
1399 break;
1400 }
1401 }
1402 if ( !hasParentKeys )
1403 return nullptr; // all legend nodes are top-level => stick with list representation
1404
1405 // make mapping from rules to nodes and do some sanity checks
1406 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1407 rule2node[QString()] = nullptr;
1408 for ( QgsLayerTreeModelLegendNode *n : nodes )
1409 {
1410 QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1411 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1412 return nullptr;
1413 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1414 return nullptr;
1415 rule2node[ruleKey] = n;
1416 }
1417
1418 // create the tree structure
1419 LayerLegendTree *tree = new LayerLegendTree;
1420 for ( QgsLayerTreeModelLegendNode *n : nodes )
1421 {
1422 QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1423 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1424 tree->parents[n] = parent;
1425 tree->children[parent] << n;
1426 }
1427 return tree;
1428}
1429
1431{
1432 double scale = 0.0;
1433 double mupp = 0.0;
1434 int dpi = 0;
1435 legendMapViewData( &mupp, &dpi, &scale );
1436 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1437
1438 // setup temporary render context
1439 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1440 context->setScaleFactor( dpi / 25.4 );
1441 context->setRendererScale( scale );
1442 context->setMapToPixel( QgsMapToPixel( mupp ) );
1444 return validData ? context.release() : nullptr;
1445}
1446
1447
1449{
1450 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1451}
1452
1453
1455{
1457 if ( data.tree )
1458 {
1459 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1460 {
1461 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1462 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1463 return index( row, 0, parentIndex );
1464 }
1465 else
1466 {
1467 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1468 int row = data.tree->children[nullptr].indexOf( legendNode );
1469 return index( row, 0, parentIndex );
1470 }
1471 }
1472
1473 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1474 Q_ASSERT( parentIndex.isValid() );
1475 int row = data.activeNodes.indexOf( legendNode );
1476 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1477 return QModelIndex();
1478
1479 return index( row, 0, parentIndex );
1480}
1481
1482
1484{
1485 const LayerLegendData &data = mLegend[node->layerNode()];
1486 if ( data.tree )
1487 return data.tree->children[node].count();
1488
1489 return 0; // they are leaves
1490}
1491
1492
1494{
1495 if ( !mLegend.contains( nL ) )
1496 return 0;
1497
1498 const LayerLegendData &data = mLegend[nL];
1499 if ( data.tree )
1500 return data.tree->children[nullptr].count();
1501
1502 int count = data.activeNodes.count();
1503 return count;
1504}
1505
1506
1507QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1508{
1509 Q_ASSERT( mLegend.contains( nL ) );
1510 const LayerLegendData &data = mLegend[nL];
1511 if ( data.tree )
1512 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1513
1514 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1515}
1516
1517
1518QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1519{
1520 const LayerLegendData &data = mLegend[node->layerNode()];
1521 if ( data.tree )
1522 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1523
1524 return QModelIndex(); // have no children
1525}
1526
1527
1529{
1530 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1531 const LayerLegendData &data = mLegend[layerNode];
1532 if ( data.tree )
1533 {
1534 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1535 {
1536 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1537 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1538 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1539 }
1540 else
1541 return indexOfParentLayerTreeNode( layerNode );
1542 }
1543
1544 return indexOfParentLayerTreeNode( layerNode );
1545}
1546
1547
1549{
1550 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1551 return QVariant();
1552 return node->data( role );
1553}
1554
1555
1557{
1558 Qt::ItemFlags f = node->flags();
1560 f &= ~Qt::ItemIsUserCheckable;
1561 return f;
1562}
1563
1564
1566{
1567 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1568}
1569
1571{
1572 return mLegend[nodeLayer].embeddedNodeInParent;
1573}
1574
1575
1577{
1578 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1579 if ( !legendNode )
1580 return QIcon();
1581 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1582}
1583
1584
1585QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1586{
1587 if ( !mLegend.contains( nodeLayer ) )
1588 return QList<QgsLayerTreeModelLegendNode *>();
1589
1590 const LayerLegendData &data = mLegend[nodeLayer];
1591 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1592 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1593 lst.prepend( data.embeddedNodeInParent );
1594 return lst;
1595}
1596
1597QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1598{
1599 return mLegend.value( nodeLayer ).originalNodes;
1600}
1601
1602QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1603{
1604 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1605 {
1606 QgsLayerTreeLayer *layer = it.key();
1607 if ( layer->layerId() == layerId )
1608 {
1609 const auto activeNodes = mLegend.value( layer ).activeNodes;
1610 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1611 {
1612 if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1613 {
1614 //found it!
1615 return legendNode;
1616 }
1617 }
1618 }
1619 }
1620
1621 return nullptr;
1622}
1623
1625{
1628 else
1630}
1631
1633{
1634 // we have varying icon sizes, and we want icon to be centered and
1635 // text to be left aligned, so we have to compute the max width of icons
1636 //
1637 // we do that for nodes which share a common parent
1638 //
1639 // we do that here because for symbols with size defined in map units
1640 // the symbol sizes changes depends on the zoom level
1641
1642 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1643
1644 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1645 {
1646 const LayerLegendData &data = mLegend.value( layerNode );
1647
1648 QList<QgsSymbolLegendNode *> symbolNodes;
1649 QMap<QString, int> widthMax;
1650 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1651 {
1652 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1653 if ( n )
1654 {
1655 const QSize sz( n->minimumIconSize( context.get() ) );
1656 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1657 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1658 n->setIconSize( sz );
1659 symbolNodes.append( n );
1660 }
1661 }
1662 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1663 {
1664 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1665 Q_ASSERT( widthMax[parentKey] > 0 );
1666 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1667 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1668 }
1669 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1671 }
1672
1673 mInvalidatedNodes.clear();
1674}
1675
1676// Legend nodes routines - end
@ UsersCannotToggleEditing
Indicates that users are not allowed to toggle editing for this layer. Note that this does not imply ...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
static int scaleIconSize(int standardSize, bool applyDevicePixelRatio=false)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
bool useEstimatedMetadata() const
Returns true if estimated metadata should be used for the connection.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:167
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage)
Loads the QLR at path into QGIS. New layers are added to given project into layer tree specified by r...
static bool exportLayerDefinition(const QString &path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Exports the selected layer tree nodes to a QLR file.
Layer tree group node serves as a container for layers and further groups.
void setName(const QString &n) override
Sets the group's name.
QString name() const override
Returns the group's name.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
void removeChildren(int from, int count)
Remove child nodes from index "from".
Layer tree node points to a map layer.
QString layerId() const
Returns the ID for the map layer associated with this node.
void layerWillBeUnloaded()
Emitted when a previously available layer got unloaded (from layer registry).
void setName(const QString &n) override
Sets the layer's name.
QString name() const override
Returns the layer's name.
void layerLoaded()
Emitted when a previously unavailable layer got loaded.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ EmbeddedWidget
Embedded widget placeholder node type.
@ ColorRampLegend
Color ramp legend (since QGIS 3.18)
@ SymbolLegend
Vector symbol legend node type.
virtual void invalidateMapBasedData()
Notification from model that information from associated map view has changed.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
void sizeChanged()
Emitted when the size of this node changes.
void dataChanged()
Emitted on internal data change so the layer tree model can forward the signal to views.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
QTimer mDeferLegendInvalidationTimer
void connectToLayer(QgsLayerTreeLayer *nodeLayer)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
Flags flags() const
Returns OR-ed combination of model flags.
double mLegendFilterByScale
scale denominator for filtering of legend nodes (<= 0 means no filtering)
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
int autoCollapseLegendNodes() const
Returns at what number of legend nodes the layer node should be collapsed. -1 means no auto-collapse ...
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
void setLegendFilterByMap(const QgsMapSettings *settings)
Force only display of legend nodes which are valid for given map settings.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
void setLegendFilterByScale(double scale)
Force only display of legend nodes which are valid for a given scale.
~QgsLayerTreeModel() override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
static QIcon iconGroup()
std::unique_ptr< QgsMapSettings > mLegendFilterMapSettings
void setLegendMapViewData(double mapUnitsPerPixel, int dpi, double scale)
Give the layer tree model hints about the currently associated map view so that legend nodes that use...
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
QgsLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
void connectToLayers(QgsLayerTreeGroup *parentGroup)
std::unique_ptr< QgsMapHitTest > mLegendFilterHitTest
QModelIndex parent(const QModelIndex &child) const override
bool mLegendFilterUsesExtent
whether to use map filtering
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QModelIndex legendParent(QgsLayerTreeModelLegendNode *legendNode) const
void setLayerTreeNodeFont(int nodeType, const QFont &font)
Sets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
int legendNodeRowCount(QgsLayerTreeModelLegendNode *node) const
void nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
void setAutoCollapseLegendNodes(int nodeCount)
Sets at what number of legend nodes the layer node should be collapsed. Setting -1 disables the auto-...
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale) const
Gets hints about map view - to be used in legend nodes.
QModelIndex currentIndex() const
Gets index of the item marked as current. Item marked as current is underlined.
void recursivelyEmitDataChanged(const QModelIndex &index=QModelIndex())
emit dataChanged() for layer tree node items
bool legendEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
void disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
QIcon legendIconEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
void layerFlagsChanged()
Emitted when layer flags have changed.
void nodeNameChanged(QgsLayerTreeNode *node, const QString &name)
Updates model when node's name has changed.
QPersistentModelIndex mCurrentIndex
Current index - will be underlined.
LayerLegendTree * tryBuildLegendTree(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Qt::DropActions supportedDropActions() const override
QModelIndex legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary render context.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
void nodeWillRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QgsLayerTree * mRootNode
Pointer to the root node of the layer tree. Not owned by the model.
QStringList mimeTypes() const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QList< QgsLayerTreeModelLegendNode * > filterLegendNodes(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Filter nodes from QgsMapLayerLegend according to the current filtering rules.
QList< QgsLayerTreeModelLegendNode * > layerOriginalLegendNodes(QgsLayerTreeLayer *nodeLayer)
Returns original (unfiltered) list of legend nodes attached to a particular layer node.
int mAutoCollapseLegendNodesCount
Minimal number of nodes when legend should be automatically collapsed. -1 = disabled.
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
Qt::ItemFlags legendNodeFlags(QgsLayerTreeModelLegendNode *node) const
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
void nodeVisibilityChanged(QgsLayerTreeNode *node)
void refreshScaleBasedLayers(const QModelIndex &index=QModelIndex(), double previousScale=0.0)
Updates layer data for scale dependent layers, should be called when map scale changes.
void removeLegendFromLayer(QgsLayerTreeLayer *nodeLayer)
QModelIndex legendRootIndex(int row, int column, QgsLayerTreeLayer *nL) const
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
QMap< QString, QString > mLayerStyleOverrides
Overrides of map layers' styles: key = layer ID, value = style XML.
void nodeWillAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
QFont layerTreeNodeFont(int nodeType) const
Gets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
void addLegendToLayer(QgsLayerTreeLayer *nodeL)
bool testFlag(Flag f) const
Check whether a flag is enabled.
void disconnectFromLayers(QgsLayerTreeGroup *parentGroup)
QModelIndex indexOfParentLayerTreeNode(QgsLayerTreeNode *parentNode) const
QSet< QgsLayerTreeLayer * > mInvalidatedNodes
Keep track of layer nodes for which the legend size needs to be recalculated.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node)
@ AllowNodeChangeVisibility
Allow user to set node visibility with a checkbox.
@ ShowLegendAsTree
For legends that support it, will show them in a tree instead of a list (needs also ShowLegend)....
@ UseTextFormatting
Layer nodes will alter text appearance based on layer properties, such as scale based visibility.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ ShowLegend
Add legend nodes for layer nodes.
@ DeferredLegendInvalidation
Defer legend model invalidation.
@ AllowNodeRename
Allow renaming of groups and layers.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
Flags mFlags
Sets of flags for the model.
void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
int legendRootRowCount(QgsLayerTreeLayer *nL) const
QgsLayerTreeModelLegendNode * findLegendNode(const QString &layerId, const QString &ruleKey) const
Searches through the layer tree to find a legend node with a matching layer ID and rule key.
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void willRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be removed from a node within the tree.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
void willAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be added to a node within the tree.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
virtual void setItemVisibilityCheckedRecursive(bool checked)
Check or uncheck a node and all its children (taking into account exclusion rules)
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
static QString legendFilterByExpression(const QgsLayerTreeLayer &layer, bool *enabled=nullptr)
Returns the expression filter of a legend layer.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Class that runs a hit test with given map settings.
Definition: qgsmaphittest.h:38
QMap< QString, QString > LayerFilterExpression
Maps an expression string to a layer id.
Definition: qgsmaphittest.h:41
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode * > &nodes)
update according to layer node's custom properties (order of items, user labels for items)
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Returns list of legend nodes to be used for a particular layer tree layer node.
Restore overridden layer style on destruction.
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void legendChanged()
Signal emitted when legend of the layer has changed.
QgsMapLayerLegend * legend() const
Can be nullptr.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
void editingStarted()
Emitted when editing on this layer has started.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual Qgis::MapLayerProperties properties() const
Returns the map layer properties of this layer.
virtual bool isEditable() const
Returns true if the layer can be edited.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:311
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual bool isModified() const
Returns true if the layer has been modified since last commit/save.
void flagsChanged()
Emitted when layer's flags have been modified.
void layerModified()
Emitted when modifications has been done on layer.
The QgsMapSettings class contains configuration for rendering of the map.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:476
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool settingsLayerTreeShowFeatureCountForNewLayers
Settings entry show feature counts for newly added layers by default.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
void setIconSize(QSize sz)
Set the icon size.
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
@ VectorLayer
Vector layer.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2782
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Structure that stores all data associated with one map layer.
Structure that stores tree representation of map layer's legend.
QMap< QgsLayerTreeModelLegendNode *, QgsLayerTreeModelLegendNode * > parents
Pointer to parent for each active node. Top-level nodes have nullptr parent. Pointers are not owned.
QMap< QgsLayerTreeModelLegendNode *, QList< QgsLayerTreeModelLegendNode * > > children
List of children for each active node. Top-level nodes are under nullptr key. Pointers are not owned.