QGIS API Documentation 3.41.0-Master (3440c17df1d)
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#include "moc_qgslayertreeview.cpp"
18
19#include "qgslayertree.h"
21#include "qgslayertreemodel.h"
23#include "qgslayertreeutils.h"
25#include "qgsmaplayer.h"
26#include "qgsmessagebar.h"
27
28#include "qgsgui.h"
29
30#include <QApplication>
31#include <QMenu>
32#include <QContextMenuEvent>
33#include <QHeaderView>
34#include <QMimeData>
35#include <QScrollBar>
36
37#ifdef ENABLE_MODELTEST
38#include "modeltest.h"
39#endif
40
43
44
46 : QTreeView( parent )
47 , mBlockDoubleClickTimer( new QTimer( this ) )
48
49{
50 mBlockDoubleClickTimer->setSingleShot( true );
51 mBlockDoubleClickTimer->setInterval( QApplication::doubleClickInterval() );
52 setHeaderHidden( true );
53
54 setDragEnabled( true );
55 setAcceptDrops( true );
56 setDropIndicatorShown( true );
57 setEditTriggers( EditKeyPressed );
58 setExpandsOnDoubleClick( false ); // normally used for other actions
59
60 // Ensure legend graphics are scrollable
61 header()->setStretchLastSection( false );
62 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
63
64 // If vertically scrolling by item, legend graphics can get clipped
65 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
66
67 setSelectionMode( ExtendedSelection );
68 setDefaultDropAction( Qt::MoveAction );
69
70 // we need a custom item delegate in order to draw indicators
71 setItemDelegate( new QgsLayerTreeViewItemDelegate( this ) );
72 setStyle( new QgsLayerTreeViewProxyStyle( this ) );
73
74 setLayerMarkWidth( static_cast< int >( QFontMetricsF( font() ).horizontalAdvance( 'l' ) * Qgis::UI_SCALE_FACTOR ) );
75
76 connect( this, &QTreeView::collapsed, this, &QgsLayerTreeView::updateExpandedStateToNode );
77 connect( this, &QTreeView::expanded, this, &QgsLayerTreeView::updateExpandedStateToNode );
78
79 connect( horizontalScrollBar(), &QScrollBar::valueChanged, this, &QgsLayerTreeView::onHorizontalScroll );
80}
81
83{
84 delete mMenuProvider;
85 delete mBlockDoubleClickTimer;
86}
87
88void QgsLayerTreeView::setModel( QAbstractItemModel *model )
89{
90 QgsLayerTreeModel *treeModel = qobject_cast<QgsLayerTreeModel *>( model );
91 if ( !treeModel )
92 return;
93
94 if ( mMessageBar )
95 connect( treeModel, &QgsLayerTreeModel::messageEmitted, this,
96 [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
97 {
98 Q_UNUSED( duration )
99 mMessageBar->pushMessage( message, level );
100 }
101 );
102
103 treeModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
104
105 mProxyModel = new QgsLayerTreeProxyModel( treeModel, this );
106
107 connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
108 connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
109
110#ifdef ENABLE_MODELTEST
111 new ModelTest( mProxyModel, this );
112#endif
113
114 mProxyModel->setShowPrivateLayers( mShowPrivateLayers );
115 mProxyModel->setHideValidLayers( mHideValidLayers );
116 QTreeView::setModel( mProxyModel );
117
119 connect( treeModel->rootGroup(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeView::onCustomPropertyChanged );
120
121 connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsLayerTreeView::onCurrentChanged );
122
123 connect( treeModel, &QAbstractItemModel::modelReset, this, &QgsLayerTreeView::onModelReset );
124
125 connect( treeModel, &QAbstractItemModel::dataChanged, this, &QgsLayerTreeView::onDataChanged );
126
128
129 //checkModel();
130}
131
133{
134 return mProxyModel ? qobject_cast<QgsLayerTreeModel *>( mProxyModel->sourceModel() ) : nullptr;
135}
136
143
149
151{
152 return layerForIndex( currentIndex() );
153}
154
156{
157 if ( !node )
158 {
159 setCurrentIndex( QModelIndex() );
160 return;
161 }
162
163 setCurrentIndex( node2index( node ) );
164}
165
167{
168 if ( !layer )
169 {
170 setCurrentIndex( QModelIndex() );
171 return;
172 }
173
174 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
175 if ( !nodeLayer )
176 return;
177
178 setCurrentNode( nodeLayer );
179}
180
182{
183 if ( !layer )
184 return;
185 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
186 if ( !nodeLayer )
187 return;
188 nodeLayer->setItemVisibilityChecked( visible );
189}
190
191void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
192{
193 if ( !mMenuProvider )
194 return;
195
196 const QModelIndex idx = indexAt( event->pos() );
197 if ( !idx.isValid() )
198 setCurrentIndex( QModelIndex() );
199
200 QMenu *menu = mMenuProvider->createContextMenu();
201 if ( menu )
202 {
203 emit contextMenuAboutToShow( menu );
204
205 if ( menu->actions().count() != 0 )
206 menu->exec( mapToGlobal( event->pos() ) );
207 delete menu;
208 }
209}
210
211
212void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end )
213{
214 QgsLayerTreeNode *parentNode = index2node( index );
215 if ( !parentNode )
216 return;
217
218 // Embedded widgets - replace placeholders in the model by actual widgets
220 {
221 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( parentNode );
222 if ( QgsMapLayer *layer = nodeLayer->layer() )
223 {
224 const int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
225 QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
226 for ( int i = 0; i < widgetsCount; ++i )
227 {
228 const QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
229 if ( QgsLayerTreeEmbeddedWidgetProvider *provider = QgsGui::layerTreeEmbeddedWidgetRegistry()->provider( providerId ) )
230 {
231 const QModelIndex index = legendNode2index( legendNodes[i] );
232 QWidget *wdgt = provider->createWidget( layer, i );
233 // Since column is resized to contents, limit the expanded width of embedded
234 // widgets, if they are not already limited, e.g. have the default MAX value.
235 // Else, embedded widget may grow very wide due to large legend graphics.
236 // NOTE: This approach DOES NOT work right. It causes horizontal scroll
237 // bar to disappear if the embedded widget is expanded and part
238 // of the last layer in the panel, even if much wider legend items
239 // are expanded above it. The correct width-limiting method should
240 // be setting fixed-width, hidpi-aware embedded widget items in a
241 // layout and appending an expanding QSpacerItem to end. This ensures
242 // full width is always created in the column by the embedded widget.
243 // See QgsLayerTreeOpacityWidget
244 //if ( wdgt->maximumWidth() == QWIDGETSIZE_MAX )
245 //{
246 // wdgt->setMaximumWidth( 250 );
247 //}
248
249 setIndexWidget( index, wdgt );
250 }
251 }
252 }
253 }
254
255
256 if ( QgsLayerTree::isLayer( parentNode ) )
257 {
258 // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
259 const QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
260 if ( expandedNodeKeys.isEmpty() )
261 return;
262
263 const auto constLayerLegendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true );
264 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
265 {
266 const QString ruleKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
267 if ( expandedNodeKeys.contains( ruleKey ) )
268 setExpanded( legendNode2index( legendNode ), true );
269 }
270 return;
271 }
272
273 QList<QgsLayerTreeNode *> children = parentNode->children();
274 for ( int i = start; i <= end && i < children.count(); ++i )
275 {
276 updateExpandedStateFromNode( children[i] );
277 }
278
279 // make sure we still have correct current layer
281}
282
284{
285 // make sure we still have correct current layer
287}
288
289void QgsLayerTreeView::updateExpandedStateToNode( const QModelIndex &index )
290{
291 if ( QgsLayerTreeNode *node = index2node( index ) )
292 {
293 node->setExpanded( isExpanded( index ) );
294 }
295 else if ( QgsLayerTreeModelLegendNode *node = index2legendNode( index ) )
296 {
297 const QString ruleKey = node->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
298 QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
299 const bool expanded = isExpanded( index );
300 const bool isInList = lst.contains( ruleKey );
301 if ( expanded && !isInList )
302 {
303 lst.append( ruleKey );
304 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
305 }
306 else if ( !expanded && isInList )
307 {
308 lst.removeAll( ruleKey );
309 node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
310 }
311 }
312}
313
315{
316 QgsMapLayer *layerCurrent = layerForIndex( currentIndex() );
317 const QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
318 if ( mCurrentLayerID == layerCurrentID )
319 return;
320
321 // update the current index in model (the item will be underlined)
322 QModelIndex proxyModelNodeLayerIndex;
323 if ( layerCurrent )
324 {
325 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
326 if ( nodeLayer )
327 proxyModelNodeLayerIndex = node2index( nodeLayer );
328 }
329
330 if ( ! proxyModelNodeLayerIndex.isValid() )
331 {
332 mCurrentLayerID = QString();
333 layerTreeModel()->setCurrentIndex( QModelIndex() );
334 }
335 else
336 {
337 mCurrentLayerID = layerCurrentID;
338 layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
339 }
340
341 //checkModel();
342
343 emit currentLayerChanged( layerCurrent );
344}
345
347{
348 const QModelIndex idx = node2index( node );
349 if ( isExpanded( idx ) != expanded )
350 setExpanded( idx, expanded );
351}
352
353void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
354{
355 if ( key != QLatin1String( "expandedLegendNodes" ) || !QgsLayerTree::isLayer( node ) )
356 return;
357
358 const QSet<QString> expandedLegendNodes = qgis::listToSet( node->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
359
360 const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( node ), true );
361 for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
362 {
363 const QString key = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) ).toString();
364 if ( !key.isEmpty() )
365 setExpanded( legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
366 }
367}
368
370{
372 //checkModel();
373}
374
376{
377 const QModelIndex idx = node2index( node );
378 setExpanded( idx, node->isExpanded() );
379
380 const auto constChildren = node->children();
381 for ( QgsLayerTreeNode *child : constChildren )
383}
384
385QgsMapLayer *QgsLayerTreeView::layerForIndex( const QModelIndex &index ) const
386{
387 // Check if model has been set and index is valid
388 if ( layerTreeModel() && index.isValid() )
389 {
390 QgsLayerTreeNode *node = index2node( index );
391 if ( node )
392 {
393 if ( QgsLayerTree::isLayer( node ) )
394 return QgsLayerTree::toLayer( node )->layer();
395 }
396 else
397 {
398 // possibly a legend node
399 QgsLayerTreeModelLegendNode *legendNode = index2legendNode( index );
400 if ( legendNode )
401 return legendNode->layerNode()->layer();
402 }
403 }
404 return nullptr;
405}
406
408{
409 return index2node( selectionModel()->currentIndex() );
410}
411
413{
415 if ( QgsLayerTree::isGroup( node ) )
416 return QgsLayerTree::toGroup( node );
417 else if ( QgsLayerTree::isLayer( node ) )
418 {
419 QgsLayerTreeNode *parent = node->parent();
420 if ( QgsLayerTree::isGroup( parent ) )
421 return QgsLayerTree::toGroup( parent );
422 }
423
424 if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( selectionModel()->currentIndex() ) )
425 {
426 QgsLayerTreeLayer *parent = legendNode->layerNode();
427 if ( QgsLayerTree::isGroup( parent->parent() ) )
428 return QgsLayerTree::toGroup( parent->parent() );
429 }
430
431 return nullptr;
432}
433
435{
436 return index2legendNode( selectionModel()->currentIndex() );
437}
438
439QList<QgsLayerTreeNode *> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
440{
441 QModelIndexList mapped;
442 const QModelIndexList selected = selectionModel()->selectedIndexes();
443 mapped.reserve( selected.size() );
444 for ( const QModelIndex &index : selected )
445 mapped << mProxyModel->mapToSource( index );
446
447 return layerTreeModel()->indexes2nodes( mapped, skipInternal );
448}
449
450QList<QgsLayerTreeLayer *> QgsLayerTreeView::selectedLayerNodes() const
451{
452 QList<QgsLayerTreeLayer *> layerNodes;
453 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
454 layerNodes.reserve( constSelectedNodes.size() );
455 for ( QgsLayerTreeNode *node : constSelectedNodes )
456 {
457 if ( QgsLayerTree::isLayer( node ) )
458 layerNodes << QgsLayerTree::toLayer( node );
459 }
460 return layerNodes;
461}
462
463QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
464{
465 QList<QgsMapLayer *> list;
466 const QList<QgsLayerTreeLayer *> constSelectedLayerNodes = selectedLayerNodes();
467 list.reserve( constSelectedLayerNodes.size() );
468 for ( QgsLayerTreeLayer *node : constSelectedLayerNodes )
469 {
470 if ( node->layer() )
471 list << node->layer();
472 }
473 return list;
474}
475
476QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeView::selectedLegendNodes() const
477{
478 QList<QgsLayerTreeModelLegendNode *> res;
479 const QModelIndexList selected = selectionModel()->selectedIndexes();
480 res.reserve( selected.size() );
481 for ( const QModelIndex &index : selected )
482 {
483 const QModelIndex &modelIndex = mProxyModel->mapToSource( index );
484 if ( QgsLayerTreeModelLegendNode *node = layerTreeModel()->index2legendNode( modelIndex ) )
485 {
486 res.push_back( node );
487 }
488 }
489
490 return res;
491}
492
494{
495 QModelIndexList mapped;
496 const QModelIndexList selected = selectionModel()->selectedIndexes();
497 mapped.reserve( selected.size() );
498 for ( const QModelIndex &index : selected )
499 mapped << mProxyModel->mapToSource( index );
500
501 const QList<QgsLayerTreeNode *> nodes = layerTreeModel()->indexes2nodes( mapped, false );
502 const QSet<QgsMapLayer *> layersSet = QgsLayerTreeUtils::collectMapLayersRecursive( nodes );
503 return qgis::setToList( layersSet );
504}
505
507{
508 if ( !mIndicators[node].contains( indicator ) )
509 {
510 mIndicators[node].append( indicator );
511 connect( indicator, &QgsLayerTreeViewIndicator::changed, this, [ = ]
512 {
513 update();
514 viewport()->repaint();
515 } );
516 update();
517 viewport()->repaint(); //update() does not automatically trigger a repaint()
518 }
519}
520
522{
523 mIndicators[node].removeOne( indicator );
524 update();
525}
526
527QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
528{
529 return mIndicators.value( node );
530}
531
533QStringList QgsLayerTreeView::viewOnlyCustomProperties()
534{
535 return QStringList() << QStringLiteral( "expandedLegendNodes" );
536}
538
539void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
540{
541 QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
542 if ( nodeLayer )
543 layerTreeModel()->refreshLayerLegend( nodeLayer );
544}
545
546
547static void _expandAllLegendNodes( QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model )
548{
549 // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
550 // if we are collapsing, we just write out an empty list
551 QStringList lst;
552 if ( expanded )
553 {
554 const auto constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
555 for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
556 {
557 const QString parentKey = legendNode->data( static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) ).toString();
558 if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
559 lst << parentKey;
560 }
561 }
562 nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
563}
564
565
566static void _expandAllNodes( QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model )
567{
568 const auto constChildren = parent->children();
569 for ( QgsLayerTreeNode *node : constChildren )
570 {
571 node->setExpanded( expanded );
572 if ( QgsLayerTree::isGroup( node ) )
573 _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
574 else if ( QgsLayerTree::isLayer( node ) )
575 _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
576 }
577}
578
579
581{
582 // unfortunately expandAll() does not emit expanded() signals
583 _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
584 expandAll();
585}
586
588{
589 // unfortunately collapseAll() does not emit collapsed() signals
590 _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
591 collapseAll();
592}
593
595{
596 if ( mMessageBar == messageBar )
597 return;
598
599 mMessageBar = messageBar;
600
601 if ( mMessageBar )
603 [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
604 {
605 Q_UNUSED( duration )
606 mMessageBar->pushMessage( message, level );
607 }
608 );
609}
610
612{
613 mShowPrivateLayers = showPrivate;
614 mProxyModel->setShowPrivateLayers( showPrivate );
615}
616
618{
619 mHideValidLayers = hideValid;
620 mProxyModel->setHideValidLayers( mHideValidLayers );
621}
622
624{
625 return mShowPrivateLayers;
626}
627
629{
630 return mHideValidLayers;
631}
632
634{
635 if ( mBlockDoubleClickTimer->isActive() )
636 event->accept();
637 else
638 QTreeView::mouseDoubleClickEvent( event );
639}
640
641void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
642{
643 // we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
644 // (the item delegate needs to know which indicator has been clicked)
645 mLastReleaseMousePos = event->pos();
646
647 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
648 if ( event->modifiers() & Qt::ControlModifier )
650 else
652 QTreeView::mouseReleaseEvent( event );
653 layerTreeModel()->setFlags( oldFlags );
654}
655
656void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
657{
658 if ( event->key() == Qt::Key_Space )
659 {
660 const QList<QgsLayerTreeNode *> constSelectedNodes = selectedNodes();
661
662 if ( !constSelectedNodes.isEmpty() )
663 {
664 const bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
665 for ( QgsLayerTreeNode *node : constSelectedNodes )
666 {
667 node->setItemVisibilityChecked( ! isFirstNodeChecked );
668 }
669
670 // if we call the original keyPress handler, the current item will be checked to the original state yet again
671 return;
672 }
673 }
674
675 const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
676 if ( event->modifiers() & Qt::ControlModifier )
678 else
680 QTreeView::keyPressEvent( event );
681 layerTreeModel()->setFlags( oldFlags );
682}
683
684void QgsLayerTreeView::dragEnterEvent( QDragEnterEvent *event )
685{
686 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
687 {
688 // the mime data are coming from layer tree, so ignore that, do not import those layers again
689 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
690 {
691 event->accept();
692 return;
693 }
694 }
695 QTreeView::dragEnterEvent( event );
696}
697
698void QgsLayerTreeView::dragMoveEvent( QDragMoveEvent *event )
699{
700 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
701 {
702 // the mime data are coming from layer tree, so ignore that, do not import those layers again
703 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
704 {
705 event->accept();
706 return;
707 }
708 }
709 QTreeView::dragMoveEvent( event );
710}
711
712void QgsLayerTreeView::dropEvent( QDropEvent *event )
713{
714 if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
715 {
716 // the mime data are coming from layer tree, so ignore that, do not import those layers again
717 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
718 {
719 event->accept();
720
721 QModelIndex index = indexAt( event->pos() );
722 if ( index.isValid() )
723 {
724 setCurrentIndex( index );
725 }
726
727 emit datasetsDropped( event );
728 return;
729 }
730 }
731 if ( event->keyboardModifiers() & Qt::AltModifier || event->keyboardModifiers() & Qt::ControlModifier )
732 {
733 event->accept();
734 }
735 QTreeView::dropEvent( event );
736}
737
738void QgsLayerTreeView::resizeEvent( QResizeEvent *event )
739{
740 // Since last column is resized to content (instead of stretched), the active
741 // selection rectangle ends at width of widest visible item in tree,
742 // regardless of which item is selected. This causes layer indicators to
743 // become 'inactive' (not clickable and no tool tip) unless their rectangle
744 // enters the view item's selection (active) rectangle.
745 // Always resetting the minimum section size relative to the viewport ensures
746 // the view item's selection rectangle extends to the right edge of the
747 // viewport, which allows indicators to become active again.
748 header()->setMinimumSectionSize( viewport()->width() );
749 QTreeView::resizeEvent( event );
750}
751
752void QgsLayerTreeView::onHorizontalScroll( int value )
753{
754 Q_UNUSED( value )
755 viewport()->update();
756}
757
758void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
759{
760 Q_UNUSED( topLeft )
761 Q_UNUSED( bottomRight )
762
763 // If an item is resized asynchronously (e.g. wms legend)
764 // The items below will need to be shifted vertically.
765 // This doesn't happen automatically, unless the viewport update is triggered.
766
767 if ( roles.contains( Qt::SizeHintRole ) )
768 viewport()->update();
769
770 mBlockDoubleClickTimer->start();
771 //checkModel();
772}
773
774#if 0
775// for model debugging
776void QgsLayerTreeView::checkModel()
777{
778 std::function<void( QgsLayerTreeNode *, int )> debug;
779 debug = [ & ]( QgsLayerTreeNode * node, int depth )
780 {
781 if ( depth == 1 )
782 qDebug() << "----------------------------------------------";
783
784 qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) );
785 Q_ASSERT( node == index2node( node2index( node ) ) );
786 Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) );
787 Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) );
788
789 for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ )
790 {
791 QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) };
792 if ( childNode )
793 debug( childNode, depth + 1 );
794 else
795 qDebug() << "Warning no child node!";
796 }
797 };
798 debug( layerTreeModel()->rootGroup(), 1 );
799}
800#endif
801
803{
804 return mProxyModel;
805}
806
807QgsLayerTreeNode *QgsLayerTreeView::index2node( const QModelIndex &index ) const
808{
809 return layerTreeModel()->index2node( mProxyModel->mapToSource( index ) );
810}
811
813{
814 return mProxyModel->mapFromSource( node2sourceIndex( node ) );
815}
816
818{
819 return layerTreeModel()->node2index( node );
820}
821
823{
824 return QgsLayerTreeModel::index2legendNode( mProxyModel->mapToSource( index ) );
825}
826
828{
829 return mProxyModel->mapFromSource( legendNode2sourceIndex( legendNode ) );
830}
831
833{
834 return layerTreeModel()->legendNode2index( legendNode );
835}
836
838 : QSortFilterProxyModel( parent )
839 , mLayerTreeModel( treeModel )
840{
841 setSourceModel( treeModel );
842}
843
844void QgsLayerTreeProxyModel::setFilterText( const QString &filterText )
845{
846 if ( filterText == mFilterText )
847 return;
848
849 mFilterText = filterText;
850 invalidateFilter();
851}
852
853bool QgsLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
854{
855 QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
856 return nodeShown( node );
857}
858
859bool QgsLayerTreeProxyModel::nodeShown( QgsLayerTreeNode *node ) const
860{
861 if ( !node )
862 return true;
863
864 if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
865 {
866 return true;
867 }
868 else
869 {
870 QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
871 if ( !layer )
872 return true;
873 if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
874 return false;
875 if ( ! mShowPrivateLayers && layer->flags().testFlag( QgsMapLayer::LayerFlag::Private ) )
876 {
877 return false;
878 }
879 if ( mHideValidLayers && layer->isValid() )
880 return false;
881
882 return true;
883 }
884}
885
887{
888 return mShowPrivateLayers;
889}
890
892{
893 if ( showPrivate == mShowPrivateLayers )
894 return;
895
896 mShowPrivateLayers = showPrivate;
897 invalidateFilter();
898}
899
901{
902 return mHideValidLayers;
903}
904
906{
907 if ( hideValid == mHideValidLayers )
908 return;
909
910 mHideValidLayers = hideValid;
911 invalidateFilter();
912}
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:5667
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Definition qgsgui.cpp:129
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.