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