QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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"
29#include "qgsvectorlayer.h"
30#include "qgslayerdefinition.h"
31#include "qgsiconutils.h"
32#include "qgsmimedatautils.h"
34#include "qgsmaplayerstyle.h"
35#include "qgsrendercontext.h"
36
37#include <QPalette>
38
40 : QAbstractItemModel( parent )
41 , mRootNode( rootNode )
42 , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
43 , mAutoCollapseLegendNodesCount( -1 )
44 , mLegendFilterByScale( 0 )
45 , mLegendFilterUsesExtent( false )
46 , mLegendMapViewMupp( 0 )
47 , mLegendMapViewDpi( 0 )
48 , mLegendMapViewScale( 0 )
49{
51
52 mFontLayer.setBold( true );
53
55 mDeferLegendInvalidationTimer.setSingleShot( true );
56}
57
59{
61}
62
63QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
64{
65 if ( !index.isValid() )
66 return mRootNode;
67
68 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
69 return qobject_cast<QgsLayerTreeNode *>( obj );
70}
71
72
73int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
74{
76 return legendNodeRowCount( nodeLegend );
77
79 if ( !n )
80 return 0;
81
82 if ( QgsLayerTree::isLayer( n ) )
83 {
84 if ( !testFlag( ShowLegend ) )
85 return 0;
86
88 }
89
90 return n->children().count();
91}
92
93int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
94{
95 Q_UNUSED( parent )
96 return 1;
97}
98
99QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
100{
101 if ( column < 0 || column >= columnCount( parent ) ||
102 row < 0 || row >= rowCount( parent ) )
103 return QModelIndex();
104
106 return legendNodeIndex( row, column, nodeLegend );
107
109 if ( !n )
110 return QModelIndex(); // have no children
111
113 {
114 return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
115 }
116
117 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
118}
119
120
121QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
122{
123 if ( !child.isValid() )
124 return QModelIndex();
125
126 if ( QgsLayerTreeNode *n = index2node( child ) )
127 {
128 return indexOfParentLayerTreeNode( n->parent() ); // must not be null
129 }
131 {
132 return legendParent( legendNode );
133 }
134 else
135 {
136 Q_ASSERT( false ); // no other node types!
137 return QModelIndex();
138 }
139
140}
141
142
144{
145 Q_ASSERT( parentNode );
146
147 QgsLayerTreeNode *grandParentNode = parentNode->parent();
148 if ( !grandParentNode )
149 return QModelIndex(); // root node -> invalid index
150
151 int row = grandParentNode->children().indexOf( parentNode );
152 Q_ASSERT( row >= 0 );
153
154 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
155}
156
157
158QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
159{
160 if ( !index.isValid() || index.column() > 1 )
161 return QVariant();
162
164 return legendNodeData( sym, role );
165
167 if ( role == Qt::DisplayRole || role == Qt::EditRole )
168 {
169 if ( QgsLayerTree::isGroup( node ) )
170 return QgsLayerTree::toGroup( node )->name();
171
172 if ( QgsLayerTree::isLayer( node ) )
173 {
174 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
175 QString name = nodeLayer->name();
176 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
177 if ( vlayer && nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
178 {
179 const bool estimatedCount = vlayer->dataProvider() ? QgsDataSourceUri( vlayer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
180 const qlonglong count = vlayer->featureCount();
181
182 // if you modify this line, please update QgsSymbolLegendNode::updateLabel
183 name += QStringLiteral( " [%1%2]" ).arg(
184 estimatedCount ? QStringLiteral( "≈" ) : QString(),
185 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
186 }
187 return name;
188 }
189 }
190 else if ( role == Qt::DecorationRole && index.column() == 0 )
191 {
192 if ( QgsLayerTree::isGroup( node ) )
193 return iconGroup();
194
195 if ( QgsLayerTree::isLayer( node ) )
196 {
197 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
198
199 QgsMapLayer *layer = nodeLayer->layer();
200 if ( !layer )
201 return QVariant();
202
203 // icons possibly overriding default icon
204 QIcon icon = QgsIconUtils::iconForLayer( layer );
205
206 // if there's just on legend entry that should be embedded in layer - do that!
207 if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
208 {
209 icon = legendIconEmbeddedInParent( nodeLayer );
210 }
211
212 if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
213 {
214 const int iconSize = scaleIconSize( 16 );
215 QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
216
217 QPainter painter( &pixmap );
218 painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
219 painter.end();
220
221 icon = QIcon( pixmap );
222 }
223
224 return icon;
225 }
226 }
227 else if ( role == Qt::CheckStateRole )
228 {
230 return QVariant();
231
232 if ( QgsLayerTree::isLayer( node ) )
233 {
234 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
235
236 if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
237 return QVariant(); // do not show checkbox for non-spatial tables
238
239 return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
240 }
241 else if ( QgsLayerTree::isGroup( node ) )
242 {
243 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
244 return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
245 }
246 }
247 else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
248 {
249 QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
250 if ( index == mCurrentIndex )
251 f.setUnderline( true );
252 if ( QgsLayerTree::isLayer( node ) )
253 {
254 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
255 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
256 {
257 f.setItalic( !f.italic() );
258 }
259 }
260 return f;
261 }
262 else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
263 {
264 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
265 if ( QgsLayerTree::isLayer( node ) )
266 {
267 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
268 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
269 {
270 QColor fadedTextColor = brush.color();
271 fadedTextColor.setAlpha( 128 );
272 brush.setColor( fadedTextColor );
273 }
274 }
275 return brush;
276 }
277 else if ( role == Qt::ToolTipRole )
278 {
279 if ( QgsLayerTree::isLayer( node ) )
280 {
281 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
282 {
283 QString title =
284 !layer->title().isEmpty() ? layer->title() :
285 !layer->shortName().isEmpty() ? layer->shortName() :
286 layer->name();
287
288 title = "<b>" + title.toHtmlEscaped() + "</b>";
289
290 if ( layer->isSpatial() && layer->crs().isValid() )
291 {
292 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
293 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
294 else
295 title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
296 }
297
298 QStringList parts;
299 parts << title;
300
301 if ( !layer->abstract().isEmpty() )
302 {
303 parts << QString();
304 const QStringList abstractLines = layer->abstract().split( '\n' );
305 for ( const auto &l : abstractLines )
306 {
307 parts << l.toHtmlEscaped();
308 }
309 parts << QString();
310 }
311
312 QString source( layer->publicSource() );
313 if ( source.size() > 1024 )
314 {
315 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
316 }
317
318 parts << "<i>" + source.toHtmlEscaped() + "</i>";
319
320 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
321 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
322 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
323 if ( showFeatureCount && estimatedCount )
324 {
325 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
326 }
327
328 return parts.join( QLatin1String( "<br/>" ) );
329 }
330 }
331 }
332
333 return QVariant();
334}
335
336
337Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
338{
339 if ( !index.isValid() )
340 {
341 Qt::ItemFlags rootFlags = Qt::ItemFlags();
342 if ( testFlag( AllowNodeReorder ) )
343 rootFlags |= Qt::ItemIsDropEnabled;
344 return rootFlags;
345 }
346
348 return legendNodeFlags( symn );
349
350 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
351
352 if ( testFlag( AllowNodeRename ) )
353 f |= Qt::ItemIsEditable;
354
356 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
357
358 if ( testFlag( AllowNodeReorder ) )
359 {
360 // only root embedded nodes can be reordered
361 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
362 f |= Qt::ItemIsDragEnabled;
363 }
364
366 f |= Qt::ItemIsUserCheckable;
367
368 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
369 f |= Qt::ItemIsDropEnabled;
370
371 return f;
372}
373
374bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
375{
377 if ( sym )
378 {
379 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
380 return false;
381 bool res = sym->setData( value, role );
382 if ( res )
383 emit dataChanged( index, index );
384 return res;
385 }
386
388 if ( !node )
389 return QAbstractItemModel::setData( index, value, role );
390
391 if ( role == Qt::CheckStateRole )
392 {
394 return false;
395
396 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
397 if ( checked && node->children().isEmpty() )
398 {
400 }
401 else if ( testFlag( ActionHierarchical ) )
402 {
403 if ( node->children().isEmpty() )
405 else
406 node->setItemVisibilityCheckedRecursive( checked );
407 }
408 else
409 {
410 node->setItemVisibilityChecked( checked );
411 }
412
414
415 return true;
416 }
417 else if ( role == Qt::EditRole )
418 {
419 if ( !testFlag( AllowNodeRename ) )
420 return false;
421
422 if ( QgsLayerTree::isLayer( node ) )
423 {
425 layer->setName( value.toString() );
426 emit dataChanged( index, index );
427 }
428 else if ( QgsLayerTree::isGroup( node ) )
429 {
430 QgsLayerTree::toGroup( node )->setName( value.toString() );
431 emit dataChanged( index, index );
432 }
433 }
434
435 return QAbstractItemModel::setData( index, value, role );
436}
437
439{
440 if ( !node || !node->parent() )
441 return QModelIndex(); // this is the only root item -> invalid index
442
443 QModelIndex parentIndex = node2index( node->parent() );
444
445 int row = node->parent()->children().indexOf( node );
446 Q_ASSERT( row >= 0 );
447 return index( row, 0, parentIndex );
448}
449
450
451static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
452{
453 if ( !child->parent() )
454 return false;
455
456 if ( child->parent() == node )
457 return true;
458
459 return _isChildOfNode( child->parent(), node );
460}
461
462static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
463{
464 for ( QgsLayerTreeNode *n : nodes )
465 {
466 if ( _isChildOfNode( child, n ) )
467 return true;
468 }
469
470 return false;
471}
472
473
474QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
475{
476 QList<QgsLayerTreeNode *> nodes;
477 const auto constList = list;
478 for ( const QModelIndex &index : constList )
479 {
481 if ( !node )
482 continue;
483
484 nodes << node;
485 }
486
487 if ( !skipInternal )
488 return nodes;
489
490 // remove any children of nodes if both parent node and children are selected
491 QList<QgsLayerTreeNode *> nodesFinal;
492 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
493 {
494 if ( !_isChildOfNodes( node, nodes ) )
495 nodesFinal << node;
496 }
497
498 return nodesFinal;
499}
500
502{
503 return mRootNode;
504}
505
507{
508 beginResetModel();
509
511
512 Q_ASSERT( mLegend.isEmpty() );
513
514 mRootNode = newRootGroup;
515
516 endResetModel();
517
519}
520
522{
523 // update title
524 QModelIndex idx = node2index( nodeLayer );
525 emit dataChanged( idx, idx );
526
527 // update children
528 int oldNodeCount = rowCount( idx );
529 if ( oldNodeCount > 0 )
530 {
531 beginRemoveRows( idx, 0, oldNodeCount - 1 );
532 removeLegendFromLayer( nodeLayer );
533 endRemoveRows();
534 }
535
536 addLegendToLayer( nodeLayer );
537 int newNodeCount = rowCount( idx );
538
539 // automatic collapse of legend nodes - useful if a layer has many legend nodes
540 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
541 nodeLayer->setExpanded( false );
542}
543
545{
546 return mCurrentIndex;
547}
548
549void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
550{
552}
553
554
555void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
556{
557 if ( nodeType == QgsLayerTreeNode::NodeGroup )
558 {
559 if ( mFontGroup != font )
560 {
561 mFontGroup = font;
563 }
564 }
565 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
566 {
567 if ( mFontLayer != font )
568 {
569 mFontLayer = font;
571 }
572 }
573 else
574 {
575 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
576 }
577}
578
579
580QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
581{
582 if ( nodeType == QgsLayerTreeNode::NodeGroup )
583 return mFontGroup;
584 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
585 return mFontLayer;
586 else
587 {
588 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
589 return QFont();
590 }
591}
592
594{
595 mLegendFilterByScale = scale;
596
597 // this could be later done in more efficient way
598 // by just updating active legend nodes, without refreshing original legend nodes
599 const auto layers = mRootNode->findLayers();
600 for ( QgsLayerTreeLayer *nodeLayer : layers )
601 refreshLayerLegend( nodeLayer );
602}
603
605{
606 setLegendFilter( settings, /* useExtent = */ true );
607}
608
609void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
610{
611 if ( settings && settings->hasValidSettings() )
612 {
613 mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
614 mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
616 mLegendFilterUsesExtent = useExtent;
617 // collect expression filters
618 if ( useExpressions )
619 {
620 const auto layers = mRootNode->findLayers();
621 for ( QgsLayerTreeLayer *nodeLayer : layers )
622 {
623 bool enabled;
624 QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
625 if ( enabled && !expr.isEmpty() )
626 {
627 exprs[ nodeLayer->layerId()] = expr;
628 }
629 }
630 }
631 bool polygonValid = !polygon.isNull() && polygon.type() == Qgis::GeometryType::Polygon;
632 if ( useExpressions && !useExtent && !polygonValid ) // only expressions
633 {
635 }
636 else
637 {
638 mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, polygon, exprs ) );
639 }
641 }
642 else
643 {
645 return; // no change
646
648 mLegendFilterHitTest.reset();
649 }
650
651 // temporarily disable autocollapse so that legend nodes stay visible
652 int bkAutoCollapse = autoCollapseLegendNodes();
654
655 // this could be later done in more efficient way
656 // by just updating active legend nodes, without refreshing original legend nodes
657 const auto layers = mRootNode->findLayers();
658 for ( QgsLayerTreeLayer *nodeLayer : layers )
659 refreshLayerLegend( nodeLayer );
660
661 setAutoCollapseLegendNodes( bkAutoCollapse );
662}
663
664void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
665{
666 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
667 return;
668
669 double previousScale = mLegendMapViewScale;
670 mLegendMapViewScale = scale;
671 mLegendMapViewMupp = mapUnitsPerPixel;
672 mLegendMapViewDpi = dpi;
673
674 // now invalidate legend nodes!
676
677 if ( scale != previousScale )
678 refreshScaleBasedLayers( QModelIndex(), previousScale );
679}
680
681void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
682{
683 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
684 if ( dpi ) *dpi = mLegendMapViewDpi;
685 if ( scale ) *scale = mLegendMapViewScale;
686}
687
688QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
689{
691}
692
693void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
694{
695 mLayerStyleOverrides = overrides;
696}
697
698int QgsLayerTreeModel::scaleIconSize( int standardSize )
699{
700 return QgsApplication::scaleIconSize( standardSize, true );
701}
702
703void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
704{
705 beginInsertRows( node2index( node ), indexFrom, indexTo );
706}
707
708static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
709{
710 QList<QgsLayerTreeNode *> children = node->children();
711 QList<QgsLayerTreeLayer *> newLayerNodes;
712 for ( int i = indexFrom; i <= indexTo; ++i )
713 {
714 QgsLayerTreeNode *child = children.at( i );
715 if ( QgsLayerTree::isLayer( child ) )
716 newLayerNodes << QgsLayerTree::toLayer( child );
717 else if ( QgsLayerTree::isGroup( child ) )
718 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
719 }
720 return newLayerNodes;
721}
722
723void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
724{
725 Q_ASSERT( node );
726
727 endInsertRows();
728
729 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
730 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
731 connectToLayer( newLayerNode );
732}
733
734void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
735{
736 Q_ASSERT( node );
737
738 beginRemoveRows( node2index( node ), indexFrom, indexTo );
739
740 // disconnect from layers and remove their legend
741 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
742 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
743 disconnectFromLayer( nodeLayer );
744}
745
747{
748 endRemoveRows();
749}
750
752{
753 Q_ASSERT( node );
754
755 const QModelIndex index = node2index( node );
756 emit dataChanged( index, index );
757}
758
759void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
760{
761 Q_UNUSED( name )
762 Q_ASSERT( node );
763
764 const QModelIndex index = node2index( node );
765 emit dataChanged( index, index );
766}
767
768
770{
771 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
773}
774
775
777{
778 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
779 if ( !nodeLayer )
780 return;
781
782 // deferred connection to the layer
783 connectToLayer( nodeLayer );
784}
785
787{
788 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
789 if ( !nodeLayer )
790 return;
791
792 disconnectFromLayer( nodeLayer );
793
794 // wait for the layer to appear again
796}
797
799{
800 if ( !mRootNode )
801 return;
802
803 if ( !testFlag( ShowLegend ) )
804 return;
805
806 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
807 if ( !layer )
808 return;
809
810 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
811 if ( !nodeLayer )
812 return;
813
814 refreshLayerLegend( nodeLayer );
815}
816
818{
819 if ( !mRootNode )
820 return;
821
822 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
823 if ( !layer )
824 return;
825
826 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
827 if ( !nodeLayer )
828 return;
829
830 const QModelIndex index = node2index( nodeLayer );
831 emit dataChanged( index, index );
832}
833
835{
836 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
837 if ( !layer )
838 return;
839
840 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
841 if ( !nodeLayer )
842 return;
843
844 QModelIndex index = node2index( nodeLayer );
845 emit dataChanged( index, index );
846
847 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
848 refreshLayerLegend( nodeLayer );
849}
850
851
853{
854 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
855 if ( !legendNode )
856 return;
857
858 QModelIndex index = legendNode2index( legendNode );
859 if ( index.isValid() )
860 emit dataChanged( index, index );
861}
862
863void QgsLayerTreeModel::legendNodeSizeChanged()
864{
865 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
866 if ( !legendNode )
867 return;
868
869 QModelIndex index = legendNode2index( legendNode );
870 if ( index.isValid() )
871 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
872}
873
874
876{
877 if ( !nodeLayer->layer() )
878 {
879 // in order to connect to layer, we need to have it loaded.
880 // keep an eye on the layer ID: once loaded, we will use it
881 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
882 return;
883 }
884
885 // watch if the layer is getting removed
886 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
887
888 if ( testFlag( ShowLegend ) )
889 {
890 addLegendToLayer( nodeLayer );
891
892 // if we aren't loading a layer from a project, setup some nice default settings
893 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
894 {
895 // automatic collapse of legend nodes - useful if a layer has many legend nodes
897 nodeLayer->setExpanded( false );
898
899 if ( nodeLayer->layer()->type() == Qgis::LayerType::Vector && QgsSettingsRegistryCore::settingsLayerTreeShowFeatureCountForNewLayers->value() )
900 {
901 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
902 }
903 }
904 }
905
906 QgsMapLayer *layer = nodeLayer->layer();
907 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
908 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
909
910 // using unique connection because there may be temporarily more nodes for a layer than just one
911 // which would create multiple connections, however disconnect() would disconnect all multiple connections
912 // even if we wanted to disconnect just one connection in each call.
913 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
914 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
915 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
916
917 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
918}
919
920// try to find out if the layer ID is present in the tree multiple times
921static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
922{
923 int count = 0;
924 const auto constChildren = group->children();
925 for ( QgsLayerTreeNode *child : constChildren )
926 {
927 if ( QgsLayerTree::isLayer( child ) )
928 {
929 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
930 count++;
931 }
932 else if ( QgsLayerTree::isGroup( child ) )
933 {
934 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
935 }
936 }
937 return count;
938}
939
941{
942 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
943
944 if ( !nodeLayer->layer() )
945 return; // we were never connected
946
947 if ( testFlag( ShowLegend ) )
948 {
949 removeLegendFromLayer( nodeLayer );
950 }
951
952 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
953 {
954 // last instance of the layer in the tree: disconnect from all signals from layer!
955 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
956 }
957}
958
960{
961 const auto constChildren = parentGroup->children();
962 for ( QgsLayerTreeNode *node : constChildren )
963 {
964 if ( QgsLayerTree::isGroup( node ) )
966 else if ( QgsLayerTree::isLayer( node ) )
968 }
969}
970
972{
973 const auto constChildren = parentGroup->children();
974 for ( QgsLayerTreeNode *node : constChildren )
975 {
976 if ( QgsLayerTree::isGroup( node ) )
978 else if ( QgsLayerTree::isLayer( node ) )
980 }
981}
982
984{
985 Q_ASSERT( mRootNode );
986
987 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
988 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
989 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
990 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
991 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
992 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
993 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
994
996}
997
999{
1000 disconnect( mRootNode, nullptr, this, nullptr );
1001
1003}
1004
1006{
1007 QgsLayerTreeNode *node = index2node( idx );
1008 if ( !node )
1009 return;
1010
1011 int count = node->children().count();
1012 if ( count == 0 )
1013 return;
1014 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1015 for ( int i = 0; i < count; ++i )
1016 recursivelyEmitDataChanged( index( i, 0, idx ) );
1017}
1018
1019void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1020{
1021 QgsLayerTreeNode *node = index2node( idx );
1022 if ( !node )
1023 return;
1024
1025 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1026 {
1027 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1028 if ( layer && layer->hasScaleBasedVisibility() )
1029 {
1030 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1031 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1032 }
1033 }
1034 int count = node->children().count();
1035 for ( int i = 0; i < count; ++i )
1036 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1037}
1038
1040{
1041 return Qt::CopyAction | Qt::MoveAction;
1042}
1043
1045{
1046 QStringList types;
1047 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1048 return types;
1049}
1050
1051
1052QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1053{
1054 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1055 QModelIndexList sortedIndexes = indexes;
1056 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1057
1058 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1059
1060 if ( nodesFinal.isEmpty() )
1061 return nullptr;
1062
1063 QMimeData *mimeData = new QMimeData();
1064
1065 QDomDocument layerTreeDoc;
1066 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1067
1068 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1069 {
1070 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1071 }
1072 layerTreeDoc.appendChild( rootLayerTreeElem );
1073
1074 QString errorMessage;
1075 QgsReadWriteContext readWriteContext;
1076 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1077 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1078
1079 QString txt = layerDefinitionsDoc.toString();
1080
1081 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1082 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1083 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1084 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1085 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1086
1087 return mimeData;
1088}
1089
1090bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1091{
1092 if ( action == Qt::IgnoreAction )
1093 return true;
1094
1095 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1096 return false;
1097
1098 if ( column >= columnCount( parent ) )
1099 return false;
1100
1101 // don't accept drops from some layer tree subclasses to non-matching subclasses
1102 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1103 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1104 return false;
1105
1106 QgsLayerTreeNode *nodeParent = index2node( parent );
1107 if ( !QgsLayerTree::isGroup( nodeParent ) )
1108 return false;
1109
1110 if ( parent.isValid() && row == -1 )
1111 row = 0; // if dropped directly onto group item, insert at first position
1112
1113 // if we are coming from another QGIS instance, we need to add the layers too
1114 bool ok = false;
1115 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1116 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1117
1118 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1119 {
1120 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1121 QDomDocument layerDefinitionDoc;
1122 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1123 return false;
1124 QgsReadWriteContext context;
1125 QString errorMessage;
1126 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1127 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1128 }
1129 else
1130 {
1131 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1132
1133 QDomDocument layerTreeDoc;
1134 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1135 return false;
1136
1137 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1138 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1139 return false;
1140
1141 QList<QgsLayerTreeNode *> nodes;
1142
1143 QDomElement elem = rootLayerTreeElem.firstChildElement();
1144 while ( !elem.isNull() )
1145 {
1147 if ( node )
1148 nodes << node;
1149
1150 elem = elem.nextSiblingElement();
1151 }
1152
1153 if ( nodes.isEmpty() )
1154 return false;
1155
1156 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1157 }
1158 return true;
1159}
1160
1161bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1162{
1163 QgsLayerTreeNode *parentNode = index2node( parent );
1164 if ( QgsLayerTree::isGroup( parentNode ) )
1165 {
1166 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1167 return true;
1168 }
1169 return false;
1170}
1171
1172void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1173{
1174 mFlags = f;
1175}
1176
1178{
1179 if ( on )
1180 mFlags |= f;
1181 else
1182 mFlags &= ~f;
1183}
1184
1185QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1186{
1187 return mFlags;
1188}
1189
1191{
1192 return mFlags.testFlag( f );
1193}
1194
1196{
1197 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1198}
1199
1200QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1201{
1202 QList<QgsLayerTreeModelLegendNode *> filtered;
1203
1204 if ( mLegendFilterByScale > 0 )
1205 {
1206 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1207 {
1208 if ( node->isScaleOK( mLegendFilterByScale ) )
1209 filtered << node;
1210 }
1211 }
1212 else if ( mLegendFilterMapSettings )
1213 {
1214 if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1215 {
1216 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1217 {
1219 {
1221 filtered << node;
1222 break;
1223
1231 {
1232 const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1233 bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1234 if ( checked )
1235 {
1236 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1237 {
1238 if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1239 filtered << node;
1240 }
1241 else
1242 {
1243 filtered << node;
1244 }
1245 }
1246 else // unknown node type or unchecked
1247 filtered << node;
1248 break;
1249 }
1250 }
1251 }
1252 }
1253 }
1254 else
1255 {
1256 return nodes;
1257 }
1258
1259 return filtered;
1260}
1261
1262
1263
1265// Legend nodes routines - start
1266
1268{
1269 const auto constMLegend = mLegend;
1270 for ( const LayerLegendData &data : constMLegend )
1271 {
1272 qDeleteAll( data.originalNodes );
1273 delete data.tree;
1274 }
1275 mLegend.clear();
1276}
1277
1278
1280{
1281 if ( mLegend.contains( nodeLayer ) )
1282 {
1283 qDeleteAll( mLegend[nodeLayer].originalNodes );
1284 delete mLegend[nodeLayer].tree;
1285 mLegend.remove( nodeLayer );
1286 }
1287}
1288
1289
1291{
1292 if ( !nodeL || !nodeL->layer() )
1293 return;
1294
1295 QgsMapLayer *ml = nodeL->layer();
1296
1297 QgsMapLayerStyleOverride styleOverride( ml );
1298 if ( mLayerStyleOverrides.contains( ml->id() ) )
1299 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1300
1301 QgsMapLayerLegend *layerLegend = ml->legend();
1302 if ( !layerLegend )
1303 return;
1304 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1305
1306 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1308
1310 {
1311 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1312 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1313 while ( widgetsCount > 0 )
1314 {
1315 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1316 --widgetsCount;
1317 }
1318 }
1319
1320 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1321
1322 const auto constLstNew = lstNew;
1323 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1324 {
1325 n->setParent( this );
1327 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1328 }
1329
1330 // See if we have an embedded node - if we do, we will not use it among active nodes.
1331 // Legend node embedded in parent does not have to be the first one,
1332 // there can be also nodes generated for embedded widgets
1333 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1334 const auto constFilteredLstNew = filteredLstNew;
1335 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1336 {
1338 {
1339 embeddedNode = legendNode;
1340 filteredLstNew.removeOne( legendNode );
1341 break;
1342 }
1343 }
1344
1345 LayerLegendTree *legendTree = nullptr;
1346
1347 // maybe the legend nodes form a tree - try to create a tree structure from the list
1348 if ( testFlag( ShowLegendAsTree ) )
1349 legendTree = tryBuildLegendTree( filteredLstNew );
1350
1351 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1352
1353 if ( !filteredLstNew.isEmpty() )
1354 {
1355 // Make sure it's clear
1356 const QModelIndex nodeIndex { node2index( nodeL ) };
1357 if ( rowCount( nodeIndex ) > 0 )
1358 {
1359 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1360 mLegend[nodeL] = LayerLegendData();
1361 endRemoveRows();
1362 }
1363 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1364 }
1365
1367 data.originalNodes = lstNew;
1368 data.activeNodes = filteredLstNew;
1369 data.embeddedNodeInParent = embeddedNode;
1370 data.tree = legendTree;
1371
1372 mLegend[nodeL] = data;
1373
1374 if ( !filteredLstNew.isEmpty() )
1375 {
1376 endInsertRows();
1377 }
1378
1379 // invalidate map based data even if the data is not map-based to make sure
1380 // the symbol sizes are computed at least once
1381 mInvalidatedNodes.insert( nodeL );
1383}
1384
1385
1386QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1387{
1388 // first check whether there are any legend nodes that are not top-level
1389 bool hasParentKeys = false;
1390 for ( QgsLayerTreeModelLegendNode *n : nodes )
1391 {
1392 if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1393 {
1394 hasParentKeys = true;
1395 break;
1396 }
1397 }
1398 if ( !hasParentKeys )
1399 return nullptr; // all legend nodes are top-level => stick with list representation
1400
1401 // make mapping from rules to nodes and do some sanity checks
1402 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1403 rule2node[QString()] = nullptr;
1404 for ( QgsLayerTreeModelLegendNode *n : nodes )
1405 {
1406 QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1407 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1408 return nullptr;
1409 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1410 return nullptr;
1411 rule2node[ruleKey] = n;
1412 }
1413
1414 // create the tree structure
1415 LayerLegendTree *tree = new LayerLegendTree;
1416 for ( QgsLayerTreeModelLegendNode *n : nodes )
1417 {
1418 QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1419 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1420 tree->parents[n] = parent;
1421 tree->children[parent] << n;
1422 }
1423 return tree;
1424}
1425
1427{
1428 double scale = 0.0;
1429 double mupp = 0.0;
1430 int dpi = 0;
1431 legendMapViewData( &mupp, &dpi, &scale );
1432 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1433
1434 // setup temporary render context
1435 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1436 context->setScaleFactor( dpi / 25.4 );
1437 context->setRendererScale( scale );
1438 context->setMapToPixel( QgsMapToPixel( mupp ) );
1440 return validData ? context.release() : nullptr;
1441}
1442
1443
1445{
1446 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1447}
1448
1449
1451{
1453 if ( data.tree )
1454 {
1455 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1456 {
1457 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1458 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1459 return index( row, 0, parentIndex );
1460 }
1461 else
1462 {
1463 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1464 int row = data.tree->children[nullptr].indexOf( legendNode );
1465 return index( row, 0, parentIndex );
1466 }
1467 }
1468
1469 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1470 Q_ASSERT( parentIndex.isValid() );
1471 int row = data.activeNodes.indexOf( legendNode );
1472 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1473 return QModelIndex();
1474
1475 return index( row, 0, parentIndex );
1476}
1477
1478
1480{
1481 const LayerLegendData &data = mLegend[node->layerNode()];
1482 if ( data.tree )
1483 return data.tree->children[node].count();
1484
1485 return 0; // they are leaves
1486}
1487
1488
1490{
1491 if ( !mLegend.contains( nL ) )
1492 return 0;
1493
1494 const LayerLegendData &data = mLegend[nL];
1495 if ( data.tree )
1496 return data.tree->children[nullptr].count();
1497
1498 int count = data.activeNodes.count();
1499 return count;
1500}
1501
1502
1503QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1504{
1505 Q_ASSERT( mLegend.contains( nL ) );
1506 const LayerLegendData &data = mLegend[nL];
1507 if ( data.tree )
1508 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1509
1510 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1511}
1512
1513
1514QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1515{
1516 const LayerLegendData &data = mLegend[node->layerNode()];
1517 if ( data.tree )
1518 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1519
1520 return QModelIndex(); // have no children
1521}
1522
1523
1525{
1526 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1527 const LayerLegendData &data = mLegend[layerNode];
1528 if ( data.tree )
1529 {
1530 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1531 {
1532 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1533 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1534 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1535 }
1536 else
1537 return indexOfParentLayerTreeNode( layerNode );
1538 }
1539
1540 return indexOfParentLayerTreeNode( layerNode );
1541}
1542
1543
1545{
1546 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1547 return QVariant();
1548 return node->data( role );
1549}
1550
1551
1553{
1554 Qt::ItemFlags f = node->flags();
1556 f &= ~Qt::ItemIsUserCheckable;
1557 return f;
1558}
1559
1560
1562{
1563 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1564}
1565
1567{
1568 return mLegend[nodeLayer].embeddedNodeInParent;
1569}
1570
1571
1573{
1574 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1575 if ( !legendNode )
1576 return QIcon();
1577 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1578}
1579
1580
1581QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1582{
1583 if ( !mLegend.contains( nodeLayer ) )
1584 return QList<QgsLayerTreeModelLegendNode *>();
1585
1586 const LayerLegendData &data = mLegend[nodeLayer];
1587 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1588 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1589 lst.prepend( data.embeddedNodeInParent );
1590 return lst;
1591}
1592
1593QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1594{
1595 return mLegend.value( nodeLayer ).originalNodes;
1596}
1597
1598QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1599{
1600 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1601 {
1602 QgsLayerTreeLayer *layer = it.key();
1603 if ( layer->layerId() == layerId )
1604 {
1605 const auto activeNodes = mLegend.value( layer ).activeNodes;
1606 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1607 {
1608 if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1609 {
1610 //found it!
1611 return legendNode;
1612 }
1613 }
1614 }
1615 }
1616
1617 return nullptr;
1618}
1619
1621{
1624 else
1626}
1627
1629{
1630 // we have varying icon sizes, and we want icon to be centered and
1631 // text to be left aligned, so we have to compute the max width of icons
1632 //
1633 // we do that for nodes which share a common parent
1634 //
1635 // we do that here because for symbols with size defined in map units
1636 // the symbol sizes changes depends on the zoom level
1637
1638 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1639
1640 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1641 {
1642 const LayerLegendData &data = mLegend.value( layerNode );
1643
1644 QList<QgsSymbolLegendNode *> symbolNodes;
1645 QMap<QString, int> widthMax;
1646 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1647 {
1648 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1649 if ( n )
1650 {
1651 const QSize sz( n->minimumIconSize( context.get() ) );
1652 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1653 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1654 n->setIconSize( sz );
1655 symbolNodes.append( n );
1656 }
1657 }
1658 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1659 {
1660 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1661 Q_ASSERT( widthMax[parentKey] > 0 );
1662 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1663 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1664 }
1665 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1667 }
1668
1669 mInvalidatedNodes.clear();
1670}
1671
1672// 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
Qgis::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.
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.
Qgis::LayerType type
Definition: qgsmaplayer.h:80
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:310
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:477
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(Qgis::WkbType type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
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:3509
#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.