QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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( 0 )
30  , mMenuProvider( 0 )
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 
42  connect( this, SIGNAL( collapsed( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
43  connect( this, SIGNAL( expanded( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
44 }
45 
47 {
48  delete mMenuProvider;
49 }
50 
51 void QgsLayerTreeView::setModel( QAbstractItemModel* model )
52 {
53  if ( !qobject_cast<QgsLayerTreeModel*>( model ) )
54  return;
55 
56  connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( modelRowsInserted( QModelIndex, int, int ) ) );
57  connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( modelRowsRemoved() ) );
58 
59  QTreeView::setModel( model );
60 
61  connect( layerTreeModel()->rootGroup(), SIGNAL( expandedChanged( QgsLayerTreeNode*, bool ) ), this, SLOT( onExpandedChanged( QgsLayerTreeNode*, bool ) ) );
62 
63  connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( onCurrentChanged() ) );
64 
65  connect( layerTreeModel(), SIGNAL( modelReset() ), this, SLOT( onModelReset() ) );
66 
68 }
69 
71 {
72  return qobject_cast<QgsLayerTreeModel*>( model() );
73 }
74 
76 {
77  if ( !mDefaultActions )
79  return mDefaultActions;
80 }
81 
83 {
84  delete mMenuProvider;
86 }
87 
89 {
90  return layerForIndex( currentIndex() );
91 }
92 
94 {
95  if ( !layer )
96  {
97  setCurrentIndex( QModelIndex() );
98  return;
99  }
100 
101  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
102  if ( !nodeLayer )
103  return;
104 
105  setCurrentIndex( layerTreeModel()->node2index( nodeLayer ) );
106 }
107 
108 
109 void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
110 {
111  if ( !mMenuProvider )
112  return;
113 
114  QModelIndex idx = indexAt( event->pos() );
115  if ( !idx.isValid() )
116  setCurrentIndex( QModelIndex() );
117 
118  QMenu* menu = mMenuProvider->createContextMenu();
119  if ( menu && menu->actions().count() != 0 )
120  menu->exec( mapToGlobal( event->pos() ) );
121  delete menu;
122 }
123 
124 
125 void QgsLayerTreeView::modelRowsInserted( QModelIndex index, int start, int end )
126 {
127  QgsLayerTreeNode* parentNode = layerTreeModel()->index2node( index );
128  if ( !parentNode )
129  return;
130 
131  if ( QgsLayerTree::isLayer( parentNode ) )
132  {
133  // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
134  QStringList expandedNodeKeys = parentNode->customProperty( "expandedLegendNodes" ).toStringList();
135  if ( expandedNodeKeys.isEmpty() )
136  return;
137 
138  foreach ( QgsLayerTreeModelLegendNode* legendNode, layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ) ) )
139  {
140  QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
141  if ( expandedNodeKeys.contains( ruleKey ) )
142  setExpanded( layerTreeModel()->legendNode2index( legendNode ), true );
143  }
144  return;
145  }
146 
147  QList<QgsLayerTreeNode*> children = parentNode->children();
148  for ( int i = start; i <= end; ++i )
149  {
150  updateExpandedStateFromNode( children[i] );
151  }
152 
153  // make sure we still have correct current layer
155 }
156 
158 {
159  // make sure we still have correct current layer
161 }
162 
164 {
165  if ( QgsLayerTreeNode* node = layerTreeModel()->index2node( index ) )
166  {
167  node->setExpanded( isExpanded( index ) );
168  }
169  else if ( QgsLayerTreeModelLegendNode* node = layerTreeModel()->index2legendNode( index ) )
170  {
171  QString ruleKey = node->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
172  QStringList lst = node->layerNode()->customProperty( "expandedLegendNodes" ).toStringList();
173  bool expanded = isExpanded( index );
174  bool isInList = lst.contains( ruleKey );
175  if ( expanded && !isInList )
176  {
177  lst.append( ruleKey );
178  node->layerNode()->setCustomProperty( "expandedLegendNodes", lst );
179  }
180  else if ( !expanded && isInList )
181  {
182  lst.removeAll( ruleKey );
183  node->layerNode()->setCustomProperty( "expandedLegendNodes", lst );
184  }
185  }
186 }
187 
189 {
190  QgsMapLayer* layerCurrent = layerForIndex( currentIndex() );
191  QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
192  if ( mCurrentLayerID == layerCurrentID )
193  return;
194 
195  // update the current index in model (the item will be underlined)
196  QModelIndex nodeLayerIndex;
197  if ( layerCurrent )
198  {
199  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
200  if ( nodeLayer )
201  nodeLayerIndex = layerTreeModel()->node2index( nodeLayer );
202  }
203  layerTreeModel()->setCurrentIndex( nodeLayerIndex );
204 
205  mCurrentLayerID = layerCurrentID;
206  emit currentLayerChanged( layerCurrent );
207 }
208 
210 {
211  QModelIndex idx = layerTreeModel()->node2index( node );
212  if ( isExpanded( idx ) != expanded )
213  setExpanded( idx, expanded );
214 }
215 
217 {
219 }
220 
222 {
223  QModelIndex idx = layerTreeModel()->node2index( node );
224  setExpanded( idx, node->isExpanded() );
225 
226  foreach ( QgsLayerTreeNode* child, node->children() )
228 }
229 
231 {
232  QgsLayerTreeNode* node = layerTreeModel()->index2node( index );
233  if ( node )
234  {
235  if ( QgsLayerTree::isLayer( node ) )
236  return QgsLayerTree::toLayer( node )->layer();
237  }
238  else
239  {
240  // possibly a legend node
242  if ( legendNode )
243  return legendNode->layerNode()->layer();
244  }
245 
246  return 0;
247 }
248 
250 {
251  return layerTreeModel()->index2node( selectionModel()->currentIndex() );
252 }
253 
255 {
256  QgsLayerTreeNode* node = currentNode();
257  if ( QgsLayerTree::isGroup( node ) )
258  return QgsLayerTree::toGroup( node );
259  else if ( QgsLayerTree::isLayer( node ) )
260  {
261  QgsLayerTreeNode* parent = node->parent();
262  if ( QgsLayerTree::isGroup( parent ) )
263  return QgsLayerTree::toGroup( parent );
264  }
265 
266  if ( QgsLayerTreeModelLegendNode* legendNode = layerTreeModel()->index2legendNode( selectionModel()->currentIndex() ) )
267  {
268  QgsLayerTreeLayer* parent = legendNode->layerNode();
269  if ( QgsLayerTree::isGroup( parent->parent() ) )
270  return QgsLayerTree::toGroup( parent->parent() );
271  }
272 
273  return 0;
274 }
275 
276 QList<QgsLayerTreeNode*> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
277 {
278  return layerTreeModel()->indexes2nodes( selectionModel()->selectedIndexes(), skipInternal );
279 }
280 
281 QList<QgsLayerTreeLayer*> QgsLayerTreeView::selectedLayerNodes() const
282 {
283  QList<QgsLayerTreeLayer*> layerNodes;
284  foreach ( QgsLayerTreeNode* node, selectedNodes() )
285  {
286  if ( QgsLayerTree::isLayer( node ) )
287  layerNodes << QgsLayerTree::toLayer( node );
288  }
289  return layerNodes;
290 }
291 
292 QList<QgsMapLayer*> QgsLayerTreeView::selectedLayers() const
293 {
294  QList<QgsMapLayer*> list;
295  foreach ( QgsLayerTreeLayer* node, selectedLayerNodes() )
296  {
297  if ( node->layer() )
298  list << node->layer();
299  }
300  return list;
301 }
302 
303 
304 void QgsLayerTreeView::refreshLayerSymbology( const QString& layerId )
305 {
306  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
307  if ( nodeLayer )
308  layerTreeModel()->refreshLayerLegend( nodeLayer );
309 }
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeModel * layerTreeModel() const
Get access to the model casted to QgsLayerTreeModel.
static unsigned index
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer)
Return list of legend nodes attached to a particular layer node.
Implementation of this interface can be implemented to allow QgsLayerTreeView instance to provide cus...
QgsMapLayer * currentLayer() const
Get currently selected layer. May be null.
void setCurrentIndex(const QModelIndex &currentIndex)
Set index of the current item. May be used by view. Item marked as current is underlined.
QgsLayerTreeViewMenuProvider * mMenuProvider
Context menu provider. Owned by the view.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QgsLayerTreeGroup * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitore...
QgsMapLayer * layer() const
void contextMenuEvent(QContextMenuEvent *event) override
QgsLayerTreeViewMenuProvider * menuProvider() const
Return pointer to the context menu provider. May be null.
QgsLayerTreeNode * currentNode() const
Get current node. May be null.
QList< QgsMapLayer * > selectedLayers() const
Get list of selected layers.
QgsLayerTreeViewDefaultActions * defaultActions()
Get access to the default actions that may be used with the tree view.
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Return list of selected nodes.
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
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer) ...
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.
void modelRowsInserted(QModelIndex index, int start, int end)
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Return index for a given legend node.
QgsLayerTreeGroup * currentGroupNode() const
Get current group node. If a layer is current node, the function will return parent group...
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Return legend node for given index.
QgsLayerTreeNode * parent()
Get pointer to the parent. If parent is a null pointer, the node is a root node.
QgsLayerTreeLayer * layerNode() const
Return pointer to the parent layer node.
This class is a base class for nodes in a layer tree.
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Definition: qgsmaplayer.cpp:98
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
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
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Return list of selected nodes filtered to just layer nodes.
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...
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
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
virtual QVariant data(int role) const =0
Return data associated with the item.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
QgsLayerTreeView(QWidget *parent=0)
void setCurrentLayer(QgsMapLayer *layer)
Set currently selected layer. Null pointer will deselect any layer.
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Set provider for context menu. Takes ownership of the instance.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Return layer tree node for given index.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QgsMapLayer * layerForIndex(const QModelIndex &index) const
void updateExpandedStateToNode(QModelIndex index)
Layer tree node points to a map layer.