QGIS API Documentation  3.6.0-Noosa (5873452)
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  }
129  else if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( child ) )
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() == QgsMapLayer::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() )
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 )
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 )
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  parts << "<i>" + layer->publicSource().toHtmlEscaped() + "</i>";
334 
335  return parts.join( QStringLiteral( "<br/>" ) );
336  }
337  }
338  }
339 
340  return QVariant();
341 }
342 
343 
344 Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
345 {
346  if ( !index.isValid() )
347  {
348  Qt::ItemFlags rootFlags = Qt::ItemFlags();
349  if ( testFlag( AllowNodeReorder ) )
350  rootFlags |= Qt::ItemIsDropEnabled;
351  return rootFlags;
352  }
353 
354  if ( QgsLayerTreeModelLegendNode *symn = index2legendNode( index ) )
355  return legendNodeFlags( symn );
356 
357  Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
358 
359  if ( testFlag( AllowNodeRename ) )
360  f |= Qt::ItemIsEditable;
361 
362  QgsLayerTreeNode *node = index2node( index );
363  bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
364 
365  if ( testFlag( AllowNodeReorder ) )
366  {
367  // only root embedded nodes can be reordered
368  if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
369  f |= Qt::ItemIsDragEnabled;
370  }
371 
373  f |= Qt::ItemIsUserCheckable;
374 
375  if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
376  f |= Qt::ItemIsDropEnabled;
377 
378  return f;
379 }
380 
381 bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
382 {
384  if ( sym )
385  {
386  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
387  return false;
388  bool res = sym->setData( value, role );
389  if ( res )
390  emit dataChanged( index, index );
391  return res;
392  }
393 
394  QgsLayerTreeNode *node = index2node( index );
395  if ( !node )
396  return QAbstractItemModel::setData( index, value, role );
397 
398  if ( role == Qt::CheckStateRole )
399  {
401  return false;
402 
403  bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
404  if ( checked && node->children().isEmpty() )
405  {
407  }
408  else if ( testFlag( ActionHierarchical ) )
409  {
410  if ( node->children().isEmpty() )
412  else
413  node->setItemVisibilityCheckedRecursive( checked );
414  }
415  else
416  {
417  node->setItemVisibilityChecked( checked );
418  }
419 
421 
422  return true;
423  }
424  else if ( role == Qt::EditRole )
425  {
426  if ( !testFlag( AllowNodeRename ) )
427  return false;
428 
429  if ( QgsLayerTree::isLayer( node ) )
430  {
431  QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
432  layer->setName( value.toString() );
433  emit dataChanged( index, index );
434  }
435  else if ( QgsLayerTree::isGroup( node ) )
436  {
437  QgsLayerTree::toGroup( node )->setName( value.toString() );
438  emit dataChanged( index, index );
439  }
440  }
441 
442  return QAbstractItemModel::setData( index, value, role );
443 }
444 
446 {
447  if ( !node || !node->parent() )
448  return QModelIndex(); // this is the only root item -> invalid index
449 
450  QModelIndex parentIndex = node2index( node->parent() );
451 
452  int row = node->parent()->children().indexOf( node );
453  Q_ASSERT( row >= 0 );
454  return index( row, 0, parentIndex );
455 }
456 
457 
458 static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
459 {
460  if ( !child->parent() )
461  return false;
462 
463  if ( child->parent() == node )
464  return true;
465 
466  return _isChildOfNode( child->parent(), node );
467 }
468 
469 static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
470 {
471  Q_FOREACH ( QgsLayerTreeNode *n, nodes )
472  {
473  if ( _isChildOfNode( child, n ) )
474  return true;
475  }
476 
477  return false;
478 }
479 
480 
481 QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
482 {
483  QList<QgsLayerTreeNode *> nodes;
484  Q_FOREACH ( const QModelIndex &index, list )
485  {
486  QgsLayerTreeNode *node = index2node( index );
487  if ( !node )
488  continue;
489 
490  nodes << node;
491  }
492 
493  if ( !skipInternal )
494  return nodes;
495 
496  // remove any children of nodes if both parent node and children are selected
497  QList<QgsLayerTreeNode *> nodesFinal;
498  Q_FOREACH ( QgsLayerTreeNode *node, nodes )
499  {
500  if ( !_isChildOfNodes( node, nodes ) )
501  nodesFinal << node;
502  }
503 
504  return nodesFinal;
505 }
506 
508 {
509  return mRootNode;
510 }
511 
513 {
514  beginResetModel();
515 
517 
518  Q_ASSERT( mLegend.isEmpty() );
519 
520  mRootNode = newRootGroup;
521 
522  endResetModel();
523 
525 }
526 
528 {
529  // update title
530  QModelIndex idx = node2index( nodeLayer );
531  emit dataChanged( idx, idx );
532 
533  // update children
534  int oldNodeCount = rowCount( idx );
535  beginRemoveRows( idx, 0, std::max( oldNodeCount - 1, 0 ) );
536  removeLegendFromLayer( nodeLayer );
537  endRemoveRows();
538 
539  addLegendToLayer( nodeLayer );
540  int newNodeCount = rowCount( idx );
541 
542  // automatic collapse of legend nodes - useful if a layer has many legend nodes
543  if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
544  nodeLayer->setExpanded( false );
545 }
546 
548 {
549  return mCurrentIndex;
550 }
551 
553 {
554  QModelIndex oldIndex = mCurrentIndex;
556 
557  if ( oldIndex.isValid() )
558  emit dataChanged( oldIndex, oldIndex );
559  if ( currentIndex.isValid() )
560  emit dataChanged( currentIndex, currentIndex );
561 }
562 
563 
564 void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
565 {
566  if ( nodeType == QgsLayerTreeNode::NodeGroup )
567  {
568  if ( mFontGroup != font )
569  {
570  mFontGroup = font;
572  }
573  }
574  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
575  {
576  if ( mFontLayer != font )
577  {
578  mFontLayer = font;
580  }
581  }
582  else
583  {
584  QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
585  }
586 }
587 
588 
589 QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
590 {
591  if ( nodeType == QgsLayerTreeNode::NodeGroup )
592  return mFontGroup;
593  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
594  return mFontLayer;
595  else
596  {
597  QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
598  return QFont();
599  }
600 }
601 
603 {
604  mLegendFilterByScale = scale;
605 
606  // this could be later done in more efficient way
607  // by just updating active legend nodes, without refreshing original legend nodes
608  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mRootNode->findLayers() )
609  refreshLayerLegend( nodeLayer );
610 }
611 
613 {
614  setLegendFilter( settings, /* useExtent = */ true );
615 }
616 
617 void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
618 {
619  if ( settings && settings->hasValidSettings() )
620  {
621  mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
622  mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
624  mLegendFilterUsesExtent = useExtent;
625  // collect expression filters
626  if ( useExpressions )
627  {
628  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mRootNode->findLayers() )
629  {
630  bool enabled;
631  QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
632  if ( enabled && !expr.isEmpty() )
633  {
634  exprs[ nodeLayer->layerId()] = expr;
635  }
636  }
637  }
638  bool polygonValid = !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry;
639  if ( useExpressions && !useExtent && !polygonValid ) // only expressions
640  {
642  }
643  else
644  {
645  mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, polygon, exprs ) );
646  }
647  mLegendFilterHitTest->run();
648  }
649  else
650  {
652  return; // no change
653 
654  mLegendFilterMapSettings.reset();
655  mLegendFilterHitTest.reset();
656  }
657 
658  // temporarily disable autocollapse so that legend nodes stay visible
659  int bkAutoCollapse = autoCollapseLegendNodes();
661 
662  // this could be later done in more efficient way
663  // by just updating active legend nodes, without refreshing original legend nodes
664  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mRootNode->findLayers() )
665  refreshLayerLegend( nodeLayer );
666 
667  setAutoCollapseLegendNodes( bkAutoCollapse );
668 }
669 
670 void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
671 {
672  if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
673  return;
674 
675  mLegendMapViewMupp = mapUnitsPerPixel;
676  mLegendMapViewDpi = dpi;
677  mLegendMapViewScale = scale;
678 
679  // now invalidate legend nodes!
681 
683 }
684 
685 void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
686 {
687  if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
688  if ( dpi ) *dpi = mLegendMapViewDpi;
689  if ( scale ) *scale = mLegendMapViewScale;
690 }
691 
692 QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
693 {
694  return mLayerStyleOverrides;
695 }
696 
697 void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
698 {
699  mLayerStyleOverrides = overrides;
700 }
701 
702 int QgsLayerTreeModel::scaleIconSize( int standardSize )
703 {
704  QFontMetrics fm( ( QFont() ) );
705  const double scale = 1.1 * standardSize / 24;
706  return static_cast< int >( std::floor( std::max( Qgis::UI_SCALE_FACTOR * fm.height() * scale, static_cast< double >( standardSize ) ) ) );
707 }
708 
709 void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
710 {
711  Q_ASSERT( node );
712  beginInsertRows( node2index( node ), indexFrom, indexTo );
713 }
714 
715 static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
716 {
717  QList<QgsLayerTreeNode *> children = node->children();
718  QList<QgsLayerTreeLayer *> newLayerNodes;
719  for ( int i = indexFrom; i <= indexTo; ++i )
720  {
721  QgsLayerTreeNode *child = children.at( i );
722  if ( QgsLayerTree::isLayer( child ) )
723  newLayerNodes << QgsLayerTree::toLayer( child );
724  else if ( QgsLayerTree::isGroup( child ) )
725  newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
726  }
727  return newLayerNodes;
728 }
729 
730 void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
731 {
732  Q_ASSERT( node );
733 
734  endInsertRows();
735 
736  Q_FOREACH ( QgsLayerTreeLayer *newLayerNode, _layerNodesInSubtree( node, indexFrom, indexTo ) )
737  connectToLayer( newLayerNode );
738 }
739 
740 void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
741 {
742  Q_ASSERT( node );
743 
744  beginRemoveRows( node2index( node ), indexFrom, indexTo );
745 
746  // disconnect from layers and remove their legend
747  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, _layerNodesInSubtree( node, indexFrom, indexTo ) )
748  disconnectFromLayer( nodeLayer );
749 }
750 
752 {
753  endRemoveRows();
754 }
755 
757 {
758  Q_ASSERT( node );
759 
760  QModelIndex index = node2index( node );
761  emit dataChanged( index, index );
762 }
763 
764 void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
765 {
766  Q_UNUSED( name );
767  Q_ASSERT( node );
768 
769  QModelIndex index = node2index( node );
770  emit dataChanged( index, index );
771 }
772 
773 
775 {
776  if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
778 }
779 
780 
782 {
783  QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
784  if ( !nodeLayer )
785  return;
786 
787  // deferred connection to the layer
788  connectToLayer( nodeLayer );
789 }
790 
792 {
793  QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
794  if ( !nodeLayer )
795  return;
796 
797  disconnectFromLayer( nodeLayer );
798 
799  // wait for the layer to appear again
801 }
802 
804 {
805  if ( !mRootNode )
806  return;
807 
808  if ( !testFlag( ShowLegend ) )
809  return;
810 
811  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
812  if ( !layer )
813  return;
814 
815  QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
816  if ( !nodeLayer )
817  return;
818 
819  refreshLayerLegend( nodeLayer );
820 }
821 
823 {
824  QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
825  if ( !layer )
826  return;
827 
828  QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
829  if ( !nodeLayer )
830  return;
831 
832  QModelIndex index = node2index( nodeLayer );
833  emit dataChanged( index, index );
834 
835  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
836  refreshLayerLegend( nodeLayer );
837 }
838 
839 
841 {
842  QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
843  if ( !legendNode )
844  return;
845 
846  QModelIndex index = legendNode2index( legendNode );
847  if ( index.isValid() )
848  emit dataChanged( index, index );
849 }
850 
851 
853 {
854  if ( !nodeLayer->layer() )
855  {
856  // in order to connect to layer, we need to have it loaded.
857  // keep an eye on the layer ID: once loaded, we will use it
859  return;
860  }
861 
862  // watch if the layer is getting removed
864 
865  if ( testFlag( ShowLegend ) )
866  {
867  addLegendToLayer( nodeLayer );
868 
869  // automatic collapse of legend nodes - useful if a layer has many legend nodes
870  if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
871  {
873  nodeLayer->setExpanded( false );
874  }
875  }
876 
877  QgsMapLayer *layer = nodeLayer->layer();
878  connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
879 
880  if ( layer->type() == QgsMapLayer::VectorLayer )
881  {
882  // using unique connection because there may be temporarily more nodes for a layer than just one
883  // which would create multiple connections, however disconnect() would disconnect all multiple connections
884  // even if we wanted to disconnect just one connection in each call.
885  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
886  connect( vl, &QgsVectorLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
887  connect( vl, &QgsVectorLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
888  connect( vl, &QgsVectorLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
889  }
890 }
891 
892 // try to find out if the layer ID is present in the tree multiple times
893 static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
894 {
895  int count = 0;
896  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
897  {
898  if ( QgsLayerTree::isLayer( child ) )
899  {
900  if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
901  count++;
902  }
903  else if ( QgsLayerTree::isGroup( child ) )
904  {
905  count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
906  }
907  }
908  return count;
909 }
910 
912 {
913  disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
914 
915  if ( !nodeLayer->layer() )
916  return; // we were never connected
917 
918  if ( testFlag( ShowLegend ) )
919  {
920  removeLegendFromLayer( nodeLayer );
921  }
922 
923  if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
924  {
925  // last instance of the layer in the tree: disconnect from all signals from layer!
926  disconnect( nodeLayer->layer(), nullptr, this, nullptr );
927  }
928 }
929 
931 {
932  Q_FOREACH ( QgsLayerTreeNode *node, parentGroup->children() )
933  {
934  if ( QgsLayerTree::isGroup( node ) )
936  else if ( QgsLayerTree::isLayer( node ) )
938  }
939 }
940 
942 {
943  Q_FOREACH ( QgsLayerTreeNode *node, parentGroup->children() )
944  {
945  if ( QgsLayerTree::isGroup( node ) )
947  else if ( QgsLayerTree::isLayer( node ) )
949  }
950 }
951 
953 {
954  Q_ASSERT( mRootNode );
955 
962 
964 
966 }
967 
969 {
970  disconnect( mRootNode, nullptr, this, nullptr );
971 
973 }
974 
975 void QgsLayerTreeModel::recursivelyEmitDataChanged( const QModelIndex &idx )
976 {
977  QgsLayerTreeNode *node = index2node( idx );
978  if ( !node )
979  return;
980 
981  int count = node->children().count();
982  if ( count == 0 )
983  return;
984  emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
985  for ( int i = 0; i < count; ++i )
986  recursivelyEmitDataChanged( index( i, 0, idx ) );
987 }
988 
989 void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx )
990 {
991  QgsLayerTreeNode *node = index2node( idx );
992  if ( !node )
993  return;
994 
995  if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
996  {
997  const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
998  if ( layer && layer->hasScaleBasedVisibility() )
999  {
1000  emit dataChanged( idx, idx );
1001  }
1002  }
1003  int count = node->children().count();
1004  for ( int i = 0; i < count; ++i )
1005  refreshScaleBasedLayers( index( i, 0, idx ) );
1006 }
1007 
1009 {
1010  return Qt::CopyAction | Qt::MoveAction;
1011 }
1012 
1013 QStringList QgsLayerTreeModel::mimeTypes() const
1014 {
1015  QStringList types;
1016  types << QStringLiteral( "application/qgis.layertreemodeldata" );
1017  return types;
1018 }
1019 
1020 
1021 QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1022 {
1023  // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1024  QModelIndexList sortedIndexes = indexes;
1025  std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1026 
1027  QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1028 
1029  if ( nodesFinal.isEmpty() )
1030  return nullptr;
1031 
1032  QMimeData *mimeData = new QMimeData();
1033 
1034  QDomDocument doc;
1035  QDomElement rootElem = doc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1036  Q_FOREACH ( QgsLayerTreeNode *node, nodesFinal )
1037  node->writeXml( rootElem, QgsReadWriteContext() );
1038  doc.appendChild( rootElem );
1039  QString txt = doc.toString();
1040 
1041  mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), txt.toUtf8() );
1042 
1043  mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1044 
1045  return mimeData;
1046 }
1047 
1048 bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1049 {
1050  if ( action == Qt::IgnoreAction )
1051  return true;
1052 
1053  if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1054  return false;
1055 
1056  if ( column >= columnCount( parent ) )
1057  return false;
1058 
1059  QgsLayerTreeNode *nodeParent = index2node( parent );
1060  if ( !QgsLayerTree::isGroup( nodeParent ) )
1061  return false;
1062 
1063  QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1064 
1065  QDomDocument doc;
1066  if ( !doc.setContent( QString::fromUtf8( encodedData ) ) )
1067  return false;
1068 
1069  QDomElement rootElem = doc.documentElement();
1070  if ( rootElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1071  return false;
1072 
1073  QList<QgsLayerTreeNode *> nodes;
1074 
1075  QDomElement elem = rootElem.firstChildElement();
1076  while ( !elem.isNull() )
1077  {
1079  if ( node )
1080  nodes << node;
1081 
1082  elem = elem.nextSiblingElement();
1083  }
1084 
1085  if ( nodes.isEmpty() )
1086  return false;
1087 
1088  if ( parent.isValid() && row == -1 )
1089  row = 0; // if dropped directly onto group item, insert at first position
1090 
1091  QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1092 
1093  return true;
1094 }
1095 
1096 bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1097 {
1098  QgsLayerTreeNode *parentNode = index2node( parent );
1099  if ( QgsLayerTree::isGroup( parentNode ) )
1100  {
1101  QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1102  return true;
1103  }
1104  return false;
1105 }
1106 
1107 void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1108 {
1109  mFlags = f;
1110 }
1111 
1113 {
1114  if ( on )
1115  mFlags |= f;
1116  else
1117  mFlags &= ~f;
1118 }
1119 
1120 QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1121 {
1122  return mFlags;
1123 }
1124 
1126 {
1127  return mFlags.testFlag( f );
1128 }
1129 
1131 {
1132  return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1133 }
1134 
1135 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1136 {
1137  QList<QgsLayerTreeModelLegendNode *> filtered;
1138 
1139  if ( mLegendFilterByScale > 0 )
1140  {
1141  Q_FOREACH ( QgsLayerTreeModelLegendNode *node, nodes )
1142  {
1143  if ( node->isScaleOK( mLegendFilterByScale ) )
1144  filtered << node;
1145  }
1146  }
1147  else if ( mLegendFilterMapSettings )
1148  {
1149  if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1150  {
1151  Q_FOREACH ( QgsLayerTreeModelLegendNode *node, nodes )
1152  {
1153  QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1154  bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1155  if ( checked )
1156  {
1157  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1158  {
1159  if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1160  filtered << node;
1161  }
1162  else
1163  {
1164  filtered << node;
1165  }
1166  }
1167  else // unknown node type or unchecked
1168  filtered << node;
1169  }
1170  }
1171  }
1172  else
1173  {
1174  return nodes;
1175  }
1176 
1177  return filtered;
1178 }
1179 
1180 
1181 
1183 // Legend nodes routines - start
1184 
1186 {
1187  Q_FOREACH ( const LayerLegendData &data, mLegend )
1188  {
1189  qDeleteAll( data.originalNodes );
1190  delete data.tree;
1191  }
1192  mLegend.clear();
1193 }
1194 
1195 
1197 {
1198  if ( mLegend.contains( nodeLayer ) )
1199  {
1200  qDeleteAll( mLegend[nodeLayer].originalNodes );
1201  delete mLegend[nodeLayer].tree;
1202  mLegend.remove( nodeLayer );
1203  }
1204 }
1205 
1206 
1208 {
1209  if ( !nodeL || !nodeL->layer() )
1210  return;
1211 
1212  QgsMapLayer *ml = nodeL->layer();
1213  QgsMapLayerLegend *layerLegend = ml->legend();
1214  if ( !layerLegend )
1215  return;
1216 
1217  QgsMapLayerStyleOverride styleOverride( ml );
1218  if ( mLayerStyleOverrides.contains( ml->id() ) )
1219  styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1220 
1221  QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1222 
1223  // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1225 
1226  if ( testFlag( UseEmbeddedWidgets ) )
1227  {
1228  // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1229  int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1230  while ( widgetsCount > 0 )
1231  {
1232  lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1233  --widgetsCount;
1234  }
1235  }
1236 
1237  QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1238 
1239  Q_FOREACH ( QgsLayerTreeModelLegendNode *n, lstNew )
1240  {
1241  n->setParent( this );
1243  }
1244 
1245  // See if we have an embedded node - if we do, we will not use it among active nodes.
1246  // Legend node embedded in parent does not have to be the first one,
1247  // there can be also nodes generated for embedded widgets
1248  QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1249  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, filteredLstNew )
1250  {
1251  if ( legendNode->isEmbeddedInParent() )
1252  {
1253  embeddedNode = legendNode;
1254  filteredLstNew.removeOne( legendNode );
1255  break;
1256  }
1257  }
1258 
1259  LayerLegendTree *legendTree = nullptr;
1260 
1261  // maybe the legend nodes form a tree - try to create a tree structure from the list
1262  if ( testFlag( ShowLegendAsTree ) )
1263  legendTree = tryBuildLegendTree( filteredLstNew );
1264 
1265  int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1266 
1267  if ( !filteredLstNew.isEmpty() ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
1268 
1270  data.originalNodes = lstNew;
1271  data.activeNodes = filteredLstNew;
1272  data.embeddedNodeInParent = embeddedNode;
1273  data.tree = legendTree;
1274 
1275  mLegend[nodeL] = data;
1276 
1277  if ( !filteredLstNew.isEmpty() ) endInsertRows();
1278 
1279  // invalidate map based data even if the data is not map-based to make sure
1280  // the symbol sizes are computed at least once
1282 }
1283 
1284 
1285 QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1286 {
1287  // first check whether there are any legend nodes that are not top-level
1288  bool hasParentKeys = false;
1289  Q_FOREACH ( QgsLayerTreeModelLegendNode *n, nodes )
1290  {
1291  if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1292  {
1293  hasParentKeys = true;
1294  break;
1295  }
1296  }
1297  if ( !hasParentKeys )
1298  return nullptr; // all legend nodes are top-level => stick with list representation
1299 
1300  // make mapping from rules to nodes and do some sanity checks
1301  QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1302  rule2node[QString()] = nullptr;
1303  Q_FOREACH ( QgsLayerTreeModelLegendNode *n, nodes )
1304  {
1305  QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1306  if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1307  return nullptr;
1308  if ( rule2node.contains( ruleKey ) ) // and they must be unique
1309  return nullptr;
1310  rule2node[ruleKey] = n;
1311  }
1312 
1313  // create the tree structure
1314  LayerLegendTree *tree = new LayerLegendTree;
1315  Q_FOREACH ( QgsLayerTreeModelLegendNode *n, nodes )
1316  {
1317  QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1318  QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1319  tree->parents[n] = parent;
1320  tree->children[parent] << n;
1321  }
1322  return tree;
1323 }
1324 
1325 QgsRenderContext *QgsLayerTreeModel::createTemporaryRenderContext() const
1326 {
1327  double scale = 0.0;
1328  double mupp = 0.0;
1329  int dpi = 0;
1330  legendMapViewData( &mupp, &dpi, &scale );
1331  bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1332 
1333  // setup temporary render context
1334  std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1335  context->setScaleFactor( dpi / 25.4 );
1336  context->setRendererScale( scale );
1337  context->setMapToPixel( QgsMapToPixel( mupp ) );
1338  return validData ? context.release() : nullptr;
1339 }
1340 
1341 
1343 {
1344  return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1345 }
1346 
1347 
1349 {
1350  const LayerLegendData &data = mLegend[legendNode->layerNode()];
1351  if ( data.tree )
1352  {
1353  if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1354  {
1355  QModelIndex parentIndex = legendNode2index( parentLegendNode );
1356  int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1357  return index( row, 0, parentIndex );
1358  }
1359  else
1360  {
1361  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1362  int row = data.tree->children[nullptr].indexOf( legendNode );
1363  return index( row, 0, parentIndex );
1364  }
1365  }
1366 
1367  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1368  Q_ASSERT( parentIndex.isValid() );
1369  int row = data.activeNodes.indexOf( legendNode );
1370  if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1371  return QModelIndex();
1372 
1373  return index( row, 0, parentIndex );
1374 }
1375 
1376 
1378 {
1379  const LayerLegendData &data = mLegend[node->layerNode()];
1380  if ( data.tree )
1381  return data.tree->children[node].count();
1382 
1383  return 0; // they are leaves
1384 }
1385 
1386 
1388 {
1389  if ( !mLegend.contains( nL ) )
1390  return 0;
1391 
1392  const LayerLegendData &data = mLegend[nL];
1393  if ( data.tree )
1394  return data.tree->children[nullptr].count();
1395 
1396  int count = data.activeNodes.count();
1397  return count;
1398 }
1399 
1400 
1401 QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1402 {
1403  Q_ASSERT( mLegend.contains( nL ) );
1404  const LayerLegendData &data = mLegend[nL];
1405  if ( data.tree )
1406  return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1407 
1408  return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1409 }
1410 
1411 
1412 QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1413 {
1414  const LayerLegendData &data = mLegend[node->layerNode()];
1415  if ( data.tree )
1416  return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1417 
1418  return QModelIndex(); // have no children
1419 }
1420 
1421 
1423 {
1424  QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1425  const LayerLegendData &data = mLegend[layerNode];
1426  if ( data.tree )
1427  {
1428  if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1429  {
1430  QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1431  int row = data.tree->children[grandParentNode].indexOf( parentNode );
1432  return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1433  }
1434  else
1435  return indexOfParentLayerTreeNode( layerNode );
1436  }
1437 
1438  return indexOfParentLayerTreeNode( layerNode );
1439 }
1440 
1441 
1443 {
1444  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1445  return QVariant();
1446  return node->data( role );
1447 }
1448 
1449 
1451 {
1452  Qt::ItemFlags f = node->flags();
1453  if ( !testFlag( AllowLegendChangeState ) )
1454  f &= ~Qt::ItemIsUserCheckable;
1455  return f;
1456 }
1457 
1458 
1460 {
1461  return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1462 }
1463 
1465 {
1466  return mLegend[nodeLayer].embeddedNodeInParent;
1467 }
1468 
1469 
1471 {
1472  QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1473  if ( !legendNode )
1474  return QIcon();
1475  return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1476 }
1477 
1478 
1479 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1480 {
1481  if ( !mLegend.contains( nodeLayer ) )
1482  return QList<QgsLayerTreeModelLegendNode *>();
1483 
1484  const LayerLegendData &data = mLegend[nodeLayer];
1485  QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1486  if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1487  lst.prepend( data.embeddedNodeInParent );
1488  return lst;
1489 }
1490 
1491 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1492 {
1493  return mLegend.value( nodeLayer ).originalNodes;
1494 }
1495 
1496 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1497 {
1498  QMap<QgsLayerTreeLayer *, LayerLegendData>::const_iterator it = mLegend.constBegin();
1499  for ( ; it != mLegend.constEnd(); ++it )
1500  {
1501  QgsLayerTreeLayer *layer = it.key();
1502  if ( layer->layerId() == layerId )
1503  {
1504  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, mLegend.value( layer ).activeNodes )
1505  {
1506  if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1507  {
1508  //found it!
1509  return legendNode;
1510  }
1511  }
1512  }
1513  }
1514 
1515  return nullptr;
1516 }
1517 
1519 {
1522  else
1523  mDeferLegendInvalidationTimer.start( 1000 );
1524 }
1525 
1527 {
1528  // we have varying icon sizes, and we want icon to be centered and
1529  // text to be left aligned, so we have to compute the max width of icons
1530  //
1531  // we do that for nodes who share a common parent
1532  //
1533  // we do that here because for symbols with size defined in map units
1534  // the symbol sizes changes depends on the zoom level
1535 
1536  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1537 
1538  Q_FOREACH ( const LayerLegendData &data, mLegend )
1539  {
1540  QList<QgsSymbolLegendNode *> symbolNodes;
1541  QMap<QString, int> widthMax;
1542  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, data.originalNodes )
1543  {
1544  QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1545  if ( n )
1546  {
1547  const QSize sz( n->minimumIconSize( context.get() ) );
1548  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1549  widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1550  n->setIconSize( sz );
1551  symbolNodes.append( n );
1552  }
1553  }
1554  Q_FOREACH ( QgsSymbolLegendNode *n, symbolNodes )
1555  {
1556  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1557  Q_ASSERT( widthMax[parentKey] > 0 );
1558  const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1559  n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1560  }
1561  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, data.originalNodes )
1562  legendNode->invalidateMapBasedData();
1563  }
1564 
1565 }
1566 
1567 // 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 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: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:64
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.
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:139
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 null.
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
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:265
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:106
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 null pointer 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...
QgsMapLayer::LayerType type() const
Returns the type of the layer.
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
Added in 3.2.
Definition: qgsmaplayer.h:111
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.
virtual bool isScaleOK(double scale) const
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 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
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()
Is emitted, when edited changes successfully have been 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...
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...
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:273
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.
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-null pointer.
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:430
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:110
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()
This signal is 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.