QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
56{
58}
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 }
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 =
281 !layer->title().isEmpty() ? layer->title() :
282 !layer->shortName().isEmpty() ? layer->shortName() :
283 layer->name();
284
285 title = "<b>" + title.toHtmlEscaped() + "</b>";
286
287 if ( layer->isSpatial() && layer->crs().isValid() )
288 {
289 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
290 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
291 else
292 title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
293 }
294
295 QStringList parts;
296 parts << title;
297
298 if ( !layer->abstract().isEmpty() )
299 {
300 parts << QString();
301 const QStringList abstractLines = layer->abstract().split( '\n' );
302 for ( const auto &l : abstractLines )
303 {
304 parts << l.toHtmlEscaped();
305 }
306 parts << QString();
307 }
308
309 QString source( layer->publicSource() );
310 if ( source.size() > 1024 )
311 {
312 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
313 }
314
315 parts << "<i>" + source.toHtmlEscaped() + "</i>";
316
317 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
318 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
319 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
320 if ( showFeatureCount && estimatedCount )
321 {
322 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
323 }
324
325 return parts.join( QLatin1String( "<br/>" ) );
326 }
327 }
328 }
329
330 return QVariant();
331}
332
333
334Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
335{
336 if ( !index.isValid() )
337 {
338 Qt::ItemFlags rootFlags = Qt::ItemFlags();
339 if ( testFlag( AllowNodeReorder ) )
340 rootFlags |= Qt::ItemIsDropEnabled;
341 return rootFlags;
342 }
343
345 return legendNodeFlags( symn );
346
347 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
348
349 if ( testFlag( AllowNodeRename ) )
350 f |= Qt::ItemIsEditable;
351
353 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
354
355 if ( testFlag( AllowNodeReorder ) )
356 {
357 // only root embedded nodes can be reordered
358 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
359 f |= Qt::ItemIsDragEnabled;
360 }
361
363 f |= Qt::ItemIsUserCheckable;
364
365 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
366 f |= Qt::ItemIsDropEnabled;
367
368 return f;
369}
370
371bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
372{
374 if ( sym )
375 {
376 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
377 return false;
378 bool res = sym->setData( value, role );
379 if ( res )
380 emit dataChanged( index, index );
381 return res;
382 }
383
385 if ( !node )
386 return QAbstractItemModel::setData( index, value, role );
387
388 if ( role == Qt::CheckStateRole )
389 {
391 return false;
392
393 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
394 if ( checked && node->children().isEmpty() )
395 {
397 }
398 else if ( testFlag( ActionHierarchical ) )
399 {
400 if ( node->children().isEmpty() )
402 else
403 node->setItemVisibilityCheckedRecursive( checked );
404 }
405 else
406 {
407 node->setItemVisibilityChecked( checked );
408 }
409
411
412 return true;
413 }
414 else if ( role == Qt::EditRole )
415 {
416 if ( !testFlag( AllowNodeRename ) )
417 return false;
418
419 if ( QgsLayerTree::isLayer( node ) )
420 {
422 layer->setName( value.toString() );
423 emit dataChanged( index, index );
424 }
425 else if ( QgsLayerTree::isGroup( node ) )
426 {
427 QgsLayerTree::toGroup( node )->setName( value.toString() );
428 emit dataChanged( index, index );
429 }
430 }
431
432 return QAbstractItemModel::setData( index, value, role );
433}
434
436{
437 if ( !node || !node->parent() )
438 return QModelIndex(); // this is the only root item -> invalid index
439
440 QModelIndex parentIndex = node2index( node->parent() );
441
442 int row = node->parent()->children().indexOf( node );
443 Q_ASSERT( row >= 0 );
444 return index( row, 0, parentIndex );
445}
446
447
448static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
449{
450 if ( !child->parent() )
451 return false;
452
453 if ( child->parent() == node )
454 return true;
455
456 return _isChildOfNode( child->parent(), node );
457}
458
459static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
460{
461 for ( QgsLayerTreeNode *n : nodes )
462 {
463 if ( _isChildOfNode( child, n ) )
464 return true;
465 }
466
467 return false;
468}
469
470
471QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
472{
473 QList<QgsLayerTreeNode *> nodes;
474 const auto constList = list;
475 for ( const QModelIndex &index : constList )
476 {
478 if ( !node )
479 continue;
480
481 nodes << node;
482 }
483
484 if ( !skipInternal )
485 return nodes;
486
487 // remove any children of nodes if both parent node and children are selected
488 QList<QgsLayerTreeNode *> nodesFinal;
489 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
490 {
491 if ( !_isChildOfNodes( node, nodes ) )
492 nodesFinal << node;
493 }
494
495 return nodesFinal;
496}
497
499{
500 return mRootNode;
501}
502
504{
505 beginResetModel();
506
508
509 Q_ASSERT( mLegend.isEmpty() );
510
511 mRootNode = newRootGroup;
512
513 endResetModel();
514
516}
517
519{
520 // update title
521 QModelIndex idx = node2index( nodeLayer );
522 emit dataChanged( idx, idx );
523
524 // update children
525 int oldNodeCount = rowCount( idx );
526 if ( oldNodeCount > 0 )
527 {
528 beginRemoveRows( idx, 0, oldNodeCount - 1 );
529 removeLegendFromLayer( nodeLayer );
530 endRemoveRows();
531 }
532
533 addLegendToLayer( nodeLayer );
534 int newNodeCount = rowCount( idx );
535
536 // automatic collapse of legend nodes - useful if a layer has many legend nodes
537 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
538 nodeLayer->setExpanded( false );
539}
540
542{
543 return mCurrentIndex;
544}
545
546void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
547{
549}
550
551
552void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
553{
554 if ( nodeType == QgsLayerTreeNode::NodeGroup )
555 {
556 if ( mFontGroup != font )
557 {
558 mFontGroup = font;
560 }
561 }
562 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
563 {
564 if ( mFontLayer != font )
565 {
566 mFontLayer = font;
568 }
569 }
570 else
571 {
572 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
573 }
574}
575
576
577QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
578{
579 if ( nodeType == QgsLayerTreeNode::NodeGroup )
580 return mFontGroup;
581 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
582 return mFontLayer;
583 else
584 {
585 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
586 return QFont();
587 }
588}
589
591{
592 mLegendFilterByScale = scale;
593
594 // this could be later done in more efficient way
595 // by just updating active legend nodes, without refreshing original legend nodes
596 const auto layers = mRootNode->findLayers();
597 for ( QgsLayerTreeLayer *nodeLayer : layers )
598 refreshLayerLegend( nodeLayer );
599}
600
602{
604 setLegendFilter( settings, /* useExtent = */ true );
606}
607
608void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
609{
610 if ( settings && settings->hasValidSettings() )
611 {
612 std::unique_ptr< QgsLayerTreeFilterSettings > filterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
613
614 if ( !useExtent ) // only expressions
615 {
617 }
618 else if ( polygon.type() == Qgis::GeometryType::Polygon )
619 {
621 }
622
623 if ( useExpressions )
624 {
626 }
627
629 }
630 else
631 {
632 setFilterSettings( nullptr );
633 }
634}
635
637{
638 return mFilterSettings ? &mFilterSettings->mapSettings() : nullptr;
639}
640
642{
643 if ( settings )
644 {
645 mFilterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
646 mFilterSettings->mapSettings().setLayerStyleOverrides( mLayerStyleOverrides );
647
648 bool hitTestWasRunning = false;
649 if ( mHitTestTask )
650 {
651 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
652 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
653 mHitTestTask->cancel();
654 mHitTestTask = nullptr;
655 hitTestWasRunning = true;
656 }
657
658 std::unique_ptr< QgsMapHitTest > blockingHitTest;
659 if ( mFlags & QgsLayerTreeModel::Flag::UseThreadedHitTest )
661 else
662 blockingHitTest = std::make_unique< QgsMapHitTest >( *mFilterSettings );
663
664 if ( mHitTestTask )
665 {
666 connect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
668
669 if ( !hitTestWasRunning )
670 emit hitTestStarted();
671 }
672 else
673 {
674 blockingHitTest->run();
675 mHitTestResults = blockingHitTest->results();
676 handleHitTestResults();
677 }
678 }
679 else
680 {
681 if ( !mFilterSettings )
682 return;
683
684 mFilterSettings.reset();
685 handleHitTestResults();
686 }
687}
688
690{
691 return mFilterSettings.get();
692}
693
694void QgsLayerTreeModel::handleHitTestResults()
695{
696 // temporarily disable autocollapse so that legend nodes stay visible
697 int bkAutoCollapse = autoCollapseLegendNodes();
699
700 // this could be later done in more efficient way
701 // by just updating active legend nodes, without refreshing original legend nodes
702 const auto layers = mRootNode->findLayers();
703 for ( QgsLayerTreeLayer *nodeLayer : layers )
704 refreshLayerLegend( nodeLayer );
705
706 setAutoCollapseLegendNodes( bkAutoCollapse );
707}
708
709void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
710{
711 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
712 return;
713
714 double previousScale = mLegendMapViewScale;
715 mLegendMapViewScale = scale;
716 mLegendMapViewMupp = mapUnitsPerPixel;
717 mLegendMapViewDpi = dpi;
718
719 // now invalidate legend nodes!
721
722 if ( scale != previousScale )
723 refreshScaleBasedLayers( QModelIndex(), previousScale );
724}
725
726void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
727{
728 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
729 if ( dpi ) *dpi = mLegendMapViewDpi;
730 if ( scale ) *scale = mLegendMapViewScale;
731}
732
733QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
734{
736}
737
738void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
739{
740 mLayerStyleOverrides = overrides;
741}
742
744{
745 if ( mTargetScreenProperties.contains( properties ) )
746 return;
747
748 mTargetScreenProperties.insert( properties );
749}
750
751QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
752{
754}
755
756int QgsLayerTreeModel::scaleIconSize( int standardSize )
757{
758 return QgsApplication::scaleIconSize( standardSize, true );
759}
760
762{
763 if ( mHitTestTask )
764 mHitTestTask->waitForFinished();
765}
766
768{
769 return static_cast< bool >( mHitTestTask );
770}
771
772void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
773{
774 beginInsertRows( node2index( node ), indexFrom, indexTo );
775}
776
777static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
778{
779 QList<QgsLayerTreeNode *> children = node->children();
780 QList<QgsLayerTreeLayer *> newLayerNodes;
781 for ( int i = indexFrom; i <= indexTo; ++i )
782 {
783 QgsLayerTreeNode *child = children.at( i );
784 if ( QgsLayerTree::isLayer( child ) )
785 newLayerNodes << QgsLayerTree::toLayer( child );
786 else if ( QgsLayerTree::isGroup( child ) )
787 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
788 }
789 return newLayerNodes;
790}
791
792void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
793{
794 Q_ASSERT( node );
795
796 endInsertRows();
797
798 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
799 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
800 connectToLayer( newLayerNode );
801}
802
803void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
804{
805 Q_ASSERT( node );
806
807 beginRemoveRows( node2index( node ), indexFrom, indexTo );
808
809 // disconnect from layers and remove their legend
810 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
811 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
812 disconnectFromLayer( nodeLayer );
813}
814
816{
817 endRemoveRows();
818}
819
821{
822 Q_ASSERT( node );
823
824 const QModelIndex index = node2index( node );
825 emit dataChanged( index, index );
826}
827
828void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
829{
830 Q_UNUSED( name )
831 Q_ASSERT( node );
832
833 const QModelIndex index = node2index( node );
834 emit dataChanged( index, index );
835}
836
837
839{
840 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
842}
843
844
846{
847 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
848 if ( !nodeLayer )
849 return;
850
851 // deferred connection to the layer
852 connectToLayer( nodeLayer );
853}
854
856{
857 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
858 if ( !nodeLayer )
859 return;
860
861 disconnectFromLayer( nodeLayer );
862
863 // wait for the layer to appear again
865}
866
868{
869 if ( !mRootNode )
870 return;
871
872 if ( !testFlag( ShowLegend ) )
873 return;
874
875 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
876 if ( !layer )
877 return;
878
879 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
880 if ( !nodeLayer )
881 return;
882
883 refreshLayerLegend( nodeLayer );
884}
885
887{
888 if ( !mRootNode )
889 return;
890
891 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
892 if ( !layer )
893 return;
894
895 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
896 if ( !nodeLayer )
897 return;
898
899 const QModelIndex index = node2index( nodeLayer );
900 emit dataChanged( index, index );
901}
902
904{
905 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
906 if ( !layer )
907 return;
908
909 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
910 if ( !nodeLayer )
911 return;
912
913 QModelIndex index = node2index( nodeLayer );
914 emit dataChanged( index, index );
915
916 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
917 refreshLayerLegend( nodeLayer );
918}
919
920
922{
923 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
924 if ( !legendNode )
925 return;
926
927 QModelIndex index = legendNode2index( legendNode );
928 if ( index.isValid() )
929 emit dataChanged( index, index );
930}
931
932void QgsLayerTreeModel::legendNodeSizeChanged()
933{
934 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
935 if ( !legendNode )
936 return;
937
938 QModelIndex index = legendNode2index( legendNode );
939 if ( index.isValid() )
940 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
941}
942
943void QgsLayerTreeModel::hitTestTaskCompleted()
944{
945 if ( mHitTestTask )
946 {
947 mHitTestResults = mHitTestTask->results();
948 handleHitTestResults();
949 emit hitTestCompleted();
950 }
951}
952
954{
955 if ( !nodeLayer->layer() )
956 {
957 // in order to connect to layer, we need to have it loaded.
958 // keep an eye on the layer ID: once loaded, we will use it
959 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
960 return;
961 }
962
963 // watch if the layer is getting removed
964 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
965
966 if ( testFlag( ShowLegend ) )
967 {
968 addLegendToLayer( nodeLayer );
969
970 // if we aren't loading a layer from a project, setup some nice default settings
971 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
972 {
973 // automatic collapse of legend nodes - useful if a layer has many legend nodes
975 nodeLayer->setExpanded( false );
976
978 {
979 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
980 }
981 }
982 }
983
984 QgsMapLayer *layer = nodeLayer->layer();
985 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
986 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
987
988 // using unique connection because there may be temporarily more nodes for a layer than just one
989 // which would create multiple connections, however disconnect() would disconnect all multiple connections
990 // even if we wanted to disconnect just one connection in each call.
991 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
992 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
993 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
994
995 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
996}
997
998// try to find out if the layer ID is present in the tree multiple times
999static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
1000{
1001 int count = 0;
1002 const auto constChildren = group->children();
1003 for ( QgsLayerTreeNode *child : constChildren )
1004 {
1005 if ( QgsLayerTree::isLayer( child ) )
1006 {
1007 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
1008 count++;
1009 }
1010 else if ( QgsLayerTree::isGroup( child ) )
1011 {
1012 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
1013 }
1014 }
1015 return count;
1016}
1017
1019{
1020 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
1021
1022 if ( !nodeLayer->layer() )
1023 return; // we were never connected
1024
1025 if ( testFlag( ShowLegend ) )
1026 {
1027 removeLegendFromLayer( nodeLayer );
1028 }
1029
1030 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
1031 {
1032 // last instance of the layer in the tree: disconnect from all signals from layer!
1033 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
1034 }
1035}
1036
1038{
1039 const auto constChildren = parentGroup->children();
1040 for ( QgsLayerTreeNode *node : constChildren )
1041 {
1042 if ( QgsLayerTree::isGroup( node ) )
1044 else if ( QgsLayerTree::isLayer( node ) )
1046 }
1047}
1048
1050{
1051 const auto constChildren = parentGroup->children();
1052 for ( QgsLayerTreeNode *node : constChildren )
1053 {
1054 if ( QgsLayerTree::isGroup( node ) )
1056 else if ( QgsLayerTree::isLayer( node ) )
1058 }
1059}
1060
1062{
1063 Q_ASSERT( mRootNode );
1064
1065 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1066 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1067 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1068 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1069 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1070 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1071 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1072
1074}
1075
1077{
1078 if ( mRootNode )
1079 {
1080 disconnect( mRootNode, nullptr, this, nullptr );
1082 }
1083}
1084
1086{
1087 QgsLayerTreeNode *node = index2node( idx );
1088 if ( !node )
1089 return;
1090
1091 int count = node->children().count();
1092 if ( count == 0 )
1093 return;
1094 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1095 for ( int i = 0; i < count; ++i )
1096 recursivelyEmitDataChanged( index( i, 0, idx ) );
1097}
1098
1099void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1100{
1101 QgsLayerTreeNode *node = index2node( idx );
1102 if ( !node )
1103 return;
1104
1105 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1106 {
1107 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1108 if ( layer && layer->hasScaleBasedVisibility() )
1109 {
1110 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1111 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1112 }
1113 }
1114 int count = node->children().count();
1115 for ( int i = 0; i < count; ++i )
1116 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1117}
1118
1120{
1121 return Qt::CopyAction | Qt::MoveAction;
1122}
1123
1125{
1126 QStringList types;
1127 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1128 return types;
1129}
1130
1131
1132QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1133{
1134 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1135 QModelIndexList sortedIndexes = indexes;
1136 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1137
1138 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1139
1140 if ( nodesFinal.isEmpty() )
1141 return nullptr;
1142
1143 QMimeData *mimeData = new QMimeData();
1144
1145 QDomDocument layerTreeDoc;
1146 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1147
1148 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1149 {
1150 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1151 }
1152 layerTreeDoc.appendChild( rootLayerTreeElem );
1153
1154 QString errorMessage;
1155 QgsReadWriteContext readWriteContext;
1156 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1157 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1158
1159 QString txt = layerDefinitionsDoc.toString();
1160
1161 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1162 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1163 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1164 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1165 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1166
1167 return mimeData;
1168}
1169
1170bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1171{
1172 if ( action == Qt::IgnoreAction )
1173 return true;
1174
1175 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1176 return false;
1177
1178 if ( column >= columnCount( parent ) )
1179 return false;
1180
1181 // don't accept drops from some layer tree subclasses to non-matching subclasses
1182 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1183 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1184 return false;
1185
1186 QgsLayerTreeNode *nodeParent = index2node( parent );
1187 if ( !QgsLayerTree::isGroup( nodeParent ) )
1188 return false;
1189
1190 if ( parent.isValid() && row == -1 )
1191 row = 0; // if dropped directly onto group item, insert at first position
1192
1193 // if we are coming from another QGIS instance, we need to add the layers too
1194 bool ok = false;
1195 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1196 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1197
1198 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1199 {
1200 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1201 QDomDocument layerDefinitionDoc;
1202 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1203 return false;
1204 QgsReadWriteContext context;
1205 QString errorMessage;
1206 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1207 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1208 }
1209 else
1210 {
1211 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1212
1213 QDomDocument layerTreeDoc;
1214 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1215 return false;
1216
1217 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1218 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1219 return false;
1220
1221 QList<QgsLayerTreeNode *> nodes;
1222
1223 QDomElement elem = rootLayerTreeElem.firstChildElement();
1224 while ( !elem.isNull() )
1225 {
1227 if ( node )
1228 nodes << node;
1229
1230 elem = elem.nextSiblingElement();
1231 }
1232
1233 if ( nodes.isEmpty() )
1234 return false;
1235
1236 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1237 }
1238 return true;
1239}
1240
1241bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1242{
1243 QgsLayerTreeNode *parentNode = index2node( parent );
1244 if ( QgsLayerTree::isGroup( parentNode ) )
1245 {
1246 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1247 return true;
1248 }
1249 return false;
1250}
1251
1253{
1254 mFlags = f;
1255}
1256
1258{
1259 if ( on )
1260 mFlags |= f;
1261 else
1262 mFlags &= ~f;
1263}
1264
1266{
1267 return mFlags;
1268}
1269
1271{
1272 return mFlags.testFlag( f );
1273}
1274
1276{
1277 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1278}
1279
1280QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1281{
1282 QList<QgsLayerTreeModelLegendNode *> filtered;
1283
1284 if ( mLegendFilterByScale > 0 )
1285 {
1286 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1287 {
1288 if ( node->isScaleOK( mLegendFilterByScale ) )
1289 filtered << node;
1290 }
1291 }
1292 else if ( mFilterSettings )
1293 {
1294 if ( !nodes.isEmpty() && mFilterSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1295 {
1296 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1297 {
1298 switch ( node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) ).value<QgsLayerTreeModelLegendNode::NodeTypes>() )
1299 {
1301 filtered << node;
1302 break;
1303
1311 {
1312 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1313 const bool checked = ( mFilterSettings && !( mFilterSettings->flags() & Qgis::LayerTreeFilterFlag::SkipVisibilityCheck ) )
1314 || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1315
1316 if ( checked )
1317 {
1318 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1319 {
1320 auto it = mHitTestResults.constFind( vl->id() );
1321 if ( it != mHitTestResults.constEnd() && it->contains( ruleKey ) )
1322 {
1323 filtered << node;
1324 }
1325 }
1326 else
1327 {
1328 filtered << node;
1329 }
1330 }
1331 else // unknown node type or unchecked
1332 filtered << node;
1333 break;
1334 }
1335 }
1336 }
1337 }
1338 }
1339 else
1340 {
1341 return nodes;
1342 }
1343
1344 return filtered;
1345}
1346
1347
1348
1350// Legend nodes routines - start
1351
1353{
1354 const auto constMLegend = mLegend;
1355 for ( const LayerLegendData &data : constMLegend )
1356 {
1357 qDeleteAll( data.originalNodes );
1358 delete data.tree;
1359 }
1360 mLegend.clear();
1361
1362 if ( mHitTestTask )
1363 {
1364 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
1365 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
1366 mHitTestTask->cancel();
1367 mHitTestTask = nullptr;
1368 }
1369}
1370
1371
1373{
1374 if ( mLegend.contains( nodeLayer ) )
1375 {
1376 qDeleteAll( mLegend[nodeLayer].originalNodes );
1377 delete mLegend[nodeLayer].tree;
1378 mLegend.remove( nodeLayer );
1379 }
1380}
1381
1382
1384{
1385 if ( !nodeL || !nodeL->layer() )
1386 return;
1387
1388 QgsMapLayer *ml = nodeL->layer();
1389
1390 QgsMapLayerStyleOverride styleOverride( ml );
1391 if ( mLayerStyleOverrides.contains( ml->id() ) )
1392 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1393
1394 QgsMapLayerLegend *layerLegend = ml->legend();
1395 if ( !layerLegend )
1396 return;
1397 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1398
1399 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1401
1403 {
1404 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1405 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1406 while ( widgetsCount > 0 )
1407 {
1408 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1409 --widgetsCount;
1410 }
1411 }
1412
1413 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1414
1415 const auto constLstNew = lstNew;
1416 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1417 {
1418 n->setParent( this );
1420 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1421 }
1422
1423 // See if we have an embedded node - if we do, we will not use it among active nodes.
1424 // Legend node embedded in parent does not have to be the first one,
1425 // there can be also nodes generated for embedded widgets
1426 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1427 const auto constFilteredLstNew = filteredLstNew;
1428 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1429 {
1431 {
1432 embeddedNode = legendNode;
1433 filteredLstNew.removeOne( legendNode );
1434 break;
1435 }
1436 }
1437
1438 LayerLegendTree *legendTree = nullptr;
1439
1440 // maybe the legend nodes form a tree - try to create a tree structure from the list
1441 if ( testFlag( ShowLegendAsTree ) )
1442 legendTree = tryBuildLegendTree( filteredLstNew );
1443
1444 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1445
1446 if ( !filteredLstNew.isEmpty() )
1447 {
1448 // Make sure it's clear
1449 const QModelIndex nodeIndex { node2index( nodeL ) };
1450 if ( rowCount( nodeIndex ) > 0 )
1451 {
1452 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1453 mLegend[nodeL] = LayerLegendData();
1454 endRemoveRows();
1455 }
1456 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1457 }
1458
1460 data.originalNodes = lstNew;
1461 data.activeNodes = filteredLstNew;
1462 data.embeddedNodeInParent = embeddedNode;
1463 data.tree = legendTree;
1464
1465 mLegend[nodeL] = data;
1466
1467 if ( !filteredLstNew.isEmpty() )
1468 {
1469 endInsertRows();
1470 }
1471
1472 // invalidate map based data even if the data is not map-based to make sure
1473 // the symbol sizes are computed at least once
1474 mInvalidatedNodes.insert( nodeL );
1476}
1477
1478
1479QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1480{
1481 // first check whether there are any legend nodes that are not top-level
1482 bool hasParentKeys = false;
1483 for ( QgsLayerTreeModelLegendNode *n : nodes )
1484 {
1485 if ( !n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString().isEmpty() )
1486 {
1487 hasParentKeys = true;
1488 break;
1489 }
1490 }
1491 if ( !hasParentKeys )
1492 return nullptr; // all legend nodes are top-level => stick with list representation
1493
1494 // make mapping from rules to nodes and do some sanity checks
1495 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1496 rule2node[QString()] = nullptr;
1497 for ( QgsLayerTreeModelLegendNode *n : nodes )
1498 {
1499 QString ruleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1500 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1501 return nullptr;
1502 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1503 return nullptr;
1504 rule2node[ruleKey] = n;
1505 }
1506
1507 // create the tree structure
1508 LayerLegendTree *tree = new LayerLegendTree;
1509 for ( QgsLayerTreeModelLegendNode *n : nodes )
1510 {
1511 QString parentRuleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
1512 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1513 tree->parents[n] = parent;
1514 tree->children[parent] << n;
1515 }
1516 return tree;
1517}
1518
1520{
1521 double scale = 0.0;
1522 double mupp = 0.0;
1523 int dpi = 0;
1524 legendMapViewData( &mupp, &dpi, &scale );
1525 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1526
1527 // setup temporary render context
1528 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1529 context->setScaleFactor( dpi / 25.4 );
1530
1531 if ( !mTargetScreenProperties.isEmpty() )
1532 {
1533 mTargetScreenProperties.begin()->updateRenderContextForScreen( *context );
1534 }
1535
1536 context->setRendererScale( scale );
1537 context->setMapToPixel( QgsMapToPixel( mupp ) );
1539 return validData ? context.release() : nullptr;
1540}
1541
1542
1544{
1545 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1546}
1547
1548
1550{
1552 if ( data.tree )
1553 {
1554 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1555 {
1556 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1557 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1558 return index( row, 0, parentIndex );
1559 }
1560 else
1561 {
1562 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1563 int row = data.tree->children[nullptr].indexOf( legendNode );
1564 return index( row, 0, parentIndex );
1565 }
1566 }
1567
1568 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1569 Q_ASSERT( parentIndex.isValid() );
1570 int row = data.activeNodes.indexOf( legendNode );
1571 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1572 return QModelIndex();
1573
1574 return index( row, 0, parentIndex );
1575}
1576
1577
1579{
1580 const LayerLegendData &data = mLegend[node->layerNode()];
1581 if ( data.tree )
1582 return data.tree->children[node].count();
1583
1584 return 0; // they are leaves
1585}
1586
1587
1589{
1590 if ( !mLegend.contains( nL ) )
1591 return 0;
1592
1593 const LayerLegendData &data = mLegend[nL];
1594 if ( data.tree )
1595 return data.tree->children[nullptr].count();
1596
1597 int count = data.activeNodes.count();
1598 return count;
1599}
1600
1601
1602QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1603{
1604 Q_ASSERT( mLegend.contains( nL ) );
1605 const LayerLegendData &data = mLegend[nL];
1606 if ( data.tree )
1607 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1608
1609 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1610}
1611
1612
1613QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1614{
1615 const LayerLegendData &data = mLegend[node->layerNode()];
1616 if ( data.tree )
1617 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1618
1619 return QModelIndex(); // have no children
1620}
1621
1622
1624{
1625 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1626 const LayerLegendData &data = mLegend[layerNode];
1627 if ( data.tree )
1628 {
1629 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1630 {
1631 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1632 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1633 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1634 }
1635 else
1636 return indexOfParentLayerTreeNode( layerNode );
1637 }
1638
1639 return indexOfParentLayerTreeNode( layerNode );
1640}
1641
1642
1644{
1645 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1646 return QVariant();
1647 return node->data( role );
1648}
1649
1650
1652{
1653 Qt::ItemFlags f = node->flags();
1655 f &= ~Qt::ItemIsUserCheckable;
1656 return f;
1657}
1658
1659
1661{
1662 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1663}
1664
1666{
1667 return mLegend[nodeLayer].embeddedNodeInParent;
1668}
1669
1670
1672{
1673 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1674 if ( !legendNode )
1675 return QIcon();
1676 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1677}
1678
1679
1680QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1681{
1682 if ( !mLegend.contains( nodeLayer ) )
1683 return QList<QgsLayerTreeModelLegendNode *>();
1684
1685 const LayerLegendData &data = mLegend[nodeLayer];
1686 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1687 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1688 lst.prepend( data.embeddedNodeInParent );
1689 return lst;
1690}
1691
1692QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1693{
1694 return mLegend.value( nodeLayer ).originalNodes;
1695}
1696
1697QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1698{
1699 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1700 {
1701 QgsLayerTreeLayer *layer = it.key();
1702 if ( layer->layerId() == layerId )
1703 {
1704 const auto activeNodes = mLegend.value( layer ).activeNodes;
1705 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1706 {
1707 if ( legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString() == ruleKey )
1708 {
1709 //found it!
1710 return legendNode;
1711 }
1712 }
1713 }
1714 }
1715
1716 return nullptr;
1717}
1718
1720{
1723 else
1725}
1726
1728{
1729 // we have varying icon sizes, and we want icon to be centered and
1730 // text to be left aligned, so we have to compute the max width of icons
1731 //
1732 // we do that for nodes which share a common parent
1733 //
1734 // we do that here because for symbols with size defined in map units
1735 // the symbol sizes changes depends on the zoom level
1736
1737 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1738
1739 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1740 {
1741 const LayerLegendData &data = mLegend.value( layerNode );
1742
1743 QList<QgsSymbolLegendNode *> symbolNodes;
1744 QMap<QString, int> widthMax;
1745 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1746 {
1747 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1748 if ( n )
1749 {
1750 const QSize sz( n->minimumIconSize( context.get() ) );
1751 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1752 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1753 n->setIconSize( sz );
1754 symbolNodes.append( n );
1755 }
1756 }
1757 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1758 {
1759 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1760 Q_ASSERT( widthMax[parentKey] > 0 );
1761 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1762 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1763 }
1764 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1766 }
1767
1768 mInvalidatedNodes.clear();
1769}
1770
1771// 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,...
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.
Definition: qgsgeometry.h:162
Qgis::GeometryType type
Definition: qgsgeometry.h:165
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.
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)
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.
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
QFlags< Flag > Flags
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.
~QgsLayerTreeModel() override
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
static QIcon iconGroup()
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)....
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.
Definition: qgslayertree.h:32
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:41
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:60
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() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Qgis::LayerType type
Definition: qgsmaplayer.h:82
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:312
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:481
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...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:5776
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5775
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
#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.