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