QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgslayertreemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreemodel.cpp
3 --------------------------------------
4 Date : May 2014
5 Copyright : (C) 2014 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include <QMimeData>
17#include <QTextStream>
18
19#include "qgslayertreemodel.h"
20#include "moc_qgslayertreemodel.cpp"
21
22#include "qgsapplication.h"
23#include "qgslayertree.h"
25#include "qgsproject.h"
26#include "qgsmaphittest.h"
27#include "qgsmaplayer.h"
28#include "qgsmaplayerlegend.h"
29#include "qgsvectorlayer.h"
30#include "qgslayerdefinition.h"
31#include "qgsiconutils.h"
32#include "qgsmimedatautils.h"
34#include "qgsmaplayerstyle.h"
35#include "qgsrendercontext.h"
37
38#include <QPalette>
39
41 : QAbstractItemModel( parent )
42 , mRootNode( rootNode )
43 , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
44{
45 if ( rootNode )
46 {
48 }
49
50 mFontLayer.setBold( true );
51
53 mDeferLegendInvalidationTimer.setSingleShot( true );
54}
55
60
61QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
62{
63 if ( !index.isValid() )
64 return mRootNode;
65
66 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
67 return qobject_cast<QgsLayerTreeNode *>( obj );
68}
69
70
71int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
72{
74 return legendNodeRowCount( nodeLegend );
75
77 if ( !n )
78 return 0;
79
80 if ( QgsLayerTree::isLayer( n ) )
81 {
82 if ( !testFlag( ShowLegend ) )
83 return 0;
84
86 }
87
88 return n->children().count();
89}
90
91int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
92{
93 Q_UNUSED( parent )
94 return 1;
95}
96
97QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
98{
99 if ( column < 0 || column >= columnCount( parent ) ||
100 row < 0 || row >= rowCount( parent ) )
101 return QModelIndex();
102
104 return legendNodeIndex( row, column, nodeLegend );
105
107 if ( !n )
108 return QModelIndex(); // have no children
109
111 {
112 return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
113 }
114
115 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
116}
117
118
119QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
120{
121 if ( !child.isValid() )
122 return QModelIndex();
123
124 if ( QgsLayerTreeNode *n = index2node( child ) )
125 {
126 return indexOfParentLayerTreeNode( n->parent() ); // must not be null
127 }
128 else if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( child ) )
129 {
130 return legendParent( legendNode );
131 }
132 else
133 {
134 Q_ASSERT( false ); // no other node types!
135 return QModelIndex();
136 }
137
138}
139
140
142{
143 Q_ASSERT( parentNode );
144
145 QgsLayerTreeNode *grandParentNode = parentNode->parent();
146 if ( !grandParentNode )
147 return QModelIndex(); // root node -> invalid index
148
149 int row = grandParentNode->children().indexOf( parentNode );
150 Q_ASSERT( row >= 0 );
151
152 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
153}
154
155
156QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
157{
158 if ( !index.isValid() || index.column() > 1 )
159 return QVariant();
160
162 return legendNodeData( sym, role );
163
165 if ( role == Qt::DisplayRole || role == Qt::EditRole )
166 {
167 if ( QgsLayerTree::isGroup( node ) )
168 return QgsLayerTree::toGroup( node )->name();
169
170 if ( QgsLayerTree::isLayer( node ) )
171 {
172 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
173 QString name = nodeLayer->name();
174 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
175 if ( vlayer && nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
176 {
177 const bool estimatedCount = vlayer->dataProvider() ? QgsDataSourceUri( vlayer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
178 const qlonglong count = vlayer->featureCount();
179
180 // if you modify this line, please update QgsSymbolLegendNode::updateLabel
181 name += QStringLiteral( " [%1%2]" ).arg(
182 estimatedCount ? QStringLiteral( "≈" ) : QString(),
183 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
184 }
185 return name;
186 }
187 }
188 else if ( role == Qt::DecorationRole && index.column() == 0 )
189 {
190 if ( QgsLayerTree::isGroup( node ) )
191 return iconGroup();
192
193 if ( QgsLayerTree::isLayer( node ) )
194 {
195 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
196
197 QgsMapLayer *layer = nodeLayer->layer();
198 if ( !layer )
199 return QVariant();
200
201 // icons possibly overriding default icon
202 QIcon icon = QgsIconUtils::iconForLayer( layer );
203
204 // if there's just on legend entry that should be embedded in layer - do that!
205 if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
206 {
207 icon = legendIconEmbeddedInParent( nodeLayer );
208 }
209
210 if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
211 {
212 const int iconSize = scaleIconSize( 16 );
213 QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
214
215 QPainter painter( &pixmap );
216 painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
217 painter.end();
218
219 icon = QIcon( pixmap );
220 }
221
222 return icon;
223 }
224 }
225 else if ( role == Qt::CheckStateRole )
226 {
228 return QVariant();
229
230 if ( QgsLayerTree::isLayer( node ) )
231 {
232 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
233
234 if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
235 return QVariant(); // do not show checkbox for non-spatial tables
236
237 return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
238 }
239 else if ( QgsLayerTree::isGroup( node ) )
240 {
241 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
242 return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
243 }
244 }
245 else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
246 {
247 QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
248 if ( index == mCurrentIndex )
249 f.setUnderline( true );
250 if ( QgsLayerTree::isLayer( node ) )
251 {
252 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
253 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
254 {
255 f.setItalic( !f.italic() );
256 }
257 }
258 return f;
259 }
260 else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
261 {
262 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
263 if ( QgsLayerTree::isLayer( node ) )
264 {
265 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
266 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
267 {
268 QColor fadedTextColor = brush.color();
269 fadedTextColor.setAlpha( 128 );
270 brush.setColor( fadedTextColor );
271 }
272 }
273 return brush;
274 }
275 else if ( role == Qt::ToolTipRole )
276 {
277 if ( QgsLayerTree::isLayer( node ) )
278 {
279 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
280 {
281 QString title = !layer->metadata().title().isEmpty() ? layer->metadata().title() :
282 !layer->serverProperties()->title().isEmpty() ? layer->serverProperties()->title() :
283 !layer->serverProperties()->shortName().isEmpty() ? layer->serverProperties()->shortName() :
284 layer->name();
285
286 title = "<b>" + title.toHtmlEscaped() + "</b>";
287
288 if ( layer->isSpatial() && layer->crs().isValid() )
289 {
290 QString layerCrs = layer->crs().authid();
291 if ( !std::isnan( layer->crs().coordinateEpoch() ) )
292 {
293 layerCrs += QStringLiteral( " @ %1" ).arg( qgsDoubleToString( layer->crs().coordinateEpoch(), 3 ) );
294 }
295 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
296 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layerCrs ).toHtmlEscaped();
297 else
298 title += tr( " (%1)" ).arg( layerCrs ).toHtmlEscaped();
299 }
300
301 QStringList parts;
302 parts << title;
303
304 const QString abstract = !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
305 if ( !abstract.isEmpty() )
306 {
307 parts << QString();
308 const QStringList abstractLines = abstract.split( '\n' );
309 for ( const auto &l : abstractLines )
310 {
311 parts << l.toHtmlEscaped();
312 }
313 parts << QString();
314 }
315
316 QString source( layer->publicSource() );
317 if ( source.size() > 1024 )
318 {
319 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
320 }
321
322 parts << "<i>" + source.toHtmlEscaped() + "</i>";
323
324 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
325 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
326 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
327 if ( showFeatureCount && estimatedCount )
328 {
329 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
330 }
331
332 return parts.join( QLatin1String( "<br/>" ) );
333 }
334 }
335 }
336
337 return QVariant();
338}
339
340
341Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
342{
343 if ( !index.isValid() )
344 {
345 Qt::ItemFlags rootFlags = Qt::ItemFlags();
346 if ( testFlag( AllowNodeReorder ) )
347 rootFlags |= Qt::ItemIsDropEnabled;
348 return rootFlags;
349 }
350
352 return legendNodeFlags( symn );
353
354 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
355
356 if ( testFlag( AllowNodeRename ) )
357 f |= Qt::ItemIsEditable;
358
360 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
361
362 if ( testFlag( AllowNodeReorder ) )
363 {
364 // only root embedded nodes can be reordered
365 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
366 f |= Qt::ItemIsDragEnabled;
367 }
368
370 f |= Qt::ItemIsUserCheckable;
371
372 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
373 f |= Qt::ItemIsDropEnabled;
374
375 return f;
376}
377
378bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
379{
381 if ( sym )
382 {
383 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
384 return false;
385 bool res = sym->setData( value, role );
386 if ( res )
387 emit dataChanged( index, index );
388 return res;
389 }
390
392 if ( !node )
393 return QAbstractItemModel::setData( index, value, role );
394
395 if ( role == Qt::CheckStateRole )
396 {
398 return false;
399
400 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
401 if ( checked && node->children().isEmpty() )
402 {
404 }
405 else if ( testFlag( ActionHierarchical ) )
406 {
407 if ( node->children().isEmpty() )
409 else
410 node->setItemVisibilityCheckedRecursive( checked );
411 }
412 else
413 {
414 node->setItemVisibilityChecked( checked );
415 }
416
418
419 return true;
420 }
421 else if ( role == Qt::EditRole )
422 {
423 if ( !testFlag( AllowNodeRename ) )
424 return false;
425
426 if ( QgsLayerTree::isLayer( node ) )
427 {
429 layer->setName( value.toString() );
430 emit dataChanged( index, index );
431 }
432 else if ( QgsLayerTree::isGroup( node ) )
433 {
434 QgsLayerTree::toGroup( node )->setName( value.toString() );
435 emit dataChanged( index, index );
436 }
437 }
438
439 return QAbstractItemModel::setData( index, value, role );
440}
441
443{
444 if ( !node || !node->parent() )
445 return QModelIndex(); // this is the only root item -> invalid index
446
447 QModelIndex parentIndex = node2index( node->parent() );
448
449 int row = node->parent()->children().indexOf( node );
450 Q_ASSERT( row >= 0 );
451 return index( row, 0, parentIndex );
452}
453
454
455static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
456{
457 if ( !child->parent() )
458 return false;
459
460 if ( child->parent() == node )
461 return true;
462
463 return _isChildOfNode( child->parent(), node );
464}
465
466static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
467{
468 for ( QgsLayerTreeNode *n : nodes )
469 {
470 if ( _isChildOfNode( child, n ) )
471 return true;
472 }
473
474 return false;
475}
476
477
478QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
479{
480 QList<QgsLayerTreeNode *> nodes;
481 const auto constList = list;
482 for ( const QModelIndex &index : constList )
483 {
485 if ( !node )
486 continue;
487
488 nodes << node;
489 }
490
491 if ( !skipInternal )
492 return nodes;
493
494 // remove any children of nodes if both parent node and children are selected
495 QList<QgsLayerTreeNode *> nodesFinal;
496 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
497 {
498 if ( !_isChildOfNodes( node, nodes ) )
499 nodesFinal << node;
500 }
501
502 return nodesFinal;
503}
504
509
511{
512 beginResetModel();
513
515
516 Q_ASSERT( mLegend.isEmpty() );
517
518 mRootNode = newRootGroup;
519
520 endResetModel();
521
523}
524
526{
527 // update title
528 QModelIndex idx = node2index( nodeLayer );
529 emit dataChanged( idx, idx );
530
531 // update children
532 int oldNodeCount = rowCount( idx );
533 if ( oldNodeCount > 0 )
534 {
535 beginRemoveRows( idx, 0, oldNodeCount - 1 );
536 removeLegendFromLayer( nodeLayer );
537 endRemoveRows();
538 }
539
540 addLegendToLayer( nodeLayer );
541 int newNodeCount = rowCount( idx );
542
543 // automatic collapse of legend nodes - useful if a layer has many legend nodes
544 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
545 nodeLayer->setExpanded( false );
546}
547
549{
550 return mCurrentIndex;
551}
552
553void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
554{
556}
557
558
559void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
560{
561 if ( nodeType == QgsLayerTreeNode::NodeGroup )
562 {
563 if ( mFontGroup != font )
564 {
565 mFontGroup = font;
567 }
568 }
569 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
570 {
571 if ( mFontLayer != font )
572 {
573 mFontLayer = font;
575 }
576 }
577 else
578 {
579 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
580 }
581}
582
583
584QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
585{
586 if ( nodeType == QgsLayerTreeNode::NodeGroup )
587 return mFontGroup;
588 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
589 return mFontLayer;
590 else
591 {
592 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
593 return QFont();
594 }
595}
596
598{
599 mLegendFilterByScale = scale;
600
601 // this could be later done in more efficient way
602 // by just updating active legend nodes, without refreshing original legend nodes
603 const auto layers = mRootNode->findLayers();
604 for ( QgsLayerTreeLayer *nodeLayer : layers )
605 refreshLayerLegend( nodeLayer );
606}
607
609{
611 setLegendFilter( settings, /* useExtent = */ true );
613}
614
615void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
616{
617 if ( settings && settings->hasValidSettings() )
618 {
619 std::unique_ptr< QgsLayerTreeFilterSettings > filterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
620
621 if ( !useExtent ) // only expressions
622 {
624 }
625 else if ( polygon.type() == Qgis::GeometryType::Polygon )
626 {
628 }
629
630 if ( useExpressions )
631 {
633 }
634
636 }
637 else
638 {
639 setFilterSettings( nullptr );
640 }
641}
642
644{
645 return mFilterSettings ? &mFilterSettings->mapSettings() : nullptr;
646}
647
649{
650 if ( settings )
651 {
652 mFilterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
653 mFilterSettings->mapSettings().setLayerStyleOverrides( mLayerStyleOverrides );
654
655 bool hitTestWasRunning = false;
656 if ( mHitTestTask )
657 {
658 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
659 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
660 mHitTestTask->cancel();
661 mHitTestTask = nullptr;
662 hitTestWasRunning = true;
663 }
664
665 std::unique_ptr< QgsMapHitTest > blockingHitTest;
668 else
669 blockingHitTest = std::make_unique< QgsMapHitTest >( *mFilterSettings );
670
671 if ( mHitTestTask )
672 {
673 connect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
675
676 if ( !hitTestWasRunning )
677 emit hitTestStarted();
678 }
679 else
680 {
681 blockingHitTest->run();
682 mHitTestResults = blockingHitTest->results();
683 handleHitTestResults();
684 }
685 }
686 else
687 {
688 if ( !mFilterSettings )
689 return;
690
691 mFilterSettings.reset();
692 handleHitTestResults();
693 }
694}
695
700
701void QgsLayerTreeModel::handleHitTestResults()
702{
703 // temporarily disable autocollapse so that legend nodes stay visible
704 int bkAutoCollapse = autoCollapseLegendNodes();
706
707 // this could be later done in more efficient way
708 // by just updating active legend nodes, without refreshing original legend nodes
709 const auto layers = mRootNode->findLayers();
710 for ( QgsLayerTreeLayer *nodeLayer : layers )
711 refreshLayerLegend( nodeLayer );
712
713 setAutoCollapseLegendNodes( bkAutoCollapse );
714}
715
716void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
717{
718 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
719 return;
720
721 double previousScale = mLegendMapViewScale;
722 mLegendMapViewScale = scale;
723 mLegendMapViewMupp = mapUnitsPerPixel;
724 mLegendMapViewDpi = dpi;
725
726 // now invalidate legend nodes!
728
729 if ( scale != previousScale )
730 refreshScaleBasedLayers( QModelIndex(), previousScale );
731}
732
733void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
734{
735 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
736 if ( dpi ) *dpi = mLegendMapViewDpi;
737 if ( scale ) *scale = mLegendMapViewScale;
738}
739
740QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
741{
743}
744
745void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
746{
747 mLayerStyleOverrides = overrides;
748}
749
751{
752 if ( mTargetScreenProperties.contains( properties ) )
753 return;
754
755 mTargetScreenProperties.insert( properties );
756}
757
758QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
759{
761}
762
763int QgsLayerTreeModel::scaleIconSize( int standardSize )
764{
765 return QgsApplication::scaleIconSize( standardSize, true );
766}
767
769{
770 if ( mHitTestTask )
771 mHitTestTask->waitForFinished();
772}
773
775{
776 return static_cast< bool >( mHitTestTask );
777}
778
779void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
780{
781 beginInsertRows( node2index( node ), indexFrom, indexTo );
782}
783
784static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
785{
786 QList<QgsLayerTreeNode *> children = node->children();
787 QList<QgsLayerTreeLayer *> newLayerNodes;
788 for ( int i = indexFrom; i <= indexTo; ++i )
789 {
790 QgsLayerTreeNode *child = children.at( i );
791 if ( QgsLayerTree::isLayer( child ) )
792 newLayerNodes << QgsLayerTree::toLayer( child );
793 else if ( QgsLayerTree::isGroup( child ) )
794 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
795 }
796 return newLayerNodes;
797}
798
799void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
800{
801 Q_ASSERT( node );
802
803 endInsertRows();
804
805 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
806 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
807 connectToLayer( newLayerNode );
808}
809
810void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
811{
812 Q_ASSERT( node );
813
814 beginRemoveRows( node2index( node ), indexFrom, indexTo );
815
816 // disconnect from layers and remove their legend
817 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
818 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
819 disconnectFromLayer( nodeLayer );
820}
821
823{
824 endRemoveRows();
825}
826
828{
829 Q_ASSERT( node );
830
831 const QModelIndex index = node2index( node );
832 emit dataChanged( index, index );
833}
834
835void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
836{
837 Q_UNUSED( name )
838 Q_ASSERT( node );
839
840 const QModelIndex index = node2index( node );
841 emit dataChanged( index, index );
842}
843
844
846{
847 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
849}
850
851
853{
854 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
855 if ( !nodeLayer )
856 return;
857
858 // deferred connection to the layer
859 connectToLayer( nodeLayer );
860}
861
863{
864 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
865 if ( !nodeLayer )
866 return;
867
868 disconnectFromLayer( nodeLayer );
869
870 // wait for the layer to appear again
872}
873
875{
876 if ( !mRootNode )
877 return;
878
879 if ( !testFlag( ShowLegend ) )
880 return;
881
882 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
883 if ( !layer )
884 return;
885
886 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
887 if ( !nodeLayer )
888 return;
889
890 refreshLayerLegend( nodeLayer );
891}
892
894{
895 if ( !mRootNode )
896 return;
897
898 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
899 if ( !layer )
900 return;
901
902 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
903 if ( !nodeLayer )
904 return;
905
906 const QModelIndex index = node2index( nodeLayer );
907 emit dataChanged( index, index );
908}
909
911{
912 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
913 if ( !layer )
914 return;
915
916 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
917 if ( !nodeLayer )
918 return;
919
920 QModelIndex index = node2index( nodeLayer );
921 emit dataChanged( index, index );
922
923 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
924 refreshLayerLegend( nodeLayer );
925}
926
927
929{
930 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
931 if ( !legendNode )
932 return;
933
934 QModelIndex index = legendNode2index( legendNode );
935 if ( index.isValid() )
936 emit dataChanged( index, index );
937}
938
939void QgsLayerTreeModel::legendNodeSizeChanged()
940{
941 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
942 if ( !legendNode )
943 return;
944
945 QModelIndex index = legendNode2index( legendNode );
946 if ( index.isValid() )
947 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
948}
949
950void QgsLayerTreeModel::hitTestTaskCompleted()
951{
952 if ( mHitTestTask )
953 {
954 mHitTestResults = mHitTestTask->results();
955 handleHitTestResults();
956 emit hitTestCompleted();
957 }
958}
959
961{
962 if ( !nodeLayer->layer() )
963 {
964 // in order to connect to layer, we need to have it loaded.
965 // keep an eye on the layer ID: once loaded, we will use it
966 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
967 return;
968 }
969
970 // watch if the layer is getting removed
971 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
972
973 if ( testFlag( ShowLegend ) )
974 {
975 addLegendToLayer( nodeLayer );
976
977 // if we aren't loading a layer from a project, setup some nice default settings
978 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
979 {
980 // automatic collapse of legend nodes - useful if a layer has many legend nodes
982 nodeLayer->setExpanded( false );
983
985 {
986 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
987 }
988 }
989 }
990
991 QgsMapLayer *layer = nodeLayer->layer();
992 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
993 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
994
995 // using unique connection because there may be temporarily more nodes for a layer than just one
996 // which would create multiple connections, however disconnect() would disconnect all multiple connections
997 // even if we wanted to disconnect just one connection in each call.
998 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
999 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1000 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
1001
1002 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
1003}
1004
1005// try to find out if the layer ID is present in the tree multiple times
1006static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
1007{
1008 int count = 0;
1009 const auto constChildren = group->children();
1010 for ( QgsLayerTreeNode *child : constChildren )
1011 {
1012 if ( QgsLayerTree::isLayer( child ) )
1013 {
1014 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
1015 count++;
1016 }
1017 else if ( QgsLayerTree::isGroup( child ) )
1018 {
1019 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
1020 }
1021 }
1022 return count;
1023}
1024
1026{
1027 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
1028
1029 if ( !nodeLayer->layer() )
1030 return; // we were never connected
1031
1032 if ( testFlag( ShowLegend ) )
1033 {
1034 removeLegendFromLayer( nodeLayer );
1035 }
1036
1037 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
1038 {
1039 // last instance of the layer in the tree: disconnect from all signals from layer!
1040 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
1041 }
1042}
1043
1045{
1046 const auto constChildren = parentGroup->children();
1047 for ( QgsLayerTreeNode *node : constChildren )
1048 {
1049 if ( QgsLayerTree::isGroup( node ) )
1051 else if ( QgsLayerTree::isLayer( node ) )
1053 }
1054}
1055
1057{
1058 const auto constChildren = parentGroup->children();
1059 for ( QgsLayerTreeNode *node : constChildren )
1060 {
1061 if ( QgsLayerTree::isGroup( node ) )
1063 else if ( QgsLayerTree::isLayer( node ) )
1065 }
1066}
1067
1069{
1070 Q_ASSERT( mRootNode );
1071
1072 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1073 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1074 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1075 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1076 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1077 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1078 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1079
1081}
1082
1084{
1085 if ( mRootNode )
1086 {
1087 disconnect( mRootNode, nullptr, this, nullptr );
1089 }
1090}
1091
1093{
1094 QgsLayerTreeNode *node = index2node( idx );
1095 if ( !node )
1096 return;
1097
1098 int count = node->children().count();
1099 if ( count == 0 )
1100 return;
1101 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1102 for ( int i = 0; i < count; ++i )
1103 recursivelyEmitDataChanged( index( i, 0, idx ) );
1104}
1105
1106void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1107{
1108 QgsLayerTreeNode *node = index2node( idx );
1109 if ( !node )
1110 return;
1111
1112 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1113 {
1114 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1115 if ( layer && layer->hasScaleBasedVisibility() )
1116 {
1117 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1118 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1119 }
1120 }
1121 int count = node->children().count();
1122 for ( int i = 0; i < count; ++i )
1123 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1124}
1125
1127{
1128 return Qt::CopyAction | Qt::MoveAction;
1129}
1130
1132{
1133 QStringList types;
1134 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1135 return types;
1136}
1137
1138
1139QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1140{
1141 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1142 QModelIndexList sortedIndexes = indexes;
1143 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1144
1145 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1146
1147 if ( nodesFinal.isEmpty() )
1148 return nullptr;
1149
1150 QMimeData *mimeData = new QMimeData();
1151
1152 QDomDocument layerTreeDoc;
1153 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1154
1155 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1156 {
1157 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1158 }
1159 layerTreeDoc.appendChild( rootLayerTreeElem );
1160
1161 QString errorMessage;
1162 QgsReadWriteContext readWriteContext;
1163 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1164 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1165
1166 QString txt = layerDefinitionsDoc.toString();
1167
1168 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1169 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1170 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1171 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1172 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1173
1174 return mimeData;
1175}
1176
1177bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1178{
1179 if ( action == Qt::IgnoreAction )
1180 return true;
1181
1182 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1183 return false;
1184
1185 if ( column >= columnCount( parent ) )
1186 return false;
1187
1188 // don't accept drops from some layer tree subclasses to non-matching subclasses
1189 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1190 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1191 return false;
1192
1193 QgsLayerTreeNode *nodeParent = index2node( parent );
1194 if ( !QgsLayerTree::isGroup( nodeParent ) )
1195 return false;
1196
1197 if ( parent.isValid() && row == -1 )
1198 row = 0; // if dropped directly onto group item, insert at first position
1199
1200 // if we are coming from another QGIS instance, we need to add the layers too
1201 bool ok = false;
1202 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1203 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1204
1205 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1206 {
1207 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1208 QDomDocument layerDefinitionDoc;
1209 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1210 return false;
1211 QgsReadWriteContext context;
1212 QString errorMessage;
1213 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context ); // skip-keyword-check
1214 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1215 }
1216 else
1217 {
1218 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1219
1220 QDomDocument layerTreeDoc;
1221 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1222 return false;
1223
1224 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1225 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1226 return false;
1227
1228 QList<QgsLayerTreeNode *> nodes;
1229
1230 QDomElement elem = rootLayerTreeElem.firstChildElement();
1231 while ( !elem.isNull() )
1232 {
1233 QgsLayerTreeNode *node = QgsLayerTreeNode::readXml( elem, QgsProject::instance() ); // skip-keyword-check
1234 if ( node )
1235 nodes << node;
1236
1237 elem = elem.nextSiblingElement();
1238 }
1239
1240 if ( nodes.isEmpty() )
1241 return false;
1242
1243 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1244 }
1245 return true;
1246}
1247
1248bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1249{
1250 QgsLayerTreeNode *parentNode = index2node( parent );
1251 if ( QgsLayerTree::isGroup( parentNode ) )
1252 {
1253 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1254 return true;
1255 }
1256 return false;
1257}
1258
1263
1265{
1266 if ( on )
1267 mFlags |= f;
1268 else
1269 mFlags &= ~f;
1270}
1271
1276
1278{
1279 return mFlags.testFlag( f );
1280}
1281
1283{
1284 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1285}
1286
1287QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1288{
1289 QList<QgsLayerTreeModelLegendNode *> filtered;
1290
1291 if ( mLegendFilterByScale > 0 )
1292 {
1293 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1294 {
1295 if ( node->isScaleOK( mLegendFilterByScale ) )
1296 filtered << node;
1297 }
1298 }
1299 else if ( mFilterSettings )
1300 {
1301 if ( !nodes.isEmpty() && mFilterSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1302 {
1303 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1304 {
1305 const QgsLayerTreeModelLegendNode::NodeTypes nodeType = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) ).value<QgsLayerTreeModelLegendNode::NodeTypes>();
1306 switch ( nodeType )
1307 {
1309 filtered << node;
1310 break;
1311
1319 {
1320 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1321 const bool isDataDefinedSize = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::IsDataDefinedSize ) ).toBool();
1322 const bool checked = ( mFilterSettings && !( mFilterSettings->flags() & Qgis::LayerTreeFilterFlag::SkipVisibilityCheck ) )
1323 || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1324
1325 if ( checked )
1326 {
1327 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1328 {
1329 auto it = mHitTestResults.constFind( vl->id() );
1330 if ( it != mHitTestResults.constEnd() &&
1331 ( it->contains( ruleKey ) ||
1332 ( !it->isEmpty() && isDataDefinedSize )
1333 )
1334 )
1335 {
1336 filtered << node;
1337 }
1338 }
1339 else
1340 {
1341 filtered << node;
1342 }
1343 }
1344 else // unknown node type or unchecked
1345 filtered << node;
1346 break;
1347 }
1348 }
1349 }
1350 }
1351 }
1352 else
1353 {
1354 return nodes;
1355 }
1356
1357 return filtered;
1358}
1359
1360
1361
1363// Legend nodes routines - start
1364
1366{
1367 const auto constMLegend = mLegend;
1368 for ( const LayerLegendData &data : constMLegend )
1369 {
1370 qDeleteAll( data.originalNodes );
1371 delete data.tree;
1372 }
1373 mLegend.clear();
1374
1375 if ( mHitTestTask )
1376 {
1377 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
1378 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
1379 mHitTestTask->cancel();
1380 mHitTestTask = nullptr;
1381 }
1382}
1383
1384
1386{
1387 if ( mLegend.contains( nodeLayer ) )
1388 {
1389 qDeleteAll( mLegend[nodeLayer].originalNodes );
1390 delete mLegend[nodeLayer].tree;
1391 mLegend.remove( nodeLayer );
1392 }
1393}
1394
1395
1397{
1398 if ( !nodeL || !nodeL->layer() )
1399 return;
1400
1401 QgsMapLayer *ml = nodeL->layer();
1402
1403 QgsMapLayerStyleOverride styleOverride( ml );
1404 if ( mLayerStyleOverrides.contains( ml->id() ) )
1405 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1406
1407 QgsMapLayerLegend *layerLegend = ml->legend();
1408 if ( !layerLegend )
1409 return;
1410 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1411
1412 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1414
1416 {
1417 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1418 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1419 while ( widgetsCount > 0 )
1420 {
1421 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1422 --widgetsCount;
1423 }
1424 }
1425
1426 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1427
1428 const auto constLstNew = lstNew;
1429 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1430 {
1431 n->setParent( this );
1433 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1434 }
1435
1436 // See if we have an embedded node - if we do, we will not use it among active nodes.
1437 // Legend node embedded in parent does not have to be the first one,
1438 // there can be also nodes generated for embedded widgets
1439 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1440 const auto constFilteredLstNew = filteredLstNew;
1441 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1442 {
1443 if ( legendNode->isEmbeddedInParent() )
1444 {
1445 embeddedNode = legendNode;
1446 filteredLstNew.removeOne( legendNode );
1447 break;
1448 }
1449 }
1450
1451 LayerLegendTree *legendTree = nullptr;
1452
1453 // maybe the legend nodes form a tree - try to create a tree structure from the list
1454 if ( testFlag( ShowLegendAsTree ) )
1455 legendTree = tryBuildLegendTree( filteredLstNew );
1456
1457 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1458
1459 if ( !filteredLstNew.isEmpty() )
1460 {
1461 // Make sure it's clear
1462 const QModelIndex nodeIndex { node2index( nodeL ) };
1463 if ( rowCount( nodeIndex ) > 0 )
1464 {
1465 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1466 mLegend[nodeL] = LayerLegendData();
1467 endRemoveRows();
1468 }
1469 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1470 }
1471
1473 data.originalNodes = lstNew;
1474 data.activeNodes = filteredLstNew;
1475 data.embeddedNodeInParent = embeddedNode;
1476 data.tree = legendTree;
1477
1478 mLegend[nodeL] = data;
1479
1480 if ( !filteredLstNew.isEmpty() )
1481 {
1482 endInsertRows();
1483 }
1484
1485 // invalidate map based data even if the data is not map-based to make sure
1486 // the symbol sizes are computed at least once
1487 mInvalidatedNodes.insert( nodeL );
1489}
1490
1491
1492QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1493{
1494 // first check whether there are any legend nodes that are not top-level
1495 bool hasParentKeys = false;
1496 for ( QgsLayerTreeModelLegendNode *n : nodes )
1497 {
1498 if ( !n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString().isEmpty() )
1499 {
1500 hasParentKeys = true;
1501 break;
1502 }
1503 }
1504 if ( !hasParentKeys )
1505 return nullptr; // all legend nodes are top-level => stick with list representation
1506
1507 // make mapping from rules to nodes and do some sanity checks
1508 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1509 rule2node[QString()] = nullptr;
1510 for ( QgsLayerTreeModelLegendNode *n : nodes )
1511 {
1512 QString ruleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
1513 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1514 return nullptr;
1515 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1516 return nullptr;
1517 rule2node[ruleKey] = n;
1518 }
1519
1520 // create the tree structure
1521 LayerLegendTree *tree = new LayerLegendTree;
1522 for ( QgsLayerTreeModelLegendNode *n : nodes )
1523 {
1524 QString parentRuleKey = n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
1525 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1526 tree->parents[n] = parent;
1527 tree->children[parent] << n;
1528 }
1529 return tree;
1530}
1531
1533{
1534 double scale = 0.0;
1535 double mupp = 0.0;
1536 int dpi = 0;
1537 legendMapViewData( &mupp, &dpi, &scale );
1538 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1539
1540 // setup temporary render context
1541 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1542 context->setScaleFactor( dpi / 25.4 );
1543
1544 if ( !mTargetScreenProperties.isEmpty() )
1545 {
1546 mTargetScreenProperties.begin()->updateRenderContextForScreen( *context );
1547 }
1548
1549 context->setRendererScale( scale );
1550 context->setMapToPixel( QgsMapToPixel( mupp ) );
1552 return validData ? context.release() : nullptr;
1553}
1554
1555
1557{
1558 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1559}
1560
1561
1563{
1564 const LayerLegendData &data = mLegend[legendNode->layerNode()];
1565 if ( data.tree )
1566 {
1567 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1568 {
1569 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1570 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1571 return index( row, 0, parentIndex );
1572 }
1573 else
1574 {
1575 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1576 int row = data.tree->children[nullptr].indexOf( legendNode );
1577 return index( row, 0, parentIndex );
1578 }
1579 }
1580
1581 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1582 Q_ASSERT( parentIndex.isValid() );
1583 int row = data.activeNodes.indexOf( legendNode );
1584 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1585 return QModelIndex();
1586
1587 return index( row, 0, parentIndex );
1588}
1589
1590
1592{
1593 const LayerLegendData &data = mLegend[node->layerNode()];
1594 if ( data.tree )
1595 return data.tree->children[node].count();
1596
1597 return 0; // they are leaves
1598}
1599
1600
1602{
1603 if ( !mLegend.contains( nL ) )
1604 return 0;
1605
1606 const LayerLegendData &data = mLegend[nL];
1607 if ( data.tree )
1608 return data.tree->children[nullptr].count();
1609
1610 int count = data.activeNodes.count();
1611 return count;
1612}
1613
1614
1615QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1616{
1617 Q_ASSERT( mLegend.contains( nL ) );
1618 const LayerLegendData &data = mLegend[nL];
1619 if ( data.tree )
1620 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1621
1622 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1623}
1624
1625
1626QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1627{
1628 const LayerLegendData &data = mLegend[node->layerNode()];
1629 if ( data.tree )
1630 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1631
1632 return QModelIndex(); // have no children
1633}
1634
1635
1637{
1638 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1639 const LayerLegendData &data = mLegend[layerNode];
1640 if ( data.tree )
1641 {
1642 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1643 {
1644 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1645 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1646 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1647 }
1648 else
1649 return indexOfParentLayerTreeNode( layerNode );
1650 }
1651
1652 return indexOfParentLayerTreeNode( layerNode );
1653}
1654
1655
1657{
1658 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1659 return QVariant();
1660 return node->data( role );
1661}
1662
1663
1665{
1666 Qt::ItemFlags f = node->flags();
1668 f &= ~Qt::ItemIsUserCheckable;
1669 return f;
1670}
1671
1672
1674{
1675 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1676}
1677
1679{
1680 return mLegend[nodeLayer].embeddedNodeInParent;
1681}
1682
1683
1685{
1686 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1687 if ( !legendNode )
1688 return QIcon();
1689 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1690}
1691
1692
1693QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1694{
1695 if ( !mLegend.contains( nodeLayer ) )
1696 return QList<QgsLayerTreeModelLegendNode *>();
1697
1698 const LayerLegendData &data = mLegend[nodeLayer];
1699 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1700 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1701 lst.prepend( data.embeddedNodeInParent );
1702 return lst;
1703}
1704
1705QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1706{
1707 return mLegend.value( nodeLayer ).originalNodes;
1708}
1709
1710QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1711{
1712 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1713 {
1714 QgsLayerTreeLayer *layer = it.key();
1715 if ( layer->layerId() == layerId )
1716 {
1717 const auto activeNodes = mLegend.value( layer ).activeNodes;
1718 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1719 {
1720 if ( legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString() == ruleKey )
1721 {
1722 //found it!
1723 return legendNode;
1724 }
1725 }
1726 }
1727 }
1728
1729 return nullptr;
1730}
1731
1739
1741{
1742 // we have varying icon sizes, and we want icon to be centered and
1743 // text to be left aligned, so we have to compute the max width of icons
1744 //
1745 // we do that for nodes which share a common parent
1746 //
1747 // we do that here because for symbols with size defined in map units
1748 // the symbol sizes changes depends on the zoom level
1749
1750 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1751
1752 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1753 {
1754 const LayerLegendData &data = mLegend.value( layerNode );
1755
1756 QList<QgsSymbolLegendNode *> symbolNodes;
1757 QMap<QString, int> widthMax;
1758 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1759 {
1760 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1761 if ( n )
1762 {
1763 const QSize sz( n->minimumIconSize( context.get() ) );
1764 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1765 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1766 n->setIconSize( sz );
1767 symbolNodes.append( n );
1768 }
1769 }
1770 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1771 {
1772 const QString parentKey( n->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString() );
1773 Q_ASSERT( widthMax[parentKey] > 0 );
1774 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1775 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1776 }
1777 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1778 legendNode->invalidateMapBasedData();
1779 }
1780
1781 mInvalidatedNodes.clear();
1782}
1783
1784// Legend nodes routines - end
@ UsersCannotToggleEditing
Indicates that users are not allowed to toggle editing for this layer. Note that this does not imply ...
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
@ Polygon
Polygons.
@ Vector
Vector layer.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
static int scaleIconSize(int standardSize, bool applyDevicePixelRatio=false)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
bool useEstimatedMetadata() const
Returns true if estimated metadata should be used for the connection.
A geometry is the spatial representation of a feature.
Qgis::GeometryType type
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, Qgis::LayerTreeInsertionMethod insertMethod=Qgis::LayerTreeInsertionMethod::OptimalInInsertionGroup, const QgsLayerTreeRegistryBridge::InsertionPoint *insertPoint=nullptr)
Loads the QLR at path into QGIS.
static bool exportLayerDefinition(const QString &path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Exports the selected layer tree nodes to a QLR file.
Contains settings relating to filtering the contents of QgsLayerTreeModel and views.
void setFilterPolygon(const QgsGeometry &polygon)
Sets the optional filter polygon, used when testing for symbols to show in the legend.
void setLayerFilterExpressionsFromLayerTree(QgsLayerTree *tree)
Sets layer filter expressions using a layer tree.
void setFlags(Qgis::LayerTreeFilterFlags flags)
Sets the filter flags.
Layer tree group node serves as a container for layers and further groups.
void setName(const QString &n) override
Sets the group's name.
QString name() const override
Returns the group's name.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
void removeChildren(int from, int count)
Remove child nodes from index "from".
Layer tree node points to a map layer.
QString layerId() const
Returns the ID for the map layer associated with this node.
void layerWillBeUnloaded()
Emitted when a previously available layer got unloaded (from layer registry).
void setName(const QString &n) override
Sets the layer's name.
QString name() const override
Returns the layer's name.
void layerLoaded()
Emitted when a previously unavailable layer got loaded.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ EmbeddedWidget
Embedded widget placeholder node type.
@ SymbolLegend
Vector symbol legend node type.
@ ParentRuleKey
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ NodeType
Type of node. Added in 3.16.
@ RuleKey
Rule key of the node (QString)
@ IsDataDefinedSize
Set when a node is related to data defined size (title or separated legend items)....
virtual void invalidateMapBasedData()
Notification from model that information from associated map view has changed.
void sizeChanged()
Emitted when the size of this node changes.
void dataChanged()
Emitted on internal data change so the layer tree model can forward the signal to views.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
void connectToLayer(QgsLayerTreeLayer *nodeLayer)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
Flags flags() const
Returns OR-ed combination of model flags.
double mLegendFilterByScale
scale denominator for filtering of legend nodes (<= 0 means no filtering)
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
int autoCollapseLegendNodes() const
Returns at what number of legend nodes the layer node should be collapsed. -1 means no auto-collapse ...
void setFilterSettings(const QgsLayerTreeFilterSettings *settings=nullptr)
Sets the filter settings to use to filter legend nodes.
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
Q_DECL_DEPRECATED void setLegendFilterByMap(const QgsMapSettings *settings)
Force only display of legend nodes which are valid for given map settings.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
void setLegendFilterByScale(double scale)
Force only display of legend nodes which are valid for a given scale.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
std::unique_ptr< QgsLayerTreeFilterSettings > mFilterSettings
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setLegendMapViewData(double mapUnitsPerPixel, int dpi, double scale)
Give the layer tree model hints about the currently associated map view so that legend nodes that use...
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
QgsLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
void connectToLayers(QgsLayerTreeGroup *parentGroup)
QMap< QString, QSet< QString > > mHitTestResults
QModelIndex parent(const QModelIndex &child) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QModelIndex legendParent(QgsLayerTreeModelLegendNode *legendNode) const
void setLayerTreeNodeFont(int nodeType, const QFont &font)
Sets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
int legendNodeRowCount(QgsLayerTreeModelLegendNode *node) const
void nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
void setAutoCollapseLegendNodes(int nodeCount)
Sets at what number of legend nodes the layer node should be collapsed. Setting -1 disables the auto-...
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale) const
Gets hints about map view - to be used in legend nodes.
QModelIndex currentIndex() const
Gets index of the item marked as current. Item marked as current is underlined.
bool hitTestInProgress() const
Returns true if a hit test for visible legend items is currently in progress.
void recursivelyEmitDataChanged(const QModelIndex &index=QModelIndex())
emit dataChanged() for layer tree node items
bool legendEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
void disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
QIcon legendIconEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
void layerFlagsChanged()
Emitted when layer flags have changed.
void nodeNameChanged(QgsLayerTreeNode *node, const QString &name)
Updates model when node's name has changed.
QPersistentModelIndex mCurrentIndex
Current index - will be underlined.
LayerLegendTree * tryBuildLegendTree(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Qt::DropActions supportedDropActions() const override
QModelIndex legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary render context.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
void nodeWillRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QgsLayerTree * mRootNode
Pointer to the root node of the layer tree. Not owned by the model.
QSet< QgsScreenProperties > targetScreenProperties() const
Returns the target screen properties to use when generating icons.
QStringList mimeTypes() const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QSet< QgsScreenProperties > mTargetScreenProperties
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QPointer< QgsMapHitTestTask > mHitTestTask
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QList< QgsLayerTreeModelLegendNode * > filterLegendNodes(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Filter nodes from QgsMapLayerLegend according to the current filtering rules.
const QgsLayerTreeFilterSettings * filterSettings() const
Returns the filter settings to use to filter legend nodes.
QList< QgsLayerTreeModelLegendNode * > layerOriginalLegendNodes(QgsLayerTreeLayer *nodeLayer)
Returns original (unfiltered) list of legend nodes attached to a particular layer node.
int mAutoCollapseLegendNodesCount
Minimal number of nodes when legend should be automatically collapsed. -1 = disabled.
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
Qt::ItemFlags legendNodeFlags(QgsLayerTreeModelLegendNode *node) const
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
void nodeVisibilityChanged(QgsLayerTreeNode *node)
void refreshScaleBasedLayers(const QModelIndex &index=QModelIndex(), double previousScale=0.0)
Updates layer data for scale dependent layers, should be called when map scale changes.
void removeLegendFromLayer(QgsLayerTreeLayer *nodeLayer)
QModelIndex legendRootIndex(int row, int column, QgsLayerTreeLayer *nL) const
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
QMap< QString, QString > mLayerStyleOverrides
Overrides of map layers' styles: key = layer ID, value = style XML.
void nodeWillAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
QFont layerTreeNodeFont(int nodeType) const
Gets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
void waitForHitTestBlocking()
When a current hit test for visible legend items is in progress, calling this method will block until...
void addLegendToLayer(QgsLayerTreeLayer *nodeL)
bool testFlag(Flag f) const
Check whether a flag is enabled.
void disconnectFromLayers(QgsLayerTreeGroup *parentGroup)
QModelIndex indexOfParentLayerTreeNode(QgsLayerTreeNode *parentNode) const
QSet< QgsLayerTreeLayer * > mInvalidatedNodes
Keep track of layer nodes for which the legend size needs to be recalculated.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node)
@ AllowNodeChangeVisibility
Allow user to set node visibility with a checkbox.
@ ShowLegendAsTree
For legends that support it, will show them in a tree instead of a list (needs also ShowLegend)....
@ UseTextFormatting
Layer nodes will alter text appearance based on layer properties, such as scale based visibility.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ ShowLegend
Add legend nodes for layer nodes.
@ DeferredLegendInvalidation
Defer legend model invalidation.
@ AllowNodeRename
Allow renaming of groups and layers.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
@ UseThreadedHitTest
Run legend hit tests in a background thread.
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
Flags mFlags
Sets of flags for the model.
Q_DECL_DEPRECATED void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
int legendRootRowCount(QgsLayerTreeLayer *nL) const
QgsLayerTreeModelLegendNode * findLegendNode(const QString &layerId, const QString &ruleKey) const
Searches through the layer tree to find a legend node with a matching layer ID and rule key.
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void willRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be removed from a node within the tree.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
void willAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be added to a node within the tree.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
virtual void setItemVisibilityCheckedRecursive(bool checked)
Check or uncheck a node and all its children (taking into account exclusion rules)
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
Namespace with helper functions for layer tree operations.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Executes a QgsMapHitTest in a background thread.
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode * > &nodes)
update according to layer node's custom properties (order of items, user labels for items)
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Returns list of legend nodes to be used for a particular layer tree layer node.
Restore overridden layer style on destruction.
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Base class for all map layer types.
Definition qgsmaplayer.h:76
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void legendChanged()
Signal emitted when legend of the layer has changed.
QgsMapLayerLegend * legend() const
Can be nullptr.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void editingStarted()
Emitted when editing on this layer has started.
QString id
Definition qgsmaplayer.h:79
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
Qgis::LayerType type
Definition qgsmaplayer.h:86
virtual Qgis::MapLayerProperties properties() const
Returns the map layer properties of this layer.
virtual bool isEditable() const
Returns true if the layer can be edited.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual bool isModified() const
Returns true if the layer has been modified since last commit/save.
void flagsChanged()
Emitted when layer's flags have been modified.
void layerModified()
Emitted when modifications has been done on layer.
The QgsMapSettings class contains configuration for rendering of the map.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
Perform transforms between map coordinates and device coordinates.
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
Stores properties relating to a screen.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool * settingsLayerTreeShowFeatureCountForNewLayers
Settings entry show feature counts for newly added layers by default.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
void setIconSize(QSize sz)
Set the icon size.
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
static QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6535
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:5875
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6534
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5958
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Structure that stores all data associated with one map layer.
LayerLegendTree * tree
Optional pointer to a tree structure - see LayerLegendTree for details.
QList< QgsLayerTreeModelLegendNode * > originalNodes
Data structure for storage of legend nodes.
Structure that stores tree representation of map layer's legend.
QMap< QgsLayerTreeModelLegendNode *, QgsLayerTreeModelLegendNode * > parents
Pointer to parent for each active node. Top-level nodes have nullptr parent. Pointers are not owned.
QMap< QgsLayerTreeModelLegendNode *, QList< QgsLayerTreeModelLegendNode * > > children
List of children for each active node. Top-level nodes are under nullptr key. Pointers are not owned.