QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
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"
24#include "qgsproject.h"
25#include "qgsmaphittest.h"
26#include "qgsmaplayer.h"
27#include "qgsmaplayerlegend.h"
28#include "qgsvectorlayer.h"
29#include "qgslayerdefinition.h"
30#include "qgsiconutils.h"
31#include "qgsmimedatautils.h"
33#include "qgsmaplayerstyle.h"
34#include "qgsrendercontext.h"
36
37#include <QPalette>
38
40 : QAbstractItemModel( parent )
41 , mRootNode( rootNode )
42 , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
43{
44 if ( rootNode )
45 {
47 }
48
49 mFontLayer.setBold( true );
50
52 mDeferLegendInvalidationTimer.setSingleShot( true );
53}
54
59
60QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
61{
62 if ( !index.isValid() )
63 return mRootNode;
64
65 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
66 return qobject_cast<QgsLayerTreeNode *>( obj );
67}
68
69
70int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
71{
73 return legendNodeRowCount( nodeLegend );
74
76 if ( !n )
77 return 0;
78
79 if ( QgsLayerTree::isLayer( n ) )
80 {
81 if ( !testFlag( ShowLegend ) )
82 return 0;
83
85 }
86
87 return n->children().count();
88}
89
90int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
91{
92 Q_UNUSED( parent )
93 return 1;
94}
95
96QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
97{
98 if ( column < 0 || column >= columnCount( parent ) ||
99 row < 0 || row >= rowCount( parent ) )
100 return QModelIndex();
101
103 return legendNodeIndex( row, column, nodeLegend );
104
106 if ( !n )
107 return QModelIndex(); // have no children
108
110 {
111 return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
112 }
113
114 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
115}
116
117
118QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
119{
120 if ( !child.isValid() )
121 return QModelIndex();
122
123 if ( QgsLayerTreeNode *n = index2node( child ) )
124 {
125 return indexOfParentLayerTreeNode( n->parent() ); // must not be null
126 }
127 else if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( child ) )
128 {
129 return legendParent( legendNode );
130 }
131 else
132 {
133 Q_ASSERT( false ); // no other node types!
134 return QModelIndex();
135 }
136
137}
138
139
141{
142 Q_ASSERT( parentNode );
143
144 QgsLayerTreeNode *grandParentNode = parentNode->parent();
145 if ( !grandParentNode )
146 return QModelIndex(); // root node -> invalid index
147
148 int row = grandParentNode->children().indexOf( parentNode );
149 Q_ASSERT( row >= 0 );
150
151 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
152}
153
154
155QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
156{
157 if ( !index.isValid() || index.column() > 1 )
158 return QVariant();
159
161 return legendNodeData( sym, role );
162
164 if ( role == Qt::DisplayRole || role == Qt::EditRole )
165 {
166 if ( QgsLayerTree::isGroup( node ) )
167 return QgsLayerTree::toGroup( node )->name();
168
169 if ( QgsLayerTree::isLayer( node ) )
170 {
171 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
172 QString name = nodeLayer->name();
173 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
174 if ( vlayer && nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
175 {
176 const bool estimatedCount = vlayer->dataProvider() ? QgsDataSourceUri( vlayer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
177 const qlonglong count = vlayer->featureCount();
178
179 // if you modify this line, please update QgsSymbolLegendNode::updateLabel
180 name += QStringLiteral( " [%1%2]" ).arg(
181 estimatedCount ? QStringLiteral( "≈" ) : QString(),
182 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
183 }
184 return name;
185 }
186 }
187 else if ( role == Qt::DecorationRole && index.column() == 0 )
188 {
189 if ( QgsLayerTree::isGroup( node ) )
190 return iconGroup();
191
192 if ( QgsLayerTree::isLayer( node ) )
193 {
194 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
195
196 QgsMapLayer *layer = nodeLayer->layer();
197 if ( !layer )
198 return QVariant();
199
200 // icons possibly overriding default icon
201 QIcon icon = QgsIconUtils::iconForLayer( layer );
202
203 // if there's just on legend entry that should be embedded in layer - do that!
204 if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
205 {
206 icon = legendIconEmbeddedInParent( nodeLayer );
207 }
208
209 if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
210 {
211 const int iconSize = scaleIconSize( 16 );
212 QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
213
214 QPainter painter( &pixmap );
215 painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
216 painter.end();
217
218 icon = QIcon( pixmap );
219 }
220
221 return icon;
222 }
223 }
224 else if ( role == Qt::CheckStateRole )
225 {
227 return QVariant();
228
229 if ( QgsLayerTree::isLayer( node ) )
230 {
231 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
232
233 if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
234 return QVariant(); // do not show checkbox for non-spatial tables
235
236 return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
237 }
238 else if ( QgsLayerTree::isGroup( node ) )
239 {
240 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
241 return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
242 }
243 }
244 else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
245 {
246 QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
247 if ( index == mCurrentIndex )
248 f.setUnderline( true );
249 if ( QgsLayerTree::isLayer( node ) )
250 {
251 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
252 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
253 {
254 f.setItalic( !f.italic() );
255 }
256 }
257 return f;
258 }
259 else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
260 {
261 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
262 if ( QgsLayerTree::isLayer( node ) )
263 {
264 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
265 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
266 {
267 QColor fadedTextColor = brush.color();
268 fadedTextColor.setAlpha( 128 );
269 brush.setColor( fadedTextColor );
270 }
271 }
272 return brush;
273 }
274 else if ( role == Qt::ToolTipRole )
275 {
276 if ( QgsLayerTree::isLayer( node ) )
277 {
278 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
279 {
280 QString title = !layer->metadata().title().isEmpty() ? layer->metadata().title() :
281 !layer->serverProperties()->title().isEmpty() ? layer->serverProperties()->title() :
282 !layer->serverProperties()->shortName().isEmpty() ? layer->serverProperties()->shortName() :
283 layer->name();
284
285 title = "<b>" + title.toHtmlEscaped() + "</b>";
286
287 if ( layer->isSpatial() && layer->crs().isValid() )
288 {
289 QString layerCrs = layer->crs().authid();
290 if ( !std::isnan( layer->crs().coordinateEpoch() ) )
291 {
292 layerCrs += QStringLiteral( " @ %1" ).arg( qgsDoubleToString( layer->crs().coordinateEpoch(), 3 ) );
293 }
294 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
295 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layerCrs ).toHtmlEscaped();
296 else
297 title += tr( " (%1)" ).arg( layerCrs ).toHtmlEscaped();
298 }
299
300 QStringList parts;
301 parts << title;
302
303 const QString abstract = !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
304 if ( !abstract.isEmpty() )
305 {
306 parts << QString();
307 const QStringList abstractLines = abstract.split( '\n' );
308 for ( const auto &l : abstractLines )
309 {
310 parts << l.toHtmlEscaped();
311 }
312 parts << QString();
313 }
314
315 QString source( layer->publicSource() );
316 if ( source.size() > 1024 )
317 {
318 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
319 }
320
321 parts << "<i>" + source.toHtmlEscaped() + "</i>";
322
323 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
324 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
325 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
326 if ( showFeatureCount && estimatedCount )
327 {
328 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
329 }
330
331 return parts.join( QLatin1String( "<br/>" ) );
332 }
333 }
334 }
335
336 return QVariant();
337}
338
339
340Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
341{
342 if ( !index.isValid() )
343 {
344 Qt::ItemFlags rootFlags = Qt::ItemFlags();
345 if ( testFlag( AllowNodeReorder ) )
346 rootFlags |= Qt::ItemIsDropEnabled;
347 return rootFlags;
348 }
349
351 return legendNodeFlags( symn );
352
353 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
354
355 if ( testFlag( AllowNodeRename ) )
356 f |= Qt::ItemIsEditable;
357
359 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
360
361 if ( testFlag( AllowNodeReorder ) )
362 {
363 // only root embedded nodes can be reordered
364 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
365 f |= Qt::ItemIsDragEnabled;
366 }
367
369 f |= Qt::ItemIsUserCheckable;
370
371 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
372 f |= Qt::ItemIsDropEnabled;
373
374 return f;
375}
376
377bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
378{
380 if ( sym )
381 {
382 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
383 return false;
384 bool res = sym->setData( value, role );
385 if ( res )
386 emit dataChanged( index, index );
387 return res;
388 }
389
391 if ( !node )
392 return QAbstractItemModel::setData( index, value, role );
393
394 if ( role == Qt::CheckStateRole )
395 {
397 return false;
398
399 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
400 if ( checked && node->children().isEmpty() )
401 {
403 }
404 else if ( testFlag( ActionHierarchical ) )
405 {
406 if ( node->children().isEmpty() )
408 else
409 node->setItemVisibilityCheckedRecursive( checked );
410 }
411 else
412 {
413 node->setItemVisibilityChecked( checked );
414 }
415
417
418 return true;
419 }
420 else if ( role == Qt::EditRole )
421 {
422 if ( !testFlag( AllowNodeRename ) )
423 return false;
424
425 if ( QgsLayerTree::isLayer( node ) )
426 {
428 layer->setName( value.toString() );
429 emit dataChanged( index, index );
430 }
431 else if ( QgsLayerTree::isGroup( node ) )
432 {
433 QgsLayerTree::toGroup( node )->setName( value.toString() );
434 emit dataChanged( index, index );
435 }
436 }
437
438 return QAbstractItemModel::setData( index, value, role );
439}
440
442{
443 if ( !node || !node->parent() )
444 return QModelIndex(); // this is the only root item -> invalid index
445
446 QModelIndex parentIndex = node2index( node->parent() );
447
448 int row = node->parent()->children().indexOf( node );
449 Q_ASSERT( row >= 0 );
450 return index( row, 0, parentIndex );
451}
452
453
454static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
455{
456 if ( !child->parent() )
457 return false;
458
459 if ( child->parent() == node )
460 return true;
461
462 return _isChildOfNode( child->parent(), node );
463}
464
465static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
466{
467 for ( QgsLayerTreeNode *n : nodes )
468 {
469 if ( _isChildOfNode( child, n ) )
470 return true;
471 }
472
473 return false;
474}
475
476
477QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
478{
479 QList<QgsLayerTreeNode *> nodes;
480 const auto constList = list;
481 for ( const QModelIndex &index : constList )
482 {
484 if ( !node )
485 continue;
486
487 nodes << node;
488 }
489
490 if ( !skipInternal )
491 return nodes;
492
493 // remove any children of nodes if both parent node and children are selected
494 QList<QgsLayerTreeNode *> nodesFinal;
495 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
496 {
497 if ( !_isChildOfNodes( node, nodes ) )
498 nodesFinal << node;
499 }
500
501 return nodesFinal;
502}
503
508
510{
511 beginResetModel();
512
514
515 Q_ASSERT( mLegend.isEmpty() );
516
517 mRootNode = newRootGroup;
518
519 endResetModel();
520
522}
523
525{
526 // update title
527 QModelIndex idx = node2index( nodeLayer );
528 emit dataChanged( idx, idx );
529
530 // update children
531 int oldNodeCount = rowCount( idx );
532 if ( oldNodeCount > 0 )
533 {
534 beginRemoveRows( idx, 0, oldNodeCount - 1 );
535 removeLegendFromLayer( nodeLayer );
536 endRemoveRows();
537 }
538
539 addLegendToLayer( nodeLayer );
540 int newNodeCount = rowCount( idx );
541
542 // automatic collapse of legend nodes - useful if a layer has many legend nodes
543 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
544 nodeLayer->setExpanded( false );
545}
546
548{
549 return mCurrentIndex;
550}
551
552void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
553{
555}
556
557
558void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
559{
560 if ( nodeType == QgsLayerTreeNode::NodeGroup )
561 {
562 if ( mFontGroup != font )
563 {
564 mFontGroup = font;
566 }
567 }
568 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
569 {
570 if ( mFontLayer != font )
571 {
572 mFontLayer = font;
574 }
575 }
576 else
577 {
578 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
579 }
580}
581
582
583QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
584{
585 if ( nodeType == QgsLayerTreeNode::NodeGroup )
586 return mFontGroup;
587 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
588 return mFontLayer;
589 else
590 {
591 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
592 return QFont();
593 }
594}
595
597{
598 mLegendFilterByScale = scale;
599
600 // this could be later done in more efficient way
601 // by just updating active legend nodes, without refreshing original legend nodes
602 const auto layers = mRootNode->findLayers();
603 for ( QgsLayerTreeLayer *nodeLayer : layers )
604 refreshLayerLegend( nodeLayer );
605}
606
608{
610 setLegendFilter( settings, /* useExtent = */ true );
612}
613
614void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
615{
616 if ( settings && settings->hasValidSettings() )
617 {
618 std::unique_ptr< QgsLayerTreeFilterSettings > filterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
619
620 if ( !useExtent ) // only expressions
621 {
623 }
624 else if ( polygon.type() == Qgis::GeometryType::Polygon )
625 {
627 }
628
629 if ( useExpressions )
630 {
632 }
633
635 }
636 else
637 {
638 setFilterSettings( nullptr );
639 }
640}
641
643{
644 return mFilterSettings ? &mFilterSettings->mapSettings() : nullptr;
645}
646
648{
649 if ( settings )
650 {
651 mFilterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
652 mFilterSettings->mapSettings().setLayerStyleOverrides( mLayerStyleOverrides );
653
654 bool hitTestWasRunning = false;
655 if ( mHitTestTask )
656 {
657 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
658 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
659 mHitTestTask->cancel();
660 mHitTestTask = nullptr;
661 hitTestWasRunning = true;
662 }
663
664 std::unique_ptr< QgsMapHitTest > blockingHitTest;
667 else
668 blockingHitTest = std::make_unique< QgsMapHitTest >( *mFilterSettings );
669
670 if ( mHitTestTask )
671 {
672 connect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
674
675 if ( !hitTestWasRunning )
676 emit hitTestStarted();
677 }
678 else
679 {
680 blockingHitTest->run();
681 mHitTestResults = blockingHitTest->results();
682 handleHitTestResults();
683 }
684 }
685 else
686 {
687 if ( !mFilterSettings )
688 return;
689
690 mFilterSettings.reset();
691 handleHitTestResults();
692 }
693}
694
699
700void QgsLayerTreeModel::handleHitTestResults()
701{
702 // temporarily disable autocollapse so that legend nodes stay visible
703 int bkAutoCollapse = autoCollapseLegendNodes();
705
706 // this could be later done in more efficient way
707 // by just updating active legend nodes, without refreshing original legend nodes
708 const auto layers = mRootNode->findLayers();
709 for ( QgsLayerTreeLayer *nodeLayer : layers )
710 refreshLayerLegend( nodeLayer );
711
712 setAutoCollapseLegendNodes( bkAutoCollapse );
713}
714
715void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
716{
717 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
718 return;
719
720 double previousScale = mLegendMapViewScale;
721 mLegendMapViewScale = scale;
722 mLegendMapViewMupp = mapUnitsPerPixel;
723 mLegendMapViewDpi = dpi;
724
725 // now invalidate legend nodes!
727
728 if ( scale != previousScale )
729 refreshScaleBasedLayers( QModelIndex(), previousScale );
730}
731
732void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
733{
734 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
735 if ( dpi ) *dpi = mLegendMapViewDpi;
736 if ( scale ) *scale = mLegendMapViewScale;
737}
738
739QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
740{
742}
743
744void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
745{
746 mLayerStyleOverrides = overrides;
747}
748
750{
751 if ( mTargetScreenProperties.contains( properties ) )
752 return;
753
754 mTargetScreenProperties.insert( properties );
755}
756
757QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
758{
760}
761
762int QgsLayerTreeModel::scaleIconSize( int standardSize )
763{
764 return QgsApplication::scaleIconSize( standardSize, true );
765}
766
768{
769 if ( mHitTestTask )
770 mHitTestTask->waitForFinished();
771}
772
774{
775 return static_cast< bool >( mHitTestTask );
776}
777
778void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
779{
780 beginInsertRows( node2index( node ), indexFrom, indexTo );
781}
782
783static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
784{
785 QList<QgsLayerTreeNode *> children = node->children();
786 QList<QgsLayerTreeLayer *> newLayerNodes;
787 for ( int i = indexFrom; i <= indexTo; ++i )
788 {
789 QgsLayerTreeNode *child = children.at( i );
790 if ( QgsLayerTree::isLayer( child ) )
791 newLayerNodes << QgsLayerTree::toLayer( child );
792 else if ( QgsLayerTree::isGroup( child ) )
793 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
794 }
795 return newLayerNodes;
796}
797
798void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
799{
800 Q_ASSERT( node );
801
802 endInsertRows();
803
804 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
805 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
806 connectToLayer( newLayerNode );
807}
808
809void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
810{
811 Q_ASSERT( node );
812
813 beginRemoveRows( node2index( node ), indexFrom, indexTo );
814
815 // disconnect from layers and remove their legend
816 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
817 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
818 disconnectFromLayer( nodeLayer );
819}
820
822{
823 endRemoveRows();
824}
825
827{
828 Q_ASSERT( node );
829
830 const QModelIndex index = node2index( node );
831 emit dataChanged( index, index );
832}
833
834void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
835{
836 Q_UNUSED( name )
837 Q_ASSERT( node );
838
839 const QModelIndex index = node2index( node );
840 emit dataChanged( index, index );
841}
842
843
845{
846 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
848}
849
850
852{
853 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
854 if ( !nodeLayer )
855 return;
856
857 // deferred connection to the layer
858 connectToLayer( nodeLayer );
859}
860
862{
863 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
864 if ( !nodeLayer )
865 return;
866
867 disconnectFromLayer( nodeLayer );
868
869 // wait for the layer to appear again
871}
872
874{
875 if ( !mRootNode )
876 return;
877
878 if ( !testFlag( ShowLegend ) )
879 return;
880
881 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
882 if ( !layer )
883 return;
884
885 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
886 if ( !nodeLayer )
887 return;
888
889 refreshLayerLegend( nodeLayer );
890}
891
893{
894 if ( !mRootNode )
895 return;
896
897 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
898 if ( !layer )
899 return;
900
901 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
902 if ( !nodeLayer )
903 return;
904
905 const QModelIndex index = node2index( nodeLayer );
906 emit dataChanged( index, index );
907}
908
910{
911 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
912 if ( !layer )
913 return;
914
915 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
916 if ( !nodeLayer )
917 return;
918
919 QModelIndex index = node2index( nodeLayer );
920 emit dataChanged( index, index );
921
922 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
923 refreshLayerLegend( nodeLayer );
924}
925
926
928{
929 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
930 if ( !legendNode )
931 return;
932
933 QModelIndex index = legendNode2index( legendNode );
934 if ( index.isValid() )
935 emit dataChanged( index, index );
936}
937
938void QgsLayerTreeModel::legendNodeSizeChanged()
939{
940 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
941 if ( !legendNode )
942 return;
943
944 QModelIndex index = legendNode2index( legendNode );
945 if ( index.isValid() )
946 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
947}
948
949void QgsLayerTreeModel::hitTestTaskCompleted()
950{
951 if ( mHitTestTask )
952 {
953 mHitTestResults = mHitTestTask->results();
954 handleHitTestResults();
955 emit hitTestCompleted();
956 }
957}
958
960{
961 if ( !nodeLayer->layer() )
962 {
963 // in order to connect to layer, we need to have it loaded.
964 // keep an eye on the layer ID: once loaded, we will use it
965 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
966 return;
967 }
968
969 // watch if the layer is getting removed
970 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
971
972 if ( testFlag( ShowLegend ) )
973 {
974 addLegendToLayer( nodeLayer );
975
976 // if we aren't loading a layer from a project, setup some nice default settings
977 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
978 {
979 // automatic collapse of legend nodes - useful if a layer has many legend nodes
981 nodeLayer->setExpanded( false );
982
984 {
985 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
986 }
987 }
988 }
989
990 QgsMapLayer *layer = nodeLayer->layer();
991 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
992 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
993
994 // using unique connection because there may be temporarily more nodes for a layer than just one
995 // which would create multiple connections, however disconnect() would disconnect all multiple connections
996 // even if we wanted to disconnect just one connection in each call.
997 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
998 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
999 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1000
1001 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
1002}
1003
1004// try to find out if the layer ID is present in the tree multiple times
1005static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
1006{
1007 int count = 0;
1008 const auto constChildren = group->children();
1009 for ( QgsLayerTreeNode *child : constChildren )
1010 {
1011 if ( QgsLayerTree::isLayer( child ) )
1012 {
1013 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
1014 count++;
1015 }
1016 else if ( QgsLayerTree::isGroup( child ) )
1017 {
1018 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
1019 }
1020 }
1021 return count;
1022}
1023
1025{
1026 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
1027
1028 if ( !nodeLayer->layer() )
1029 return; // we were never connected
1030
1031 if ( testFlag( ShowLegend ) )
1032 {
1033 removeLegendFromLayer( nodeLayer );
1034 }
1035
1036 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
1037 {
1038 // last instance of the layer in the tree: disconnect from all signals from layer!
1039 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
1040 }
1041}
1042
1044{
1045 const auto constChildren = parentGroup->children();
1046 for ( QgsLayerTreeNode *node : constChildren )
1047 {
1048 if ( QgsLayerTree::isGroup( node ) )
1050 else if ( QgsLayerTree::isLayer( node ) )
1052 }
1053}
1054
1056{
1057 const auto constChildren = parentGroup->children();
1058 for ( QgsLayerTreeNode *node : constChildren )
1059 {
1060 if ( QgsLayerTree::isGroup( node ) )
1062 else if ( QgsLayerTree::isLayer( node ) )
1064 }
1065}
1066
1068{
1069 Q_ASSERT( mRootNode );
1070
1071 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1072 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1073 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1074 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1075 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1076 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1077 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1078
1080}
1081
1083{
1084 if ( mRootNode )
1085 {
1086 disconnect( mRootNode, nullptr, this, nullptr );
1088 }
1089}
1090
1092{
1093 QgsLayerTreeNode *node = index2node( idx );
1094 if ( !node )
1095 return;
1096
1097 int count = node->children().count();
1098 if ( count == 0 )
1099 return;
1100 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1101 for ( int i = 0; i < count; ++i )
1102 recursivelyEmitDataChanged( index( i, 0, idx ) );
1103}
1104
1105void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1106{
1107 QgsLayerTreeNode *node = index2node( idx );
1108 if ( !node )
1109 return;
1110
1111 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1112 {
1113 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1114 if ( layer && layer->hasScaleBasedVisibility() )
1115 {
1116 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1117 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1118 }
1119 }
1120 int count = node->children().count();
1121 for ( int i = 0; i < count; ++i )
1122 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1123}
1124
1126{
1127 return Qt::CopyAction | Qt::MoveAction;
1128}
1129
1131{
1132 QStringList types;
1133 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1134 return types;
1135}
1136
1137
1138QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1139{
1140 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1141 QModelIndexList sortedIndexes = indexes;
1142 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1143
1144 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1145
1146 if ( nodesFinal.isEmpty() )
1147 return nullptr;
1148
1149 QMimeData *mimeData = new QMimeData();
1150
1151 QDomDocument layerTreeDoc;
1152 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1153
1154 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1155 {
1156 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1157 }
1158 layerTreeDoc.appendChild( rootLayerTreeElem );
1159
1160 QString errorMessage;
1161 QgsReadWriteContext readWriteContext;
1162 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1163 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1164
1165 QString txt = layerDefinitionsDoc.toString();
1166
1167 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1168 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1169 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1170 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1171 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1172
1173 return mimeData;
1174}
1175
1176bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1177{
1178 if ( action == Qt::IgnoreAction )
1179 return true;
1180
1181 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1182 return false;
1183
1184 if ( column >= columnCount( parent ) )
1185 return false;
1186
1187 // don't accept drops from some layer tree subclasses to non-matching subclasses
1188 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1189 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1190 return false;
1191
1192 QgsLayerTreeNode *nodeParent = index2node( parent );
1193 if ( !QgsLayerTree::isGroup( nodeParent ) )
1194 return false;
1195
1196 if ( parent.isValid() && row == -1 )
1197 row = 0; // if dropped directly onto group item, insert at first position
1198
1199 // if we are coming from another QGIS instance, we need to add the layers too
1200 bool ok = false;
1201 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1202 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1203
1204 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1205 {
1206 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1207 QDomDocument layerDefinitionDoc;
1208 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1209 return false;
1210 QgsReadWriteContext context;
1211 QString errorMessage;
1212 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1213 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1214 }
1215 else
1216 {
1217 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1218
1219 QDomDocument layerTreeDoc;
1220 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1221 return false;
1222
1223 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1224 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1225 return false;
1226
1227 QList<QgsLayerTreeNode *> nodes;
1228
1229 QDomElement elem = rootLayerTreeElem.firstChildElement();
1230 while ( !elem.isNull() )
1231 {
1233 if ( node )
1234 nodes << node;
1235
1236 elem = elem.nextSiblingElement();
1237 }
1238
1239 if ( nodes.isEmpty() )
1240 return false;
1241
1242 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1243 }
1244 return true;
1245}
1246
1247bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1248{
1249 QgsLayerTreeNode *parentNode = index2node( parent );
1250 if ( QgsLayerTree::isGroup( parentNode ) )
1251 {
1252 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1253 return true;
1254 }
1255 return false;
1256}
1257
1262
1264{
1265 if ( on )
1266 mFlags |= f;
1267 else
1268 mFlags &= ~f;
1269}
1270
1275
1277{
1278 return mFlags.testFlag( f );
1279}
1280
1282{
1283 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1284}
1285
1286QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1287{
1288 QList<QgsLayerTreeModelLegendNode *> filtered;
1289
1290 if ( mLegendFilterByScale > 0 )
1291 {
1292 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1293 {
1294 if ( node->isScaleOK( mLegendFilterByScale ) )
1295 filtered << node;
1296 }
1297 }
1298 else if ( mFilterSettings )
1299 {
1300 if ( !nodes.isEmpty() && mFilterSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1301 {
1302 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1303 {
1304 const QgsLayerTreeModelLegendNode::NodeTypes nodeType = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) ).value<QgsLayerTreeModelLegendNode::NodeTypes>();
1305 switch ( nodeType )
1306 {
1308 filtered << node;
1309 break;
1310
1318 {
1319 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1320 const bool isDataDefinedSize = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::IsDataDefinedSize ) ).toBool();
1321 const bool checked = ( mFilterSettings && !( mFilterSettings->flags() & Qgis::LayerTreeFilterFlag::SkipVisibilityCheck ) )
1322 || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1323
1324 if ( checked )
1325 {
1326 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1327 {
1328 auto it = mHitTestResults.constFind( vl->id() );
1329 if ( it != mHitTestResults.constEnd() &&
1330 ( it->contains( ruleKey ) ||
1331 ( !it->isEmpty() && isDataDefinedSize )
1332 )
1333 )
1334 {
1335 filtered << node;
1336 }
1337 }
1338 else
1339 {
1340 filtered << node;
1341 }
1342 }
1343 else // unknown node type or unchecked
1344 filtered << node;
1345 break;
1346 }
1347 }
1348 }
1349 }
1350 }
1351 else
1352 {
1353 return nodes;
1354 }
1355
1356 return filtered;
1357}
1358
1359
1360
1362// Legend nodes routines - start
1363
1365{
1366 const auto constMLegend = mLegend;
1367 for ( const LayerLegendData &data : constMLegend )
1368 {
1369 qDeleteAll( data.originalNodes );
1370 delete data.tree;
1371 }
1372 mLegend.clear();
1373
1374 if ( mHitTestTask )
1375 {
1376 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
1377 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
1378 mHitTestTask->cancel();
1379 mHitTestTask = nullptr;
1380 }
1381}
1382
1383
1385{
1386 if ( mLegend.contains( nodeLayer ) )
1387 {
1388 qDeleteAll( mLegend[nodeLayer].originalNodes );
1389 delete mLegend[nodeLayer].tree;
1390 mLegend.remove( nodeLayer );
1391 }
1392}
1393
1394
1396{
1397 if ( !nodeL || !nodeL->layer() )
1398 return;
1399
1400 QgsMapLayer *ml = nodeL->layer();
1401
1402 QgsMapLayerStyleOverride styleOverride( ml );
1403 if ( mLayerStyleOverrides.contains( ml->id() ) )
1404 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1405
1406 QgsMapLayerLegend *layerLegend = ml->legend();
1407 if ( !layerLegend )
1408 return;
1409 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1410
1411 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1413
1415 {
1416 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1417 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1418 while ( widgetsCount > 0 )
1419 {
1420 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1421 --widgetsCount;
1422 }
1423 }
1424
1425 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1426
1427 const auto constLstNew = lstNew;
1428 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1429 {
1430 n->setParent( this );
1432 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1433 }
1434
1435 // See if we have an embedded node - if we do, we will not use it among active nodes.
1436 // Legend node embedded in parent does not have to be the first one,
1437 // there can be also nodes generated for embedded widgets
1438 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1439 const auto constFilteredLstNew = filteredLstNew;
1440 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1441 {
1442 if ( legendNode->isEmbeddedInParent() )
1443 {
1444 embeddedNode = legendNode;
1445 filteredLstNew.removeOne( legendNode );
1446 break;
1447 }
1448 }
1449
1450 LayerLegendTree *legendTree = nullptr;
1451
1452 // maybe the legend nodes form a tree - try to create a tree structure from the list
1453 if ( testFlag( ShowLegendAsTree ) )
1454 legendTree = tryBuildLegendTree( filteredLstNew );
1455
1456 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1457
1458 if ( !filteredLstNew.isEmpty() )
1459 {
1460 // Make sure it's clear
1461 const QModelIndex nodeIndex { node2index( nodeL ) };
1462 if ( rowCount( nodeIndex ) > 0 )
1463 {
1464 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1465 mLegend[nodeL] = LayerLegendData();
1466 endRemoveRows();
1467 }
1468 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1469 }
1470
1472 data.originalNodes = lstNew;
1473 data.activeNodes = filteredLstNew;
1474 data.embeddedNodeInParent = embeddedNode;
1475 data.tree = legendTree;
1476
1477 mLegend[nodeL] = data;
1478
1479 if ( !filteredLstNew.isEmpty() )
1480 {
1481 endInsertRows();
1482 }
1483
1484 // invalidate map based data even if the data is not map-based to make sure
1485 // the symbol sizes are computed at least once
1486 mInvalidatedNodes.insert( nodeL );
1488}
1489
1490
1491QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1492{
1493 // first check whether there are any legend nodes that are not top-level
1494 bool hasParentKeys = false;
1495 for ( QgsLayerTreeModelLegendNode *n : nodes )
1496 {
1497 if ( !n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString().isEmpty() )
1498 {
1499 hasParentKeys = true;
1500 break;
1501 }
1502 }
1503 if ( !hasParentKeys )
1504 return nullptr; // all legend nodes are top-level => stick with list representation
1505
1506 // make mapping from rules to nodes and do some sanity checks
1507 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1508 rule2node[QString()] = nullptr;
1509 for ( QgsLayerTreeModelLegendNode *n : nodes )
1510 {
1511 QString ruleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1512 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1513 return nullptr;
1514 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1515 return nullptr;
1516 rule2node[ruleKey] = n;
1517 }
1518
1519 // create the tree structure
1520 LayerLegendTree *tree = new LayerLegendTree;
1521 for ( QgsLayerTreeModelLegendNode *n : nodes )
1522 {
1523 QString parentRuleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
1524 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1525 tree->parents[n] = parent;
1526 tree->children[parent] << n;
1527 }
1528 return tree;
1529}
1530
1532{
1533 double scale = 0.0;
1534 double mupp = 0.0;
1535 int dpi = 0;
1536 legendMapViewData( &mupp, &dpi, &scale );
1537 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1538
1539 // setup temporary render context
1540 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1541 context->setScaleFactor( dpi / 25.4 );
1542
1543 if ( !mTargetScreenProperties.isEmpty() )
1544 {
1545 mTargetScreenProperties.begin()->updateRenderContextForScreen( *context );
1546 }
1547
1548 context->setRendererScale( scale );
1549 context->setMapToPixel( QgsMapToPixel( mupp ) );
1551 return validData ? context.release() : nullptr;
1552}
1553
1554
1556{
1557 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1558}
1559
1560
1562{
1563 const LayerLegendData &data = mLegend[legendNode->layerNode()];
1564 if ( data.tree )
1565 {
1566 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1567 {
1568 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1569 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1570 return index( row, 0, parentIndex );
1571 }
1572 else
1573 {
1574 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1575 int row = data.tree->children[nullptr].indexOf( legendNode );
1576 return index( row, 0, parentIndex );
1577 }
1578 }
1579
1580 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1581 Q_ASSERT( parentIndex.isValid() );
1582 int row = data.activeNodes.indexOf( legendNode );
1583 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1584 return QModelIndex();
1585
1586 return index( row, 0, parentIndex );
1587}
1588
1589
1591{
1592 const LayerLegendData &data = mLegend[node->layerNode()];
1593 if ( data.tree )
1594 return data.tree->children[node].count();
1595
1596 return 0; // they are leaves
1597}
1598
1599
1601{
1602 if ( !mLegend.contains( nL ) )
1603 return 0;
1604
1605 const LayerLegendData &data = mLegend[nL];
1606 if ( data.tree )
1607 return data.tree->children[nullptr].count();
1608
1609 int count = data.activeNodes.count();
1610 return count;
1611}
1612
1613
1614QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1615{
1616 Q_ASSERT( mLegend.contains( nL ) );
1617 const LayerLegendData &data = mLegend[nL];
1618 if ( data.tree )
1619 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1620
1621 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1622}
1623
1624
1625QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1626{
1627 const LayerLegendData &data = mLegend[node->layerNode()];
1628 if ( data.tree )
1629 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1630
1631 return QModelIndex(); // have no children
1632}
1633
1634
1636{
1637 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1638 const LayerLegendData &data = mLegend[layerNode];
1639 if ( data.tree )
1640 {
1641 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1642 {
1643 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1644 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1645 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1646 }
1647 else
1648 return indexOfParentLayerTreeNode( layerNode );
1649 }
1650
1651 return indexOfParentLayerTreeNode( layerNode );
1652}
1653
1654
1656{
1657 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1658 return QVariant();
1659 return node->data( role );
1660}
1661
1662
1664{
1665 Qt::ItemFlags f = node->flags();
1667 f &= ~Qt::ItemIsUserCheckable;
1668 return f;
1669}
1670
1671
1673{
1674 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1675}
1676
1678{
1679 return mLegend[nodeLayer].embeddedNodeInParent;
1680}
1681
1682
1684{
1685 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1686 if ( !legendNode )
1687 return QIcon();
1688 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1689}
1690
1691
1692QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1693{
1694 if ( !mLegend.contains( nodeLayer ) )
1695 return QList<QgsLayerTreeModelLegendNode *>();
1696
1697 const LayerLegendData &data = mLegend[nodeLayer];
1698 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1699 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1700 lst.prepend( data.embeddedNodeInParent );
1701 return lst;
1702}
1703
1704QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1705{
1706 return mLegend.value( nodeLayer ).originalNodes;
1707}
1708
1709QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1710{
1711 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1712 {
1713 QgsLayerTreeLayer *layer = it.key();
1714 if ( layer->layerId() == layerId )
1715 {
1716 const auto activeNodes = mLegend.value( layer ).activeNodes;
1717 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1718 {
1719 if ( legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString() == ruleKey )
1720 {
1721 //found it!
1722 return legendNode;
1723 }
1724 }
1725 }
1726 }
1727
1728 return nullptr;
1729}
1730
1738
1740{
1741 // we have varying icon sizes, and we want icon to be centered and
1742 // text to be left aligned, so we have to compute the max width of icons
1743 //
1744 // we do that for nodes which share a common parent
1745 //
1746 // we do that here because for symbols with size defined in map units
1747 // the symbol sizes changes depends on the zoom level
1748
1749 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1750
1751 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1752 {
1753 const LayerLegendData &data = mLegend.value( layerNode );
1754
1755 QList<QgsSymbolLegendNode *> symbolNodes;
1756 QMap<QString, int> widthMax;
1757 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1758 {
1759 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1760 if ( n )
1761 {
1762 const QSize sz( n->minimumIconSize( context.get() ) );
1763 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1764 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1765 n->setIconSize( sz );
1766 symbolNodes.append( n );
1767 }
1768 }
1769 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1770 {
1771 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1772 Q_ASSERT( widthMax[parentKey] > 0 );
1773 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1774 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1775 }
1776 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1777 legendNode->invalidateMapBasedData();
1778 }
1779
1780 mInvalidatedNodes.clear();
1781}
1782
1783// Legend nodes routines - end
@ UsersCannotToggleEditing
Indicates that users are not allowed to toggle editing for this layer. Note that this does not imply ...
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
@ Polygon
Polygons.
@ Vector
Vector layer.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
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.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
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.
Qgis::GeometryType type
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, Qgis::LayerTreeInsertionMethod insertMethod=Qgis::LayerTreeInsertionMethod::OptimalInInsertionGroup, const QgsLayerTreeRegistryBridge::InsertionPoint *insertPoint=nullptr)
Loads the QLR at path into QGIS.
static bool exportLayerDefinition(const QString &path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Exports the selected layer tree nodes to a QLR file.
Contains settings relating to filtering the contents of QgsLayerTreeModel and views.
void setFilterPolygon(const QgsGeometry &polygon)
Sets the optional filter polygon, used when testing for symbols to show in the legend.
void setLayerFilterExpressionsFromLayerTree(QgsLayerTree *tree)
Sets layer filter expressions using a layer tree.
void setFlags(Qgis::LayerTreeFilterFlags flags)
Sets the filter flags.
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.
@ ParentRuleKey
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ NodeType
Type of node. Added in 3.16.
@ RuleKey
Rule key of the node (QString)
@ IsDataDefinedSize
Set when a node is related to data defined size (title or separated legend items)....
virtual void invalidateMapBasedData()
Notification from model that information from associated map view has changed.
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.
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 ...
void setFilterSettings(const QgsLayerTreeFilterSettings *settings=nullptr)
Sets the filter settings to use to filter legend nodes.
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
Q_DECL_DEPRECATED 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.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
std::unique_ptr< QgsLayerTreeFilterSettings > mFilterSettings
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
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)
QMap< QString, QSet< QString > > mHitTestResults
QModelIndex parent(const QModelIndex &child) const override
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.
bool hitTestInProgress() const
Returns true if a hit test for visible legend items is currently in progress.
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.
QSet< QgsScreenProperties > targetScreenProperties() const
Returns the target screen properties to use when generating icons.
QStringList mimeTypes() const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QSet< QgsScreenProperties > mTargetScreenProperties
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QPointer< QgsMapHitTestTask > mHitTestTask
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.
const QgsLayerTreeFilterSettings * filterSettings() const
Returns the filter settings to use to filter legend nodes.
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)
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
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 waitForHitTestBlocking()
When a current hit test for visible legend items is in progress, calling this method will block until...
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)....
@ UseThreadedHitTest
Run legend hit tests in a background thread (since QGIS 3.30)
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
Flags mFlags
Sets of flags for the model.
Q_DECL_DEPRECATED 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.
Namespace with helper functions for layer tree operations.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Executes a QgsMapHitTest in a background thread.
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:75
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
Definition qgsmaplayer.h:78
QgsLayerMetadata metadata
Definition qgsmaplayer.h:81
Qgis::LayerType type
Definition qgsmaplayer.h:85
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.
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.
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.
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.
Stores properties relating to a screen.
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.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
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)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6042
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:5382
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6041
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5465
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Structure that stores all data associated with one map layer.
LayerLegendTree * tree
Optional pointer to a tree structure - see LayerLegendTree for details.
QList< QgsLayerTreeModelLegendNode * > originalNodes
Data structure for storage of legend nodes.
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.