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