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