QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgslayertreemodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreemodel.cpp
3  --------------------------------------
4  Date : May 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QMimeData>
17 #include <QTextStream>
18 
19 #include "qgslayertreemodel.h"
20 
21 #include "qgsapplication.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
25 #include "qgsproject.h"
26 #include "qgsmaphittest.h"
27 #include "qgsmaplayer.h"
28 #include "qgsmaplayerlegend.h"
30 #include "qgspluginlayer.h"
31 #include "qgsrasterlayer.h"
32 #include "qgsrenderer.h"
33 #include "qgssymbollayerutils.h"
34 #include "qgsvectorlayer.h"
35 #include "qgslayerdefinition.h"
36 #include "qgsiconutils.h"
37 #include "qgsmimedatautils.h"
38 
39 #include <QPalette>
40 
42  : QAbstractItemModel( parent )
43  , mRootNode( rootNode )
44  , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
45  , mAutoCollapseLegendNodesCount( -1 )
46  , mLegendFilterByScale( 0 )
47  , mLegendFilterUsesExtent( false )
48  , mLegendMapViewMupp( 0 )
49  , mLegendMapViewDpi( 0 )
50  , mLegendMapViewScale( 0 )
51 {
53 
54  mFontLayer.setBold( true );
55 
57  mDeferLegendInvalidationTimer.setSingleShot( true );
58 }
59 
61 {
62  legendCleanup();
63 }
64 
65 QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
66 {
67  if ( !index.isValid() )
68  return mRootNode;
69 
70  QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
71  return qobject_cast<QgsLayerTreeNode *>( obj );
72 }
73 
74 
75 int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
76 {
78  return legendNodeRowCount( nodeLegend );
79 
81  if ( !n )
82  return 0;
83 
84  if ( QgsLayerTree::isLayer( n ) )
85  {
86  if ( !testFlag( ShowLegend ) )
87  return 0;
88 
90  }
91 
92  return n->children().count();
93 }
94 
95 int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
96 {
97  Q_UNUSED( parent )
98  return 1;
99 }
100 
101 QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
102 {
103  if ( column < 0 || column >= columnCount( parent ) ||
104  row < 0 || row >= rowCount( parent ) )
105  return QModelIndex();
106 
107  if ( QgsLayerTreeModelLegendNode *nodeLegend = index2legendNode( parent ) )
108  return legendNodeIndex( row, column, nodeLegend );
109 
111  if ( !n )
112  return QModelIndex(); // have no children
113 
114  if ( testFlag( ShowLegend ) && QgsLayerTree::isLayer( n ) )
115  {
116  return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
117  }
118 
119  return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
120 }
121 
122 
123 QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
124 {
125  if ( !child.isValid() )
126  return QModelIndex();
127 
128  if ( QgsLayerTreeNode *n = index2node( child ) )
129  {
130  return indexOfParentLayerTreeNode( n->parent() ); // must not be null
131  }
133  {
134  return legendParent( legendNode );
135  }
136  else
137  {
138  Q_ASSERT( false ); // no other node types!
139  return QModelIndex();
140  }
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 
160 QVariant 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 
168  QgsLayerTreeNode *node = index2node( index );
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  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
179  {
180  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
181  if ( vlayer && vlayer->featureCount() >= 0 )
182  name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
183  }
184  return name;
185  }
186  }
187  else if ( role == Qt::DecorationRole && index.column() == 0 )
188  {
189  if ( QgsLayerTree::isGroup( node ) )
190  return iconGroup();
191 
192  if ( QgsLayerTree::isLayer( node ) )
193  {
194  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
195 
196  QgsMapLayer *layer = nodeLayer->layer();
197  if ( !layer )
198  return QVariant();
199 
200  // icons possibly overriding default icon
201  QIcon icon;
202 
203  switch ( layer->type() )
204  {
206  icon = QgsIconUtils::iconRaster();
207  break;
208 
210  icon = QgsIconUtils::iconMesh();
211  break;
212 
215  break;
216 
219  break;
220 
222  {
223  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
224  if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry )
225  icon = QgsIconUtils::iconPoint();
226  else if ( vlayer->geometryType() == QgsWkbTypes::LineGeometry )
227  icon = QgsIconUtils::iconLine();
228  else if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry )
229  icon = QgsIconUtils::iconPolygon();
230  else if ( vlayer->geometryType() == QgsWkbTypes::NullGeometry )
231  icon = QgsIconUtils::iconTable();
232  else
234  break;
235  }
236 
239  break;
240  }
241 
242  // if there's just on legend entry that should be embedded in layer - do that!
243  if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
244  {
245  icon = legendIconEmbeddedInParent( nodeLayer );
246  }
247 
248  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
249  if ( vlayer && vlayer->isEditable() && testFlag( UseTextFormatting ) )
250  {
251  const int iconSize = scaleIconSize( 16 );
252  QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
253 
254  QPainter painter( &pixmap );
255  painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( vlayer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
256  painter.end();
257 
258  icon = QIcon( pixmap );
259  }
260 
261  return icon;
262  }
263  }
264  else if ( role == Qt::CheckStateRole )
265  {
267  return QVariant();
268 
269  if ( QgsLayerTree::isLayer( node ) )
270  {
271  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
272 
273  if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
274  return QVariant(); // do not show checkbox for non-spatial tables
275 
276  return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
277  }
278  else if ( QgsLayerTree::isGroup( node ) )
279  {
280  QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
281  return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
282  }
283  }
284  else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
285  {
286  QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
287  if ( index == mCurrentIndex )
288  f.setUnderline( true );
289  if ( QgsLayerTree::isLayer( node ) )
290  {
291  const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
292  if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
293  {
294  f.setItalic( !f.italic() );
295  }
296  }
297  return f;
298  }
299  else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
300  {
301  QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
302  if ( QgsLayerTree::isLayer( node ) )
303  {
304  const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
305  if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
306  {
307  QColor fadedTextColor = brush.color();
308  fadedTextColor.setAlpha( 128 );
309  brush.setColor( fadedTextColor );
310  }
311  }
312  return brush;
313  }
314  else if ( role == Qt::ToolTipRole )
315  {
316  if ( QgsLayerTree::isLayer( node ) )
317  {
318  if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
319  {
320  QString title =
321  !layer->title().isEmpty() ? layer->title() :
322  !layer->shortName().isEmpty() ? layer->shortName() :
323  layer->name();
324 
325  title = "<b>" + title.toHtmlEscaped() + "</b>";
326 
327  if ( layer->isSpatial() && layer->crs().isValid() )
328  {
329  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
330  title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
331  else
332  title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
333  }
334 
335  QStringList parts;
336  parts << title;
337 
338  if ( !layer->abstract().isEmpty() )
339  {
340  parts << QString();
341  const QStringList abstractLines = layer->abstract().split( '\n' );
342  for ( const auto &l : abstractLines )
343  {
344  parts << l.toHtmlEscaped();
345  }
346  parts << QString();
347  }
348 
349  QString source( layer->publicSource() );
350  if ( source.size() > 1024 )
351  {
352  source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
353  }
354 
355  parts << "<i>" + source.toHtmlEscaped() + "</i>";
356 
357  return parts.join( QLatin1String( "<br/>" ) );
358  }
359  }
360  }
361 
362  return QVariant();
363 }
364 
365 
366 Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
367 {
368  if ( !index.isValid() )
369  {
370  Qt::ItemFlags rootFlags = Qt::ItemFlags();
371  if ( testFlag( AllowNodeReorder ) )
372  rootFlags |= Qt::ItemIsDropEnabled;
373  return rootFlags;
374  }
375 
377  return legendNodeFlags( symn );
378 
379  Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
380 
381  if ( testFlag( AllowNodeRename ) )
382  f |= Qt::ItemIsEditable;
383 
384  QgsLayerTreeNode *node = index2node( index );
385  bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
386 
387  if ( testFlag( AllowNodeReorder ) )
388  {
389  // only root embedded nodes can be reordered
390  if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
391  f |= Qt::ItemIsDragEnabled;
392  }
393 
395  f |= Qt::ItemIsUserCheckable;
396 
397  if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
398  f |= Qt::ItemIsDropEnabled;
399 
400  return f;
401 }
402 
403 bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
404 {
406  if ( sym )
407  {
408  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
409  return false;
410  bool res = sym->setData( value, role );
411  if ( res )
412  emit dataChanged( index, index );
413  return res;
414  }
415 
416  QgsLayerTreeNode *node = index2node( index );
417  if ( !node )
418  return QAbstractItemModel::setData( index, value, role );
419 
420  if ( role == Qt::CheckStateRole )
421  {
423  return false;
424 
425  bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
426  if ( checked && node->children().isEmpty() )
427  {
429  }
430  else if ( testFlag( ActionHierarchical ) )
431  {
432  if ( node->children().isEmpty() )
434  else
435  node->setItemVisibilityCheckedRecursive( checked );
436  }
437  else
438  {
439  node->setItemVisibilityChecked( checked );
440  }
441 
443 
444  return true;
445  }
446  else if ( role == Qt::EditRole )
447  {
448  if ( !testFlag( AllowNodeRename ) )
449  return false;
450 
451  if ( QgsLayerTree::isLayer( node ) )
452  {
453  QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
454  layer->setName( value.toString() );
455  emit dataChanged( index, index );
456  }
457  else if ( QgsLayerTree::isGroup( node ) )
458  {
459  QgsLayerTree::toGroup( node )->setName( value.toString() );
460  emit dataChanged( index, index );
461  }
462  }
463 
464  return QAbstractItemModel::setData( index, value, role );
465 }
466 
468 {
469  if ( !node || !node->parent() )
470  return QModelIndex(); // this is the only root item -> invalid index
471 
472  QModelIndex parentIndex = node2index( node->parent() );
473 
474  int row = node->parent()->children().indexOf( node );
475  Q_ASSERT( row >= 0 );
476  return index( row, 0, parentIndex );
477 }
478 
479 
480 static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
481 {
482  if ( !child->parent() )
483  return false;
484 
485  if ( child->parent() == node )
486  return true;
487 
488  return _isChildOfNode( child->parent(), node );
489 }
490 
491 static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
492 {
493  for ( QgsLayerTreeNode *n : nodes )
494  {
495  if ( _isChildOfNode( child, n ) )
496  return true;
497  }
498 
499  return false;
500 }
501 
502 
503 QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
504 {
505  QList<QgsLayerTreeNode *> nodes;
506  const auto constList = list;
507  for ( const QModelIndex &index : constList )
508  {
509  QgsLayerTreeNode *node = index2node( index );
510  if ( !node )
511  continue;
512 
513  nodes << node;
514  }
515 
516  if ( !skipInternal )
517  return nodes;
518 
519  // remove any children of nodes if both parent node and children are selected
520  QList<QgsLayerTreeNode *> nodesFinal;
521  for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
522  {
523  if ( !_isChildOfNodes( node, nodes ) )
524  nodesFinal << node;
525  }
526 
527  return nodesFinal;
528 }
529 
531 {
532  return mRootNode;
533 }
534 
536 {
537  beginResetModel();
538 
540 
541  Q_ASSERT( mLegend.isEmpty() );
542 
543  mRootNode = newRootGroup;
544 
545  endResetModel();
546 
548 }
549 
551 {
552  // update title
553  QModelIndex idx = node2index( nodeLayer );
554  emit dataChanged( idx, idx );
555 
556  // update children
557  int oldNodeCount = rowCount( idx );
558  if ( oldNodeCount > 0 )
559  {
560  beginRemoveRows( idx, 0, oldNodeCount - 1 );
561  removeLegendFromLayer( nodeLayer );
562  endRemoveRows();
563  }
564 
565  addLegendToLayer( nodeLayer );
566  int newNodeCount = rowCount( idx );
567 
568  // automatic collapse of legend nodes - useful if a layer has many legend nodes
569  if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
570  nodeLayer->setExpanded( false );
571 }
572 
574 {
575  return mCurrentIndex;
576 }
577 
578 void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
579 {
581 }
582 
583 
584 void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
585 {
586  if ( nodeType == QgsLayerTreeNode::NodeGroup )
587  {
588  if ( mFontGroup != font )
589  {
590  mFontGroup = font;
592  }
593  }
594  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
595  {
596  if ( mFontLayer != font )
597  {
598  mFontLayer = font;
600  }
601  }
602  else
603  {
604  QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
605  }
606 }
607 
608 
609 QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
610 {
611  if ( nodeType == QgsLayerTreeNode::NodeGroup )
612  return mFontGroup;
613  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
614  return mFontLayer;
615  else
616  {
617  QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
618  return QFont();
619  }
620 }
621 
623 {
624  mLegendFilterByScale = scale;
625 
626  // this could be later done in more efficient way
627  // by just updating active legend nodes, without refreshing original legend nodes
628  const auto layers = mRootNode->findLayers();
629  for ( QgsLayerTreeLayer *nodeLayer : layers )
630  refreshLayerLegend( nodeLayer );
631 }
632 
634 {
635  setLegendFilter( settings, /* useExtent = */ true );
636 }
637 
638 void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
639 {
640  if ( settings && settings->hasValidSettings() )
641  {
642  mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
643  mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
645  mLegendFilterUsesExtent = useExtent;
646  // collect expression filters
647  if ( useExpressions )
648  {
649  const auto layers = mRootNode->findLayers();
650  for ( QgsLayerTreeLayer *nodeLayer : layers )
651  {
652  bool enabled;
653  QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
654  if ( enabled && !expr.isEmpty() )
655  {
656  exprs[ nodeLayer->layerId()] = expr;
657  }
658  }
659  }
660  bool polygonValid = !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry;
661  if ( useExpressions && !useExtent && !polygonValid ) // only expressions
662  {
664  }
665  else
666  {
667  mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, polygon, exprs ) );
668  }
669  mLegendFilterHitTest->run();
670  }
671  else
672  {
674  return; // no change
675 
676  mLegendFilterMapSettings.reset();
677  mLegendFilterHitTest.reset();
678  }
679 
680  // temporarily disable autocollapse so that legend nodes stay visible
681  int bkAutoCollapse = autoCollapseLegendNodes();
683 
684  // this could be later done in more efficient way
685  // by just updating active legend nodes, without refreshing original legend nodes
686  const auto layers = mRootNode->findLayers();
687  for ( QgsLayerTreeLayer *nodeLayer : layers )
688  refreshLayerLegend( nodeLayer );
689 
690  setAutoCollapseLegendNodes( bkAutoCollapse );
691 }
692 
693 void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
694 {
695  if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
696  return;
697 
698  double previousScale = mLegendMapViewScale;
699  mLegendMapViewScale = scale;
700  mLegendMapViewMupp = mapUnitsPerPixel;
701  mLegendMapViewDpi = dpi;
702 
703  // now invalidate legend nodes!
705 
706  if ( scale != previousScale )
707  refreshScaleBasedLayers( QModelIndex(), previousScale );
708 }
709 
710 void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
711 {
712  if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
713  if ( dpi ) *dpi = mLegendMapViewDpi;
714  if ( scale ) *scale = mLegendMapViewScale;
715 }
716 
717 QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
718 {
719  return mLayerStyleOverrides;
720 }
721 
722 void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
723 {
724  mLayerStyleOverrides = overrides;
725 }
726 
727 int QgsLayerTreeModel::scaleIconSize( int standardSize )
728 {
729  return QgsApplication::scaleIconSize( standardSize, true );
730 }
731 
732 void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
733 {
734  beginInsertRows( node2index( node ), indexFrom, indexTo );
735 }
736 
737 static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
738 {
739  QList<QgsLayerTreeNode *> children = node->children();
740  QList<QgsLayerTreeLayer *> newLayerNodes;
741  for ( int i = indexFrom; i <= indexTo; ++i )
742  {
743  QgsLayerTreeNode *child = children.at( i );
744  if ( QgsLayerTree::isLayer( child ) )
745  newLayerNodes << QgsLayerTree::toLayer( child );
746  else if ( QgsLayerTree::isGroup( child ) )
747  newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
748  }
749  return newLayerNodes;
750 }
751 
752 void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
753 {
754  Q_ASSERT( node );
755 
756  endInsertRows();
757 
758  const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
759  for ( QgsLayerTreeLayer *newLayerNode : subNodes )
760  connectToLayer( newLayerNode );
761 }
762 
763 void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
764 {
765  Q_ASSERT( node );
766 
767  beginRemoveRows( node2index( node ), indexFrom, indexTo );
768 
769  // disconnect from layers and remove their legend
770  const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
771  for ( QgsLayerTreeLayer *nodeLayer : subNodes )
772  disconnectFromLayer( nodeLayer );
773 }
774 
776 {
777  endRemoveRows();
778 }
779 
781 {
782  Q_ASSERT( node );
783 
784  const QModelIndex index = node2index( node );
785  emit dataChanged( index, index );
786 }
787 
788 void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
789 {
790  Q_UNUSED( name )
791  Q_ASSERT( node );
792 
793  const QModelIndex index = node2index( node );
794  emit dataChanged( index, index );
795 }
796 
797 
799 {
800  if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
802 }
803 
804 
806 {
807  QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
808  if ( !nodeLayer )
809  return;
810 
811  // deferred connection to the layer
812  connectToLayer( nodeLayer );
813 }
814 
816 {
817  QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
818  if ( !nodeLayer )
819  return;
820 
821  disconnectFromLayer( nodeLayer );
822 
823  // wait for the layer to appear again
825 }
826 
828 {
829  if ( !mRootNode )
830  return;
831 
832  if ( !testFlag( ShowLegend ) )
833  return;
834 
835  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
836  if ( !layer )
837  return;
838 
839  QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
840  if ( !nodeLayer )
841  return;
842 
843  refreshLayerLegend( nodeLayer );
844 }
845 
847 {
848  if ( !mRootNode )
849  return;
850 
851  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
852  if ( !layer )
853  return;
854 
855  QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
856  if ( !nodeLayer )
857  return;
858 
859  const QModelIndex index = node2index( nodeLayer );
860  emit dataChanged( index, index );
861 }
862 
864 {
865  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
866  if ( !layer )
867  return;
868 
869  QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
870  if ( !nodeLayer )
871  return;
872 
873  QModelIndex index = node2index( nodeLayer );
874  emit dataChanged( index, index );
875 
876  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
877  refreshLayerLegend( nodeLayer );
878 }
879 
880 
882 {
883  QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
884  if ( !legendNode )
885  return;
886 
887  QModelIndex index = legendNode2index( legendNode );
888  if ( index.isValid() )
889  emit dataChanged( index, index );
890 }
891 
892 void QgsLayerTreeModel::legendNodeSizeChanged()
893 {
894  QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
895  if ( !legendNode )
896  return;
897 
898  QModelIndex index = legendNode2index( legendNode );
899  if ( index.isValid() )
900  emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
901 }
902 
903 
905 {
906  if ( !nodeLayer->layer() )
907  {
908  // in order to connect to layer, we need to have it loaded.
909  // keep an eye on the layer ID: once loaded, we will use it
910  connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
911  return;
912  }
913 
914  // watch if the layer is getting removed
915  connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
916 
917  if ( testFlag( ShowLegend ) )
918  {
919  addLegendToLayer( nodeLayer );
920 
921  // automatic collapse of legend nodes - useful if a layer has many legend nodes
922  if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
923  {
925  nodeLayer->setExpanded( false );
926  }
927  }
928 
929  QgsMapLayer *layer = nodeLayer->layer();
930  connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
931  connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
932 
933  if ( layer->type() == QgsMapLayerType::VectorLayer )
934  {
935  // using unique connection because there may be temporarily more nodes for a layer than just one
936  // which would create multiple connections, however disconnect() would disconnect all multiple connections
937  // even if we wanted to disconnect just one connection in each call.
938  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
939  connect( vl, &QgsVectorLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
940  connect( vl, &QgsVectorLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
941  connect( vl, &QgsVectorLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
942  }
943 
944  emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
945 }
946 
947 // try to find out if the layer ID is present in the tree multiple times
948 static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
949 {
950  int count = 0;
951  const auto constChildren = group->children();
952  for ( QgsLayerTreeNode *child : constChildren )
953  {
954  if ( QgsLayerTree::isLayer( child ) )
955  {
956  if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
957  count++;
958  }
959  else if ( QgsLayerTree::isGroup( child ) )
960  {
961  count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
962  }
963  }
964  return count;
965 }
966 
968 {
969  disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
970 
971  if ( !nodeLayer->layer() )
972  return; // we were never connected
973 
974  if ( testFlag( ShowLegend ) )
975  {
976  removeLegendFromLayer( nodeLayer );
977  }
978 
979  if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
980  {
981  // last instance of the layer in the tree: disconnect from all signals from layer!
982  disconnect( nodeLayer->layer(), nullptr, this, nullptr );
983  }
984 }
985 
987 {
988  const auto constChildren = parentGroup->children();
989  for ( QgsLayerTreeNode *node : constChildren )
990  {
991  if ( QgsLayerTree::isGroup( node ) )
993  else if ( QgsLayerTree::isLayer( node ) )
995  }
996 }
997 
999 {
1000  const auto constChildren = parentGroup->children();
1001  for ( QgsLayerTreeNode *node : constChildren )
1002  {
1003  if ( QgsLayerTree::isGroup( node ) )
1005  else if ( QgsLayerTree::isLayer( node ) )
1007  }
1008 }
1009 
1011 {
1012  Q_ASSERT( mRootNode );
1013 
1014  connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1015  connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1016  connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1017  connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1018  connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1019  connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1020  connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1021 
1023 }
1024 
1026 {
1027  disconnect( mRootNode, nullptr, this, nullptr );
1028 
1030 }
1031 
1033 {
1034  QgsLayerTreeNode *node = index2node( idx );
1035  if ( !node )
1036  return;
1037 
1038  int count = node->children().count();
1039  if ( count == 0 )
1040  return;
1041  emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1042  for ( int i = 0; i < count; ++i )
1043  recursivelyEmitDataChanged( index( i, 0, idx ) );
1044 }
1045 
1046 void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1047 {
1048  QgsLayerTreeNode *node = index2node( idx );
1049  if ( !node )
1050  return;
1051 
1052  if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1053  {
1054  const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1055  if ( layer && layer->hasScaleBasedVisibility() )
1056  {
1057  if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1058  emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1059  }
1060  }
1061  int count = node->children().count();
1062  for ( int i = 0; i < count; ++i )
1063  refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1064 }
1065 
1067 {
1068  return Qt::CopyAction | Qt::MoveAction;
1069 }
1070 
1071 QStringList QgsLayerTreeModel::mimeTypes() const
1072 {
1073  QStringList types;
1074  types << QStringLiteral( "application/qgis.layertreemodeldata" );
1075  return types;
1076 }
1077 
1078 
1079 QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1080 {
1081  // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1082  QModelIndexList sortedIndexes = indexes;
1083  std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1084 
1085  QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1086 
1087  if ( nodesFinal.isEmpty() )
1088  return nullptr;
1089 
1090  QMimeData *mimeData = new QMimeData();
1091 
1092  QDomDocument layerTreeDoc;
1093  QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1094 
1095  for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1096  {
1097  node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1098  }
1099  layerTreeDoc.appendChild( rootLayerTreeElem );
1100 
1101  QString errorMessage;
1102  QgsReadWriteContext readWriteContext;
1103  QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1104  QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1105 
1106  QString txt = layerDefinitionsDoc.toString();
1107 
1108  mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1109  mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1110  mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1111  mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1112 
1113  return mimeData;
1114 }
1115 
1116 bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1117 {
1118  if ( action == Qt::IgnoreAction )
1119  return true;
1120 
1121  if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1122  return false;
1123 
1124  if ( column >= columnCount( parent ) )
1125  return false;
1126 
1127  QgsLayerTreeNode *nodeParent = index2node( parent );
1128  if ( !QgsLayerTree::isGroup( nodeParent ) )
1129  return false;
1130 
1131  if ( parent.isValid() && row == -1 )
1132  row = 0; // if dropped directly onto group item, insert at first position
1133 
1134  // if we are coming from another QGIS instance, we need to add the layers too
1135  bool ok = false;
1136  // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1137  qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1138 
1139  if ( ok && qgisPid != QCoreApplication::applicationPid() )
1140  {
1141  QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1142  QDomDocument layerDefinitionDoc;
1143  if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1144  return false;
1145  QgsReadWriteContext context;
1146  QString errorMessage;
1147  QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1148  emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1149  }
1150  else
1151  {
1152  QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1153 
1154  QDomDocument layerTreeDoc;
1155  if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1156  return false;
1157 
1158  QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1159  if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1160  return false;
1161 
1162  QList<QgsLayerTreeNode *> nodes;
1163 
1164  QDomElement elem = rootLayerTreeElem.firstChildElement();
1165  while ( !elem.isNull() )
1166  {
1168  if ( node )
1169  nodes << node;
1170 
1171  elem = elem.nextSiblingElement();
1172  }
1173 
1174  if ( nodes.isEmpty() )
1175  return false;
1176 
1177  QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1178  }
1179  return true;
1180 }
1181 
1182 bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1183 {
1184  QgsLayerTreeNode *parentNode = index2node( parent );
1185  if ( QgsLayerTree::isGroup( parentNode ) )
1186  {
1187  QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1188  return true;
1189  }
1190  return false;
1191 }
1192 
1193 void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1194 {
1195  mFlags = f;
1196 }
1197 
1199 {
1200  if ( on )
1201  mFlags |= f;
1202  else
1203  mFlags &= ~f;
1204 }
1205 
1206 QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1207 {
1208  return mFlags;
1209 }
1210 
1212 {
1213  return mFlags.testFlag( f );
1214 }
1215 
1217 {
1218  return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1219 }
1220 
1221 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1222 {
1223  QList<QgsLayerTreeModelLegendNode *> filtered;
1224 
1225  if ( mLegendFilterByScale > 0 )
1226  {
1227  for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1228  {
1229  if ( node->isScaleOK( mLegendFilterByScale ) )
1230  filtered << node;
1231  }
1232  }
1233  else if ( mLegendFilterMapSettings )
1234  {
1235  if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1236  {
1237  for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1238  {
1240  {
1242  filtered << node;
1243  break;
1244 
1252  {
1253  const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1254  bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1255  if ( checked )
1256  {
1257  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1258  {
1259  if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1260  filtered << node;
1261  }
1262  else
1263  {
1264  filtered << node;
1265  }
1266  }
1267  else // unknown node type or unchecked
1268  filtered << node;
1269  break;
1270  }
1271  }
1272  }
1273  }
1274  }
1275  else
1276  {
1277  return nodes;
1278  }
1279 
1280  return filtered;
1281 }
1282 
1283 
1284 
1286 // Legend nodes routines - start
1287 
1289 {
1290  const auto constMLegend = mLegend;
1291  for ( const LayerLegendData &data : constMLegend )
1292  {
1293  qDeleteAll( data.originalNodes );
1294  delete data.tree;
1295  }
1296  mLegend.clear();
1297 }
1298 
1299 
1301 {
1302  if ( mLegend.contains( nodeLayer ) )
1303  {
1304  qDeleteAll( mLegend[nodeLayer].originalNodes );
1305  delete mLegend[nodeLayer].tree;
1306  mLegend.remove( nodeLayer );
1307  }
1308 }
1309 
1310 
1312 {
1313  if ( !nodeL || !nodeL->layer() )
1314  return;
1315 
1316  QgsMapLayer *ml = nodeL->layer();
1317 
1318  QgsMapLayerStyleOverride styleOverride( ml );
1319  if ( mLayerStyleOverrides.contains( ml->id() ) )
1320  styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1321 
1322  QgsMapLayerLegend *layerLegend = ml->legend();
1323  if ( !layerLegend )
1324  return;
1325  QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1326 
1327  // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1329 
1330  if ( testFlag( UseEmbeddedWidgets ) )
1331  {
1332  // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1333  int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1334  while ( widgetsCount > 0 )
1335  {
1336  lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1337  --widgetsCount;
1338  }
1339  }
1340 
1341  QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1342 
1343  const auto constLstNew = lstNew;
1344  for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1345  {
1346  n->setParent( this );
1348  connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1349  }
1350 
1351  // See if we have an embedded node - if we do, we will not use it among active nodes.
1352  // Legend node embedded in parent does not have to be the first one,
1353  // there can be also nodes generated for embedded widgets
1354  QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1355  const auto constFilteredLstNew = filteredLstNew;
1356  for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1357  {
1358  if ( legendNode->isEmbeddedInParent() )
1359  {
1360  embeddedNode = legendNode;
1361  filteredLstNew.removeOne( legendNode );
1362  break;
1363  }
1364  }
1365 
1366  LayerLegendTree *legendTree = nullptr;
1367 
1368  // maybe the legend nodes form a tree - try to create a tree structure from the list
1369  if ( testFlag( ShowLegendAsTree ) )
1370  legendTree = tryBuildLegendTree( filteredLstNew );
1371 
1372  int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1373 
1374  if ( !filteredLstNew.isEmpty() )
1375  {
1376  // Make sure it's clear
1377  const QModelIndex nodeIndex { node2index( nodeL ) };
1378  if ( rowCount( nodeIndex ) > 0 )
1379  {
1380  beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1381  mLegend[nodeL] = LayerLegendData();
1382  endRemoveRows();
1383  }
1384  beginInsertRows( node2index( nodeL ), 0, count - 1 );
1385  }
1386 
1388  data.originalNodes = lstNew;
1389  data.activeNodes = filteredLstNew;
1390  data.embeddedNodeInParent = embeddedNode;
1391  data.tree = legendTree;
1392 
1393  mLegend[nodeL] = data;
1394 
1395  if ( !filteredLstNew.isEmpty() )
1396  {
1397  endInsertRows();
1398  }
1399 
1400  // invalidate map based data even if the data is not map-based to make sure
1401  // the symbol sizes are computed at least once
1402  mInvalidatedNodes.insert( nodeL );
1404 }
1405 
1406 
1407 QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1408 {
1409  // first check whether there are any legend nodes that are not top-level
1410  bool hasParentKeys = false;
1411  for ( QgsLayerTreeModelLegendNode *n : nodes )
1412  {
1413  if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1414  {
1415  hasParentKeys = true;
1416  break;
1417  }
1418  }
1419  if ( !hasParentKeys )
1420  return nullptr; // all legend nodes are top-level => stick with list representation
1421 
1422  // make mapping from rules to nodes and do some sanity checks
1423  QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1424  rule2node[QString()] = nullptr;
1425  for ( QgsLayerTreeModelLegendNode *n : nodes )
1426  {
1427  QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1428  if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1429  return nullptr;
1430  if ( rule2node.contains( ruleKey ) ) // and they must be unique
1431  return nullptr;
1432  rule2node[ruleKey] = n;
1433  }
1434 
1435  // create the tree structure
1436  LayerLegendTree *tree = new LayerLegendTree;
1437  for ( QgsLayerTreeModelLegendNode *n : nodes )
1438  {
1439  QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1440  QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1441  tree->parents[n] = parent;
1442  tree->children[parent] << n;
1443  }
1444  return tree;
1445 }
1446 
1447 QgsRenderContext *QgsLayerTreeModel::createTemporaryRenderContext() const
1448 {
1449  double scale = 0.0;
1450  double mupp = 0.0;
1451  int dpi = 0;
1452  legendMapViewData( &mupp, &dpi, &scale );
1453  bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1454 
1455  // setup temporary render context
1456  std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1457  context->setScaleFactor( dpi / 25.4 );
1458  context->setRendererScale( scale );
1459  context->setMapToPixel( QgsMapToPixel( mupp ) );
1460  context->setFlag( QgsRenderContext::RenderSymbolPreview );
1461  return validData ? context.release() : nullptr;
1462 }
1463 
1464 
1466 {
1467  return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1468 }
1469 
1470 
1472 {
1474  if ( data.tree )
1475  {
1476  if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1477  {
1478  QModelIndex parentIndex = legendNode2index( parentLegendNode );
1479  int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1480  return index( row, 0, parentIndex );
1481  }
1482  else
1483  {
1484  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1485  int row = data.tree->children[nullptr].indexOf( legendNode );
1486  return index( row, 0, parentIndex );
1487  }
1488  }
1489 
1490  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1491  Q_ASSERT( parentIndex.isValid() );
1492  int row = data.activeNodes.indexOf( legendNode );
1493  if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1494  return QModelIndex();
1495 
1496  return index( row, 0, parentIndex );
1497 }
1498 
1499 
1501 {
1502  const LayerLegendData &data = mLegend[node->layerNode()];
1503  if ( data.tree )
1504  return data.tree->children[node].count();
1505 
1506  return 0; // they are leaves
1507 }
1508 
1509 
1511 {
1512  if ( !mLegend.contains( nL ) )
1513  return 0;
1514 
1515  const LayerLegendData &data = mLegend[nL];
1516  if ( data.tree )
1517  return data.tree->children[nullptr].count();
1518 
1519  int count = data.activeNodes.count();
1520  return count;
1521 }
1522 
1523 
1524 QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1525 {
1526  Q_ASSERT( mLegend.contains( nL ) );
1527  const LayerLegendData &data = mLegend[nL];
1528  if ( data.tree )
1529  return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1530 
1531  return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1532 }
1533 
1534 
1535 QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1536 {
1537  const LayerLegendData &data = mLegend[node->layerNode()];
1538  if ( data.tree )
1539  return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1540 
1541  return QModelIndex(); // have no children
1542 }
1543 
1544 
1546 {
1547  QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1548  const LayerLegendData &data = mLegend[layerNode];
1549  if ( data.tree )
1550  {
1551  if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1552  {
1553  QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1554  int row = data.tree->children[grandParentNode].indexOf( parentNode );
1555  return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1556  }
1557  else
1558  return indexOfParentLayerTreeNode( layerNode );
1559  }
1560 
1561  return indexOfParentLayerTreeNode( layerNode );
1562 }
1563 
1564 
1566 {
1567  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1568  return QVariant();
1569  return node->data( role );
1570 }
1571 
1572 
1574 {
1575  Qt::ItemFlags f = node->flags();
1576  if ( !testFlag( AllowLegendChangeState ) )
1577  f &= ~Qt::ItemIsUserCheckable;
1578  return f;
1579 }
1580 
1581 
1583 {
1584  return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1585 }
1586 
1588 {
1589  return mLegend[nodeLayer].embeddedNodeInParent;
1590 }
1591 
1592 
1594 {
1595  QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1596  if ( !legendNode )
1597  return QIcon();
1598  return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1599 }
1600 
1601 
1602 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1603 {
1604  if ( !mLegend.contains( nodeLayer ) )
1605  return QList<QgsLayerTreeModelLegendNode *>();
1606 
1607  const LayerLegendData &data = mLegend[nodeLayer];
1608  QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1609  if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1610  lst.prepend( data.embeddedNodeInParent );
1611  return lst;
1612 }
1613 
1614 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1615 {
1616  return mLegend.value( nodeLayer ).originalNodes;
1617 }
1618 
1619 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1620 {
1621  for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1622  {
1623  QgsLayerTreeLayer *layer = it.key();
1624  if ( layer->layerId() == layerId )
1625  {
1626  const auto activeNodes = mLegend.value( layer ).activeNodes;
1627  for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1628  {
1629  if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1630  {
1631  //found it!
1632  return legendNode;
1633  }
1634  }
1635  }
1636  }
1637 
1638  return nullptr;
1639 }
1640 
1642 {
1645  else
1646  mDeferLegendInvalidationTimer.start( 10 );
1647 }
1648 
1650 {
1651  // we have varying icon sizes, and we want icon to be centered and
1652  // text to be left aligned, so we have to compute the max width of icons
1653  //
1654  // we do that for nodes which share a common parent
1655  //
1656  // we do that here because for symbols with size defined in map units
1657  // the symbol sizes changes depends on the zoom level
1658 
1659  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1660 
1661  for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1662  {
1663  const LayerLegendData &data = mLegend.value( layerNode );
1664 
1665  QList<QgsSymbolLegendNode *> symbolNodes;
1666  QMap<QString, int> widthMax;
1667  for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1668  {
1669  QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1670  if ( n )
1671  {
1672  const QSize sz( n->minimumIconSize( context.get() ) );
1673  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1674  widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1675  n->setIconSize( sz );
1676  symbolNodes.append( n );
1677  }
1678  }
1679  for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1680  {
1681  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1682  Q_ASSERT( widthMax[parentKey] > 0 );
1683  const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1684  n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1685  }
1686  for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1688  }
1689 
1690  mInvalidatedNodes.clear();
1691 }
1692 
1693 // Legend nodes routines - end
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.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
static QIcon iconLine()
Returns an icon representing line geometries.
static QIcon iconPolygon()
Returns an icon representing polygon geometries.
static QIcon iconDefaultLayer()
Returns a default icon for layers, which aren't the standard raster/vector/...
static QIcon iconPointCloud()
Returns an icon representing point cloud layers.
static QIcon iconPoint()
Returns an icon representing point geometries.
static QIcon iconTable()
Returns an icon representing non-spatial layers (tables).
static QIcon iconRaster()
Returns an icon representing raster layers.
static QIcon iconMesh()
Returns an icon representing mesh layers.
static QIcon iconVectorTile()
Returns an icon representing vector tile layers.
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage)
Loads the QLR at path into QGIS. New layers are added to given project into layer tree specified by r...
static bool exportLayerDefinition(QString path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Export the selected layer tree nodes to a QLR file.
Layer tree group node serves as a container for layers and further groups.
void setName(const QString &n) override
Sets the group's name.
QString name() const override
Returns the group's name.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
void removeChildren(int from, int count)
Remove child nodes from index "from".
Layer tree node points to a map layer.
QString layerId() const
Returns the ID for the map layer associated with this node.
void layerWillBeUnloaded()
Emitted when a previously available layer got unloaded (from layer registry).
void setName(const QString &n) override
Sets the layer's name.
QString name() const override
Returns the layer's name.
void layerLoaded()
Emitted when a previously unavailable layer got loaded.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ EmbeddedWidget
Embedded widget placeholder node type.
@ ColorRampLegend
Color ramp legend (since QGIS 3.18)
@ SymbolLegend
Vector symbol legend node type.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
virtual void invalidateMapBasedData()
Notification from model that information from associated map view has changed.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
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.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
QTimer mDeferLegendInvalidationTimer
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 ...
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
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.
~QgsLayerTreeModel() override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
static QIcon iconGroup()
std::unique_ptr< QgsMapSettings > mLegendFilterMapSettings
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)
std::unique_ptr< QgsMapHitTest > mLegendFilterHitTest
QModelIndex parent(const QModelIndex &child) const override
bool mLegendFilterUsesExtent
whether to use map filtering
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.
void recursivelyEmitDataChanged(const QModelIndex &index=QModelIndex())
emit dataChanged() for layer tree node items
bool legendEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
void disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
QIcon legendIconEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
void layerFlagsChanged()
Emitted when layer flags have changed.
void nodeNameChanged(QgsLayerTreeNode *node, const QString &name)
Updates model when node's name has changed.
QPersistentModelIndex mCurrentIndex
Current index - will be underlined.
LayerLegendTree * tryBuildLegendTree(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Qt::DropActions supportedDropActions() const override
QModelIndex legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const
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.
QStringList mimeTypes() const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QList< QgsLayerTreeModelLegendNode * > filterLegendNodes(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Filter nodes from QgsMapLayerLegend according to the current filtering rules.
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)
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 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)....
Flags mFlags
Sets of flags for the model.
void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
int legendRootRowCount(QgsLayerTreeLayer *nL) const
QgsLayerTreeModelLegendNode * findLegendNode(const QString &layerId, const QString &ruleKey) const
Searches through the layer tree to find a legend node with a matching layer ID and rule key.
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
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.
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.
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.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void willAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be added to a node within the tree.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
virtual void setItemVisibilityCheckedRecursive(bool checked)
Check or uncheck a node and all its children (taking into account exclusion rules)
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
static QString legendFilterByExpression(const QgsLayerTreeLayer &layer, bool *enabled=nullptr)
Returns the expression filter of a legend layer.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Class that runs a hit test with given map settings.
Definition: qgsmaphittest.h:38
QMap< QString, QString > LayerFilterExpression
Maps an expression string to a layer id.
Definition: qgsmaphittest.h:41
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode * > &nodes)
update according to layer node's custom properties (order of items, user labels for items)
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Returns list of legend nodes to be used for a particular layer tree layer node.
Restore overridden layer style on destruction.
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Base class for all map layer types.
Definition: qgsmaplayer.h:70
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.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:77
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:287
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
void flagsChanged()
Emitted when layer's flags have been modified.
The QgsMapSettings class contains configuration for rendering of the map.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
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.
Definition: qgsproject.cpp:467
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
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.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
void editingStarted()
Emitted when editing on this layer has started.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
void layerModified()
Emitted when modifications has been done on layer.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:598
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
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.