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