QGIS API Documentation  3.25.0-Master (dec16ba68b)
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.layerdefinitions" ), txt.toUtf8() );
1075  mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1076 
1077  return mimeData;
1078 }
1079 
1080 bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1081 {
1082  if ( action == Qt::IgnoreAction )
1083  return true;
1084 
1085  if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1086  return false;
1087 
1088  if ( column >= columnCount( parent ) )
1089  return false;
1090 
1091  QgsLayerTreeNode *nodeParent = index2node( parent );
1092  if ( !QgsLayerTree::isGroup( nodeParent ) )
1093  return false;
1094 
1095  if ( parent.isValid() && row == -1 )
1096  row = 0; // if dropped directly onto group item, insert at first position
1097 
1098  // if we are coming from another QGIS instance, we need to add the layers too
1099  bool ok = false;
1100  // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1101  qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1102 
1103  if ( ok && qgisPid != QCoreApplication::applicationPid() )
1104  {
1105  QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1106  QDomDocument layerDefinitionDoc;
1107  if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1108  return false;
1109  QgsReadWriteContext context;
1110  QString errorMessage;
1111  QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1112  emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1113  }
1114  else
1115  {
1116  QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1117 
1118  QDomDocument layerTreeDoc;
1119  if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1120  return false;
1121 
1122  QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1123  if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1124  return false;
1125 
1126  QList<QgsLayerTreeNode *> nodes;
1127 
1128  QDomElement elem = rootLayerTreeElem.firstChildElement();
1129  while ( !elem.isNull() )
1130  {
1132  if ( node )
1133  nodes << node;
1134 
1135  elem = elem.nextSiblingElement();
1136  }
1137 
1138  if ( nodes.isEmpty() )
1139  return false;
1140 
1141  QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1142  }
1143  return true;
1144 }
1145 
1146 bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1147 {
1148  QgsLayerTreeNode *parentNode = index2node( parent );
1149  if ( QgsLayerTree::isGroup( parentNode ) )
1150  {
1151  QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1152  return true;
1153  }
1154  return false;
1155 }
1156 
1157 void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1158 {
1159  mFlags = f;
1160 }
1161 
1163 {
1164  if ( on )
1165  mFlags |= f;
1166  else
1167  mFlags &= ~f;
1168 }
1169 
1170 QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1171 {
1172  return mFlags;
1173 }
1174 
1176 {
1177  return mFlags.testFlag( f );
1178 }
1179 
1181 {
1182  return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1183 }
1184 
1185 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1186 {
1187  QList<QgsLayerTreeModelLegendNode *> filtered;
1188 
1189  if ( mLegendFilterByScale > 0 )
1190  {
1191  for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1192  {
1193  if ( node->isScaleOK( mLegendFilterByScale ) )
1194  filtered << node;
1195  }
1196  }
1197  else if ( mLegendFilterMapSettings )
1198  {
1199  if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1200  {
1201  for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1202  {
1204  {
1206  filtered << node;
1207  break;
1208 
1216  {
1217  const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1218  bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1219  if ( checked )
1220  {
1221  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1222  {
1223  if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1224  filtered << node;
1225  }
1226  else
1227  {
1228  filtered << node;
1229  }
1230  }
1231  else // unknown node type or unchecked
1232  filtered << node;
1233  break;
1234  }
1235  }
1236  }
1237  }
1238  }
1239  else
1240  {
1241  return nodes;
1242  }
1243 
1244  return filtered;
1245 }
1246 
1247 
1248 
1250 // Legend nodes routines - start
1251 
1253 {
1254  const auto constMLegend = mLegend;
1255  for ( const LayerLegendData &data : constMLegend )
1256  {
1257  qDeleteAll( data.originalNodes );
1258  delete data.tree;
1259  }
1260  mLegend.clear();
1261 }
1262 
1263 
1265 {
1266  if ( mLegend.contains( nodeLayer ) )
1267  {
1268  qDeleteAll( mLegend[nodeLayer].originalNodes );
1269  delete mLegend[nodeLayer].tree;
1270  mLegend.remove( nodeLayer );
1271  }
1272 }
1273 
1274 
1276 {
1277  if ( !nodeL || !nodeL->layer() )
1278  return;
1279 
1280  QgsMapLayer *ml = nodeL->layer();
1281 
1282  QgsMapLayerStyleOverride styleOverride( ml );
1283  if ( mLayerStyleOverrides.contains( ml->id() ) )
1284  styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1285 
1286  QgsMapLayerLegend *layerLegend = ml->legend();
1287  if ( !layerLegend )
1288  return;
1289  QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1290 
1291  // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1293 
1294  if ( testFlag( UseEmbeddedWidgets ) )
1295  {
1296  // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1297  int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1298  while ( widgetsCount > 0 )
1299  {
1300  lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1301  --widgetsCount;
1302  }
1303  }
1304 
1305  QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1306 
1307  const auto constLstNew = lstNew;
1308  for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1309  {
1310  n->setParent( this );
1312  connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1313  }
1314 
1315  // See if we have an embedded node - if we do, we will not use it among active nodes.
1316  // Legend node embedded in parent does not have to be the first one,
1317  // there can be also nodes generated for embedded widgets
1318  QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1319  const auto constFilteredLstNew = filteredLstNew;
1320  for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1321  {
1322  if ( legendNode->isEmbeddedInParent() )
1323  {
1324  embeddedNode = legendNode;
1325  filteredLstNew.removeOne( legendNode );
1326  break;
1327  }
1328  }
1329 
1330  LayerLegendTree *legendTree = nullptr;
1331 
1332  // maybe the legend nodes form a tree - try to create a tree structure from the list
1333  if ( testFlag( ShowLegendAsTree ) )
1334  legendTree = tryBuildLegendTree( filteredLstNew );
1335 
1336  int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1337 
1338  if ( !filteredLstNew.isEmpty() )
1339  {
1340  // Make sure it's clear
1341  const QModelIndex nodeIndex { node2index( nodeL ) };
1342  if ( rowCount( nodeIndex ) > 0 )
1343  {
1344  beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1345  mLegend[nodeL] = LayerLegendData();
1346  endRemoveRows();
1347  }
1348  beginInsertRows( node2index( nodeL ), 0, count - 1 );
1349  }
1350 
1352  data.originalNodes = lstNew;
1353  data.activeNodes = filteredLstNew;
1354  data.embeddedNodeInParent = embeddedNode;
1355  data.tree = legendTree;
1356 
1357  mLegend[nodeL] = data;
1358 
1359  if ( !filteredLstNew.isEmpty() )
1360  {
1361  endInsertRows();
1362  }
1363 
1364  // invalidate map based data even if the data is not map-based to make sure
1365  // the symbol sizes are computed at least once
1366  mInvalidatedNodes.insert( nodeL );
1368 }
1369 
1370 
1371 QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1372 {
1373  // first check whether there are any legend nodes that are not top-level
1374  bool hasParentKeys = false;
1375  for ( QgsLayerTreeModelLegendNode *n : nodes )
1376  {
1377  if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1378  {
1379  hasParentKeys = true;
1380  break;
1381  }
1382  }
1383  if ( !hasParentKeys )
1384  return nullptr; // all legend nodes are top-level => stick with list representation
1385 
1386  // make mapping from rules to nodes and do some sanity checks
1387  QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1388  rule2node[QString()] = nullptr;
1389  for ( QgsLayerTreeModelLegendNode *n : nodes )
1390  {
1391  QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1392  if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1393  return nullptr;
1394  if ( rule2node.contains( ruleKey ) ) // and they must be unique
1395  return nullptr;
1396  rule2node[ruleKey] = n;
1397  }
1398 
1399  // create the tree structure
1400  LayerLegendTree *tree = new LayerLegendTree;
1401  for ( QgsLayerTreeModelLegendNode *n : nodes )
1402  {
1403  QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1404  QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1405  tree->parents[n] = parent;
1406  tree->children[parent] << n;
1407  }
1408  return tree;
1409 }
1410 
1412 {
1413  double scale = 0.0;
1414  double mupp = 0.0;
1415  int dpi = 0;
1416  legendMapViewData( &mupp, &dpi, &scale );
1417  bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1418 
1419  // setup temporary render context
1420  std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1421  context->setScaleFactor( dpi / 25.4 );
1422  context->setRendererScale( scale );
1423  context->setMapToPixel( QgsMapToPixel( mupp ) );
1425  return validData ? context.release() : nullptr;
1426 }
1427 
1428 
1430 {
1431  return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1432 }
1433 
1434 
1436 {
1438  if ( data.tree )
1439  {
1440  if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1441  {
1442  QModelIndex parentIndex = legendNode2index( parentLegendNode );
1443  int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1444  return index( row, 0, parentIndex );
1445  }
1446  else
1447  {
1448  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1449  int row = data.tree->children[nullptr].indexOf( legendNode );
1450  return index( row, 0, parentIndex );
1451  }
1452  }
1453 
1454  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1455  Q_ASSERT( parentIndex.isValid() );
1456  int row = data.activeNodes.indexOf( legendNode );
1457  if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1458  return QModelIndex();
1459 
1460  return index( row, 0, parentIndex );
1461 }
1462 
1463 
1465 {
1466  const LayerLegendData &data = mLegend[node->layerNode()];
1467  if ( data.tree )
1468  return data.tree->children[node].count();
1469 
1470  return 0; // they are leaves
1471 }
1472 
1473 
1475 {
1476  if ( !mLegend.contains( nL ) )
1477  return 0;
1478 
1479  const LayerLegendData &data = mLegend[nL];
1480  if ( data.tree )
1481  return data.tree->children[nullptr].count();
1482 
1483  int count = data.activeNodes.count();
1484  return count;
1485 }
1486 
1487 
1488 QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1489 {
1490  Q_ASSERT( mLegend.contains( nL ) );
1491  const LayerLegendData &data = mLegend[nL];
1492  if ( data.tree )
1493  return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1494 
1495  return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1496 }
1497 
1498 
1499 QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1500 {
1501  const LayerLegendData &data = mLegend[node->layerNode()];
1502  if ( data.tree )
1503  return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1504 
1505  return QModelIndex(); // have no children
1506 }
1507 
1508 
1510 {
1511  QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1512  const LayerLegendData &data = mLegend[layerNode];
1513  if ( data.tree )
1514  {
1515  if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1516  {
1517  QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1518  int row = data.tree->children[grandParentNode].indexOf( parentNode );
1519  return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1520  }
1521  else
1522  return indexOfParentLayerTreeNode( layerNode );
1523  }
1524 
1525  return indexOfParentLayerTreeNode( layerNode );
1526 }
1527 
1528 
1530 {
1531  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1532  return QVariant();
1533  return node->data( role );
1534 }
1535 
1536 
1538 {
1539  Qt::ItemFlags f = node->flags();
1540  if ( !testFlag( AllowLegendChangeState ) )
1541  f &= ~Qt::ItemIsUserCheckable;
1542  return f;
1543 }
1544 
1545 
1547 {
1548  return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1549 }
1550 
1552 {
1553  return mLegend[nodeLayer].embeddedNodeInParent;
1554 }
1555 
1556 
1558 {
1559  QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1560  if ( !legendNode )
1561  return QIcon();
1562  return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1563 }
1564 
1565 
1566 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1567 {
1568  if ( !mLegend.contains( nodeLayer ) )
1569  return QList<QgsLayerTreeModelLegendNode *>();
1570 
1571  const LayerLegendData &data = mLegend[nodeLayer];
1572  QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1573  if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1574  lst.prepend( data.embeddedNodeInParent );
1575  return lst;
1576 }
1577 
1578 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1579 {
1580  return mLegend.value( nodeLayer ).originalNodes;
1581 }
1582 
1583 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1584 {
1585  for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1586  {
1587  QgsLayerTreeLayer *layer = it.key();
1588  if ( layer->layerId() == layerId )
1589  {
1590  const auto activeNodes = mLegend.value( layer ).activeNodes;
1591  for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1592  {
1593  if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1594  {
1595  //found it!
1596  return legendNode;
1597  }
1598  }
1599  }
1600  }
1601 
1602  return nullptr;
1603 }
1604 
1606 {
1609  else
1610  mDeferLegendInvalidationTimer.start( 10 );
1611 }
1612 
1614 {
1615  // we have varying icon sizes, and we want icon to be centered and
1616  // text to be left aligned, so we have to compute the max width of icons
1617  //
1618  // we do that for nodes which share a common parent
1619  //
1620  // we do that here because for symbols with size defined in map units
1621  // the symbol sizes changes depends on the zoom level
1622 
1623  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1624 
1625  for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1626  {
1627  const LayerLegendData &data = mLegend.value( layerNode );
1628 
1629  QList<QgsSymbolLegendNode *> symbolNodes;
1630  QMap<QString, int> widthMax;
1631  for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1632  {
1633  QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1634  if ( n )
1635  {
1636  const QSize sz( n->minimumIconSize( context.get() ) );
1637  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1638  widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1639  n->setIconSize( sz );
1640  symbolNodes.append( n );
1641  }
1642  }
1643  for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1644  {
1645  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1646  Q_ASSERT( widthMax[parentKey] > 0 );
1647  const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1648  n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1649  }
1650  for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1652  }
1653 
1654  mInvalidatedNodes.clear();
1655 }
1656 
1657 // 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:474
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:1990
#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.