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 <QMimeData>
17 #include <QTextStream>
18 
19 #include "qgslayertreemodel.h"
20 
21 #include "qgsapplication.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
24 #include "qgslayertreemodellegendnode.h"
25 #include "qgsproject.h"
26 #include "qgsmaphittest.h"
27 #include "qgsmaplayer.h"
28 #include "qgsmaplayerlegend.h"
29 #include "qgsmaplayerstylemanager.h"
30 #include "qgsmeshlayer.h"
31 #include "qgspluginlayer.h"
32 #include "qgsrasterlayer.h"
33 #include "qgsrenderer.h"
34 #include "qgssymbollayerutils.h"
35 #include "qgsvectorlayer.h"
36 #include "qgslayerdefinition.h"
37 #include "qgsiconutils.h"
38 #include "qgsmimedatautils.h"
39 
40 #include <QPalette>
41 
QgsLayerTreeModel(QgsLayerTree * rootNode,QObject * parent)42 QgsLayerTreeModel::QgsLayerTreeModel( QgsLayerTree *rootNode, QObject *parent )
43   : QAbstractItemModel( parent )
44   , mRootNode( rootNode )
45   , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
46   , mAutoCollapseLegendNodesCount( -1 )
47   , mLegendFilterByScale( 0 )
48   , mLegendFilterUsesExtent( false )
49   , mLegendMapViewMupp( 0 )
50   , mLegendMapViewDpi( 0 )
51   , mLegendMapViewScale( 0 )
52 {
53   connectToRootNode();
54 
55   mFontLayer.setBold( true );
56 
57   connect( &mDeferLegendInvalidationTimer, &QTimer::timeout, this, &QgsLayerTreeModel::invalidateLegendMapBasedData );
58   mDeferLegendInvalidationTimer.setSingleShot( true );
59 }
60 
~QgsLayerTreeModel()61 QgsLayerTreeModel::~QgsLayerTreeModel()
62 {
63   legendCleanup();
64 }
65 
index2node(const QModelIndex & index) const66 QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
67 {
68   if ( !index.isValid() )
69     return mRootNode;
70 
71   QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
72   return qobject_cast<QgsLayerTreeNode *>( obj );
73 }
74 
75 
rowCount(const QModelIndex & parent) const76 int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
77 {
78   if ( QgsLayerTreeModelLegendNode *nodeLegend = index2legendNode( parent ) )
79     return legendNodeRowCount( nodeLegend );
80 
81   QgsLayerTreeNode *n = index2node( parent );
82   if ( !n )
83     return 0;
84 
85   if ( QgsLayerTree::isLayer( n ) )
86   {
87     if ( !testFlag( ShowLegend ) )
88       return 0;
89 
90     return legendRootRowCount( QgsLayerTree::toLayer( n ) );
91   }
92 
93   return n->children().count();
94 }
95 
columnCount(const QModelIndex & parent) const96 int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
97 {
98   Q_UNUSED( parent )
99   return 1;
100 }
101 
index(int row,int column,const QModelIndex & parent) const102 QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
103 {
104   if ( column < 0 || column >= columnCount( parent ) ||
105        row < 0 || row >= rowCount( parent ) )
106     return QModelIndex();
107 
108   if ( QgsLayerTreeModelLegendNode *nodeLegend = index2legendNode( parent ) )
109     return legendNodeIndex( row, column, nodeLegend );
110 
111   QgsLayerTreeNode *n = index2node( parent );
112   if ( !n )
113     return QModelIndex(); // have no children
114 
115   if ( testFlag( ShowLegend ) && QgsLayerTree::isLayer( n ) )
116   {
117     return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
118   }
119 
120   return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
121 }
122 
123 
parent(const QModelIndex & child) const124 QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
125 {
126   if ( !child.isValid() )
127     return QModelIndex();
128 
129   if ( QgsLayerTreeNode *n = index2node( child ) )
130   {
131     return indexOfParentLayerTreeNode( n->parent() ); // must not be null
132   }
133   else if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( child ) )
134   {
135     return legendParent( legendNode );
136   }
137   else
138   {
139     Q_ASSERT( false ); // no other node types!
140     return QModelIndex();
141   }
142 
143 }
144 
145 
indexOfParentLayerTreeNode(QgsLayerTreeNode * parentNode) const146 QModelIndex QgsLayerTreeModel::indexOfParentLayerTreeNode( QgsLayerTreeNode *parentNode ) const
147 {
148   Q_ASSERT( parentNode );
149 
150   QgsLayerTreeNode *grandParentNode = parentNode->parent();
151   if ( !grandParentNode )
152     return QModelIndex();  // root node -> invalid index
153 
154   int row = grandParentNode->children().indexOf( parentNode );
155   Q_ASSERT( row >= 0 );
156 
157   return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
158 }
159 
160 
data(const QModelIndex & index,int role) const161 QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
162 {
163   if ( !index.isValid() || index.column() > 1 )
164     return QVariant();
165 
166   if ( QgsLayerTreeModelLegendNode *sym = index2legendNode( index ) )
167     return legendNodeData( sym, role );
168 
169   QgsLayerTreeNode *node = index2node( index );
170   if ( role == Qt::DisplayRole || role == Qt::EditRole )
171   {
172     if ( QgsLayerTree::isGroup( node ) )
173       return QgsLayerTree::toGroup( node )->name();
174 
175     if ( QgsLayerTree::isLayer( node ) )
176     {
177       QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
178       QString name = nodeLayer->name();
179       if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
180       {
181         QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
182         if ( vlayer && vlayer->featureCount() >= 0 )
183           name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
184       }
185       return name;
186     }
187   }
188   else if ( role == Qt::DecorationRole && index.column() == 0 )
189   {
190     if ( QgsLayerTree::isGroup( node ) )
191       return iconGroup();
192 
193     if ( QgsLayerTree::isLayer( node ) )
194     {
195       QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
196 
197       QgsMapLayer *layer = nodeLayer->layer();
198       if ( !layer )
199         return QVariant();
200 
201       // icons possibly overriding default icon
202       QIcon icon = QgsIconUtils::iconForLayer( layer );
203 
204       // if there's just on legend entry that should be embedded in layer - do that!
205       if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
206       {
207         icon = legendIconEmbeddedInParent( nodeLayer );
208       }
209 
210       if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
211       {
212         const int iconSize = scaleIconSize( 16 );
213         QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
214 
215         QPainter painter( &pixmap );
216         painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
217         painter.end();
218 
219         icon = QIcon( pixmap );
220       }
221 
222       return icon;
223     }
224   }
225   else if ( role == Qt::CheckStateRole )
226   {
227     if ( !testFlag( AllowNodeChangeVisibility ) )
228       return QVariant();
229 
230     if ( QgsLayerTree::isLayer( node ) )
231     {
232       QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
233 
234       if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
235         return QVariant(); // do not show checkbox for non-spatial tables
236 
237       return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
238     }
239     else if ( QgsLayerTree::isGroup( node ) )
240     {
241       QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
242       return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
243     }
244   }
245   else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
246   {
247     QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
248     if ( index == mCurrentIndex )
249       f.setUnderline( true );
250     if ( QgsLayerTree::isLayer( node ) )
251     {
252       const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
253       if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
254       {
255         f.setItalic( !f.italic() );
256       }
257     }
258     return f;
259   }
260   else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
261   {
262     QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
263     if ( QgsLayerTree::isLayer( node ) )
264     {
265       const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
266       if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
267       {
268         QColor fadedTextColor = brush.color();
269         fadedTextColor.setAlpha( 128 );
270         brush.setColor( fadedTextColor );
271       }
272     }
273     return brush;
274   }
275   else if ( role == Qt::ToolTipRole )
276   {
277     if ( QgsLayerTree::isLayer( node ) )
278     {
279       if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
280       {
281         QString title =
282           !layer->title().isEmpty() ? layer->title() :
283           !layer->shortName().isEmpty() ? layer->shortName() :
284           layer->name();
285 
286         title = "<b>" + title.toHtmlEscaped() + "</b>";
287 
288         if ( layer->isSpatial() && layer->crs().isValid() )
289         {
290           if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
291             title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
292           else
293             title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
294         }
295 
296         QStringList parts;
297         parts << title;
298 
299         if ( !layer->abstract().isEmpty() )
300         {
301           parts << QString();
302           const QStringList abstractLines = layer->abstract().split( '\n' );
303           for ( const auto &l : abstractLines )
304           {
305             parts << l.toHtmlEscaped();
306           }
307           parts << QString();
308         }
309 
310         QString source( layer->publicSource() );
311         if ( source.size() > 1024 )
312         {
313           source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
314         }
315 
316         parts << "<i>" + source.toHtmlEscaped() + "</i>";
317 
318         return parts.join( QLatin1String( "<br/>" ) );
319       }
320     }
321   }
322 
323   return QVariant();
324 }
325 
326 
flags(const QModelIndex & index) const327 Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
328 {
329   if ( !index.isValid() )
330   {
331     Qt::ItemFlags rootFlags = Qt::ItemFlags();
332     if ( testFlag( AllowNodeReorder ) )
333       rootFlags |= Qt::ItemIsDropEnabled;
334     return rootFlags;
335   }
336 
337   if ( QgsLayerTreeModelLegendNode *symn = index2legendNode( index ) )
338     return legendNodeFlags( symn );
339 
340   Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
341 
342   if ( testFlag( AllowNodeRename ) )
343     f |= Qt::ItemIsEditable;
344 
345   QgsLayerTreeNode *node = index2node( index );
346   bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
347 
348   if ( testFlag( AllowNodeReorder ) )
349   {
350     // only root embedded nodes can be reordered
351     if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
352       f |= Qt::ItemIsDragEnabled;
353   }
354 
355   if ( testFlag( AllowNodeChangeVisibility ) && ( QgsLayerTree::isLayer( node ) || QgsLayerTree::isGroup( node ) ) )
356     f |= Qt::ItemIsUserCheckable;
357 
358   if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
359     f |= Qt::ItemIsDropEnabled;
360 
361   return f;
362 }
363 
setData(const QModelIndex & index,const QVariant & value,int role)364 bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
365 {
366   QgsLayerTreeModelLegendNode *sym = index2legendNode( index );
367   if ( sym )
368   {
369     if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
370       return false;
371     bool res = sym->setData( value, role );
372     if ( res )
373       emit dataChanged( index, index );
374     return res;
375   }
376 
377   QgsLayerTreeNode *node = index2node( index );
378   if ( !node )
379     return QAbstractItemModel::setData( index, value, role );
380 
381   if ( role == Qt::CheckStateRole )
382   {
383     if ( !testFlag( AllowNodeChangeVisibility ) )
384       return false;
385 
386     bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
387     if ( checked &&  node->children().isEmpty() )
388     {
389       node->setItemVisibilityCheckedParentRecursive( checked );
390     }
391     else if ( testFlag( ActionHierarchical ) )
392     {
393       if ( node->children().isEmpty() )
394         node->setItemVisibilityCheckedParentRecursive( checked );
395       else
396         node->setItemVisibilityCheckedRecursive( checked );
397     }
398     else
399     {
400       node->setItemVisibilityChecked( checked );
401     }
402 
403     recursivelyEmitDataChanged( index );
404 
405     return true;
406   }
407   else if ( role == Qt::EditRole )
408   {
409     if ( !testFlag( AllowNodeRename ) )
410       return false;
411 
412     if ( QgsLayerTree::isLayer( node ) )
413     {
414       QgsLayerTreeLayer *layer = QgsLayerTree::toLayer( node );
415       layer->setName( value.toString() );
416       emit dataChanged( index, index );
417     }
418     else if ( QgsLayerTree::isGroup( node ) )
419     {
420       QgsLayerTree::toGroup( node )->setName( value.toString() );
421       emit dataChanged( index, index );
422     }
423   }
424 
425   return QAbstractItemModel::setData( index, value, role );
426 }
427 
node2index(QgsLayerTreeNode * node) const428 QModelIndex QgsLayerTreeModel::node2index( QgsLayerTreeNode *node ) const
429 {
430   if ( !node || !node->parent() )
431     return QModelIndex(); // this is the only root item -> invalid index
432 
433   QModelIndex parentIndex = node2index( node->parent() );
434 
435   int row = node->parent()->children().indexOf( node );
436   Q_ASSERT( row >= 0 );
437   return index( row, 0, parentIndex );
438 }
439 
440 
_isChildOfNode(QgsLayerTreeNode * child,QgsLayerTreeNode * node)441 static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
442 {
443   if ( !child->parent() )
444     return false;
445 
446   if ( child->parent() == node )
447     return true;
448 
449   return _isChildOfNode( child->parent(), node );
450 }
451 
_isChildOfNodes(QgsLayerTreeNode * child,const QList<QgsLayerTreeNode * > & nodes)452 static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
453 {
454   for ( QgsLayerTreeNode *n : nodes )
455   {
456     if ( _isChildOfNode( child, n ) )
457       return true;
458   }
459 
460   return false;
461 }
462 
463 
indexes2nodes(const QModelIndexList & list,bool skipInternal) const464 QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
465 {
466   QList<QgsLayerTreeNode *> nodes;
467   const auto constList = list;
468   for ( const QModelIndex &index : constList )
469   {
470     QgsLayerTreeNode *node = index2node( index );
471     if ( !node )
472       continue;
473 
474     nodes << node;
475   }
476 
477   if ( !skipInternal )
478     return nodes;
479 
480   // remove any children of nodes if both parent node and children are selected
481   QList<QgsLayerTreeNode *> nodesFinal;
482   for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
483   {
484     if ( !_isChildOfNodes( node, nodes ) )
485       nodesFinal << node;
486   }
487 
488   return nodesFinal;
489 }
490 
rootGroup() const491 QgsLayerTree *QgsLayerTreeModel::rootGroup() const
492 {
493   return mRootNode;
494 }
495 
setRootGroup(QgsLayerTree * newRootGroup)496 void QgsLayerTreeModel::setRootGroup( QgsLayerTree *newRootGroup )
497 {
498   beginResetModel();
499 
500   disconnectFromRootNode();
501 
502   Q_ASSERT( mLegend.isEmpty() );
503 
504   mRootNode = newRootGroup;
505 
506   endResetModel();
507 
508   connectToRootNode();
509 }
510 
refreshLayerLegend(QgsLayerTreeLayer * nodeLayer)511 void QgsLayerTreeModel::refreshLayerLegend( QgsLayerTreeLayer *nodeLayer )
512 {
513   // update title
514   QModelIndex idx = node2index( nodeLayer );
515   emit dataChanged( idx, idx );
516 
517   // update children
518   int oldNodeCount = rowCount( idx );
519   if ( oldNodeCount > 0 )
520   {
521     beginRemoveRows( idx, 0, oldNodeCount - 1 );
522     removeLegendFromLayer( nodeLayer );
523     endRemoveRows();
524   }
525 
526   addLegendToLayer( nodeLayer );
527   int newNodeCount = rowCount( idx );
528 
529   // automatic collapse of legend nodes - useful if a layer has many legend nodes
530   if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
531     nodeLayer->setExpanded( false );
532 }
533 
currentIndex() const534 QModelIndex QgsLayerTreeModel::currentIndex() const
535 {
536   return mCurrentIndex;
537 }
538 
setCurrentIndex(const QModelIndex & currentIndex)539 void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
540 {
541   mCurrentIndex = currentIndex;
542 }
543 
544 
setLayerTreeNodeFont(int nodeType,const QFont & font)545 void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
546 {
547   if ( nodeType == QgsLayerTreeNode::NodeGroup )
548   {
549     if ( mFontGroup != font )
550     {
551       mFontGroup = font;
552       recursivelyEmitDataChanged();
553     }
554   }
555   else if ( nodeType == QgsLayerTreeNode::NodeLayer )
556   {
557     if ( mFontLayer != font )
558     {
559       mFontLayer = font;
560       recursivelyEmitDataChanged();
561     }
562   }
563   else
564   {
565     QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
566   }
567 }
568 
569 
layerTreeNodeFont(int nodeType) const570 QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
571 {
572   if ( nodeType == QgsLayerTreeNode::NodeGroup )
573     return mFontGroup;
574   else if ( nodeType == QgsLayerTreeNode::NodeLayer )
575     return mFontLayer;
576   else
577   {
578     QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
579     return QFont();
580   }
581 }
582 
setLegendFilterByScale(double scale)583 void QgsLayerTreeModel::setLegendFilterByScale( double scale )
584 {
585   mLegendFilterByScale = scale;
586 
587   // this could be later done in more efficient way
588   // by just updating active legend nodes, without refreshing original legend nodes
589   const auto layers = mRootNode->findLayers();
590   for ( QgsLayerTreeLayer *nodeLayer : layers )
591     refreshLayerLegend( nodeLayer );
592 }
593 
setLegendFilterByMap(const QgsMapSettings * settings)594 void QgsLayerTreeModel::setLegendFilterByMap( const QgsMapSettings *settings )
595 {
596   setLegendFilter( settings, /* useExtent = */ true );
597 }
598 
setLegendFilter(const QgsMapSettings * settings,bool useExtent,const QgsGeometry & polygon,bool useExpressions)599 void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
600 {
601   if ( settings && settings->hasValidSettings() )
602   {
603     mLegendFilterMapSettings.reset( new QgsMapSettings( *settings ) );
604     mLegendFilterMapSettings->setLayerStyleOverrides( mLayerStyleOverrides );
605     QgsMapHitTest::LayerFilterExpression exprs;
606     mLegendFilterUsesExtent = useExtent;
607     // collect expression filters
608     if ( useExpressions )
609     {
610       const auto layers = mRootNode->findLayers();
611       for ( QgsLayerTreeLayer *nodeLayer : layers )
612       {
613         bool enabled;
614         QString expr = QgsLayerTreeUtils::legendFilterByExpression( *nodeLayer, &enabled );
615         if ( enabled && !expr.isEmpty() )
616         {
617           exprs[ nodeLayer->layerId()] = expr;
618         }
619       }
620     }
621     bool polygonValid = !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry;
622     if ( useExpressions && !useExtent && !polygonValid ) // only expressions
623     {
624       mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, exprs ) );
625     }
626     else
627     {
628       mLegendFilterHitTest.reset( new QgsMapHitTest( *mLegendFilterMapSettings, polygon, exprs ) );
629     }
630     mLegendFilterHitTest->run();
631   }
632   else
633   {
634     if ( !mLegendFilterMapSettings )
635       return; // no change
636 
637     mLegendFilterMapSettings.reset();
638     mLegendFilterHitTest.reset();
639   }
640 
641   // temporarily disable autocollapse so that legend nodes stay visible
642   int bkAutoCollapse = autoCollapseLegendNodes();
643   setAutoCollapseLegendNodes( -1 );
644 
645   // this could be later done in more efficient way
646   // by just updating active legend nodes, without refreshing original legend nodes
647   const auto layers = mRootNode->findLayers();
648   for ( QgsLayerTreeLayer *nodeLayer : layers )
649     refreshLayerLegend( nodeLayer );
650 
651   setAutoCollapseLegendNodes( bkAutoCollapse );
652 }
653 
setLegendMapViewData(double mapUnitsPerPixel,int dpi,double scale)654 void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
655 {
656   if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
657     return;
658 
659   double previousScale = mLegendMapViewScale;
660   mLegendMapViewScale = scale;
661   mLegendMapViewMupp = mapUnitsPerPixel;
662   mLegendMapViewDpi = dpi;
663 
664   // now invalidate legend nodes!
665   legendInvalidateMapBasedData();
666 
667   if ( scale != previousScale )
668     refreshScaleBasedLayers( QModelIndex(), previousScale );
669 }
670 
legendMapViewData(double * mapUnitsPerPixel,int * dpi,double * scale) const671 void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
672 {
673   if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
674   if ( dpi ) *dpi = mLegendMapViewDpi;
675   if ( scale ) *scale = mLegendMapViewScale;
676 }
677 
layerStyleOverrides() const678 QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
679 {
680   return mLayerStyleOverrides;
681 }
682 
setLayerStyleOverrides(const QMap<QString,QString> & overrides)683 void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
684 {
685   mLayerStyleOverrides = overrides;
686 }
687 
scaleIconSize(int standardSize)688 int QgsLayerTreeModel::scaleIconSize( int standardSize )
689 {
690   return QgsApplication::scaleIconSize( standardSize, true );
691 }
692 
nodeWillAddChildren(QgsLayerTreeNode * node,int indexFrom,int indexTo)693 void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
694 {
695   beginInsertRows( node2index( node ), indexFrom, indexTo );
696 }
697 
_layerNodesInSubtree(QgsLayerTreeNode * node,int indexFrom,int indexTo)698 static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
699 {
700   QList<QgsLayerTreeNode *> children = node->children();
701   QList<QgsLayerTreeLayer *> newLayerNodes;
702   for ( int i = indexFrom; i <= indexTo; ++i )
703   {
704     QgsLayerTreeNode *child = children.at( i );
705     if ( QgsLayerTree::isLayer( child ) )
706       newLayerNodes << QgsLayerTree::toLayer( child );
707     else if ( QgsLayerTree::isGroup( child ) )
708       newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
709   }
710   return newLayerNodes;
711 }
712 
nodeAddedChildren(QgsLayerTreeNode * node,int indexFrom,int indexTo)713 void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
714 {
715   Q_ASSERT( node );
716 
717   endInsertRows();
718 
719   const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
720   for ( QgsLayerTreeLayer *newLayerNode : subNodes )
721     connectToLayer( newLayerNode );
722 }
723 
nodeWillRemoveChildren(QgsLayerTreeNode * node,int indexFrom,int indexTo)724 void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
725 {
726   Q_ASSERT( node );
727 
728   beginRemoveRows( node2index( node ), indexFrom, indexTo );
729 
730   // disconnect from layers and remove their legend
731   const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
732   for ( QgsLayerTreeLayer *nodeLayer : subNodes )
733     disconnectFromLayer( nodeLayer );
734 }
735 
nodeRemovedChildren()736 void QgsLayerTreeModel::nodeRemovedChildren()
737 {
738   endRemoveRows();
739 }
740 
nodeVisibilityChanged(QgsLayerTreeNode * node)741 void QgsLayerTreeModel::nodeVisibilityChanged( QgsLayerTreeNode *node )
742 {
743   Q_ASSERT( node );
744 
745   const QModelIndex index = node2index( node );
746   emit dataChanged( index, index );
747 }
748 
nodeNameChanged(QgsLayerTreeNode * node,const QString & name)749 void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
750 {
751   Q_UNUSED( name )
752   Q_ASSERT( node );
753 
754   const QModelIndex index = node2index( node );
755   emit dataChanged( index, index );
756 }
757 
758 
nodeCustomPropertyChanged(QgsLayerTreeNode * node,const QString & key)759 void QgsLayerTreeModel::nodeCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
760 {
761   if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
762     refreshLayerLegend( QgsLayerTree::toLayer( node ) );
763 }
764 
765 
nodeLayerLoaded()766 void QgsLayerTreeModel::nodeLayerLoaded()
767 {
768   QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
769   if ( !nodeLayer )
770     return;
771 
772   // deferred connection to the layer
773   connectToLayer( nodeLayer );
774 }
775 
nodeLayerWillBeUnloaded()776 void QgsLayerTreeModel::nodeLayerWillBeUnloaded()
777 {
778   QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
779   if ( !nodeLayer )
780     return;
781 
782   disconnectFromLayer( nodeLayer );
783 
784   // wait for the layer to appear again
785   connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded );
786 }
787 
layerLegendChanged()788 void QgsLayerTreeModel::layerLegendChanged()
789 {
790   if ( !mRootNode )
791     return;
792 
793   if ( !testFlag( ShowLegend ) )
794     return;
795 
796   QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
797   if ( !layer )
798     return;
799 
800   QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
801   if ( !nodeLayer )
802     return;
803 
804   refreshLayerLegend( nodeLayer );
805 }
806 
layerFlagsChanged()807 void QgsLayerTreeModel::layerFlagsChanged()
808 {
809   if ( !mRootNode )
810     return;
811 
812   QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
813   if ( !layer )
814     return;
815 
816   QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
817   if ( !nodeLayer )
818     return;
819 
820   const QModelIndex index = node2index( nodeLayer );
821   emit dataChanged( index, index );
822 }
823 
layerNeedsUpdate()824 void QgsLayerTreeModel::layerNeedsUpdate()
825 {
826   QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
827   if ( !layer )
828     return;
829 
830   QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
831   if ( !nodeLayer )
832     return;
833 
834   QModelIndex index = node2index( nodeLayer );
835   emit dataChanged( index, index );
836 
837   if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
838     refreshLayerLegend( nodeLayer );
839 }
840 
841 
legendNodeDataChanged()842 void QgsLayerTreeModel::legendNodeDataChanged()
843 {
844   QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
845   if ( !legendNode )
846     return;
847 
848   QModelIndex index = legendNode2index( legendNode );
849   if ( index.isValid() )
850     emit dataChanged( index, index );
851 }
852 
legendNodeSizeChanged()853 void QgsLayerTreeModel::legendNodeSizeChanged()
854 {
855   QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
856   if ( !legendNode )
857     return;
858 
859   QModelIndex index = legendNode2index( legendNode );
860   if ( index.isValid() )
861     emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
862 }
863 
864 
connectToLayer(QgsLayerTreeLayer * nodeLayer)865 void QgsLayerTreeModel::connectToLayer( QgsLayerTreeLayer *nodeLayer )
866 {
867   if ( !nodeLayer->layer() )
868   {
869     // in order to connect to layer, we need to have it loaded.
870     // keep an eye on the layer ID: once loaded, we will use it
871     connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
872     return;
873   }
874 
875   // watch if the layer is getting removed
876   connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
877 
878   if ( testFlag( ShowLegend ) )
879   {
880     addLegendToLayer( nodeLayer );
881 
882     // automatic collapse of legend nodes - useful if a layer has many legend nodes
883     if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
884     {
885       if ( mAutoCollapseLegendNodesCount != -1 && rowCount( node2index( nodeLayer ) )  >= mAutoCollapseLegendNodesCount )
886         nodeLayer->setExpanded( false );
887     }
888   }
889 
890   QgsMapLayer *layer = nodeLayer->layer();
891   connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
892   connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
893 
894   // using unique connection because there may be temporarily more nodes for a layer than just one
895   // which would create multiple connections, however disconnect() would disconnect all multiple connections
896   // even if we wanted to disconnect just one connection in each call.
897   connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
898   connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
899   connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
900 
901   emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
902 }
903 
904 // try to find out if the layer ID is present in the tree multiple times
_numLayerCount(QgsLayerTreeGroup * group,const QString & layerId)905 static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
906 {
907   int count = 0;
908   const auto constChildren = group->children();
909   for ( QgsLayerTreeNode *child : constChildren )
910   {
911     if ( QgsLayerTree::isLayer( child ) )
912     {
913       if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
914         count++;
915     }
916     else if ( QgsLayerTree::isGroup( child ) )
917     {
918       count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
919     }
920   }
921   return count;
922 }
923 
disconnectFromLayer(QgsLayerTreeLayer * nodeLayer)924 void QgsLayerTreeModel::disconnectFromLayer( QgsLayerTreeLayer *nodeLayer )
925 {
926   disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
927 
928   if ( !nodeLayer->layer() )
929     return; // we were never connected
930 
931   if ( testFlag( ShowLegend ) )
932   {
933     removeLegendFromLayer( nodeLayer );
934   }
935 
936   if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
937   {
938     // last instance of the layer in the tree: disconnect from all signals from layer!
939     disconnect( nodeLayer->layer(), nullptr, this, nullptr );
940   }
941 }
942 
connectToLayers(QgsLayerTreeGroup * parentGroup)943 void QgsLayerTreeModel::connectToLayers( QgsLayerTreeGroup *parentGroup )
944 {
945   const auto constChildren = parentGroup->children();
946   for ( QgsLayerTreeNode *node : constChildren )
947   {
948     if ( QgsLayerTree::isGroup( node ) )
949       connectToLayers( QgsLayerTree::toGroup( node ) );
950     else if ( QgsLayerTree::isLayer( node ) )
951       connectToLayer( QgsLayerTree::toLayer( node ) );
952   }
953 }
954 
disconnectFromLayers(QgsLayerTreeGroup * parentGroup)955 void QgsLayerTreeModel::disconnectFromLayers( QgsLayerTreeGroup *parentGroup )
956 {
957   const auto constChildren = parentGroup->children();
958   for ( QgsLayerTreeNode *node : constChildren )
959   {
960     if ( QgsLayerTree::isGroup( node ) )
961       disconnectFromLayers( QgsLayerTree::toGroup( node ) );
962     else if ( QgsLayerTree::isLayer( node ) )
963       disconnectFromLayer( QgsLayerTree::toLayer( node ) );
964   }
965 }
966 
connectToRootNode()967 void QgsLayerTreeModel::connectToRootNode()
968 {
969   Q_ASSERT( mRootNode );
970 
971   connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
972   connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
973   connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
974   connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
975   connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
976   connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
977   connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
978 
979   connectToLayers( mRootNode );
980 }
981 
disconnectFromRootNode()982 void QgsLayerTreeModel::disconnectFromRootNode()
983 {
984   disconnect( mRootNode, nullptr, this, nullptr );
985 
986   disconnectFromLayers( mRootNode );
987 }
988 
recursivelyEmitDataChanged(const QModelIndex & idx)989 void QgsLayerTreeModel::recursivelyEmitDataChanged( const QModelIndex &idx )
990 {
991   QgsLayerTreeNode *node = index2node( idx );
992   if ( !node )
993     return;
994 
995   int count = node->children().count();
996   if ( count == 0 )
997     return;
998   emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
999   for ( int i = 0; i < count; ++i )
1000     recursivelyEmitDataChanged( index( i, 0, idx ) );
1001 }
1002 
refreshScaleBasedLayers(const QModelIndex & idx,double previousScale)1003 void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1004 {
1005   QgsLayerTreeNode *node = index2node( idx );
1006   if ( !node )
1007     return;
1008 
1009   if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1010   {
1011     const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1012     if ( layer && layer->hasScaleBasedVisibility() )
1013     {
1014       if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1015         emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1016     }
1017   }
1018   int count = node->children().count();
1019   for ( int i = 0; i < count; ++i )
1020     refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1021 }
1022 
supportedDropActions() const1023 Qt::DropActions QgsLayerTreeModel::supportedDropActions() const
1024 {
1025   return Qt::CopyAction | Qt::MoveAction;
1026 }
1027 
mimeTypes() const1028 QStringList QgsLayerTreeModel::mimeTypes() const
1029 {
1030   QStringList types;
1031   types << QStringLiteral( "application/qgis.layertreemodeldata" );
1032   return types;
1033 }
1034 
1035 
mimeData(const QModelIndexList & indexes) const1036 QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1037 {
1038   // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1039   QModelIndexList sortedIndexes = indexes;
1040   std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1041 
1042   QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1043 
1044   if ( nodesFinal.isEmpty() )
1045     return nullptr;
1046 
1047   QMimeData *mimeData = new QMimeData();
1048 
1049   QDomDocument layerTreeDoc;
1050   QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1051 
1052   for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1053   {
1054     node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1055   }
1056   layerTreeDoc.appendChild( rootLayerTreeElem );
1057 
1058   QString errorMessage;
1059   QgsReadWriteContext readWriteContext;
1060   QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1061   QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1062 
1063   QString txt = layerDefinitionsDoc.toString();
1064 
1065   mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1066   mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1067   mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1068   mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1069 
1070   return mimeData;
1071 }
1072 
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)1073 bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1074 {
1075   if ( action == Qt::IgnoreAction )
1076     return true;
1077 
1078   if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1079     return false;
1080 
1081   if ( column >= columnCount( parent ) )
1082     return false;
1083 
1084   QgsLayerTreeNode *nodeParent = index2node( parent );
1085   if ( !QgsLayerTree::isGroup( nodeParent ) )
1086     return false;
1087 
1088   if ( parent.isValid() && row == -1 )
1089     row = 0; // if dropped directly onto group item, insert at first position
1090 
1091   // if we are coming from another QGIS instance, we need to add the layers too
1092   bool ok = false;
1093   // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1094   qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1095 
1096   if ( ok && qgisPid != QCoreApplication::applicationPid() )
1097   {
1098     QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1099     QDomDocument layerDefinitionDoc;
1100     if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1101       return false;
1102     QgsReadWriteContext context;
1103     QString errorMessage;
1104     QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1105     emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1106   }
1107   else
1108   {
1109     QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1110 
1111     QDomDocument layerTreeDoc;
1112     if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1113       return false;
1114 
1115     QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1116     if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1117       return false;
1118 
1119     QList<QgsLayerTreeNode *> nodes;
1120 
1121     QDomElement elem = rootLayerTreeElem.firstChildElement();
1122     while ( !elem.isNull() )
1123     {
1124       QgsLayerTreeNode *node = QgsLayerTreeNode::readXml( elem, QgsProject::instance() );
1125       if ( node )
1126         nodes << node;
1127 
1128       elem = elem.nextSiblingElement();
1129     }
1130 
1131     if ( nodes.isEmpty() )
1132       return false;
1133 
1134     QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1135   }
1136   return true;
1137 }
1138 
removeRows(int row,int count,const QModelIndex & parent)1139 bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1140 {
1141   QgsLayerTreeNode *parentNode = index2node( parent );
1142   if ( QgsLayerTree::isGroup( parentNode ) )
1143   {
1144     QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1145     return true;
1146   }
1147   return false;
1148 }
1149 
setFlags(QgsLayerTreeModel::Flags f)1150 void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1151 {
1152   mFlags = f;
1153 }
1154 
setFlag(QgsLayerTreeModel::Flag f,bool on)1155 void QgsLayerTreeModel::setFlag( QgsLayerTreeModel::Flag f, bool on )
1156 {
1157   if ( on )
1158     mFlags |= f;
1159   else
1160     mFlags &= ~f;
1161 }
1162 
flags() const1163 QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1164 {
1165   return mFlags;
1166 }
1167 
testFlag(QgsLayerTreeModel::Flag f) const1168 bool QgsLayerTreeModel::testFlag( QgsLayerTreeModel::Flag f ) const
1169 {
1170   return mFlags.testFlag( f );
1171 }
1172 
iconGroup()1173 QIcon QgsLayerTreeModel::iconGroup()
1174 {
1175   return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1176 }
1177 
filterLegendNodes(const QList<QgsLayerTreeModelLegendNode * > & nodes)1178 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1179 {
1180   QList<QgsLayerTreeModelLegendNode *> filtered;
1181 
1182   if ( mLegendFilterByScale > 0 )
1183   {
1184     for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1185     {
1186       if ( node->isScaleOK( mLegendFilterByScale ) )
1187         filtered << node;
1188     }
1189   }
1190   else if ( mLegendFilterMapSettings )
1191   {
1192     if ( !nodes.isEmpty() && mLegendFilterMapSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1193     {
1194       for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1195       {
1196         switch ( node->data( QgsSymbolLegendNode::NodeTypeRole ).value<QgsLayerTreeModelLegendNode::NodeTypes>() )
1197         {
1198           case QgsLayerTreeModelLegendNode::EmbeddedWidget:
1199             filtered << node;
1200             break;
1201 
1202           case QgsLayerTreeModelLegendNode::SimpleLegend:
1203           case QgsLayerTreeModelLegendNode::SymbolLegend:
1204           case QgsLayerTreeModelLegendNode::RasterSymbolLegend:
1205           case QgsLayerTreeModelLegendNode::ImageLegend:
1206           case QgsLayerTreeModelLegendNode::WmsLegend:
1207           case QgsLayerTreeModelLegendNode::DataDefinedSizeLegend:
1208           case QgsLayerTreeModelLegendNode::ColorRampLegend:
1209           {
1210             const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1211             bool checked = mLegendFilterUsesExtent || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1212             if ( checked )
1213             {
1214               if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1215               {
1216                 if ( mLegendFilterHitTest->legendKeyVisible( ruleKey, vl ) )
1217                   filtered << node;
1218               }
1219               else
1220               {
1221                 filtered << node;
1222               }
1223             }
1224             else  // unknown node type or unchecked
1225               filtered << node;
1226             break;
1227           }
1228         }
1229       }
1230     }
1231   }
1232   else
1233   {
1234     return nodes;
1235   }
1236 
1237   return filtered;
1238 }
1239 
1240 
1241 
1242 ///////////////////////////////////////////////////////////////////////////////
1243 // Legend nodes routines - start
1244 
legendCleanup()1245 void QgsLayerTreeModel::legendCleanup()
1246 {
1247   const auto constMLegend = mLegend;
1248   for ( const LayerLegendData &data : constMLegend )
1249   {
1250     qDeleteAll( data.originalNodes );
1251     delete data.tree;
1252   }
1253   mLegend.clear();
1254 }
1255 
1256 
removeLegendFromLayer(QgsLayerTreeLayer * nodeLayer)1257 void QgsLayerTreeModel::removeLegendFromLayer( QgsLayerTreeLayer *nodeLayer )
1258 {
1259   if ( mLegend.contains( nodeLayer ) )
1260   {
1261     qDeleteAll( mLegend[nodeLayer].originalNodes );
1262     delete mLegend[nodeLayer].tree;
1263     mLegend.remove( nodeLayer );
1264   }
1265 }
1266 
1267 
addLegendToLayer(QgsLayerTreeLayer * nodeL)1268 void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL )
1269 {
1270   if ( !nodeL || !nodeL->layer() )
1271     return;
1272 
1273   QgsMapLayer *ml = nodeL->layer();
1274 
1275   QgsMapLayerStyleOverride styleOverride( ml );
1276   if ( mLayerStyleOverrides.contains( ml->id() ) )
1277     styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1278 
1279   QgsMapLayerLegend *layerLegend = ml->legend();
1280   if ( !layerLegend )
1281     return;
1282   QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1283 
1284   // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1285   QgsMapLayerLegendUtils::applyLayerNodeProperties( nodeL, lstNew );
1286 
1287   if ( testFlag( UseEmbeddedWidgets ) )
1288   {
1289     // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1290     int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1291     while ( widgetsCount > 0 )
1292     {
1293       lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1294       --widgetsCount;
1295     }
1296   }
1297 
1298   QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1299 
1300   const auto constLstNew = lstNew;
1301   for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1302   {
1303     n->setParent( this );
1304     connect( n, &QgsLayerTreeModelLegendNode::dataChanged, this, &QgsLayerTreeModel::legendNodeDataChanged );
1305     connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1306   }
1307 
1308   // See if we have an embedded node - if we do, we will not use it among active nodes.
1309   // Legend node embedded in parent does not have to be the first one,
1310   // there can be also nodes generated for embedded widgets
1311   QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1312   const auto constFilteredLstNew = filteredLstNew;
1313   for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1314   {
1315     if ( legendNode->isEmbeddedInParent() )
1316     {
1317       embeddedNode = legendNode;
1318       filteredLstNew.removeOne( legendNode );
1319       break;
1320     }
1321   }
1322 
1323   LayerLegendTree *legendTree = nullptr;
1324 
1325   // maybe the legend nodes form a tree - try to create a tree structure from the list
1326   if ( testFlag( ShowLegendAsTree ) )
1327     legendTree = tryBuildLegendTree( filteredLstNew );
1328 
1329   int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1330 
1331   if ( !filteredLstNew.isEmpty() )
1332   {
1333     // Make sure it's clear
1334     const QModelIndex nodeIndex { node2index( nodeL ) };
1335     if ( rowCount( nodeIndex ) > 0 )
1336     {
1337       beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1338       mLegend[nodeL] = LayerLegendData();
1339       endRemoveRows();
1340     }
1341     beginInsertRows( node2index( nodeL ), 0, count - 1 );
1342   }
1343 
1344   LayerLegendData data;
1345   data.originalNodes = lstNew;
1346   data.activeNodes = filteredLstNew;
1347   data.embeddedNodeInParent = embeddedNode;
1348   data.tree = legendTree;
1349 
1350   mLegend[nodeL] = data;
1351 
1352   if ( !filteredLstNew.isEmpty() )
1353   {
1354     endInsertRows();
1355   }
1356 
1357   // invalidate map based data even if the data is not map-based to make sure
1358   // the symbol sizes are computed at least once
1359   mInvalidatedNodes.insert( nodeL );
1360   legendInvalidateMapBasedData();
1361 }
1362 
1363 
tryBuildLegendTree(const QList<QgsLayerTreeModelLegendNode * > & nodes)1364 QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1365 {
1366   // first check whether there are any legend nodes that are not top-level
1367   bool hasParentKeys = false;
1368   for ( QgsLayerTreeModelLegendNode *n : nodes )
1369   {
1370     if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1371     {
1372       hasParentKeys = true;
1373       break;
1374     }
1375   }
1376   if ( !hasParentKeys )
1377     return nullptr; // all legend nodes are top-level => stick with list representation
1378 
1379   // make mapping from rules to nodes and do some sanity checks
1380   QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1381   rule2node[QString()] = nullptr;
1382   for ( QgsLayerTreeModelLegendNode *n : nodes )
1383   {
1384     QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1385     if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1386       return nullptr;
1387     if ( rule2node.contains( ruleKey ) ) // and they must be unique
1388       return nullptr;
1389     rule2node[ruleKey] = n;
1390   }
1391 
1392   // create the tree structure
1393   LayerLegendTree *tree = new LayerLegendTree;
1394   for ( QgsLayerTreeModelLegendNode *n : nodes )
1395   {
1396     QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1397     QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1398     tree->parents[n] = parent;
1399     tree->children[parent] << n;
1400   }
1401   return tree;
1402 }
1403 
createTemporaryRenderContext() const1404 QgsRenderContext *QgsLayerTreeModel::createTemporaryRenderContext() const
1405 {
1406   double scale = 0.0;
1407   double mupp = 0.0;
1408   int dpi = 0;
1409   legendMapViewData( &mupp, &dpi, &scale );
1410   bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1411 
1412   // setup temporary render context
1413   std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1414   context->setScaleFactor( dpi / 25.4 );
1415   context->setRendererScale( scale );
1416   context->setMapToPixel( QgsMapToPixel( mupp ) );
1417   context->setFlag( Qgis::RenderContextFlag::RenderSymbolPreview );
1418   return validData ? context.release() : nullptr;
1419 }
1420 
1421 
index2legendNode(const QModelIndex & index)1422 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::index2legendNode( const QModelIndex &index )
1423 {
1424   return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1425 }
1426 
1427 
legendNode2index(QgsLayerTreeModelLegendNode * legendNode)1428 QModelIndex QgsLayerTreeModel::legendNode2index( QgsLayerTreeModelLegendNode *legendNode )
1429 {
1430   const LayerLegendData &data = mLegend[legendNode->layerNode()];
1431   if ( data.tree )
1432   {
1433     if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1434     {
1435       QModelIndex parentIndex = legendNode2index( parentLegendNode );
1436       int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1437       return index( row, 0, parentIndex );
1438     }
1439     else
1440     {
1441       QModelIndex parentIndex = node2index( legendNode->layerNode() );
1442       int row = data.tree->children[nullptr].indexOf( legendNode );
1443       return index( row, 0, parentIndex );
1444     }
1445   }
1446 
1447   QModelIndex parentIndex = node2index( legendNode->layerNode() );
1448   Q_ASSERT( parentIndex.isValid() );
1449   int row = data.activeNodes.indexOf( legendNode );
1450   if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1451     return QModelIndex();
1452 
1453   return index( row, 0, parentIndex );
1454 }
1455 
1456 
legendNodeRowCount(QgsLayerTreeModelLegendNode * node) const1457 int QgsLayerTreeModel::legendNodeRowCount( QgsLayerTreeModelLegendNode *node ) const
1458 {
1459   const LayerLegendData &data = mLegend[node->layerNode()];
1460   if ( data.tree )
1461     return data.tree->children[node].count();
1462 
1463   return 0; // they are leaves
1464 }
1465 
1466 
legendRootRowCount(QgsLayerTreeLayer * nL) const1467 int QgsLayerTreeModel::legendRootRowCount( QgsLayerTreeLayer *nL ) const
1468 {
1469   if ( !mLegend.contains( nL ) )
1470     return 0;
1471 
1472   const LayerLegendData &data = mLegend[nL];
1473   if ( data.tree )
1474     return data.tree->children[nullptr].count();
1475 
1476   int count = data.activeNodes.count();
1477   return count;
1478 }
1479 
1480 
legendRootIndex(int row,int column,QgsLayerTreeLayer * nL) const1481 QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1482 {
1483   Q_ASSERT( mLegend.contains( nL ) );
1484   const LayerLegendData &data = mLegend[nL];
1485   if ( data.tree )
1486     return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1487 
1488   return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1489 }
1490 
1491 
legendNodeIndex(int row,int column,QgsLayerTreeModelLegendNode * node) const1492 QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1493 {
1494   const LayerLegendData &data = mLegend[node->layerNode()];
1495   if ( data.tree )
1496     return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1497 
1498   return QModelIndex(); // have no children
1499 }
1500 
1501 
legendParent(QgsLayerTreeModelLegendNode * legendNode) const1502 QModelIndex QgsLayerTreeModel::legendParent( QgsLayerTreeModelLegendNode *legendNode ) const
1503 {
1504   QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1505   const LayerLegendData &data = mLegend[layerNode];
1506   if ( data.tree )
1507   {
1508     if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1509     {
1510       QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1511       int row = data.tree->children[grandParentNode].indexOf( parentNode );
1512       return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1513     }
1514     else
1515       return indexOfParentLayerTreeNode( layerNode );
1516   }
1517 
1518   return indexOfParentLayerTreeNode( layerNode );
1519 }
1520 
1521 
legendNodeData(QgsLayerTreeModelLegendNode * node,int role) const1522 QVariant QgsLayerTreeModel::legendNodeData( QgsLayerTreeModelLegendNode *node, int role ) const
1523 {
1524   if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1525     return QVariant();
1526   return node->data( role );
1527 }
1528 
1529 
legendNodeFlags(QgsLayerTreeModelLegendNode * node) const1530 Qt::ItemFlags QgsLayerTreeModel::legendNodeFlags( QgsLayerTreeModelLegendNode *node ) const
1531 {
1532   Qt::ItemFlags f = node->flags();
1533   if ( !testFlag( AllowLegendChangeState ) )
1534     f &= ~Qt::ItemIsUserCheckable;
1535   return f;
1536 }
1537 
1538 
legendEmbeddedInParent(QgsLayerTreeLayer * nodeLayer) const1539 bool QgsLayerTreeModel::legendEmbeddedInParent( QgsLayerTreeLayer *nodeLayer ) const
1540 {
1541   return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1542 }
1543 
legendNodeEmbeddedInParent(QgsLayerTreeLayer * nodeLayer) const1544 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::legendNodeEmbeddedInParent( QgsLayerTreeLayer *nodeLayer ) const
1545 {
1546   return mLegend[nodeLayer].embeddedNodeInParent;
1547 }
1548 
1549 
legendIconEmbeddedInParent(QgsLayerTreeLayer * nodeLayer) const1550 QIcon QgsLayerTreeModel::legendIconEmbeddedInParent( QgsLayerTreeLayer *nodeLayer ) const
1551 {
1552   QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1553   if ( !legendNode )
1554     return QIcon();
1555   return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1556 }
1557 
1558 
layerLegendNodes(QgsLayerTreeLayer * nodeLayer,bool skipNodeEmbeddedInParent)1559 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1560 {
1561   if ( !mLegend.contains( nodeLayer ) )
1562     return QList<QgsLayerTreeModelLegendNode *>();
1563 
1564   const LayerLegendData &data = mLegend[nodeLayer];
1565   QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1566   if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1567     lst.prepend( data.embeddedNodeInParent );
1568   return lst;
1569 }
1570 
layerOriginalLegendNodes(QgsLayerTreeLayer * nodeLayer)1571 QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1572 {
1573   return mLegend.value( nodeLayer ).originalNodes;
1574 }
1575 
findLegendNode(const QString & layerId,const QString & ruleKey) const1576 QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1577 {
1578   for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1579   {
1580     QgsLayerTreeLayer *layer = it.key();
1581     if ( layer->layerId() == layerId )
1582     {
1583       const auto activeNodes = mLegend.value( layer ).activeNodes;
1584       for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1585       {
1586         if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1587         {
1588           //found it!
1589           return legendNode;
1590         }
1591       }
1592     }
1593   }
1594 
1595   return nullptr;
1596 }
1597 
legendInvalidateMapBasedData()1598 void QgsLayerTreeModel::legendInvalidateMapBasedData()
1599 {
1600   if ( !testFlag( DeferredLegendInvalidation ) )
1601     invalidateLegendMapBasedData();
1602   else
1603     mDeferLegendInvalidationTimer.start( 10 );
1604 }
1605 
invalidateLegendMapBasedData()1606 void QgsLayerTreeModel::invalidateLegendMapBasedData()
1607 {
1608   // we have varying icon sizes, and we want icon to be centered and
1609   // text to be left aligned, so we have to compute the max width of icons
1610   //
1611   // we do that for nodes which share a common parent
1612   //
1613   // we do that here because for symbols with size defined in map units
1614   // the symbol sizes changes depends on the zoom level
1615 
1616   std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1617 
1618   for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1619   {
1620     const LayerLegendData &data = mLegend.value( layerNode );
1621 
1622     QList<QgsSymbolLegendNode *> symbolNodes;
1623     QMap<QString, int> widthMax;
1624     for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1625     {
1626       QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1627       if ( n )
1628       {
1629         const QSize sz( n->minimumIconSize( context.get() ) );
1630         const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1631         widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1632         n->setIconSize( sz );
1633         symbolNodes.append( n );
1634       }
1635     }
1636     for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1637     {
1638       const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1639       Q_ASSERT( widthMax[parentKey] > 0 );
1640       const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1641       n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1642     }
1643     for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1644       legendNode->invalidateMapBasedData();
1645   }
1646 
1647   mInvalidatedNodes.clear();
1648 }
1649 
1650 // Legend nodes routines - end
1651 ///////////////////////////////////////////////////////////////////////////////
1652