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