QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgslayertreeview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreeview.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 "qgslayertreeview.h"
17
18#include "qgslayertree.h"
20#include "qgslayertreemodel.h"
22#include "qgslayertreeutils.h"
24#include "qgsmaplayer.h"
25#include "qgsmessagebar.h"
26
27#include "qgsgui.h"
28
29#include <QApplication>
30#include <QMenu>
31#include <QContextMenuEvent>
32#include <QHeaderView>
33#include <QMimeData>
34#include <QScrollBar>
35
36#ifdef ENABLE_MODELTEST
37#include "modeltest.h"
38#endif
39
42
43
45 : QTreeView( parent )
46 , mBlockDoubleClickTimer( new QTimer( this ) )
47
48{
49 mBlockDoubleClickTimer->setSingleShot( true );
50 mBlockDoubleClickTimer->setInterval( QApplication::doubleClickInterval() );
51 setHeaderHidden( true );
52
53 setDragEnabled( true );
54 setAcceptDrops( true );
55 setDropIndicatorShown( true );
56 setEditTriggers( EditKeyPressed );
57 setExpandsOnDoubleClick( false ); // normally used for other actions
58
59 // Ensure legend graphics are scrollable
60 header()->setStretchLastSection( false );
61 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
62
63 // If vertically scrolling by item, legend graphics can get clipped
64 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
65
66 setSelectionMode( ExtendedSelection );
67 setDefaultDropAction( Qt::MoveAction );
68
69 // we need a custom item delegate in order to draw indicators
70 setItemDelegate( new QgsLayerTreeViewItemDelegate( this ) );
71 setStyle( new QgsLayerTreeViewProxyStyle( this ) );
72
73 setLayerMarkWidth( static_cast< int >( QFontMetricsF( font() ).horizontalAdvance( 'l' ) * Qgis::UI_SCALE_FACTOR ) );
74
75 connect( this, &QTreeView::collapsed, this, &QgsLayerTreeView::updateExpandedStateToNode );
76 connect( this, &QTreeView::expanded, this, &QgsLayerTreeView::updateExpandedStateToNode );
77
78 connect( horizontalScrollBar(), &QScrollBar::valueChanged, this, &QgsLayerTreeView::onHorizontalScroll );
79}
80
82{
83 delete mMenuProvider;
84 delete mBlockDoubleClickTimer;
85}
86
87void QgsLayerTreeView::setModel( QAbstractItemModel *model )
88{
89 QgsLayerTreeModel *treeModel = qobject_cast<QgsLayerTreeModel *>( model );
90 if ( !treeModel )
91 return;
92
93 if ( mMessageBar )
94 connect( treeModel, &QgsLayerTreeModel::messageEmitted, this,
95 [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
96 {
97 Q_UNUSED( duration )
98 mMessageBar->pushMessage( message, level );
99 }
100 );
101
102 treeModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
103
104 mProxyModel = new QgsLayerTreeProxyModel( treeModel, this );
105
106 connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
107 connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
108
109#ifdef ENABLE_MODELTEST
110 new ModelTest( mProxyModel, this );
111#endif
112
113 mProxyModel->setShowPrivateLayers( mShowPrivateLayers );
114 mProxyModel->setHideValidLayers( mHideValidLayers );
115 QTreeView::setModel( mProxyModel );
116
118 connect( treeModel->rootGroup(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeView::onCustomPropertyChanged );
119
120 connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsLayerTreeView::onCurrentChanged );
121
122 connect( treeModel, &QAbstractItemModel::modelReset, this, &QgsLayerTreeView::onModelReset );
123
124 connect( treeModel, &QAbstractItemModel::dataChanged, this, &QgsLayerTreeView::onDataChanged );
125
127
128 //checkModel();
129}
130
132{
133 return mProxyModel ? qobject_cast<QgsLayerTreeModel *>( mProxyModel->sourceModel() ) : nullptr;
134}
135
137{
138 if ( !mDefaultActions )
140 return mDefaultActions;
141}
142
144{
145 delete mMenuProvider;
147}
148
150{
151 return layerForIndex( currentIndex() );
152}
153
155{
156 if ( !layer )
157 {
158 setCurrentIndex( QModelIndex() );
159 return;
160 }
161
162 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
163 if ( !nodeLayer )
164 return;
165
166 setCurrentIndex( node2index( nodeLayer ) );
167}
168
170{
171 if ( !layer )
172 return;
173 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
174 if ( !nodeLayer )
175 return;
176 nodeLayer->setItemVisibilityChecked( visible );
177}
178
179void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
180{
181 if ( !mMenuProvider )
182 return;
183
184 const QModelIndex idx = indexAt( event->pos() );
185 if ( !idx.isValid() )
186 setCurrentIndex( QModelIndex() );
187
188 QMenu *menu = mMenuProvider->createContextMenu();
189 if ( menu )
190 {
191 emit contextMenuAboutToShow( menu );
192
193 if ( menu->actions().count() != 0 )
194 menu->exec( mapToGlobal( event->pos() ) );
195 delete menu;
196 }
197}
198
199
200void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end )
201{
202 QgsLayerTreeNode *parentNode = index2node( index );
203 if ( !parentNode )
204 return;
205
206 // Embedded widgets - replace placeholders in the model by actual widgets
208 {
209 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( parentNode );
210 if ( QgsMapLayer *layer = nodeLayer->layer() )
211 {
212 const int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
213 QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
214 for ( int i = 0; i < widgetsCount; ++i )
215 {
216 const QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
217 if ( QgsLayerTreeEmbeddedWidgetProvider *provider = QgsGui::layerTreeEmbeddedWidgetRegistry()->provider( providerId ) )
218 {
219 const QModelIndex index = legendNode2index( legendNodes[i] );
220 QWidget *wdgt = provider->createWidget( layer, i );
221 // Since column is resized to contents, limit the expanded width of embedded
222 // widgets, if they are not already limited, e.g. have the default MAX value.
223 // Else, embedded widget may grow very wide due to large legend graphics.
224 // NOTE: This approach DOES NOT work right. It causes horizontal scroll
225 // bar to disappear if the embedded widget is expanded and part
226 // of the last layer in the panel, even if much wider legend items
227 // are expanded above it. The correct width-limiting method should
228 // be setting fixed-width, hidpi-aware embedded widget items in a
229 // layout and appending an expanding QSpacerItem to end. This ensures
230 // full width is always created in the column by the embedded widget.
231 // See QgsLayerTreeOpacityWidget
232 //if ( wdgt->maximumWidth() == QWIDGETSIZE_MAX )
233 //{
234 // wdgt->setMaximumWidth( 250 );
235 //}
236
237 setIndexWidget( index, wdgt );
238 }
239 }
240 }
241 }
242
243
244 if ( QgsLayerTree::isLayer( parentNode ) )
245 {
246 // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
247 const QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
248 if ( expandedNodeKeys.isEmpty() )
249 return;
250
251 const auto constLayerLegendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true );
252 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
253 {
254 const QString ruleKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
255 if ( expandedNodeKeys.contains( ruleKey ) )
256 setExpanded( legendNode2index( legendNode ), true );
257 }
258 return;
259 }
260
261 QList<QgsLayerTreeNode *> children = parentNode->children();
262 for ( int i = start; i <= end && i < children.count(); ++i )
263 {
264 updateExpandedStateFromNode( children[i] );
265 }
266
267 // make sure we still have correct current layer
269}
270
272{
273 // make sure we still have correct current layer
275}
276
277void QgsLayerTreeView::updateExpandedStateToNode( const QModelIndex &index )
278{
279 if ( QgsLayerTreeNode *node = index2node( index ) )
280 {
281 node->setExpanded( isExpanded( index ) );
282 }
283 else if ( QgsLayerTreeModelLegendNode *node = index2legendNode( index ) )
284 {
285 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
286 QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
287 const bool expanded = isExpanded( index );
288 const bool isInList = lst.contains( ruleKey );
289 if ( expanded && !isInList )
290 {
291 lst.append( ruleKey );
292 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
293 }
294 else if ( !expanded && isInList )
295 {
296 lst.removeAll( ruleKey );
297 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
298 }
299 }
300}
301
303{
304 QgsMapLayer *layerCurrent = layerForIndex( currentIndex() );
305 const QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
306 if ( mCurrentLayerID == layerCurrentID )
307 return;
308
309 // update the current index in model (the item will be underlined)
310 QModelIndex proxyModelNodeLayerIndex;
311 if ( layerCurrent )
312 {
313 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
314 if ( nodeLayer )
315 proxyModelNodeLayerIndex = node2index( nodeLayer );
316 }
317
318 if ( ! proxyModelNodeLayerIndex.isValid() )
319 {
320 mCurrentLayerID = QString();
321 layerTreeModel()->setCurrentIndex( QModelIndex() );
322 }
323 else
324 {
325 mCurrentLayerID = layerCurrentID;
326 layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
327 }
328
329 //checkModel();
330
331 emit currentLayerChanged( layerCurrent );
332}
333
335{
336 const QModelIndex idx = node2index( node );
337 if ( isExpanded( idx ) != expanded )
338 setExpanded( idx, expanded );
339}
340
341void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
342{
343 if ( key != QLatin1String( "expandedLegendNodes" ) || !QgsLayerTree::isLayer( node ) )
344 return;
345
346 const QSet<QString> expandedLegendNodes = qgis::listToSet( node->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
347
348 const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( node ), true );
349 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
350 {
351 const QString key = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
352 if ( !key.isEmpty() )
353 setExpanded( legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
354 }
355}
356
358{
360 //checkModel();
361}
362
364{
365 const QModelIndex idx = node2index( node );
366 setExpanded( idx, node->isExpanded() );
367
368 const auto constChildren = node->children();
369 for ( QgsLayerTreeNode *child : constChildren )
371}
372
373QgsMapLayer *QgsLayerTreeView::layerForIndex( const QModelIndex &index ) const
374{
375 // Check if model has been set and index is valid
376 if ( layerTreeModel() && index.isValid() )
377 {
378 QgsLayerTreeNode *node = index2node( index );
379 if ( node )
380 {
381 if ( QgsLayerTree::isLayer( node ) )
382 return QgsLayerTree::toLayer( node )->layer();
383 }
384 else
385 {
386 // possibly a legend node
388 if ( legendNode )
389 return legendNode->layerNode()->layer();
390 }
391 }
392 return nullptr;
393}
394
396{
397 return index2node( selectionModel()->currentIndex() );
398}
399
401{
403 if ( QgsLayerTree::isGroup( node ) )
404 return QgsLayerTree::toGroup( node );
405 else if ( QgsLayerTree::isLayer( node ) )
406 {
407 QgsLayerTreeNode *parent = node->parent();
408 if ( QgsLayerTree::isGroup( parent ) )
409 return QgsLayerTree::toGroup( parent );
410 }
411
412 if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( selectionModel()->currentIndex() ) )
413 {
415 if ( QgsLayerTree::isGroup( parent->parent() ) )
416 return QgsLayerTree::toGroup( parent->parent() );
417 }
418
419 return nullptr;
420}
421
423{
424 return index2legendNode( selectionModel()->currentIndex() );
425}
426
427QList<QgsLayerTreeNode *> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
428{
429 QModelIndexList mapped;
430 const QModelIndexList selected = selectionModel()->selectedIndexes();
431 mapped.reserve( selected.size() );
432 for ( const QModelIndex &index : selected )
433 mapped << mProxyModel->mapToSource( index );
434
435 return layerTreeModel()->indexes2nodes( mapped, skipInternal );
436}
437
438QList<QgsLayerTreeLayer *> QgsLayerTreeView::selectedLayerNodes() const
439{
440 QList<QgsLayerTreeLayer *> layerNodes;
441 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
442 layerNodes.reserve( constSelectedNodes.size() );
443 for ( QgsLayerTreeNode *node : constSelectedNodes )
444 {
445 if ( QgsLayerTree::isLayer( node ) )
446 layerNodes << QgsLayerTree::toLayer( node );
447 }
448 return layerNodes;
449}
450
451QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
452{
453 QList<QgsMapLayer *> list;
454 const QList<QgsLayerTreeLayer *> constSelectedLayerNodes = selectedLayerNodes();
455 list.reserve( constSelectedLayerNodes.size() );
456 for ( QgsLayerTreeLayer *node : constSelectedLayerNodes )
457 {
458 if ( node->layer() )
459 list << node->layer();
460 }
461 return list;
462}
463
464QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeView::selectedLegendNodes() const
465{
466 QList<QgsLayerTreeModelLegendNode *> res;
467 const QModelIndexList selected = selectionModel()->selectedIndexes();
468 res.reserve( selected.size() );
469 for ( const QModelIndex &index : selected )
470 {
471 const QModelIndex &modelIndex = mProxyModel->mapToSource( index );
472 if ( QgsLayerTreeModelLegendNode *node = layerTreeModel()->index2legendNode( modelIndex ) )
473 {
474 res.push_back( node );
475 }
476 }
477
478 return res;
479}
480
482{
483 QModelIndexList mapped;
484 const QModelIndexList selected = selectionModel()->selectedIndexes();
485 mapped.reserve( selected.size() );
486 for ( const QModelIndex &index : selected )
487 mapped << mProxyModel->mapToSource( index );
488
489 const QList<QgsLayerTreeNode *> nodes = layerTreeModel()->indexes2nodes( mapped, false );
490 const QSet<QgsMapLayer *> layersSet = QgsLayerTreeUtils::collectMapLayersRecursive( nodes );
491 return qgis::setToList( layersSet );
492}
493
495{
496 if ( !mIndicators[node].contains( indicator ) )
497 {
498 mIndicators[node].append( indicator );
499 connect( indicator, &QgsLayerTreeViewIndicator::changed, this, [ = ]
500 {
501 update();
502 viewport()->repaint();
503 } );
504 update();
505 viewport()->repaint(); //update() does not automatically trigger a repaint()
506 }
507}
508
510{
511 mIndicators[node].removeOne( indicator );
512 update();
513}
514
515QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
516{
517 return mIndicators.value( node );
518}
519
521QStringList QgsLayerTreeView::viewOnlyCustomProperties()
522{
523 return QStringList() << QStringLiteral( "expandedLegendNodes" );
524}
526
527void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
528{
529 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
530 if ( nodeLayer )
531 layerTreeModel()->refreshLayerLegend( nodeLayer );
532}
533
534
535static void _expandAllLegendNodes( QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model )
536{
537 // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
538 // if we are collapsing, we just write out an empty list
539 QStringList lst;
540 if ( expanded )
541 {
542 const auto constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
543 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
544 {
545 const QString parentKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
546 if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
547 lst << parentKey;
548 }
549 }
550 nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
551}
552
553
554static void _expandAllNodes( QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model )
555{
556 const auto constChildren = parent->children();
557 for ( QgsLayerTreeNode *node : constChildren )
558 {
559 node->setExpanded( expanded );
560 if ( QgsLayerTree::isGroup( node ) )
561 _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
562 else if ( QgsLayerTree::isLayer( node ) )
563 _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
564 }
565}
566
567
569{
570 // unfortunately expandAll() does not emit expanded() signals
571 _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
572 expandAll();
573}
574
576{
577 // unfortunately collapseAll() does not emit collapsed() signals
578 _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
579 collapseAll();
580}
581
583{
584 if ( mMessageBar == messageBar )
585 return;
586
587 mMessageBar = messageBar;
588
589 if ( mMessageBar )
591 [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
592 {
593 Q_UNUSED( duration )
594 mMessageBar->pushMessage( message, level );
595 }
596 );
597}
598
600{
601 mShowPrivateLayers = showPrivate;
602 mProxyModel->setShowPrivateLayers( showPrivate );
603}
604
606{
607 mHideValidLayers = hideValid;
608 mProxyModel->setHideValidLayers( mHideValidLayers );
609}
610
612{
613 return mShowPrivateLayers;
614}
615
617{
618 return mHideValidLayers;
619}
620
622{
623 if ( mBlockDoubleClickTimer->isActive() )
624 event->accept();
625 else
626 QTreeView::mouseDoubleClickEvent( event );
627}
628
629void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
630{
631 // we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
632 // (the item delegate needs to know which indicator has been clicked)
633 mLastReleaseMousePos = event->pos();
634
635 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
636 if ( event->modifiers() & Qt::ControlModifier )
638 else
640 QTreeView::mouseReleaseEvent( event );
641 layerTreeModel()->setFlags( oldFlags );
642}
643
644void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
645{
646 if ( event->key() == Qt::Key_Space )
647 {
648 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
649
650 if ( !constSelectedNodes.isEmpty() )
651 {
652 const bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
653 for ( QgsLayerTreeNode *node : constSelectedNodes )
654 {
655 node->setItemVisibilityChecked( ! isFirstNodeChecked );
656 }
657
658 // if we call the original keyPress handler, the current item will be checked to the original state yet again
659 return;
660 }
661 }
662
663 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
664 if ( event->modifiers() & Qt::ControlModifier )
666 else
668 QTreeView::keyPressEvent( event );
669 layerTreeModel()->setFlags( oldFlags );
670}
671
672void QgsLayerTreeView::dragEnterEvent( QDragEnterEvent *event )
673{
674 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
675 {
676 // the mime data are coming from layer tree, so ignore that, do not import those layers again
677 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
678 {
679 event->accept();
680 return;
681 }
682 }
683 QTreeView::dragEnterEvent( event );
684}
685
686void QgsLayerTreeView::dragMoveEvent( QDragMoveEvent *event )
687{
688 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
689 {
690 // the mime data are coming from layer tree, so ignore that, do not import those layers again
691 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
692 {
693 event->accept();
694 return;
695 }
696 }
697 QTreeView::dragMoveEvent( event );
698}
699
700void QgsLayerTreeView::dropEvent( QDropEvent *event )
701{
702 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
703 {
704 // the mime data are coming from layer tree, so ignore that, do not import those layers again
705 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
706 {
707 event->accept();
708
709 QModelIndex index = indexAt( event->pos() );
710 if ( index.isValid() )
711 {
712 setCurrentIndex( index );
713 }
714
715 emit datasetsDropped( event );
716 return;
717 }
718 }
719 if ( event->keyboardModifiers() & Qt::AltModifier || event->keyboardModifiers() & Qt::ControlModifier )
720 {
721 event->accept();
722 }
723 QTreeView::dropEvent( event );
724}
725
726void QgsLayerTreeView::resizeEvent( QResizeEvent *event )
727{
728 // Since last column is resized to content (instead of stretched), the active
729 // selection rectangle ends at width of widest visible item in tree,
730 // regardless of which item is selected. This causes layer indicators to
731 // become 'inactive' (not clickable and no tool tip) unless their rectangle
732 // enters the view item's selection (active) rectangle.
733 // Always resetting the minimum section size relative to the viewport ensures
734 // the view item's selection rectangle extends to the right edge of the
735 // viewport, which allows indicators to become active again.
736 header()->setMinimumSectionSize( viewport()->width() );
737 QTreeView::resizeEvent( event );
738}
739
740void QgsLayerTreeView::onHorizontalScroll( int value )
741{
742 Q_UNUSED( value )
743 viewport()->update();
744}
745
746void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
747{
748 Q_UNUSED( topLeft )
749 Q_UNUSED( bottomRight )
750
751 // If an item is resized asynchronously (e.g. wms legend)
752 // The items below will need to be shifted vertically.
753 // This doesn't happen automatically, unless the viewport update is triggered.
754
755 if ( roles.contains( Qt::SizeHintRole ) )
756 viewport()->update();
757
758 mBlockDoubleClickTimer->start();
759 //checkModel();
760}
761
762#if 0
763// for model debugging
764void QgsLayerTreeView::checkModel()
765{
766 std::function<void( QgsLayerTreeNode *, int )> debug;
767 debug = [ & ]( QgsLayerTreeNode * node, int depth )
768 {
769 if ( depth == 1 )
770 qDebug() << "----------------------------------------------";
771
772 qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) );
773 Q_ASSERT( node == index2node( node2index( node ) ) );
774 Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) );
775 Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) );
776
777 for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ )
778 {
779 QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) };
780 if ( childNode )
781 debug( childNode, depth + 1 );
782 else
783 qDebug() << "Warning no child node!";
784 }
785 };
786 debug( layerTreeModel()->rootGroup(), 1 );
787}
788#endif
789
791{
792 return mProxyModel;
793}
794
795QgsLayerTreeNode *QgsLayerTreeView::index2node( const QModelIndex &index ) const
796{
797 return layerTreeModel()->index2node( mProxyModel->mapToSource( index ) );
798}
799
801{
802 return mProxyModel->mapFromSource( node2sourceIndex( node ) );
803}
804
806{
807 return layerTreeModel()->node2index( node );
808}
809
811{
812 return QgsLayerTreeModel::index2legendNode( mProxyModel->mapToSource( index ) );
813}
814
816{
817 return mProxyModel->mapFromSource( legendNode2sourceIndex( legendNode ) );
818}
819
821{
823}
824
826 : QSortFilterProxyModel( parent )
827 , mLayerTreeModel( treeModel )
828{
829 setSourceModel( treeModel );
830}
831
832void QgsLayerTreeProxyModel::setFilterText( const QString &filterText )
833{
834 if ( filterText == mFilterText )
835 return;
836
837 mFilterText = filterText;
838 invalidateFilter();
839}
840
841bool QgsLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
842{
843 QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
844 return nodeShown( node );
845}
846
847bool QgsLayerTreeProxyModel::nodeShown( QgsLayerTreeNode *node ) const
848{
849 if ( !node )
850 return true;
851
852 if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
853 {
854 return true;
855 }
856 else
857 {
858 QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
859 if ( !layer )
860 return true;
861 if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
862 return false;
863 if ( ! mShowPrivateLayers && layer->flags().testFlag( QgsMapLayer::LayerFlag::Private ) )
864 {
865 return false;
866 }
867 if ( mHideValidLayers && layer->isValid() )
868 return false;
869
870 return true;
871 }
872}
873
875{
876 return mShowPrivateLayers;
877}
878
880{
881 if ( showPrivate == mShowPrivateLayers )
882 return;
883
884 mShowPrivateLayers = showPrivate;
885 invalidateFilter();
886}
887
889{
890 return mHideValidLayers;
891}
892
894{
895 if ( hideValid == mHideValidLayers )
896 return;
897
898 mHideValidLayers = hideValid;
899 invalidateFilter();
900}
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:99
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:4927
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Definition: qgsgui.cpp:124
Provider interface to be implemented in order to introduce new kinds of embedded widgets for use in l...
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
@ ParentRuleKey
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKey
Rule key of the node (QString)
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
QFlags< Flag > Flags
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
Qt::ItemFlags flags(const QModelIndex &index) const override
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node)
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
virtual QString name() const =0
Returns name of the node.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
void expandedChanged(QgsLayerTreeNode *node, bool expanded)
Emitted when the collapsed/expanded state of a node within the tree has been changed.
The QgsLayerTreeProxyModel class is a proxy model for QgsLayerTreeModel, supports private layers and ...
bool showPrivateLayers() const
Returns if private layers are shown.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
void setFilterText(const QString &filterText=QString())
Sets filter to filterText.
bool hideValidLayers() const
Returns if valid layers should be hidden (i.e.
void setHideValidLayers(bool hideValid)
Sets whether valid layers should be hidden (i.e.
void setShowPrivateLayers(bool showPrivate)
Determines if private layers are shown.
QgsLayerTreeProxyModel(QgsLayerTreeModel *treeModel, QObject *parent)
Constructs QgsLayerTreeProxyModel with source model treeModel and a parent.
static QSet< QgsMapLayer * > collectMapLayersRecursive(const QList< QgsLayerTreeNode * > &nodes)
Returns map layers from the given list of layer tree nodes.
The QgsLayerTreeViewDefaultActions class serves as a factory of actions that can be used together wit...
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
void changed()
Emitted when the indicator changes state (e.g.
Implementation of this interface can be implemented to allow QgsLayerTreeView instance to provide cus...
virtual QMenu * createContextMenu()=0
Returns a newly created menu instance (or nullptr on error)
void expandAllNodes()
Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes.
QList< QgsLayerTreeViewIndicator * > indicators(QgsLayerTreeNode *node) const
Returns list of indicators associated with a particular layer tree node.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
void contextMenuAboutToShow(QMenu *menu)
Emitted when the context menu is about to show.
void collapseAllNodes()
Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns proxy model index for a given legend node.
QgsLayerTreeViewDefaultActions * defaultActions()
Gets access to the default actions that may be used with the tree view.
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer)
void datasetsDropped(QDropEvent *event)
Emitted when datasets are dropped onto the layer tree view.
void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
QHash< QgsLayerTreeNode *, QList< QgsLayerTreeViewIndicator * > > mIndicators
Storage of indicators used with the tree view.
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Returns the list of selected nodes filtered to just layer nodes (QgsLayerTreeLayer).
void removeIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Removes a previously added indicator to a layer tree node.
void refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitore...
void dropEvent(QDropEvent *event) override
QgsLayerTreeView(QWidget *parent=nullptr)
Constructor for QgsLayerTreeView.
QgsMapLayer * currentLayer() const
Returns the currently selected layer, or nullptr if no layers is selected.
QgsLayerTreeViewMenuProvider * menuProvider() const
Returns pointer to the context menu provider. May be nullptr.
void onExpandedChanged(QgsLayerTreeNode *node, bool expanded)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given proxy model tree index.
void resizeEvent(QResizeEvent *event) override
QList< QgsMapLayer * > selectedLayersRecursive() const
Gets list of selected layers, including those that are not directly selected, but their ancestor grou...
void mouseReleaseEvent(QMouseEvent *event) override
bool hideValidLayers() const
Returns if valid layers should be hidden (i.e.
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
QgsLayerTreeModelLegendNode * currentLegendNode() const
Gets current legend node.
QgsLayerTreeModel * layerTreeModel() const
Gets access to the model casted to QgsLayerTreeModel.
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Sets provider for context menu. Takes ownership of the instance.
void setLayerVisible(QgsMapLayer *layer, bool visible)
Convenience methods which sets the visible state of the specified map layer.
QModelIndex node2sourceIndex(QgsLayerTreeNode *node) const
Returns source model index for a given node.
QgsMapLayer * layerForIndex(const QModelIndex &index) const
void dragMoveEvent(QDragMoveEvent *event) override
void mouseDoubleClickEvent(QMouseEvent *event) override
QPoint mLastReleaseMousePos
Used by the item delegate for identification of which indicator has been clicked.
friend class QgsLayerTreeViewItemDelegate
void updateExpandedStateToNode(const QModelIndex &index)
QgsLayerTreeProxyModel * proxyModel() const
Returns the proxy model used by the view.
QgsLayerTreeViewMenuProvider * mMenuProvider
Context menu provider. Owned by the view.
QList< QgsLayerTreeModelLegendNode * > selectedLegendNodes() const
Returns the list of selected legend nodes.
QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index) const
Returns legend node for given proxy model tree index.
void contextMenuEvent(QContextMenuEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void setMessageBar(QgsMessageBar *messageBar)
Set the message bar to display messages from the layer tree.
void addIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Adds an indicator to the given layer tree node.
void setHideValidLayers(bool hideValid)
Sets whether valid layers should be hidden (i.e.
void modelRowsInserted(const QModelIndex &index, int start, int end)
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Returns the list of selected layer tree nodes.
void setLayerMarkWidth(int width)
Set width of contextual menu mark, at right of layer node items.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns proxy model index for a given node.
void setCurrentLayer(QgsMapLayer *layer)
Sets the currently selected layer.
void setShowPrivateLayers(bool showPrivate)
Set the show private layers to showPrivate.
QgsLayerTreeNode * currentNode() const
Gets current node. May be nullptr.
~QgsLayerTreeView() override
QgsLayerTreeGroup * currentGroupNode() const
Gets current group node. If a layer is current node, the function will return parent group....
void dragEnterEvent(QDragEnterEvent *event) override
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
QList< QgsMapLayer * > selectedLayers() const
Returns the list of selected layers.
QModelIndex legendNode2sourceIndex(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
bool showPrivateLayers() const
Returns the show private layers status.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:41
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:60
Base class for all map layer types.
Definition: qgsmaplayer.h:75
QString name
Definition: qgsmaplayer.h:78
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
bool isValid
Definition: qgsmaplayer.h:83
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
Stores properties relating to a screen.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)