QGIS API Documentation 4.3.0-Master (b46232e4f9a)
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 const QString layerCrs = layer->crs().userFriendlyIdentifier( Qgis::CrsIdentifierType::ShortString );
303 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
304 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layerCrs ).toHtmlEscaped();
305 else
306 title += tr( " (%1)" ).arg( layerCrs ).toHtmlEscaped();
307 }
308
309 QStringList parts;
310 parts << title;
311
312 const QString abstract = !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
313 if ( !abstract.isEmpty() )
314 {
315 parts << QString();
316 const QStringList abstractLines = abstract.split( '\n' );
317 for ( const auto &l : abstractLines )
318 {
319 parts << l.toHtmlEscaped();
320 }
321 parts << QString();
322 }
323
324 QString source( layer->publicSource() );
325 if ( source.size() > 1024 )
326 {
327 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
328 }
329
330 parts << "<i>" + source.toHtmlEscaped() + "</i>";
331
332 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
333 const bool showFeatureCount = nodeLayer->customProperty( u"showFeatureCount"_s, 0 ).toBool();
334 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
335 if ( showFeatureCount && estimatedCount )
336 {
337 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
338 }
339
341 {
342 parts << tr( "ID: %1" ).arg( layer->id() );
343 }
344
345 return parts.join( "<br/>"_L1 );
346 }
347 }
348 }
349
350 return QVariant();
351}
352
353
354Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
355{
356 if ( !index.isValid() )
357 {
358 Qt::ItemFlags rootFlags = Qt::ItemFlags();
359 if ( testFlag( AllowNodeReorder ) )
360 rootFlags |= Qt::ItemIsDropEnabled;
361 return rootFlags;
362 }
363
365 return legendNodeFlags( symn );
366
367 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
368
369 if ( testFlag( AllowNodeRename ) )
370 f |= Qt::ItemIsEditable;
371
373 bool isEmbedded = node->customProperty( u"embedded"_s ).toInt();
374
375 if ( testFlag( AllowNodeReorder ) )
376 {
377 // only root embedded nodes can be reordered
378 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( u"embedded"_s ).toInt() ) )
379 f |= Qt::ItemIsDragEnabled;
380 }
381
383 f |= Qt::ItemIsUserCheckable;
384
385 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
386 f |= Qt::ItemIsDropEnabled;
387
388 return f;
389}
390
391bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
392{
394 if ( sym )
395 {
396 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
397 return false;
398 bool res = sym->setData( value, role );
399 if ( res )
400 emit dataChanged( index, index );
401 return res;
402 }
403
405 if ( !node )
406 return QAbstractItemModel::setData( index, value, role );
407
408 if ( role == Qt::CheckStateRole )
409 {
411 return false;
412
413 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
414 if ( checked && node->children().isEmpty() )
415 {
417 }
418 else if ( testFlag( ActionHierarchical ) )
419 {
420 if ( node->children().isEmpty() )
422 else
423 node->setItemVisibilityCheckedRecursive( checked );
424 }
425 else
426 {
427 node->setItemVisibilityChecked( checked );
428 }
429
431
432 return true;
433 }
434 else if ( role == Qt::EditRole )
435 {
436 if ( !testFlag( AllowNodeRename ) )
437 return false;
438
439 if ( QgsLayerTree::isLayer( node ) )
440 {
442 layer->setName( value.toString() );
443 emit dataChanged( index, index );
444 }
445 else if ( QgsLayerTree::isGroup( node ) )
446 {
447 QgsLayerTree::toGroup( node )->setName( value.toString() );
448 emit dataChanged( index, index );
449 }
450 else if ( QgsLayerTree::isCustomNode( node ) )
451 {
452 QgsLayerTree::toCustomNode( node )->setName( value.toString() );
453 emit dataChanged( index, index );
454 }
455 }
456
457 return QAbstractItemModel::setData( index, value, role );
458}
459
461{
462 if ( !node || !node->parent() )
463 return QModelIndex(); // this is the only root item -> invalid index
464
465 QModelIndex parentIndex = node2index( node->parent() );
466
467 int row = node->parent()->children().indexOf( node );
468 Q_ASSERT( row >= 0 );
469 return index( row, 0, parentIndex );
470}
471
472
473static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
474{
475 if ( !child->parent() )
476 return false;
477
478 if ( child->parent() == node )
479 return true;
480
481 return _isChildOfNode( child->parent(), node );
482}
483
484static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
485{
486 for ( QgsLayerTreeNode *n : nodes )
487 {
488 if ( _isChildOfNode( child, n ) )
489 return true;
490 }
491
492 return false;
493}
494
495
496QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
497{
498 QList<QgsLayerTreeNode *> nodes;
499 const auto constList = list;
500 for ( const QModelIndex &index : constList )
501 {
503 if ( !node )
504 continue;
505
506 nodes << node;
507 }
508
509 if ( !skipInternal )
510 return nodes;
511
512 // remove any children of nodes if both parent node and children are selected
513 QList<QgsLayerTreeNode *> nodesFinal;
514 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
515 {
516 if ( !_isChildOfNodes( node, nodes ) )
517 nodesFinal << node;
518 }
519
520 return nodesFinal;
521}
522
527
529{
530 beginResetModel();
531
533
534 Q_ASSERT( mLegend.isEmpty() );
535
536 mRootNode = newRootGroup;
537
538 endResetModel();
539
541}
542
544{
545 // update title
546 QModelIndex idx = node2index( nodeLayer );
547 emit dataChanged( idx, idx );
548
549 // update children
550 int oldNodeCount = rowCount( idx );
551 if ( oldNodeCount > 0 )
552 {
553 beginRemoveRows( idx, 0, oldNodeCount - 1 );
554 removeLegendFromLayer( nodeLayer );
555 endRemoveRows();
556 }
557
558 addLegendToLayer( nodeLayer );
559 int newNodeCount = rowCount( idx );
560
561 // automatic collapse of legend nodes - useful if a layer has many legend nodes
562 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
563 nodeLayer->setExpanded( false );
564}
565
567{
568 return mCurrentIndex;
569}
570
575
576
577void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
578{
579 if ( nodeType == QgsLayerTreeNode::NodeGroup )
580 {
581 if ( mFontGroup != font )
582 {
583 mFontGroup = font;
585 }
586 }
587 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
588 {
589 if ( mFontLayer != font )
590 {
591 mFontLayer = font;
593 }
594 }
595 else
596 {
597 QgsDebugMsgLevel( u"invalid node type"_s, 4 );
598 }
599}
600
601
602QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
603{
604 if ( nodeType == QgsLayerTreeNode::NodeGroup )
605 return mFontGroup;
606 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
607 return mFontLayer;
608 else
609 {
610 QgsDebugMsgLevel( u"invalid node type"_s, 4 );
611 return QFont();
612 }
613}
614
616{
617 mLegendFilterByScale = scale;
618
619 // this could be later done in more efficient way
620 // by just updating active legend nodes, without refreshing original legend nodes
621 const auto layers = mRootNode->findLayers();
622 for ( QgsLayerTreeLayer *nodeLayer : layers )
623 refreshLayerLegend( nodeLayer );
624}
625
627{
629 setLegendFilter( settings, /* useExtent = */ true );
631}
632
633void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
634{
635 if ( settings && settings->hasValidSettings() )
636 {
637 auto filterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
638
639 if ( !useExtent ) // only expressions
640 {
642 }
643 else if ( polygon.type() == Qgis::GeometryType::Polygon )
644 {
645 filterSettings->setFilterPolygon( polygon );
646 }
647
648 if ( useExpressions )
649 {
650 filterSettings->setLayerFilterExpressionsFromLayerTree( mRootNode );
651 }
652
654 }
655 else
656 {
657 setFilterSettings( nullptr );
658 }
659}
660
662{
663 return mFilterSettings ? &mFilterSettings->mapSettings() : nullptr;
664}
665
667{
668 if ( settings )
669 {
670 mFilterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
671 mFilterSettings->mapSettings().setLayerStyleOverrides( mLayerStyleOverrides );
672
673 bool hitTestWasRunning = false;
674 if ( mHitTestTask )
675 {
676 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
677 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
678 mHitTestTask->cancel();
679 mHitTestTask = nullptr;
680 hitTestWasRunning = true;
681 }
682
684 {
686 connect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
688
689 if ( !hitTestWasRunning )
690 emit hitTestStarted();
691 }
692 else
693 {
694 auto blockingHitTest = std::make_unique< QgsMapHitTest >( *mFilterSettings );
695 blockingHitTest->run();
696 mHitTestResults = blockingHitTest->results();
697 mHitTestResultsRendererUpdatedCanvas = blockingHitTest->resultsRenderersUpdatedCanvas();
698 handleHitTestResults();
699 }
700 }
701 else
702 {
703 if ( !mFilterSettings )
704 return;
705
706 mFilterSettings.reset();
707 handleHitTestResults();
708 }
709}
710
715
716void QgsLayerTreeModel::handleHitTestResults()
717{
718 // temporarily disable autocollapse so that legend nodes stay visible
719 int bkAutoCollapse = autoCollapseLegendNodes();
721
722 // this could be later done in more efficient way
723 // by just updating active legend nodes, without refreshing original legend nodes
724 const auto layers = mRootNode->findLayers();
725 for ( QgsLayerTreeLayer *nodeLayer : layers )
726 refreshLayerLegend( nodeLayer );
727
728 setAutoCollapseLegendNodes( bkAutoCollapse );
729
730 // update any color ramp legend nodes with new min/max from hit test
732 {
733 const QList<QgsLayerTreeLayer *> treeLayers = rootGroup()->findLayers();
734
735 for ( QgsLayerTreeLayer *layerTreeLayer : treeLayers )
736 {
737 const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerLegendNodes( layerTreeLayer );
738
739 if ( mHitTestResultsRendererUpdatedCanvas.contains( layerTreeLayer->layerId() ) )
740 {
741 QPair<double, double> limits = mHitTestResultsRendererUpdatedCanvas.value( layerTreeLayer->layerId() );
742
743 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
744 {
745 if ( auto *colorRampNode = dynamic_cast<QgsColorRampLegendNode *>( legendNode ) )
746 {
747 colorRampNode->setMinimum( limits.first );
748 colorRampNode->setMaximum( limits.second );
749 }
750 }
751 }
752 }
753 }
754}
755
756void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
757{
758 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
759 return;
760
761 double previousScale = mLegendMapViewScale;
762 mLegendMapViewScale = scale;
763 mLegendMapViewMupp = mapUnitsPerPixel;
764 mLegendMapViewDpi = dpi;
765
766 // now invalidate legend nodes!
768
769 if ( scale != previousScale )
770 refreshScaleBasedLayers( QModelIndex(), previousScale );
771}
772
773void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
774{
775 if ( mapUnitsPerPixel )
776 *mapUnitsPerPixel = mLegendMapViewMupp;
777 if ( dpi )
778 *dpi = mLegendMapViewDpi;
779 if ( scale )
780 *scale = mLegendMapViewScale;
781}
782
783QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
784{
786}
787
788void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
789{
790 mLayerStyleOverrides = overrides;
791}
792
794{
795 if ( mTargetScreenProperties.contains( properties ) )
796 return;
797
798 mTargetScreenProperties.insert( properties );
799}
800
801void QgsLayerTreeModel::setTargetScreenProperties( const QSet<QgsScreenProperties> &properties )
802{
803 mTargetScreenProperties = properties;
805}
806
807QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
808{
810}
811
812int QgsLayerTreeModel::scaleIconSize( int standardSize )
813{
814 return QgsApplication::scaleIconSize( standardSize, true );
815}
816
818{
819 if ( mHitTestTask )
820 mHitTestTask->waitForFinished();
821}
822
824{
825 return static_cast< bool >( mHitTestTask );
826}
827
829{
830 std::function< void( QgsLayerTreeNode * ) > invalidateNode;
831 invalidateNode = [this, &invalidateNode]( QgsLayerTreeNode *node ) {
832 if ( !node )
833 return;
834
835 switch ( node->nodeType() )
836 {
838 {
839 auto layerNode = qobject_cast< QgsLayerTreeLayer * >( node );
840 const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerLegendNodes( layerNode );
841 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
842 {
843 legendNode->invalidateDisplayData();
844 }
845
846 break;
847 }
848
851 break;
852 }
853
854 const QList<QgsLayerTreeNode *> children = node->children();
855 for ( QgsLayerTreeNode *childNode : children )
856 {
857 invalidateNode( childNode );
858 }
859 };
860 invalidateNode( mRootNode );
861}
862
863void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
864{
865 beginInsertRows( node2index( node ), indexFrom, indexTo );
866}
867
868static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
869{
870 QList<QgsLayerTreeNode *> children = node->children();
871 QList<QgsLayerTreeLayer *> newLayerNodes;
872 for ( int i = indexFrom; i <= indexTo; ++i )
873 {
874 QgsLayerTreeNode *child = children.at( i );
875 if ( QgsLayerTree::isLayer( child ) )
876 newLayerNodes << QgsLayerTree::toLayer( child );
877 else if ( QgsLayerTree::isGroup( child ) )
878 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
879 }
880 return newLayerNodes;
881}
882
883void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
884{
885 Q_ASSERT( node );
886
887 endInsertRows();
888
889 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
890 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
891 connectToLayer( newLayerNode );
892}
893
894void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
895{
896 Q_ASSERT( node );
897
898 beginRemoveRows( node2index( node ), indexFrom, indexTo );
899
900 // disconnect from layers and remove their legend
901 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
902 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
903 disconnectFromLayer( nodeLayer );
904}
905
907{
908 endRemoveRows();
909}
910
912{
913 Q_ASSERT( node );
914
915 const QModelIndex index = node2index( node );
916 emit dataChanged( index, index );
917}
918
919void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
920{
921 Q_UNUSED( name )
922 Q_ASSERT( node );
923
924 const QModelIndex index = node2index( node );
925 emit dataChanged( index, index );
926}
927
928
930{
931 if ( QgsLayerTree::isLayer( node ) && key == "showFeatureCount"_L1 )
933}
934
935
937{
938 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
939 if ( !nodeLayer )
940 return;
941
942 // deferred connection to the layer
943 connectToLayer( nodeLayer );
944}
945
947{
948 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
949 if ( !nodeLayer )
950 return;
951
952 disconnectFromLayer( nodeLayer );
953
954 // wait for the layer to appear again
956}
957
959{
960 if ( !mRootNode )
961 return;
962
963 if ( !testFlag( ShowLegend ) )
964 return;
965
966 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
967 if ( !layer )
968 return;
969
970 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
971 if ( !nodeLayer )
972 return;
973
974 refreshLayerLegend( nodeLayer );
975}
976
978{
979 if ( !mRootNode )
980 return;
981
982 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
983 if ( !layer )
984 return;
985
986 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
987 if ( !nodeLayer )
988 return;
989
990 const QModelIndex index = node2index( nodeLayer );
991 emit dataChanged( index, index );
992}
993
995{
996 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
997 if ( !layer )
998 return;
999
1000 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
1001 if ( !nodeLayer )
1002 return;
1003
1004 QModelIndex index = node2index( nodeLayer );
1005 emit dataChanged( index, index );
1006
1007 if ( nodeLayer->customProperty( u"showFeatureCount"_s ).toInt() )
1008 refreshLayerLegend( nodeLayer );
1009}
1010
1011
1013{
1014 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
1015 if ( !legendNode )
1016 return;
1017
1018 QModelIndex index = legendNode2index( legendNode );
1019 if ( index.isValid() )
1020 emit dataChanged( index, index );
1021}
1022
1023void QgsLayerTreeModel::legendNodeSizeChanged()
1024{
1025 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
1026 if ( !legendNode )
1027 return;
1028
1029 QModelIndex index = legendNode2index( legendNode );
1030 if ( index.isValid() )
1031 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
1032}
1033
1034void QgsLayerTreeModel::hitTestTaskCompleted()
1035{
1036 if ( mHitTestTask )
1037 {
1038 mHitTestResults = mHitTestTask->results();
1039 mHitTestResultsRendererUpdatedCanvas = mHitTestTask->resultsRenderersUpdatedCanvas();
1040
1041 handleHitTestResults();
1042 emit hitTestCompleted();
1043 }
1044}
1045
1047{
1048 if ( !nodeLayer->layer() )
1049 {
1050 // in order to connect to layer, we need to have it loaded.
1051 // keep an eye on the layer ID: once loaded, we will use it
1052 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
1053 return;
1054 }
1055
1056 // watch if the layer is getting removed
1057 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
1058
1059 if ( testFlag( ShowLegend ) )
1060 {
1061 addLegendToLayer( nodeLayer );
1062
1063 // if we aren't loading a layer from a project, setup some nice default settings
1064 if ( !mRootNode->customProperty( u"loading"_s ).toBool() )
1065 {
1066 // automatic collapse of legend nodes - useful if a layer has many legend nodes
1068 nodeLayer->setExpanded( false );
1069
1071 {
1072 nodeLayer->setCustomProperty( u"showFeatureCount"_s, true );
1073 }
1074 }
1075 }
1076
1077 QgsMapLayer *layer = nodeLayer->layer();
1078 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
1079 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
1080
1081 if ( QgsMapLayerElevationProperties *elevationProperties = layer->elevationProperties() )
1082 {
1083 connect( elevationProperties, &QgsMapLayerElevationProperties::profileGenerationPropertyChanged, this, &QgsLayerTreeModel::layerProfileGenerationPropertyChanged, Qt::UniqueConnection );
1084 }
1085
1086 // using unique connection because there may be temporarily more nodes for a layer than just one
1087 // which would create multiple connections, however disconnect() would disconnect all multiple connections
1088 // even if we wanted to disconnect just one connection in each call.
1089 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1090 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1091 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1092
1093 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
1094}
1095
1096// try to find out if the layer ID is present in the tree multiple times
1097static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
1098{
1099 int count = 0;
1100 const auto constChildren = group->children();
1101 for ( QgsLayerTreeNode *child : constChildren )
1102 {
1103 if ( QgsLayerTree::isLayer( child ) )
1104 {
1105 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
1106 count++;
1107 }
1108 else if ( QgsLayerTree::isGroup( child ) )
1109 {
1110 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
1111 }
1112 }
1113 return count;
1114}
1115
1117{
1118 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
1119
1120 if ( !nodeLayer->layer() )
1121 return; // we were never connected
1122
1123 if ( testFlag( ShowLegend ) )
1124 {
1125 removeLegendFromLayer( nodeLayer );
1126 }
1127
1128 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
1129 {
1130 // last instance of the layer in the tree: disconnect from all signals from layer!
1131 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
1132 }
1133}
1134
1136{
1137 const auto constChildren = parentGroup->children();
1138 for ( QgsLayerTreeNode *node : constChildren )
1139 {
1140 if ( QgsLayerTree::isGroup( node ) )
1142 else if ( QgsLayerTree::isLayer( node ) )
1144 }
1145}
1146
1148{
1149 const auto constChildren = parentGroup->children();
1150 for ( QgsLayerTreeNode *node : constChildren )
1151 {
1152 if ( QgsLayerTree::isGroup( node ) )
1154 else if ( QgsLayerTree::isLayer( node ) )
1156 }
1157}
1158
1160{
1161 Q_ASSERT( mRootNode );
1162
1163 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1164 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1165 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1166 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1167 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1168 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1169 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1170
1172}
1173
1175{
1176 if ( mRootNode )
1177 {
1178 disconnect( mRootNode, nullptr, this, nullptr );
1180 }
1181}
1182
1184{
1185 QgsLayerTreeNode *node = index2node( idx );
1186 if ( !node )
1187 return;
1188
1189 int count = node->children().count();
1190 if ( count == 0 )
1191 return;
1192 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1193 for ( int i = 0; i < count; ++i )
1194 recursivelyEmitDataChanged( index( i, 0, idx ) );
1195}
1196
1197void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1198{
1199 QgsLayerTreeNode *node = index2node( idx );
1200 if ( !node )
1201 return;
1202
1203 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1204 {
1205 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1206 if ( layer && layer->hasScaleBasedVisibility() )
1207 {
1208 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1209 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1210 }
1211 }
1212 int count = node->children().count();
1213 for ( int i = 0; i < count; ++i )
1214 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1215}
1216
1218{
1219 return Qt::CopyAction | Qt::MoveAction;
1220}
1221
1223{
1224 QStringList types;
1225 types << u"application/qgis.layertreemodeldata"_s;
1226 return types;
1227}
1228
1229
1230QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1231{
1232 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1233 QModelIndexList sortedIndexes = indexes;
1234 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1235
1236 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1237
1238 if ( nodesFinal.isEmpty() )
1239 return nullptr;
1240
1241 QMimeData *mimeData = new QMimeData();
1242
1243 QDomDocument layerTreeDoc;
1244 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( u"layer_tree_model_data"_s );
1245
1246 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1247 {
1248 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1249 }
1250 layerTreeDoc.appendChild( rootLayerTreeElem );
1251
1252 QString errorMessage;
1253 QgsReadWriteContext readWriteContext;
1254 QDomDocument layerDefinitionsDoc( u"qgis-layer-definition"_s );
1255 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1256
1257 QString txt = layerDefinitionsDoc.toString();
1258
1259 mimeData->setData( u"application/qgis.layertreemodeldata"_s, layerTreeDoc.toString().toUtf8() );
1260 mimeData->setData( u"application/qgis.application.pid"_s, QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1261 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() );
1262 mimeData->setData( u"application/qgis.layertree.layerdefinitions"_s, txt.toUtf8() );
1263 mimeData->setData( u"application/x-vnd.qgis.qgis.uri"_s, QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1264
1265 return mimeData;
1266}
1267
1268bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1269{
1270 if ( action == Qt::IgnoreAction )
1271 return true;
1272
1273 if ( !data->hasFormat( u"application/qgis.layertreemodeldata"_s ) )
1274 return false;
1275
1276 if ( column >= columnCount( parent ) )
1277 return false;
1278
1279 // don't accept drops from some layer tree subclasses to non-matching subclasses
1280 const QString restrictTypes( data->data( u"application/qgis.restrictlayertreemodelsubclass"_s ) );
1281 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1282 return false;
1283
1284 QgsLayerTreeNode *nodeParent = index2node( parent );
1285 if ( !QgsLayerTree::isGroup( nodeParent ) )
1286 return false;
1287
1288 if ( parent.isValid() && row == -1 )
1289 row = 0; // if dropped directly onto group item, insert at first position
1290
1291 // if we are coming from another QGIS instance, we need to add the layers too
1292 bool ok = false;
1293 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1294 qint64 qgisPid = data->data( u"application/qgis.application.pid"_s ).toInt( &ok );
1295
1296 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1297 {
1298 QByteArray encodedLayerDefinitionData = data->data( u"application/qgis.layertree.layerdefinitions"_s );
1299 QDomDocument layerDefinitionDoc;
1300 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1301 return false;
1302 QgsReadWriteContext context;
1303 QString errorMessage;
1304 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context ); // skip-keyword-check
1305 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1306 }
1307 else
1308 {
1309 QByteArray encodedLayerTreeData = data->data( u"application/qgis.layertreemodeldata"_s );
1310
1311 QDomDocument layerTreeDoc;
1312 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1313 return false;
1314
1315 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1316 if ( rootLayerTreeElem.tagName() != "layer_tree_model_data"_L1 )
1317 return false;
1318
1319 QList<QgsLayerTreeNode *> nodes;
1320
1321 QDomElement elem = rootLayerTreeElem.firstChildElement();
1322 while ( !elem.isNull() )
1323 {
1324 QgsLayerTreeNode *node = QgsLayerTreeNode::readXml( elem, QgsProject::instance() ); // skip-keyword-check
1325 if ( node )
1326 nodes << node;
1327
1328 elem = elem.nextSiblingElement();
1329 }
1330
1331 if ( nodes.isEmpty() )
1332 return false;
1333
1334 // moving around layer group preserves id but copying creates new id for the group + subgroups
1335 if ( action == Qt::CopyAction )
1336 {
1337 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
1339 }
1340
1341 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1342 }
1343 return true;
1344}
1345
1346bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1347{
1348 QgsLayerTreeNode *parentNode = index2node( parent );
1349 if ( QgsLayerTree::isGroup( parentNode ) )
1350 {
1351 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1352 return true;
1353 }
1354 return false;
1355}
1356
1361
1363{
1364 if ( on )
1365 mFlags |= f;
1366 else
1367 mFlags &= ~f;
1368}
1369
1374
1376{
1377 return mFlags.testFlag( f );
1378}
1379
1381{
1382 return QgsApplication::getThemeIcon( u"/mActionFolder.svg"_s );
1383}
1384
1385QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1386{
1387 QList<QgsLayerTreeModelLegendNode *> filtered;
1388
1389 if ( mLegendFilterByScale > 0 )
1390 {
1391 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1392 {
1393 if ( node->isScaleOK( mLegendFilterByScale ) )
1394 filtered << node;
1395 }
1396 }
1397 else if ( mFilterSettings )
1398 {
1399 if ( !nodes.isEmpty() && mFilterSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1400 {
1401 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1402 {
1404 switch ( nodeType )
1405 {
1407 filtered << node;
1408 break;
1409
1417 {
1418 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1419 const bool isDataDefinedSize = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::IsDataDefinedSize ) ).toBool();
1420 const bool checked = ( mFilterSettings && !( mFilterSettings->flags() & Qgis::LayerTreeFilterFlag::SkipVisibilityCheck ) ) || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1421
1422 if ( checked )
1423 {
1424 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1425 {
1426 auto it = mHitTestResults.constFind( vl->id() );
1427 if ( it != mHitTestResults.constEnd() && ( it->contains( ruleKey ) || ( !it->isEmpty() && isDataDefinedSize ) ) )
1428 {
1429 filtered << node;
1430 }
1431 }
1432 else
1433 {
1434 filtered << node;
1435 }
1436 }
1437 else // unknown node type or unchecked
1438 filtered << node;
1439 break;
1440 }
1441 }
1442 }
1443 }
1444 }
1445 else
1446 {
1447 return nodes;
1448 }
1449
1450 return filtered;
1451}
1452
1453
1455// Legend nodes routines - start
1456
1458{
1459 const auto constMLegend = mLegend;
1460 for ( const LayerLegendData &data : constMLegend )
1461 {
1462 qDeleteAll( data.originalNodes );
1463 delete data.tree;
1464 }
1465 mLegend.clear();
1466
1467 if ( mHitTestTask )
1468 {
1469 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
1470 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
1471 mHitTestTask->cancel();
1472 mHitTestTask = nullptr;
1473 }
1474}
1475
1476
1478{
1479 if ( mLegend.contains( nodeLayer ) )
1480 {
1481 qDeleteAll( mLegend[nodeLayer].originalNodes );
1482 delete mLegend[nodeLayer].tree;
1483 mLegend.remove( nodeLayer );
1484 }
1485}
1486
1487
1489{
1490 if ( !nodeL || !nodeL->layer() )
1491 return;
1492
1493 QgsMapLayer *ml = nodeL->layer();
1494
1495 QgsMapLayerStyleOverride styleOverride( ml );
1496 if ( mLayerStyleOverrides.contains( ml->id() ) )
1497 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1498
1499 QgsMapLayerLegend *layerLegend = ml->legend();
1500 if ( !layerLegend )
1501 return;
1502 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1503
1504 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1506
1508 {
1509 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1510 int widgetsCount = ml->customProperty( u"embeddedWidgets/count"_s, 0 ).toInt();
1511 while ( widgetsCount > 0 )
1512 {
1513 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1514 --widgetsCount;
1515 }
1516 }
1517
1518 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1519
1520 const auto constLstNew = lstNew;
1521 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1522 {
1523 n->setParent( this );
1525 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1526 }
1527
1528 // See if we have an embedded node - if we do, we will not use it among active nodes.
1529 // Legend node embedded in parent does not have to be the first one,
1530 // there can be also nodes generated for embedded widgets
1531 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1532 const auto constFilteredLstNew = filteredLstNew;
1533 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1534 {
1535 if ( legendNode->isEmbeddedInParent() )
1536 {
1537 embeddedNode = legendNode;
1538 filteredLstNew.removeOne( legendNode );
1539 break;
1540 }
1541 }
1542
1543 LayerLegendTree *legendTree = nullptr;
1544
1545 // maybe the legend nodes form a tree - try to create a tree structure from the list
1546 if ( testFlag( ShowLegendAsTree ) )
1547 legendTree = tryBuildLegendTree( filteredLstNew );
1548
1549 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1550
1551 if ( !filteredLstNew.isEmpty() )
1552 {
1553 // Make sure it's clear
1554 const QModelIndex nodeIndex { node2index( nodeL ) };
1555 if ( rowCount( nodeIndex ) > 0 )
1556 {
1557 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1558 mLegend[nodeL] = LayerLegendData();
1559 endRemoveRows();
1560 }
1561 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1562 }
1563
1565 data.originalNodes = lstNew;
1566 data.activeNodes = filteredLstNew;
1567 data.embeddedNodeInParent = embeddedNode;
1568 data.tree = legendTree;
1569
1570 mLegend[nodeL] = data;
1571
1572 if ( !filteredLstNew.isEmpty() )
1573 {
1574 endInsertRows();
1575 }
1576
1577 // invalidate map based data even if the data is not map-based to make sure
1578 // the symbol sizes are computed at least once
1579 mInvalidatedNodes.insert( nodeL );
1581}
1582
1583
1584QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1585{
1586 // first check whether there are any legend nodes that are not top-level
1587 bool hasParentKeys = false;
1588 for ( QgsLayerTreeModelLegendNode *n : nodes )
1589 {
1590 if ( !n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString().isEmpty() )
1591 {
1592 hasParentKeys = true;
1593 break;
1594 }
1595 }
1596 if ( !hasParentKeys )
1597 return nullptr; // all legend nodes are top-level => stick with list representation
1598
1599 // make mapping from rules to nodes and do some sanity checks
1600 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1601 rule2node[QString()] = nullptr;
1602 for ( QgsLayerTreeModelLegendNode *n : nodes )
1603 {
1604 QString ruleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1605 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1606 return nullptr;
1607 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1608 return nullptr;
1609 rule2node[ruleKey] = n;
1610 }
1611
1612 // create the tree structure
1613 LayerLegendTree *tree = new LayerLegendTree;
1614 for ( QgsLayerTreeModelLegendNode *n : nodes )
1615 {
1616 QString parentRuleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
1617 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1618 tree->parents[n] = parent;
1619 tree->children[parent] << n;
1620 }
1621 return tree;
1622}
1623
1625{
1626 double scale = 0.0;
1627 double mupp = 0.0;
1628 int dpi = 0;
1629 legendMapViewData( &mupp, &dpi, &scale );
1630 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1631
1632 // setup temporary render context
1633 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1634 context->setScaleFactor( dpi / 25.4 );
1635
1636 if ( !mTargetScreenProperties.isEmpty() )
1637 {
1638 mTargetScreenProperties.begin()->updateRenderContextForScreen( *context );
1639 }
1640
1641 context->setRendererScale( scale );
1642 context->setMapToPixel( QgsMapToPixel( mupp ) );
1643 context->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1644 context->setFlag( Qgis::RenderContextFlag::RenderSymbolPreview, true );
1645 context->setFlag( Qgis::RenderContextFlag::RenderLayerTree, true );
1646 return validData ? context.release() : nullptr;
1647}
1648
1650{
1651 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1652}
1653
1654
1656{
1657 const LayerLegendData &data = mLegend[legendNode->layerNode()];
1658 if ( data.tree )
1659 {
1660 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1661 {
1662 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1663 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1664 return index( row, 0, parentIndex );
1665 }
1666 else
1667 {
1668 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1669 int row = data.tree->children[nullptr].indexOf( legendNode );
1670 return index( row, 0, parentIndex );
1671 }
1672 }
1673
1674 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1675 Q_ASSERT( parentIndex.isValid() );
1676 int row = data.activeNodes.indexOf( legendNode );
1677 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1678 return QModelIndex();
1679
1680 return index( row, 0, parentIndex );
1681}
1682
1683
1685{
1686 const LayerLegendData &data = mLegend[node->layerNode()];
1687 if ( data.tree )
1688 return data.tree->children[node].count();
1689
1690 return 0; // they are leaves
1691}
1692
1693
1695{
1696 if ( !mLegend.contains( nL ) )
1697 return 0;
1698
1699 const LayerLegendData &data = mLegend[nL];
1700 if ( data.tree )
1701 return data.tree->children[nullptr].count();
1702
1703 int count = data.activeNodes.count();
1704 return count;
1705}
1706
1707
1708QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1709{
1710 Q_ASSERT( mLegend.contains( nL ) );
1711 const LayerLegendData &data = mLegend[nL];
1712 if ( data.tree )
1713 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1714
1715 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1716}
1717
1718
1719QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1720{
1721 const LayerLegendData &data = mLegend[node->layerNode()];
1722 if ( data.tree )
1723 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1724
1725 return QModelIndex(); // have no children
1726}
1727
1728
1730{
1731 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1732 const LayerLegendData &data = mLegend[layerNode];
1733 if ( data.tree )
1734 {
1735 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1736 {
1737 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1738 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1739 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1740 }
1741 else
1742 return indexOfParentLayerTreeNode( layerNode );
1743 }
1744
1745 return indexOfParentLayerTreeNode( layerNode );
1746}
1747
1748
1750{
1751 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1752 return QVariant();
1753 return node->data( role );
1754}
1755
1756
1758{
1759 Qt::ItemFlags f = node->flags();
1761 f &= ~Qt::ItemIsUserCheckable;
1762 return f;
1763}
1764
1765
1767{
1768 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1769}
1770
1772{
1773 return mLegend[nodeLayer].embeddedNodeInParent;
1774}
1775
1776
1778{
1779 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1780 if ( !legendNode )
1781 return QIcon();
1782 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1783}
1784
1785
1786QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1787{
1788 if ( !mLegend.contains( nodeLayer ) )
1789 return QList<QgsLayerTreeModelLegendNode *>();
1790
1791 const LayerLegendData &data = mLegend[nodeLayer];
1792 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1793 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1794 lst.prepend( data.embeddedNodeInParent );
1795 return lst;
1796}
1797
1798QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1799{
1800 return mLegend.value( nodeLayer ).originalNodes;
1801}
1802
1803QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1804{
1805 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1806 {
1807 QgsLayerTreeLayer *layer = it.key();
1808 if ( layer->layerId() == layerId )
1809 {
1810 const auto activeNodes = mLegend.value( layer ).activeNodes;
1811 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1812 {
1813 if ( legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString() == ruleKey )
1814 {
1815 //found it!
1816 return legendNode;
1817 }
1818 }
1819 }
1820 }
1821
1822 return nullptr;
1823}
1824
1832
1834{
1835 // we have varying icon sizes, and we want icon to be centered and
1836 // text to be left aligned, so we have to compute the max width of icons
1837 //
1838 // we do that for nodes which share a common parent
1839 //
1840 // we do that here because for symbols with size defined in map units
1841 // the symbol sizes changes depends on the zoom level
1842
1843 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1844
1845 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1846 {
1847 const LayerLegendData &data = mLegend.value( layerNode );
1848
1849 QList<QgsSymbolLegendNode *> symbolNodes;
1850 QMap<QString, int> widthMax;
1851 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1852 {
1853 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1854 if ( n )
1855 {
1856 const QSize sz( n->minimumIconSize( context.get() ) );
1857 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1858 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1859 n->setIconSize( sz );
1860 symbolNodes.append( n );
1861 }
1862 }
1863 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1864 {
1865 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1866 Q_ASSERT( widthMax[parentKey] > 0 );
1867 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1868 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1869 }
1870 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1871 legendNode->invalidateMapBasedData();
1872 }
1873
1874 mInvalidatedNodes.clear();
1875}
1876
1877void QgsLayerTreeModel::layerProfileGenerationPropertyChanged()
1878{
1879 if ( !mRootNode )
1880 return;
1881
1882 QgsMapLayerElevationProperties *elevationProperties = qobject_cast<QgsMapLayerElevationProperties *>( sender() );
1883 if ( !elevationProperties )
1884 return;
1885
1886 if ( QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( elevationProperties->parent() ) )
1887 {
1888 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
1889 if ( !nodeLayer )
1890 return;
1891
1892 QModelIndex index = node2index( nodeLayer );
1893 emit dataChanged( index, index );
1894 }
1895}
1896
1897// 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:2465
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
Definition qgis.h:2604
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
Definition qgis.h:5049
@ Polygon
Polygons.
Definition qgis.h:402
@ 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:2976
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
Definition qgis.h:2987
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2972
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 Q_DECL_DEPRECATED 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:8361
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8360
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
#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.