QGIS API Documentation  2.14.11-Essen
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"
19 #include "qgslayertreemodel.h"
22 #include "qgsmaplayer.h"
23 
24 #include <QMenu>
25 #include <QContextMenuEvent>
26 
28  : QTreeView( parent )
29  , mDefaultActions( nullptr )
30  , mMenuProvider( nullptr )
31 {
32  setHeaderHidden( true );
33 
34  setDragEnabled( true );
35  setAcceptDrops( true );
36  setDropIndicatorShown( true );
37  setEditTriggers( EditKeyPressed );
38  setExpandsOnDoubleClick( false ); // normally used for other actions
39 
40  setSelectionMode( ExtendedSelection );
41  setDefaultDropAction( Qt::MoveAction );
42 
43  connect( this, SIGNAL( collapsed( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
44  connect( this, SIGNAL( expanded( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
45 }
46 
48 {
49  delete mMenuProvider;
50 }
51 
53 {
54  if ( !qobject_cast<QgsLayerTreeModel*>( model ) )
55  return;
56 
57  connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( modelRowsInserted( QModelIndex, int, int ) ) );
58  connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( modelRowsRemoved() ) );
59 
60  QTreeView::setModel( model );
61 
62  connect( layerTreeModel()->rootGroup(), SIGNAL( expandedChanged( QgsLayerTreeNode*, bool ) ), this, SLOT( onExpandedChanged( QgsLayerTreeNode*, bool ) ) );
63 
64  connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( onCurrentChanged() ) );
65 
66  connect( layerTreeModel(), SIGNAL( modelReset() ), this, SLOT( onModelReset() ) );
67 
69 }
70 
72 {
73  return qobject_cast<QgsLayerTreeModel*>( model() );
74 }
75 
77 {
78  if ( !mDefaultActions )
80  return mDefaultActions;
81 }
82 
84 {
85  delete mMenuProvider;
87 }
88 
90 {
91  return layerForIndex( currentIndex() );
92 }
93 
95 {
96  if ( !layer )
97  {
99  return;
100  }
101 
102  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
103  if ( !nodeLayer )
104  return;
105 
106  setCurrentIndex( layerTreeModel()->node2index( nodeLayer ) );
107 }
108 
109 
111 {
112  if ( !mMenuProvider )
113  return;
114 
115  QModelIndex idx = indexAt( event->pos() );
116  if ( !idx.isValid() )
118 
120  if ( menu && menu->actions().count() != 0 )
121  menu->exec( mapToGlobal( event->pos() ) );
122  delete menu;
123 }
124 
125 
126 void QgsLayerTreeView::modelRowsInserted( const QModelIndex& index, int start, int end )
127 {
128  QgsLayerTreeNode* parentNode = layerTreeModel()->index2node( index );
129  if ( !parentNode )
130  return;
131 
132  if ( QgsLayerTree::isLayer( parentNode ) )
133  {
134  // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
135  QStringList expandedNodeKeys = parentNode->customProperty( "expandedLegendNodes" ).toStringList();
136  if ( expandedNodeKeys.isEmpty() )
137  return;
138 
139  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ) ) )
140  {
142  if ( expandedNodeKeys.contains( ruleKey ) )
143  setExpanded( layerTreeModel()->legendNode2index( legendNode ), true );
144  }
145  return;
146  }
147 
149  for ( int i = start; i <= end; ++i )
150  {
151  updateExpandedStateFromNode( children[i] );
152  }
153 
154  // make sure we still have correct current layer
156 }
157 
159 {
160  // make sure we still have correct current layer
162 }
163 
165 {
166  if ( QgsLayerTreeNode* node = layerTreeModel()->index2node( index ) )
167  {
168  node->setExpanded( isExpanded( index ) );
169  }
170  else if ( QgsLayerTreeModelLegendNode* node = layerTreeModel()->index2legendNode( index ) )
171  {
172  QString ruleKey = node->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
173  QStringList lst = node->layerNode()->customProperty( "expandedLegendNodes" ).toStringList();
174  bool expanded = isExpanded( index );
175  bool isInList = lst.contains( ruleKey );
176  if ( expanded && !isInList )
177  {
178  lst.append( ruleKey );
179  node->layerNode()->setCustomProperty( "expandedLegendNodes", lst );
180  }
181  else if ( !expanded && isInList )
182  {
183  lst.removeAll( ruleKey );
184  node->layerNode()->setCustomProperty( "expandedLegendNodes", lst );
185  }
186  }
187 }
188 
190 {
191  QgsMapLayer* layerCurrent = layerForIndex( currentIndex() );
192  QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
193  if ( mCurrentLayerID == layerCurrentID )
194  return;
195 
196  // update the current index in model (the item will be underlined)
197  QModelIndex nodeLayerIndex;
198  if ( layerCurrent )
199  {
200  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
201  if ( nodeLayer )
202  nodeLayerIndex = layerTreeModel()->node2index( nodeLayer );
203  }
204  layerTreeModel()->setCurrentIndex( nodeLayerIndex );
205 
206  mCurrentLayerID = layerCurrentID;
207  emit currentLayerChanged( layerCurrent );
208 }
209 
211 {
212  QModelIndex idx = layerTreeModel()->node2index( node );
213  if ( isExpanded( idx ) != expanded )
214  setExpanded( idx, expanded );
215 }
216 
218 {
220 }
221 
223 {
224  QModelIndex idx = layerTreeModel()->node2index( node );
225  setExpanded( idx, node->isExpanded() );
226 
227  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
229 }
230 
232 {
233  QgsLayerTreeNode* node = layerTreeModel()->index2node( index );
234  if ( node )
235  {
236  if ( QgsLayerTree::isLayer( node ) )
237  return QgsLayerTree::toLayer( node )->layer();
238  }
239  else
240  {
241  // possibly a legend node
243  if ( legendNode )
244  return legendNode->layerNode()->layer();
245  }
246 
247  return nullptr;
248 }
249 
251 {
253 }
254 
256 {
257  QgsLayerTreeNode* node = currentNode();
258  if ( QgsLayerTree::isGroup( node ) )
259  return QgsLayerTree::toGroup( node );
260  else if ( QgsLayerTree::isLayer( node ) )
261  {
262  QgsLayerTreeNode* parent = node->parent();
263  if ( QgsLayerTree::isGroup( parent ) )
264  return QgsLayerTree::toGroup( parent );
265  }
266 
267  if ( QgsLayerTreeModelLegendNode* legendNode = layerTreeModel()->index2legendNode( selectionModel()->currentIndex() ) )
268  {
269  QgsLayerTreeLayer* parent = legendNode->layerNode();
270  if ( QgsLayerTree::isGroup( parent->parent() ) )
271  return QgsLayerTree::toGroup( parent->parent() );
272  }
273 
274  return nullptr;
275 }
276 
278 {
280 }
281 
283 {
284  return layerTreeModel()->indexes2nodes( selectionModel()->selectedIndexes(), skipInternal );
285 }
286 
288 {
289  QList<QgsLayerTreeLayer*> layerNodes;
290  Q_FOREACH ( QgsLayerTreeNode* node, selectedNodes() )
291  {
292  if ( QgsLayerTree::isLayer( node ) )
293  layerNodes << QgsLayerTree::toLayer( node );
294  }
295  return layerNodes;
296 }
297 
299 {
300  QList<QgsMapLayer*> list;
301  Q_FOREACH ( QgsLayerTreeLayer* node, selectedLayerNodes() )
302  {
303  if ( node->layer() )
304  list << node->layer();
305  }
306  return list;
307 }
308 
309 
311 {
312  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
313  if ( nodeLayer )
314  layerTreeModel()->refreshLayerLegend( nodeLayer );
315 }
316 
317 
319 {
320  // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
321  // if we are collapsing, we just write out an empty list
322  QStringList lst;
323  if ( expanded )
324  {
325  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, model->layerLegendNodes( nodeLayer ) )
326  {
328  if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
329  lst << parentKey;
330  }
331  }
332  nodeLayer->setCustomProperty( "expandedLegendNodes", lst );
333 }
334 
335 
337 {
338  Q_FOREACH ( QgsLayerTreeNode* node, parent->children() )
339  {
340  node->setExpanded( expanded );
341  if ( QgsLayerTree::isGroup( node ) )
342  _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
343  else if ( QgsLayerTree::isLayer( node ) )
344  _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
345  }
346 }
347 
348 
350 {
351  // unfortunately expandAll() does not emit expanded() signals
352  _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
353  expandAll();
354 }
355 
357 {
358  // unfortunately collapseAll() does not emit collapsed() signals
359  _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
360  collapseAll();
361 }
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
Layer tree group node serves as a container for layers and further groups.
static unsigned index
QList< QgsMapLayer * > selectedLayers() const
Get list of selected layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QgsLayerTreeGroup * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer)
Return filtered list of active legend nodes attached to a particular layer node.
Implementation of this interface can be implemented to allow QgsLayerTreeView instance to provide cus...
void setCurrentIndex(const QModelIndex &currentIndex)
Set index of the current item. May be used by view. Item marked as current is underlined.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QgsLayerTreeViewMenuProvider * mMenuProvider
Context menu provider. Owned by the view.
void setCurrentIndex(const QModelIndex &index)
void setSelectionMode(QAbstractItemView::SelectionMode mode)
static void _expandAllLegendNodes(QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model)
QItemSelectionModel * selectionModel() const
void refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer&#39;s renderer are monitore...
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
void contextMenuEvent(QContextMenuEvent *event) override
void collapseAll()
void collapsed(const QModelIndex &index)
const QObjectList & children() const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QPoint mapToGlobal(const QPoint &pos) const
QgsLayerTreeLayer * layerNode() const
Return pointer to the parent layer node.
void modelRowsInserted(const QModelIndex &index, int start, int end)
QgsMapLayer * currentLayer() const
Get currently selected layer. May be null.
void rowsRemoved(const QModelIndex &parent, int start, int end)
QgsLayerTreeViewDefaultActions * defaultActions()
Get access to the default actions that may be used with the tree view.
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
QModelIndex node2index(QgsLayerTreeNode *node) const
Return index for a given node. If the node does not belong to the layer tree, the result is undefined...
virtual QMenu * createContextMenu()=0
Return a newly created menu instance (or null pointer on error)
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Return list of selected nodes filtered to just layer nodes.
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer) ...
void setExpanded(const QModelIndex &index, bool expanded)
The QgsLayerTreeViewDefaultActions class serves as a factory of actions that can be used together wit...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2...
bool isValid() const
void updateExpandedStateToNode(const QModelIndex &index)
void append(const T &value)
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Return index for a given legend node.
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Return legend node for given index.
bool isEmpty() const
QgsLayerTreeNode * parent()
Get pointer to the parent. If parent is a null pointer, the node is a root node.
QgsLayerTreeGroup * currentGroupNode() const
Get current group node. If a layer is current node, the function will return parent group...
bool isEmpty() const
int removeAll(const T &value)
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void setEditTriggers(QFlags< QAbstractItemView::EditTrigger > triggers)
This class is a base class for nodes in a layer tree.
void collapseAllNodes()
Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes...
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
bool isExpanded(const QModelIndex &index) const
QAction * exec()
void setAcceptDrops(bool on)
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
QgsLayerTreeModel * layerTreeModel() const
Get access to the model casted to QgsLayerTreeModel.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
void setExpanded(bool expanded)
Set whether the node should be shown as expanded or collapsed in GUI.
void expandAll()
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
QgsMapLayer * layer() const
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
void setExpandsOnDoubleClick(bool enable)
virtual bool event(QEvent *event)
const QPoint & pos() const
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Return list of selected nodes.
QStringList toStringList() const
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Return layer tree node for given index.
void onExpandedChanged(QgsLayerTreeNode *node, bool expanded)
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
void setHeaderHidden(bool hide)
QgsLayerTreeModelLegendNode * currentLegendNode() const
Get current legend node.
virtual void setModel(QAbstractItemModel *model)
QgsLayerTreeViewMenuProvider * menuProvider() const
Return pointer to the context menu provider. May be null.
virtual QVariant data(int role) const =0
Return data associated with the item.
QgsMapLayer * layerForIndex(const QModelIndex &index) const
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QModelIndexList selectedIndexes() const
virtual void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
void setCurrentLayer(QgsMapLayer *layer)
Set currently selected layer. Null pointer will deselect any layer.
QgsLayerTreeView(QWidget *parent=nullptr)
void expanded(const QModelIndex &index)
void setDefaultDropAction(Qt::DropAction dropAction)
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Set provider for context menu. Takes ownership of the instance.
QAbstractItemModel * model() const
QModelIndex currentIndex() const
void expandAllNodes()
Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes...
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QList< QAction * > actions() const
QObject * parent() const
QChar * data()
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QString toString() const
virtual QModelIndex indexAt(const QPoint &point) const
void setDropIndicatorShown(bool enable)
void setDragEnabled(bool enable)
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
Layer tree node points to a map layer.
static void _expandAllNodes(QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model)
QgsLayerTreeNode * currentNode() const
Get current node. May be null.