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