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