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