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