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