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