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