QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
Loading...
Searching...
No Matches
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
142
148
150{
151 return layerForIndex( currentIndex() );
152}
153
155{
156 if ( !node )
157 {
158 setCurrentIndex( QModelIndex() );
159 return;
160 }
161
162 setCurrentIndex( node2index( node ) );
163}
164
166{
167 if ( !layer )
168 {
169 setCurrentIndex( QModelIndex() );
170 return;
171 }
172
173 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
174 if ( !nodeLayer )
175 return;
176
177 setCurrentNode( nodeLayer );
178}
179
181{
182 if ( !layer )
183 return;
184 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
185 if ( !nodeLayer )
186 return;
187 nodeLayer->setItemVisibilityChecked( visible );
188}
189
190void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
191{
192 if ( !mMenuProvider )
193 return;
194
195 const QModelIndex idx = indexAt( event->pos() );
196 if ( !idx.isValid() )
197 setCurrentIndex( QModelIndex() );
198
199 QMenu *menu = mMenuProvider->createContextMenu();
200 if ( menu )
201 {
202 emit contextMenuAboutToShow( menu );
203
204 if ( menu->actions().count() != 0 )
205 menu->exec( mapToGlobal( event->pos() ) );
206 delete menu;
207 }
208}
209
210
211void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end )
212{
213 QgsLayerTreeNode *parentNode = index2node( index );
214 if ( !parentNode )
215 return;
216
217 // Embedded widgets - replace placeholders in the model by actual widgets
219 {
220 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( parentNode );
221 if ( QgsMapLayer *layer = nodeLayer->layer() )
222 {
223 const int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
224 QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
225 for ( int i = 0; i < widgetsCount; ++i )
226 {
227 const QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
228 if ( QgsLayerTreeEmbeddedWidgetProvider *provider = QgsGui::layerTreeEmbeddedWidgetRegistry()->provider( providerId ) )
229 {
230 const QModelIndex index = legendNode2index( legendNodes[i] );
231 QWidget *wdgt = provider->createWidget( layer, i );
232 // Since column is resized to contents, limit the expanded width of embedded
233 // widgets, if they are not already limited, e.g. have the default MAX value.
234 // Else, embedded widget may grow very wide due to large legend graphics.
235 // NOTE: This approach DOES NOT work right. It causes horizontal scroll
236 // bar to disappear if the embedded widget is expanded and part
237 // of the last layer in the panel, even if much wider legend items
238 // are expanded above it. The correct width-limiting method should
239 // be setting fixed-width, hidpi-aware embedded widget items in a
240 // layout and appending an expanding QSpacerItem to end. This ensures
241 // full width is always created in the column by the embedded widget.
242 // See QgsLayerTreeOpacityWidget
243 //if ( wdgt->maximumWidth() == QWIDGETSIZE_MAX )
244 //{
245 // wdgt->setMaximumWidth( 250 );
246 //}
247
248 setIndexWidget( index, wdgt );
249 }
250 }
251 }
252 }
253
254
255 if ( QgsLayerTree::isLayer( parentNode ) )
256 {
257 // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
258 const QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
259 if ( expandedNodeKeys.isEmpty() )
260 return;
261
262 const auto constLayerLegendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true );
263 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
264 {
265 const QString ruleKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
266 if ( expandedNodeKeys.contains( ruleKey ) )
267 setExpanded( legendNode2index( legendNode ), true );
268 }
269 return;
270 }
271
272 QList<QgsLayerTreeNode *> children = parentNode->children();
273 for ( int i = start; i <= end && i < children.count(); ++i )
274 {
275 updateExpandedStateFromNode( children[i] );
276 }
277
278 // make sure we still have correct current layer
280}
281
283{
284 // make sure we still have correct current layer
286}
287
288void QgsLayerTreeView::updateExpandedStateToNode( const QModelIndex &index )
289{
290 if ( QgsLayerTreeNode *node = index2node( index ) )
291 {
292 node->setExpanded( isExpanded( index ) );
293 }
294 else if ( QgsLayerTreeModelLegendNode *node = index2legendNode( index ) )
295 {
296 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
297 QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
298 const bool expanded = isExpanded( index );
299 const bool isInList = lst.contains( ruleKey );
300 if ( expanded && !isInList )
301 {
302 lst.append( ruleKey );
303 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
304 }
305 else if ( !expanded && isInList )
306 {
307 lst.removeAll( ruleKey );
308 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
309 }
310 }
311}
312
314{
315 QgsMapLayer *layerCurrent = layerForIndex( currentIndex() );
316 const QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
317 if ( mCurrentLayerID == layerCurrentID )
318 return;
319
320 // update the current index in model (the item will be underlined)
321 QModelIndex proxyModelNodeLayerIndex;
322 if ( layerCurrent )
323 {
324 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
325 if ( nodeLayer )
326 proxyModelNodeLayerIndex = node2index( nodeLayer );
327 }
328
329 if ( ! proxyModelNodeLayerIndex.isValid() )
330 {
331 mCurrentLayerID = QString();
332 layerTreeModel()->setCurrentIndex( QModelIndex() );
333 }
334 else
335 {
336 mCurrentLayerID = layerCurrentID;
337 layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
338 }
339
340 //checkModel();
341
342 emit currentLayerChanged( layerCurrent );
343}
344
346{
347 const QModelIndex idx = node2index( node );
348 if ( isExpanded( idx ) != expanded )
349 setExpanded( idx, expanded );
350}
351
352void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
353{
354 if ( key != QLatin1String( "expandedLegendNodes" ) || !QgsLayerTree::isLayer( node ) )
355 return;
356
357 const QSet<QString> expandedLegendNodes = qgis::listToSet( node->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
358
359 const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( node ), true );
360 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
361 {
362 const QString key = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
363 if ( !key.isEmpty() )
364 setExpanded( legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
365 }
366}
367
369{
371 //checkModel();
372}
373
375{
376 const QModelIndex idx = node2index( node );
377 setExpanded( idx, node->isExpanded() );
378
379 const auto constChildren = node->children();
380 for ( QgsLayerTreeNode *child : constChildren )
382}
383
384QgsMapLayer *QgsLayerTreeView::layerForIndex( const QModelIndex &index ) const
385{
386 // Check if model has been set and index is valid
387 if ( layerTreeModel() && index.isValid() )
388 {
389 QgsLayerTreeNode *node = index2node( index );
390 if ( node )
391 {
392 if ( QgsLayerTree::isLayer( node ) )
393 return QgsLayerTree::toLayer( node )->layer();
394 }
395 else
396 {
397 // possibly a legend node
398 QgsLayerTreeModelLegendNode *legendNode = index2legendNode( index );
399 if ( legendNode )
400 return legendNode->layerNode()->layer();
401 }
402 }
403 return nullptr;
404}
405
407{
408 return index2node( selectionModel()->currentIndex() );
409}
410
412{
414 if ( QgsLayerTree::isGroup( node ) )
415 return QgsLayerTree::toGroup( node );
416 else if ( QgsLayerTree::isLayer( node ) )
417 {
418 QgsLayerTreeNode *parent = node->parent();
419 if ( QgsLayerTree::isGroup( parent ) )
420 return QgsLayerTree::toGroup( parent );
421 }
422
423 if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( selectionModel()->currentIndex() ) )
424 {
425 QgsLayerTreeLayer *parent = legendNode->layerNode();
426 if ( QgsLayerTree::isGroup( parent->parent() ) )
427 return QgsLayerTree::toGroup( parent->parent() );
428 }
429
430 return nullptr;
431}
432
434{
435 return index2legendNode( selectionModel()->currentIndex() );
436}
437
438QList<QgsLayerTreeNode *> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
439{
440 QModelIndexList mapped;
441 const QModelIndexList selected = selectionModel()->selectedIndexes();
442 mapped.reserve( selected.size() );
443 for ( const QModelIndex &index : selected )
444 mapped << mProxyModel->mapToSource( index );
445
446 return layerTreeModel()->indexes2nodes( mapped, skipInternal );
447}
448
449QList<QgsLayerTreeLayer *> QgsLayerTreeView::selectedLayerNodes() const
450{
451 QList<QgsLayerTreeLayer *> layerNodes;
452 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
453 layerNodes.reserve( constSelectedNodes.size() );
454 for ( QgsLayerTreeNode *node : constSelectedNodes )
455 {
456 if ( QgsLayerTree::isLayer( node ) )
457 layerNodes << QgsLayerTree::toLayer( node );
458 }
459 return layerNodes;
460}
461
462QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
463{
464 QList<QgsMapLayer *> list;
465 const QList<QgsLayerTreeLayer *> constSelectedLayerNodes = selectedLayerNodes();
466 list.reserve( constSelectedLayerNodes.size() );
467 for ( QgsLayerTreeLayer *node : constSelectedLayerNodes )
468 {
469 if ( node->layer() )
470 list << node->layer();
471 }
472 return list;
473}
474
475QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeView::selectedLegendNodes() const
476{
477 QList<QgsLayerTreeModelLegendNode *> res;
478 const QModelIndexList selected = selectionModel()->selectedIndexes();
479 res.reserve( selected.size() );
480 for ( const QModelIndex &index : selected )
481 {
482 const QModelIndex &modelIndex = mProxyModel->mapToSource( index );
483 if ( QgsLayerTreeModelLegendNode *node = layerTreeModel()->index2legendNode( modelIndex ) )
484 {
485 res.push_back( node );
486 }
487 }
488
489 return res;
490}
491
493{
494 QModelIndexList mapped;
495 const QModelIndexList selected = selectionModel()->selectedIndexes();
496 mapped.reserve( selected.size() );
497 for ( const QModelIndex &index : selected )
498 mapped << mProxyModel->mapToSource( index );
499
500 const QList<QgsLayerTreeNode *> nodes = layerTreeModel()->indexes2nodes( mapped, false );
501 const QSet<QgsMapLayer *> layersSet = QgsLayerTreeUtils::collectMapLayersRecursive( nodes );
502 return qgis::setToList( layersSet );
503}
504
506{
507 if ( !mIndicators[node].contains( indicator ) )
508 {
509 mIndicators[node].append( indicator );
510 connect( indicator, &QgsLayerTreeViewIndicator::changed, this, [ = ]
511 {
512 update();
513 viewport()->repaint();
514 } );
515 update();
516 viewport()->repaint(); //update() does not automatically trigger a repaint()
517 }
518}
519
521{
522 mIndicators[node].removeOne( indicator );
523 update();
524}
525
526QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
527{
528 return mIndicators.value( node );
529}
530
532QStringList QgsLayerTreeView::viewOnlyCustomProperties()
533{
534 return QStringList() << QStringLiteral( "expandedLegendNodes" );
535}
537
538void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
539{
540 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
541 if ( nodeLayer )
542 layerTreeModel()->refreshLayerLegend( nodeLayer );
543}
544
545
546static void _expandAllLegendNodes( QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model )
547{
548 // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
549 // if we are collapsing, we just write out an empty list
550 QStringList lst;
551 if ( expanded )
552 {
553 const auto constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
554 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
555 {
556 const QString parentKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
557 if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
558 lst << parentKey;
559 }
560 }
561 nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
562}
563
564
565static void _expandAllNodes( QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model )
566{
567 const auto constChildren = parent->children();
568 for ( QgsLayerTreeNode *node : constChildren )
569 {
570 node->setExpanded( expanded );
571 if ( QgsLayerTree::isGroup( node ) )
572 _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
573 else if ( QgsLayerTree::isLayer( node ) )
574 _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
575 }
576}
577
578
580{
581 // unfortunately expandAll() does not emit expanded() signals
582 _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
583 expandAll();
584}
585
587{
588 // unfortunately collapseAll() does not emit collapsed() signals
589 _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
590 collapseAll();
591}
592
594{
595 if ( mMessageBar == messageBar )
596 return;
597
598 mMessageBar = messageBar;
599
600 if ( mMessageBar )
602 [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
603 {
604 Q_UNUSED( duration )
605 mMessageBar->pushMessage( message, level );
606 }
607 );
608}
609
611{
612 mShowPrivateLayers = showPrivate;
613 mProxyModel->setShowPrivateLayers( showPrivate );
614}
615
617{
618 mHideValidLayers = hideValid;
619 mProxyModel->setHideValidLayers( mHideValidLayers );
620}
621
623{
624 return mShowPrivateLayers;
625}
626
628{
629 return mHideValidLayers;
630}
631
633{
634 if ( mBlockDoubleClickTimer->isActive() )
635 event->accept();
636 else
637 QTreeView::mouseDoubleClickEvent( event );
638}
639
640void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
641{
642 // we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
643 // (the item delegate needs to know which indicator has been clicked)
644 mLastReleaseMousePos = event->pos();
645
646 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
647 if ( event->modifiers() & Qt::ControlModifier )
649 else
651 QTreeView::mouseReleaseEvent( event );
652 layerTreeModel()->setFlags( oldFlags );
653}
654
655void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
656{
657 if ( event->key() == Qt::Key_Space )
658 {
659 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
660
661 if ( !constSelectedNodes.isEmpty() )
662 {
663 const bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
664 for ( QgsLayerTreeNode *node : constSelectedNodes )
665 {
666 node->setItemVisibilityChecked( ! isFirstNodeChecked );
667 }
668
669 // if we call the original keyPress handler, the current item will be checked to the original state yet again
670 return;
671 }
672 }
673
674 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
675 if ( event->modifiers() & Qt::ControlModifier )
677 else
679 QTreeView::keyPressEvent( event );
680 layerTreeModel()->setFlags( oldFlags );
681}
682
683void QgsLayerTreeView::dragEnterEvent( QDragEnterEvent *event )
684{
685 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
686 {
687 // the mime data are coming from layer tree, so ignore that, do not import those layers again
688 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
689 {
690 event->accept();
691 return;
692 }
693 }
694 QTreeView::dragEnterEvent( event );
695}
696
697void QgsLayerTreeView::dragMoveEvent( QDragMoveEvent *event )
698{
699 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
700 {
701 // the mime data are coming from layer tree, so ignore that, do not import those layers again
702 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
703 {
704 event->accept();
705 return;
706 }
707 }
708 QTreeView::dragMoveEvent( event );
709}
710
711void QgsLayerTreeView::dropEvent( QDropEvent *event )
712{
713 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
714 {
715 // the mime data are coming from layer tree, so ignore that, do not import those layers again
716 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
717 {
718 event->accept();
719
720 QModelIndex index = indexAt( event->pos() );
721 if ( index.isValid() )
722 {
723 setCurrentIndex( index );
724 }
725
726 emit datasetsDropped( event );
727 return;
728 }
729 }
730 if ( event->keyboardModifiers() & Qt::AltModifier || event->keyboardModifiers() & Qt::ControlModifier )
731 {
732 event->accept();
733 }
734 QTreeView::dropEvent( event );
735}
736
737void QgsLayerTreeView::resizeEvent( QResizeEvent *event )
738{
739 // Since last column is resized to content (instead of stretched), the active
740 // selection rectangle ends at width of widest visible item in tree,
741 // regardless of which item is selected. This causes layer indicators to
742 // become 'inactive' (not clickable and no tool tip) unless their rectangle
743 // enters the view item's selection (active) rectangle.
744 // Always resetting the minimum section size relative to the viewport ensures
745 // the view item's selection rectangle extends to the right edge of the
746 // viewport, which allows indicators to become active again.
747 header()->setMinimumSectionSize( viewport()->width() );
748 QTreeView::resizeEvent( event );
749}
750
751void QgsLayerTreeView::onHorizontalScroll( int value )
752{
753 Q_UNUSED( value )
754 viewport()->update();
755}
756
757void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
758{
759 Q_UNUSED( topLeft )
760 Q_UNUSED( bottomRight )
761
762 // If an item is resized asynchronously (e.g. wms legend)
763 // The items below will need to be shifted vertically.
764 // This doesn't happen automatically, unless the viewport update is triggered.
765
766 if ( roles.contains( Qt::SizeHintRole ) )
767 viewport()->update();
768
769 mBlockDoubleClickTimer->start();
770 //checkModel();
771}
772
773#if 0
774// for model debugging
775void QgsLayerTreeView::checkModel()
776{
777 std::function<void( QgsLayerTreeNode *, int )> debug;
778 debug = [ & ]( QgsLayerTreeNode * node, int depth )
779 {
780 if ( depth == 1 )
781 qDebug() << "----------------------------------------------";
782
783 qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) );
784 Q_ASSERT( node == index2node( node2index( node ) ) );
785 Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) );
786 Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) );
787
788 for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ )
789 {
790 QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) };
791 if ( childNode )
792 debug( childNode, depth + 1 );
793 else
794 qDebug() << "Warning no child node!";
795 }
796 };
797 debug( layerTreeModel()->rootGroup(), 1 );
798}
799#endif
800
802{
803 return mProxyModel;
804}
805
806QgsLayerTreeNode *QgsLayerTreeView::index2node( const QModelIndex &index ) const
807{
808 return layerTreeModel()->index2node( mProxyModel->mapToSource( index ) );
809}
810
812{
813 return mProxyModel->mapFromSource( node2sourceIndex( node ) );
814}
815
817{
818 return layerTreeModel()->node2index( node );
819}
820
822{
823 return QgsLayerTreeModel::index2legendNode( mProxyModel->mapToSource( index ) );
824}
825
827{
828 return mProxyModel->mapFromSource( legendNode2sourceIndex( legendNode ) );
829}
830
832{
833 return layerTreeModel()->legendNode2index( legendNode );
834}
835
837 : QSortFilterProxyModel( parent )
838 , mLayerTreeModel( treeModel )
839{
840 setSourceModel( treeModel );
841}
842
843void QgsLayerTreeProxyModel::setFilterText( const QString &filterText )
844{
845 if ( filterText == mFilterText )
846 return;
847
848 mFilterText = filterText;
849 invalidateFilter();
850}
851
852bool QgsLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
853{
854 QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
855 return nodeShown( node );
856}
857
858bool QgsLayerTreeProxyModel::nodeShown( QgsLayerTreeNode *node ) const
859{
860 if ( !node )
861 return true;
862
863 if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
864 {
865 return true;
866 }
867 else
868 {
869 QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
870 if ( !layer )
871 return true;
872 if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
873 return false;
874 if ( ! mShowPrivateLayers && layer->flags().testFlag( QgsMapLayer::LayerFlag::Private ) )
875 {
876 return false;
877 }
878 if ( mHideValidLayers && layer->isValid() )
879 return false;
880
881 return true;
882 }
883}
884
886{
887 return mShowPrivateLayers;
888}
889
891{
892 if ( showPrivate == mShowPrivateLayers )
893 return;
894
895 mShowPrivateLayers = showPrivate;
896 invalidateFilter();
897}
898
900{
901 return mHideValidLayers;
902}
903
905{
906 if ( hideValid == mHideValidLayers )
907 return;
908
909 mHideValidLayers = hideValid;
910 invalidateFilter();
911}
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition qgis.h:154
@ Info
Information message.
Definition qgis.h:155
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:5627
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Definition qgsgui.cpp:128
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...
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.
void setCurrentNode(QgsLayerTreeNode *node)
Sets the currently selected node.
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.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QString id
Definition qgsmaplayer.h:79
@ Private
Determines if the layer is meant to be exposed to the GUI, i.e. visible in the layer legend tree.
A bar for displaying non-blocking messages to the user.
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.