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 ¤tIndex )
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