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