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