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