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