QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsrendererv2.h"
32 #include "qgssymbollayerv2utils.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 = "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() ) );
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 
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 
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 
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 
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->layerName();
202  if ( nodeLayer->customProperty( "showFeatureCount", 0 ).toInt() && role == Qt::DisplayRole )
203  {
204  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( nodeLayer->layer() );
205  if ( vlayer && vlayer->featureCount() >= 0 )
206  name += QString( " [%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  // avoid to create preview for wms that imply a better multi thread management
231  // due to async nature of wms.
232  // The following two line of code is part of the fix of
233  // https://issues.qgis.org/issues/16803
234  if ( rlayer->providerType() == QLatin1String( "wms" ) )
235  return QgsLayerItem::iconRaster();
236  return QIcon( QPixmap::fromImage( rlayer->previewAsImage( QSize( 32, 32 ) ) ) );
237  }
238  else
239  {
240  return QgsLayerItem::iconRaster();
241  }
242  }
243 
244  QgsVectorLayer *vlayer = dynamic_cast<QgsVectorLayer*>( layer );
245  QIcon icon;
246 
247  // if there's just on legend entry that should be embedded in layer - do that!
248  if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
249  {
250  icon = legendIconEmbeddedInParent( nodeLayer );
251  }
252  else if ( vlayer && layer->type() == QgsMapLayer::VectorLayer )
253  {
254  if ( vlayer->geometryType() == QGis::Point )
255  icon = QgsLayerItem::iconPoint();
256  else if ( vlayer->geometryType() == QGis::Line )
257  icon = QgsLayerItem::iconLine();
258  else if ( vlayer->geometryType() == QGis::Polygon )
259  icon = QgsLayerItem::iconPolygon();
260  else if ( vlayer->geometryType() == QGis::NoGeometry )
261  icon = QgsLayerItem::iconTable();
262  else
263  icon = QgsLayerItem::iconDefault();
264  }
265 
266  if ( vlayer && vlayer->isEditable() )
267  {
268  QPixmap pixmap( icon.pixmap( 16, 16 ) );
269 
270  QPainter painter( &pixmap );
271  painter.drawPixmap( 0, 0, 16, 16, QgsApplication::getThemePixmap( vlayer->isModified() ? "/mIconEditableEdits.png" : "/mIconEditable.png" ) );
272  painter.end();
273 
274  icon = QIcon( pixmap );
275  }
276 
277  return icon;
278  }
279  }
280  else if ( role == Qt::CheckStateRole )
281  {
283  return QVariant();
284 
285  if ( QgsLayerTree::isLayer( node ) )
286  {
287  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
288  if ( nodeLayer->layer() && nodeLayer->layer()->type() == QgsMapLayer::VectorLayer )
289  {
290  if ( qobject_cast<QgsVectorLayer*>( nodeLayer->layer() )->geometryType() == QGis::NoGeometry )
291  return QVariant(); // do not show checkbox for non-spatial tables
292  }
293  return nodeLayer->isVisible();
294  }
295  else if ( QgsLayerTree::isGroup( node ) )
296  {
297  QgsLayerTreeGroup* nodeGroup = QgsLayerTree::toGroup( node );
298  return nodeGroup->isVisible();
299  }
300  }
301  else if ( role == Qt::FontRole )
302  {
303  QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
304  if ( node->customProperty( "embedded" ).toInt() )
305  f.setItalic( true );
306  if ( index == mCurrentIndex )
307  f.setUnderline( true );
308  return f;
309  }
310  else if ( role == Qt::ForegroundRole )
311  {
312  QBrush brush( Qt::black, Qt::SolidPattern );
313  if ( QgsLayerTree::isLayer( node ) )
314  {
315  const QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();
316  if ( layer && !layer->isInScaleRange( mLegendMapViewScale ) )
317  {
318  brush.setColor( Qt::lightGray );
319  }
320  }
321  return brush;
322  }
323  else if ( role == Qt::ToolTipRole )
324  {
325  if ( QgsLayerTree::isLayer( node ) )
326  {
327  if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
328  {
329  QStringList parts;
330  QString title = layer->title().isEmpty() ? layer->shortName() : layer->title();
331  if ( title.isEmpty() )
332  title = layer->name();
333  title = "<b>" + title + "</b>";
334  if ( layer->crs().isValid() )
335  title = tr( "%1 (%2)" ).arg( title, layer->crs().authid() );
336 
337  parts << title;
338 
339  if ( !layer->abstract().isEmpty() )
340  parts << "<br/>" + layer->abstract().replace( QLatin1String( "\n" ), QLatin1String( "<br/>" ) );
341  parts << "<i>" + layer->publicSource() + "</i>";
342  return parts.join( "<br/>" );
343  }
344  }
345  }
346 
347  return QVariant();
348 }
349 
350 
352 {
353  if ( !index.isValid() )
354  {
355  Qt::ItemFlags rootFlags = Qt::ItemFlags();
356  if ( testFlag( AllowNodeReorder ) )
357  rootFlags |= Qt::ItemIsDropEnabled;
358  return rootFlags;
359  }
360 
361  if ( QgsLayerTreeModelLegendNode* symn = index2legendNode( index ) )
362  return legendNodeFlags( symn );
363 
364  Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
365 
366  if ( testFlag( AllowNodeRename ) )
367  f |= Qt::ItemIsEditable;
368 
369  QgsLayerTreeNode* node = index2node( index );
370  bool isEmbedded = node->customProperty( "embedded" ).toInt();
371 
372  if ( testFlag( AllowNodeReorder ) )
373  {
374  // only root embedded nodes can be reordered
375  if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( "embedded" ).toInt() ) )
376  f |= Qt::ItemIsDragEnabled;
377  }
378 
380  f |= Qt::ItemIsUserCheckable;
381 
382  if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
383  f |= Qt::ItemIsDropEnabled;
384 
385  return f;
386 }
387 
388 bool QgsLayerTreeModel::setData( const QModelIndex& index, const QVariant& value, int role )
389 {
391  if ( sym )
392  {
393  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
394  return false;
395  bool res = sym->setData( value, role );
396  if ( res )
397  emit dataChanged( index, index );
398  return res;
399  }
400 
401  QgsLayerTreeNode* node = index2node( index );
402  if ( !node )
403  return QgsLayerTreeModel::setData( index, value, role );
404 
405  if ( role == Qt::CheckStateRole )
406  {
408  return false;
409 
410  if ( QgsLayerTree::isLayer( node ) )
411  {
412  QgsLayerTreeLayer* layer = QgsLayerTree::toLayer( node );
413  layer->setVisible( static_cast< Qt::CheckState >( value.toInt() ) );
414  return true;
415  }
416 
417  if ( QgsLayerTree::isGroup( node ) )
418  {
419  QgsLayerTreeGroup* group = QgsLayerTree::toGroup( node );
420  group->setVisible( static_cast< Qt::CheckState >( value.toInt() ) );
421  return true;
422  }
423 
424  return true;
425  }
426  else if ( role == Qt::EditRole )
427  {
428  if ( !testFlag( AllowNodeRename ) )
429  return false;
430 
431  if ( QgsLayerTree::isLayer( node ) )
432  {
433  QgsLayerTreeLayer* layer = QgsLayerTree::toLayer( node );
434  layer->setLayerName( value.toString() );
435  emit dataChanged( index, index );
436  }
437  else if ( QgsLayerTree::isGroup( node ) )
438  {
439  QgsLayerTree::toGroup( node )->setName( value.toString() );
440  emit dataChanged( index, index );
441  }
442  }
443 
444  return QAbstractItemModel::setData( index, value, role );
445 }
446 
448 {
449  if ( !node->parent() )
450  return QModelIndex(); // this is the only root item -> invalid index
451 
452  QModelIndex parentIndex = node2index( node->parent() );
453 
454  int row = node->parent()->children().indexOf( node );
455  Q_ASSERT( row >= 0 );
456  return index( row, 0, parentIndex );
457 }
458 
459 
461 {
462  if ( !child->parent() )
463  return false;
464 
465  if ( child->parent() == node )
466  return true;
467 
468  return _isChildOfNode( child->parent(), node );
469 }
470 
472 {
473  Q_FOREACH ( QgsLayerTreeNode* n, nodes )
474  {
475  if ( _isChildOfNode( child, n ) )
476  return true;
477  }
478 
479  return false;
480 }
481 
482 
483 QList<QgsLayerTreeNode*> QgsLayerTreeModel::indexes2nodes( const QModelIndexList& list, bool skipInternal ) const
484 {
486  Q_FOREACH ( const QModelIndex& index, list )
487  {
488  QgsLayerTreeNode* node = index2node( index );
489  if ( !node )
490  continue;
491 
492  nodes << node;
493  }
494 
495  if ( !skipInternal )
496  return nodes;
497 
498  // remove any children of nodes if both parent node and children are selected
499  QList<QgsLayerTreeNode*> nodesFinal;
500  Q_FOREACH ( QgsLayerTreeNode* node, nodes )
501  {
502  if ( !_isChildOfNodes( node, nodes ) )
503  nodesFinal << node;
504  }
505 
506  return nodesFinal;
507 }
508 
510 {
511  return nullptr != index2legendNode( index );
512 }
513 
515 {
516  QgsLayerTreeModelLegendNode* symNode = index2legendNode( index );
517  return symNode ? symNode->layerNode() : nullptr;
518 }
519 
521 {
522  return mRootNode;
523 }
524 
526 {
527  beginResetModel();
528 
530 
531  Q_ASSERT( mLegend.isEmpty() );
532 
533  mRootNode = newRootGroup;
534 
535  endResetModel();
536 
538 }
539 
541 {
542  // update title
543  QModelIndex idx = node2index( nodeLayer );
544  emit dataChanged( idx, idx );
545 
546  // update children
547  int oldNodeCount = rowCount( idx );
548  beginRemoveRows( idx, 0, oldNodeCount - 1 );
549  removeLegendFromLayer( nodeLayer );
550  endRemoveRows();
551 
552  addLegendToLayer( nodeLayer );
553  int newNodeCount = rowCount( idx );
554 
555  // automatic collapse of legend nodes - useful if a layer has many legend nodes
556  if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
557  nodeLayer->setExpanded( false );
558 }
559 
561 {
562  return mCurrentIndex;
563 }
564 
566 {
567  QModelIndex oldIndex = mCurrentIndex;
569 
570  if ( oldIndex.isValid() )
571  emit dataChanged( oldIndex, oldIndex );
572  if ( currentIndex.isValid() )
573  emit dataChanged( currentIndex, currentIndex );
574 }
575 
576 
577 void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont& font )
578 {
579  if ( nodeType == QgsLayerTreeNode::NodeGroup )
580  {
581  if ( mFontGroup != font )
582  {
583  mFontGroup = font;
585  }
586  }
587  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
588  {
589  if ( mFontLayer != font )
590  {
591  mFontLayer = font;
593  }
594  }
595  else
596  {
597  QgsDebugMsg( "invalid node type" );
598  }
599 }
600 
601 
603 {
604  if ( nodeType == QgsLayerTreeNode::NodeGroup )
605  return mFontGroup;
606  else if ( nodeType == QgsLayerTreeNode::NodeLayer )
607  return mFontLayer;
608  else
609  {
610  QgsDebugMsg( "invalid node type" );
611  return QFont();
612  }
613 }
614 
615 void QgsLayerTreeModel::setLegendFilterByScale( double scaleDenominator )
616 {
617  mLegendFilterByScale = scaleDenominator;
618 
619  // this could be later done in more efficient way
620  // by just updating active legend nodes, without refreshing original legend nodes
621  Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mRootNode->findLayers() )
622  refreshLayerLegend( nodeLayer );
623 }
624 
626 {
627  setLegendFilter( settings, /* useExtent = */ true );
628 }
629 
630 void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings* settings, bool useExtent, const QgsGeometry& polygon, bool useExpressions )
631 {
632  if ( settings && settings->hasValidSettings() )
633  {
634  mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
635  mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
637  mLegendFilterUsesExtent = useExtent;
638  // collect expression filters
639  if ( useExpressions )
640  {
641  Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mRootNode->findLayers() )
642  {
643  bool enabled;
644  QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
645  if ( enabled && !expr.isEmpty() )
646  {
647  exprs[ nodeLayer->layerId()] = expr;
648  }
649  }
650  }
651  bool polygonValid = !polygon.isEmpty() && polygon.type() == QGis::Polygon;
652  if ( useExpressions && !useExtent && !polygonValid ) // only expressions
653  {
655  }
656  else
657  {
659  }
660  mLegendFilterHitTest->run();
661  }
662  else
663  {
665  return; // no change
666 
669  }
670 
671  // temporarily disable autocollapse so that legend nodes stay visible
672  int bkAutoCollapse = autoCollapseLegendNodes();
674 
675  // this could be later done in more efficient way
676  // by just updating active legend nodes, without refreshing original legend nodes
677  Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mRootNode->findLayers() )
678  refreshLayerLegend( nodeLayer );
679 
680  setAutoCollapseLegendNodes( bkAutoCollapse );
681 }
682 
683 void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
684 {
685  if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
686  return;
687 
688  mLegendMapViewMupp = mapUnitsPerPixel;
689  mLegendMapViewDpi = dpi;
690  mLegendMapViewScale = scale;
691 
692  // now invalidate legend nodes!
694 
696 }
697 
698 void QgsLayerTreeModel::legendMapViewData( double* mapUnitsPerPixel, int* dpi, double* scale )
699 {
700  if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
701  if ( dpi ) *dpi = mLegendMapViewDpi;
702  if ( scale ) *scale = mLegendMapViewScale;
703 }
704 
706 {
707  return mLayerStyleOverrides;
708 }
709 
711 {
712  mLayerStyleOverrides = overrides;
713 }
714 
715 void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
716 {
717  Q_ASSERT( node );
718  beginInsertRows( node2index( node ), indexFrom, indexTo );
719 }
720 
721 static QList<QgsLayerTreeLayer*> _layerNodesInSubtree( QgsLayerTreeNode* node, int indexFrom, int indexTo )
722 {
724  QList<QgsLayerTreeLayer*> newLayerNodes;
725  for ( int i = indexFrom; i <= indexTo; ++i )
726  {
727  QgsLayerTreeNode* child = children.at( i );
728  if ( QgsLayerTree::isLayer( child ) )
729  newLayerNodes << QgsLayerTree::toLayer( child );
730  else if ( QgsLayerTree::isGroup( child ) )
731  newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
732  }
733  return newLayerNodes;
734 }
735 
736 void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
737 {
738  Q_ASSERT( node );
739 
740  endInsertRows();
741 
742  Q_FOREACH ( QgsLayerTreeLayer* newLayerNode, _layerNodesInSubtree( node, indexFrom, indexTo ) )
743  connectToLayer( newLayerNode );
744 }
745 
746 void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
747 {
748  Q_ASSERT( node );
749 
750  beginRemoveRows( node2index( node ), indexFrom, indexTo );
751 
752  // disconnect from layers and remove their legend
753  Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, _layerNodesInSubtree( node, indexFrom, indexTo ) )
754  disconnectFromLayer( nodeLayer );
755 }
756 
758 {
759  endRemoveRows();
760 }
761 
763 {
764  Q_ASSERT( node );
765 
766  QModelIndex index = node2index( node );
767  emit dataChanged( index, index );
768 }
769 
771 {
772  Q_UNUSED( name );
773  Q_ASSERT( node );
774 
775  QModelIndex index = node2index( node );
776  emit dataChanged( index, index );
777 }
778 
779 
781 {
782  if ( QgsLayerTree::isLayer( node ) && key == "showFeatureCount" )
784 }
785 
786 
788 {
789  QgsLayerTreeLayer* nodeLayer = qobject_cast<QgsLayerTreeLayer*>( sender() );
790  if ( !nodeLayer )
791  return;
792 
793  // deferred connection to the layer
794  connectToLayer( nodeLayer );
795 }
796 
798 {
799  QgsLayerTreeLayer* nodeLayer = qobject_cast<QgsLayerTreeLayer*>( sender() );
800  if ( !nodeLayer )
801  return;
802 
803  disconnectFromLayer( nodeLayer );
804 
805  // wait for the layer to appear again
806  connect( nodeLayer, SIGNAL( layerLoaded() ), this, SLOT( nodeLayerLoaded() ) );
807 }
808 
810 {
811  if ( !testFlag( ShowLegend ) )
812  return;
813 
814  QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( sender() );
815  if ( !layer )
816  return;
817 
818  QgsLayerTreeLayer* nodeLayer = mRootNode->findLayer( layer->id() );
819  if ( !nodeLayer )
820  return;
821 
822  refreshLayerLegend( nodeLayer );
823 }
824 
826 {
827  QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( sender() );
828  if ( !layer )
829  return;
830 
831  QgsLayerTreeLayer* nodeLayer = mRootNode->findLayer( layer->id() );
832  if ( !nodeLayer )
833  return;
834 
835  QModelIndex index = node2index( nodeLayer );
836  emit dataChanged( index, index );
837 
838  if ( nodeLayer->customProperty( "showFeatureCount" ).toInt() )
839  refreshLayerLegend( nodeLayer );
840 }
841 
842 
844 {
845  QgsLayerTreeModelLegendNode* legendNode = qobject_cast<QgsLayerTreeModelLegendNode*>( sender() );
846  if ( !legendNode )
847  return;
848 
849  QModelIndex index = legendNode2index( legendNode );
850  if ( index.isValid() )
851  emit dataChanged( index, index );
852 }
853 
854 
856 {
857  if ( !nodeLayer->layer() )
858  {
859  // in order to connect to layer, we need to have it loaded.
860  // keep an eye on the layer ID: once loaded, we will use it
861  connect( nodeLayer, SIGNAL( layerLoaded() ), this, SLOT( nodeLayerLoaded() ) );
862  return;
863  }
864 
865  // watch if the layer is getting removed
866  connect( nodeLayer, SIGNAL( layerWillBeUnloaded() ), this, SLOT( nodeLayerWillBeUnloaded() ) );
867 
868  if ( testFlag( ShowLegend ) )
869  {
870  addLegendToLayer( nodeLayer );
871 
872  // automatic collapse of legend nodes - useful if a layer has many legend nodes
873  if ( !mRootNode->customProperty( "loading" ).toBool() )
874  {
876  nodeLayer->setExpanded( false );
877  }
878  }
879 
880  QgsMapLayer* layer = nodeLayer->layer();
881  connect( layer, SIGNAL( legendChanged() ), this, SLOT( layerLegendChanged() ), Qt::UniqueConnection );
882 
883  if ( layer->type() == QgsMapLayer::VectorLayer )
884  {
885  // using unique connection because there may be temporarily more nodes for a layer than just one
886  // which would create multiple connections, however disconnect() would disconnect all multiple connections
887  // even if we wanted to disconnect just one connection in each call.
888  connect( layer, SIGNAL( editingStarted() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection );
889  connect( layer, SIGNAL( editingStopped() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection );
890  connect( layer, SIGNAL( layerModified() ), this, SLOT( layerNeedsUpdate() ), Qt::UniqueConnection );
891  }
892 }
893 
894 // try to find out if the layer ID is present in the tree multiple times
895 static int _numLayerCount( QgsLayerTreeGroup* group, const QString& layerId )
896 {
897  int count = 0;
898  Q_FOREACH ( QgsLayerTreeNode* child, group->children() )
899  {
900  if ( QgsLayerTree::isLayer( child ) )
901  {
902  if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
903  count++;
904  }
905  else if ( QgsLayerTree::isGroup( child ) )
906  {
907  count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
908  }
909  }
910  return count;
911 }
912 
914 {
915  disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
916 
917  if ( !nodeLayer->layer() )
918  return; // we were never connected
919 
920  if ( testFlag( ShowLegend ) )
921  {
922  removeLegendFromLayer( nodeLayer );
923  }
924 
925  if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
926  {
927  // last instance of the layer in the tree: disconnect from all signals from layer!
928  disconnect( nodeLayer->layer(), nullptr, this, nullptr );
929  }
930 }
931 
933 {
934  Q_FOREACH ( QgsLayerTreeNode* node, parentGroup->children() )
935  {
936  if ( QgsLayerTree::isGroup( node ) )
938  else if ( QgsLayerTree::isLayer( node ) )
940  }
941 }
942 
944 {
945  Q_FOREACH ( QgsLayerTreeNode* node, parentGroup->children() )
946  {
947  if ( QgsLayerTree::isGroup( node ) )
949  else if ( QgsLayerTree::isLayer( node ) )
951  }
952 }
953 
955 {
956  Q_ASSERT( mRootNode );
957 
958  connect( mRootNode, SIGNAL( willAddChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeWillAddChildren( QgsLayerTreeNode*, int, int ) ) );
959  connect( mRootNode, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
960  connect( mRootNode, SIGNAL( willRemoveChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeWillRemoveChildren( QgsLayerTreeNode*, int, int ) ) );
961  connect( mRootNode, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
962  connect( mRootNode, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
963  connect( mRootNode, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeNameChanged( QgsLayerTreeNode*, QString ) ) );
964 
965  connect( mRootNode, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
966 
968 }
969 
971 {
972  disconnect( mRootNode, nullptr, this, nullptr );
973 
975 }
976 
978 {
979  QgsLayerTreeNode* node = index2node( idx );
980  if ( !node )
981  return;
982 
983  int count = node->children().count();
984  if ( count == 0 )
985  return;
986  emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
987  for ( int i = 0; i < count; ++i )
988  recursivelyEmitDataChanged( index( i, 0, idx ) );
989 }
990 
992 {
993  QgsLayerTreeNode* node = index2node( idx );
994  if ( !node )
995  return;
996 
997  if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
998  {
999  const QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();
1000  if ( layer && layer->hasScaleBasedVisibility() )
1001  {
1002  emit dataChanged( idx, idx );
1003  }
1004  }
1005  int count = node->children().count();
1006  for ( int i = 0; i < count; ++i )
1007  refreshScaleBasedLayers( index( i, 0, idx ) );
1008 }
1009 
1011 {
1012  return Qt::CopyAction | Qt::MoveAction;
1013 }
1014 
1016 {
1017  QStringList types;
1018  types << "application/qgis.layertreemodeldata";
1019  return types;
1020 }
1021 
1022 
1023 QMimeData* QgsLayerTreeModel::mimeData( const QModelIndexList& indexes ) const
1024 {
1025  // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1026  QModelIndexList sortedIndexes = indexes;
1027  qSort( sortedIndexes.begin(), sortedIndexes.end(), qLess<QModelIndex>() );
1028 
1029  QList<QgsLayerTreeNode*> nodesFinal = indexes2nodes( sortedIndexes, true );
1030 
1031  if ( nodesFinal.isEmpty() )
1032  return nullptr;
1033 
1034  QMimeData *mimeData = new QMimeData();
1035 
1036  QDomDocument doc;
1037  QDomElement rootElem = doc.createElement( "layer_tree_model_data" );
1038  Q_FOREACH ( QgsLayerTreeNode* node, nodesFinal )
1039  node->writeXML( rootElem );
1040  doc.appendChild( rootElem );
1041  QString txt = doc.toString();
1042 
1043  mimeData->setData( "application/qgis.layertreemodeldata", txt.toUtf8() );
1044  return mimeData;
1045 }
1046 
1047 bool QgsLayerTreeModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent )
1048 {
1049  if ( action == Qt::IgnoreAction )
1050  return true;
1051 
1052  if ( !data->hasFormat( "application/qgis.layertreemodeldata" ) )
1053  return false;
1054 
1055  if ( column >= columnCount( parent ) )
1056  return false;
1057 
1058  QgsLayerTreeNode* nodeParent = index2node( parent );
1059  if ( !QgsLayerTree::isGroup( nodeParent ) )
1060  return false;
1061 
1062  QByteArray encodedData = data->data( "application/qgis.layertreemodeldata" );
1063 
1064  QDomDocument doc;
1065  if ( !doc.setContent( QString::fromUtf8( encodedData ) ) )
1066  return false;
1067 
1068  QDomElement rootElem = doc.documentElement();
1069  if ( rootElem.tagName() != "layer_tree_model_data" )
1070  return false;
1071 
1073 
1074  QDomElement elem = rootElem.firstChildElement();
1075  while ( !elem.isNull() )
1076  {
1078  if ( node )
1079  nodes << node;
1080 
1081  elem = elem.nextSiblingElement();
1082  }
1083 
1084  if ( nodes.isEmpty() )
1085  return false;
1086 
1087  if ( parent.isValid() && row == -1 )
1088  row = 0; // if dropped directly onto group item, insert at first position
1089 
1090  QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1091 
1092  return true;
1093 }
1094 
1095 bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex& parent )
1096 {
1097  QgsLayerTreeNode* parentNode = index2node( parent );
1098  if ( QgsLayerTree::isGroup( parentNode ) )
1099  {
1100  QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1101  return true;
1102  }
1103  return false;
1104 }
1105 
1106 void QgsLayerTreeModel::setFlags( const QgsLayerTreeModel::Flags& f )
1107 {
1108  mFlags = f;
1109 }
1110 
1112 {
1113  if ( on )
1114  mFlags |= f;
1115  else
1116  mFlags &= ~f;
1117 }
1118 
1119 QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1120 {
1121  return mFlags;
1122 }
1123 
1125 {
1126  return mFlags.testFlag( f );
1127 }
1128 
1130 {
1131  static QIcon icon;
1132 
1133  if ( icon.isNull() )
1134  icon = QgsApplication::getThemeIcon( "/mActionFolder.svg" );
1135 
1136  return icon;
1137 }
1138 
1140 {
1142 
1143  if ( mLegendFilterByScale > 0 )
1144  {
1145  Q_FOREACH ( QgsLayerTreeModelLegendNode* node, nodes )
1146  {
1147  if ( node->isScaleOK( mLegendFilterByScale ) )
1148  filtered << node;
1149  }
1150  }
1151  else if ( mLegendFilterMapSettings )
1152  {
1153  if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layerId() ) )
1154  {
1155  Q_FOREACH ( QgsLayerTreeModelLegendNode* node, nodes )
1156  {
1157  QgsSymbolV2* ruleKey = reinterpret_cast< QgsSymbolV2* >( node->data( QgsSymbolV2LegendNode::SymbolV2LegacyRuleKeyRole ).value<void*>() );
1158  bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1159  if ( ruleKey && checked )
1160  {
1162  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( node->layerNode()->layer() ) )
1163  {
1164  if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1165  filtered << node;
1166  }
1167  }
1168  else // unknown node type or unchecked
1169  filtered << node;
1170  }
1171  }
1172  }
1173  else
1174  {
1175  return nodes;
1176  }
1177 
1178  return filtered;
1179 }
1180 
1181 
1182 
1184 // Legend nodes routines - start
1185 
1187 {
1188  Q_FOREACH ( const LayerLegendData& data, mLegend )
1189  {
1190  qDeleteAll( data.originalNodes );
1191  delete data.tree;
1192  }
1193  mLegend.clear();
1194 }
1195 
1196 
1198 {
1199  if ( mLegend.contains( nodeLayer ) )
1200  {
1201  qDeleteAll( mLegend[nodeLayer].originalNodes );
1202  delete mLegend[nodeLayer].tree;
1203  mLegend.remove( nodeLayer );
1204  }
1205 }
1206 
1207 
1209 {
1210  if ( !nodeL->layer() )
1211  return;
1212 
1213  QgsMapLayer* ml = nodeL->layer();
1214  QgsMapLayerLegend* layerLegend = ml->legend();
1215  if ( !layerLegend )
1216  return;
1217 
1218  bool hasStyleOverride = mLayerStyleOverrides.contains( ml->id() );
1219  if ( hasStyleOverride )
1221 
1223 
1224  // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1226 
1227  if ( testFlag( UseEmbeddedWidgets ) )
1228  {
1229  // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1230  int widgetsCount = ml->customProperty( "embeddedWidgets/count", 0 ).toInt();
1231  while ( widgetsCount > 0 )
1232  {
1233  lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1234  --widgetsCount;
1235  }
1236  }
1237 
1238  QList<QgsLayerTreeModelLegendNode*> filteredLstNew = filterLegendNodes( lstNew );
1239 
1240  Q_FOREACH ( QgsLayerTreeModelLegendNode* n, lstNew )
1241  {
1242  n->setParent( this );
1243  connect( n, SIGNAL( dataChanged() ), this, SLOT( legendNodeDataChanged() ) );
1244  }
1245 
1246  // See if we have an embedded node - if we do, we will not use it among active nodes.
1247  // Legend node embedded in parent does not have to be the first one,
1248  // there can be also nodes generated for embedded widgets
1249  QgsLayerTreeModelLegendNode* embeddedNode = nullptr;
1250  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, filteredLstNew )
1251  {
1252  if ( legendNode->isEmbeddedInParent() )
1253  {
1254  embeddedNode = legendNode;
1255  filteredLstNew.removeOne( legendNode );
1256  break;
1257  }
1258  }
1259 
1260  LayerLegendTree* legendTree = nullptr;
1261 
1262  // maybe the legend nodes form a tree - try to create a tree structure from the list
1263  if ( testFlag( ShowLegendAsTree ) )
1264  legendTree = tryBuildLegendTree( filteredLstNew );
1265 
1266  int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1267 
1268  if ( !filteredLstNew.isEmpty() ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
1269 
1271  data.originalNodes = lstNew;
1272  data.activeNodes = filteredLstNew;
1273  data.embeddedNodeInParent = embeddedNode;
1274  data.tree = legendTree;
1275 
1276  mLegend[nodeL] = data;
1277 
1278  if ( !filteredLstNew.isEmpty() ) endInsertRows();
1279 
1280  if ( hasStyleOverride )
1282 
1283  // invalidate map based data even if the data is not map-based to make sure
1284  // the symbol sizes are computed at least once
1286 }
1287 
1288 
1290 {
1291  // first check whether there are any legend nodes that are not top-level
1292  bool hasParentKeys = false;
1293  Q_FOREACH ( QgsLayerTreeModelLegendNode* n, nodes )
1294  {
1296  {
1297  hasParentKeys = true;
1298  break;
1299  }
1300  }
1301  if ( !hasParentKeys )
1302  return nullptr; // all legend nodes are top-level => stick with list representation
1303 
1304  // make mapping from rules to nodes and do some sanity checks
1306  rule2node[QString()] = nullptr;
1307  Q_FOREACH ( QgsLayerTreeModelLegendNode* n, nodes )
1308  {
1310  if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1311  return nullptr;
1312  if ( rule2node.contains( ruleKey ) ) // and they must be unique
1313  return nullptr;
1314  rule2node[ruleKey] = n;
1315  }
1316 
1317  // create the tree structure
1318  LayerLegendTree* tree = new LayerLegendTree;
1319  Q_FOREACH ( QgsLayerTreeModelLegendNode* n, nodes )
1320  {
1322  QgsLayerTreeModelLegendNode* parent = rule2node.value( parentRuleKey, nullptr );
1323  tree->parents[n] = parent;
1324  tree->children[parent] << n;
1325  }
1326  return tree;
1327 }
1328 
1329 
1331 {
1332  return qobject_cast<QgsLayerTreeModelLegendNode*>( reinterpret_cast<QObject*>( index.internalPointer() ) );
1333 }
1334 
1335 
1337 {
1338  const LayerLegendData& data = mLegend[legendNode->layerNode()];
1339  if ( data.tree )
1340  {
1341  if ( QgsLayerTreeModelLegendNode* parentLegendNode = data.tree->parents[legendNode] )
1342  {
1343  QModelIndex parentIndex = legendNode2index( parentLegendNode );
1344  int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1345  return index( row, 0, parentIndex );
1346  }
1347  else
1348  {
1349  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1350  int row = data.tree->children[nullptr].indexOf( legendNode );
1351  return index( row, 0, parentIndex );
1352  }
1353  }
1354 
1355  QModelIndex parentIndex = node2index( legendNode->layerNode() );
1356  Q_ASSERT( parentIndex.isValid() );
1357  int row = data.activeNodes.indexOf( legendNode );
1358  if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1359  return QModelIndex();
1360 
1361  return index( row, 0, parentIndex );
1362 }
1363 
1364 
1366 {
1367  const LayerLegendData& data = mLegend[node->layerNode()];
1368  if ( data.tree )
1369  return data.tree->children[node].count();
1370 
1371  return 0; // they are leaves
1372 }
1373 
1374 
1376 {
1377  if ( !mLegend.contains( nL ) )
1378  return 0;
1379 
1380  const LayerLegendData& data = mLegend[nL];
1381  if ( data.tree )
1382  return data.tree->children[nullptr].count();
1383 
1384  int count = data.activeNodes.count();
1385  return count;
1386 }
1387 
1388 
1390 {
1391  Q_ASSERT( mLegend.contains( nL ) );
1392  const LayerLegendData& data = mLegend[nL];
1393  if ( data.tree )
1394  return createIndex( row, column, static_cast<QObject*>( data.tree->children[nullptr].at( row ) ) );
1395 
1396  return createIndex( row, column, static_cast<QObject*>( data.activeNodes.at( row ) ) );
1397 }
1398 
1399 
1401 {
1402  const LayerLegendData& data = mLegend[node->layerNode()];
1403  if ( data.tree )
1404  return createIndex( row, column, static_cast<QObject*>( data.tree->children[node].at( row ) ) );
1405 
1406  return QModelIndex(); // have no children
1407 }
1408 
1409 
1411 {
1412  QgsLayerTreeLayer* layerNode = legendNode->layerNode();
1413  const LayerLegendData& data = mLegend[layerNode];
1414  if ( data.tree )
1415  {
1416  if ( QgsLayerTreeModelLegendNode* parentNode = data.tree->parents[legendNode] )
1417  {
1418  QgsLayerTreeModelLegendNode* grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1419  int row = data.tree->children[grandParentNode].indexOf( parentNode );
1420  return createIndex( row, 0, static_cast<QObject*>( parentNode ) );
1421  }
1422  else
1423  return indexOfParentLayerTreeNode( layerNode );
1424  }
1425 
1426  return indexOfParentLayerTreeNode( layerNode );
1427 }
1428 
1429 
1431 {
1432  if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1433  return QVariant();
1434  return node->data( role );
1435 }
1436 
1437 
1439 {
1440  Qt::ItemFlags f = node->flags();
1441  if ( !testFlag( AllowLegendChangeState ) )
1442  f &= ~Qt::ItemIsUserCheckable;
1443  return f;
1444 }
1445 
1446 
1448 {
1449  return mLegend[nodeLayer].embeddedNodeInParent != nullptr;
1450 }
1451 
1453 {
1454  return mLegend[nodeLayer].embeddedNodeInParent;
1455 }
1456 
1457 
1459 {
1460  QgsLayerTreeModelLegendNode* legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1461  if ( !legendNode )
1462  return QIcon();
1463  return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1464 }
1465 
1466 
1468 {
1469  if ( !mLegend.contains( nodeLayer ) )
1471 
1472  const LayerLegendData& data = mLegend[nodeLayer];
1474  if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1475  lst.prepend( data.embeddedNodeInParent );
1476  return lst;
1477 }
1478 
1480 {
1481  return mLegend.value( nodeLayer ).originalNodes;
1482 }
1483 
1485 {
1487  for ( ; it != mLegend.constEnd(); ++it )
1488  {
1489  QgsLayerTreeLayer* layer = it.key();
1490  if ( layer->layerId() == layerId )
1491  {
1492  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, mLegend.value( layer ).activeNodes )
1493  {
1494  if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1495  {
1496  //found it!
1497  return legendNode;
1498  }
1499  }
1500  }
1501  }
1502 
1503  return nullptr;
1504 }
1505 
1507 {
1510  else
1512 }
1513 
1515 {
1516  QgsDebugCall;
1517 
1518  // we have varying icon sizes, and we want icon to be centered and
1519  // text to be left aligned, so we have to compute the max width of icons
1520  //
1521  // we do that for nodes who share a common parent
1522  //
1523  // we do that here because for symbols with size defined in map units
1524  // the symbol sizes changes depends on the zoom level
1525 
1526  Q_FOREACH ( const LayerLegendData& data, mLegend )
1527  {
1528  QList<QgsSymbolV2LegendNode*> symbolNodes;
1529  QMap<QString, int> widthMax;
1530  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, data.originalNodes )
1531  {
1532  QgsSymbolV2LegendNode* n = dynamic_cast<QgsSymbolV2LegendNode*>( legendNode );
1533  if ( n )
1534  {
1535  const QSize sz( n->minimumIconSize() );
1537  widthMax[parentKey] = qMax( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1538  n->setIconSize( sz );
1539  symbolNodes.append( n );
1540  }
1541  }
1542  Q_FOREACH ( QgsSymbolV2LegendNode* n, symbolNodes )
1543  {
1545  Q_ASSERT( widthMax[parentKey] > 0 );
1546  const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1547  n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1548  }
1549  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, data.originalNodes )
1550  legendNode->invalidateMapBasedData();
1551  }
1552 
1553 }
1554 
1555 // Legend nodes routines - end
bool restoreOverrideStyle()
Restore the original store after a call to setOverrideStyle()
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
Layer tree group node serves as a container for layers and further groups.
static const QIcon & iconGroup()
void removeChildren(int from, int count)
Remove child nodes from index "from". The nodes will be deleted.
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:49
QgsLayerTreeGroup * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
void setLayerName(const QString &n)
QMimeData * mimeData(const QModelIndexList &indexes) const override
QByteArray data(const QString &mimeType) const
virtual Qt::ItemFlags flags() const
Return item flags associated with the item.
void removeLegendFromLayer(QgsLayerTreeLayer *nodeLayer)
void connectToLayer(QgsLayerTreeLayer *nodeLayer)
Q_DECL_DEPRECATED bool isIndexSymbologyNode(const QModelIndex &index) const
Return true if index represents a legend node (instead of layer node)
void setCurrentIndex(const QModelIndex &currentIndex)
Set index of the current item. May be used by view. Item marked as current is underlined.
bool contains(const Key &key) const
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
LayerLegendTree * tree
Optional pointer to a tree structure - see LayerLegendTree for details.
QDomNode appendChild(const QDomNode &newChild)
static const QIcon & iconDefault()
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.
static const QIcon & iconPoint()
Definition: qgsdataitem.cpp:99
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool setData(const QVariant &value, int role)
Set some data associated with the item.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
virtual bool hasFormat(const QString &mimeType) const
QModelIndex indexOfParentLayerTreeNode(QgsLayerTreeNode *parentNode) const
QString toString(int indent) const
QObject * sender() const
bool testFlag(Flag f) const
Check whether a flag is enabled.
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.
const T & at(int i) const
const QObjectList & children() const
NodeType nodeType()
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
Qt::CheckState isVisible() const
Structure that stores tree representation of map layer&#39;s legend.
void setUnderline(bool enable)
QDomElement nextSiblingElement(const QString &tagName) const
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...
T value() const
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.
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void recursivelyEmitDataChanged(const QModelIndex &index=QModelIndex())
emit dataChanged() for layer tree node items
static bool _isChildOfNode(QgsLayerTreeNode *child, QgsLayerTreeNode *node)
QDomElement documentElement() const
static const QIcon & iconPolygon()
QString join(const QString &separator) const
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
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Flags mFlags
Set of flags for the model.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
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
Get the type of the layer.
Definition: qgsmaplayer.cpp:99
QString tr(const char *sourceText, const char *disambiguation, int n)
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:353
virtual void writeXML(QDomElement &parentElement)=0
Write layer tree to XML.
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)
QPixmap pixmap(const QSize &size, Mode mode, State state) const
void connectToLayers(QgsLayerTreeGroup *parentGroup)
int & rheight()
void reset(T *other)
The QgsMapSettings class contains configuration for rendering of the map.
int indexOf(const T &value, int from) const
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
Q_DECL_DEPRECATED QgsLayerTreeLayer * layerNodeForSymbologyNode(const QModelIndex &index) const
Return layer node to which a legend node belongs to.
QgsMapLayerStyleManager * styleManager() const
Get access to the layer&#39;s style manager.
void setBold(bool enable)
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Allow renaming of groups and layers.
const char * name() const
rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2...
bool isValid() const
QModelIndex legendParent(QgsLayerTreeModelLegendNode *legendNode) const
int count(const T &value) const
void append(const T &value)
QString fromUtf8(const char *str, int size)
void disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
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
int toInt(bool *ok) const
const Key & key() const
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Return index for a given legend node.
static const QIcon & iconRaster()
void disconnectFromLayers(QgsLayerTreeGroup *parentGroup)
void setLegendFilterByMap(const QgsMapSettings *settings)
Force only display of legend nodes which are valid for given map settings.
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
virtual QVariant data(int role) const override
Return data associated with the item.
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Return legend node for given index.
bool isEmpty() const
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
bool isEmpty() const
QMap< QString, QString > layerStyleOverrides() const
Get map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void setName(const QString &n) override
Set group&#39;s name.
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale)
Get hints about map view - to be used in legend nodes.
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer...
bool isEmpty() const
Returns true if the geometry is empty (ie, contains no underlying geometry accessible via geometry)...
void * internalPointer() const
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 setVisible(Qt::CheckState visible)
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.
QGis::GeometryType geometryType() const
Returns point, line or polygon.
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:34
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.
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
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.
QModelIndex legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const
QModelIndex createIndex(int row, int column, void *ptr) const
QPersistentModelIndex mCurrentIndex
Current index - will be underlined.
#define QgsDebugCall
Definition: qgslogger.h:32
void setRootGroup(QgsLayerTreeGroup *newRootGroup)
Reset the model and use a new root group node.
const T value(const Key &key) const
double mLegendFilterByScale
scale denominator for filtering of legend nodes (<= 0 means no filtering)
void setParent(QObject *parent)
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 setItalic(bool enable)
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
void beginInsertRows(const QModelIndex &parent, int first, int last)
bool isNull() const
QgsMapLayer * layer() const
static const QIcon & iconTable()
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
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
static QgsLayerTreeNode * readXML(QDomElement &element, bool looseMatch=false)
Read layer tree from XML.
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.
QSize minimumIconSize() const
Get the minimum icon size to prevent cropping.
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
typedef DropActions
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) ...
void insert(int i, const T &value)
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView). Added in 2.16.
bool isNull() const
QDomElement firstChildElement(const QString &tagName) const
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
virtual QVariant data(int role) const =0
Return data associated with the item.
int column() const
static const QIcon & iconLine()
bool toBool() const
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
Qt::CheckState isVisible() const
Return the check state of the group node.
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Return filtered list of active legend nodes attached to a particular layer node (by default it return...
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
for QgsSymbolV2LegendNode only - legacy rule key (void ptr, to be cast to QgsSymbolV2 ptr) ...
QString layerName() const
Allow user to set node visibility with a check box.
void start(int msec)
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
void prepend(const T &value)
bool contains(const Key &key) const
QString name() const override
Get group&#39;s name.
Flags flags() const
Return OR-ed combination of model flags.
QString tagName() const
bool setOverrideStyle(const QString &styleDef)
Temporarily apply a different style to the layer.
void setData(const QString &mimeType, const QByteArray &data)
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.
QDomElement createElement(const QString &tagName)
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.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
QObject * parent() const
Represents a vector layer which manages a vector based data sets.
Will use real preview of raster layer as icon (may be slow)
void setVisible(Qt::CheckState state)
Set check state of the group node - will also update children.
void setColor(const QColor &color)
void setFlags(const QgsLayerTreeModel::Flags &f)
Set OR-ed combination of model flags.
QScopedPointer< QgsMapSettings > mLegendFilterMapSettings
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QString toString() const
bool removeOne(const T &value)
int count(const Key &key) const
void setIconSize(QSize sz)
Set the icon size.
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.
QUuid createUuid()
QMap< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer&#39;s legend nodes.
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.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QModelIndex currentIndex() const
Get index of the item marked as current. Item marked as current is underlined.
const T value(const Key &key) const
void replace(int i, const T &value)
void setSingleShot(bool singleShot)
void nodeVisibilityChanged(QgsLayerTreeNode *node)
Structure that stores all data associated with one map layer.
typedef ItemFlags
QByteArray toUtf8() const