QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgscustomlayerorderwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscustomlayerorderwidget.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 
17 
18 #include <QCheckBox>
19 #include <QListView>
20 #include <QMimeData>
21 #include <QVBoxLayout>
22 
23 #include "qgslayertree.h"
25 
26 #include "qgsmaplayer.h"
27 #include "qgsproject.h"
28 
29 
30 
31 
33  : QWidget( parent )
34  , mBridge( bridge )
35 {
36  mModel = new CustomLayerOrderModel( bridge, this );
37 
38  mView = new QListView( this );
39  mView->setDragEnabled( true );
40  mView->setAcceptDrops( true );
41  mView->setDropIndicatorShown( true );
42  mView->setSelectionMode( QAbstractItemView::ExtendedSelection );
43  mView->setDefaultDropAction( Qt::MoveAction );
44 
45  mView->setModel( mModel );
46 
47  mChkOverride = new QCheckBox( tr( "Control rendering order" ) );
48  bridgeHasCustomLayerOrderChanged( bridge->rootGroup()->hasCustomLayerOrder() );
49  connect( mChkOverride, &QAbstractButton::toggled, bridge->rootGroup(), &QgsLayerTree::setHasCustomLayerOrder );
50 
51  connect( bridge->rootGroup(), &QgsLayerTree::hasCustomLayerOrderChanged, this, &QgsCustomLayerOrderWidget::bridgeHasCustomLayerOrderChanged );
52  connect( bridge->rootGroup(), &QgsLayerTree::customLayerOrderChanged, this, &QgsCustomLayerOrderWidget::bridgeCustomLayerOrderChanged );
53 
54  connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsCustomLayerOrderWidget::modelUpdated );
55  connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsCustomLayerOrderWidget::modelUpdated );
56 
57  connect( bridge->rootGroup(), &QgsLayerTreeNode::visibilityChanged, this, &QgsCustomLayerOrderWidget::nodeVisibilityChanged );
58 
59  QVBoxLayout *l = new QVBoxLayout;
60  l->setMargin( 0 );
61  l->addWidget( mView );
62  l->addWidget( mChkOverride );
63  setLayout( l );
64 }
65 
66 void QgsCustomLayerOrderWidget::bridgeHasCustomLayerOrderChanged( bool state )
67 {
68  mChkOverride->setChecked( state );
69  mView->setEnabled( state );
70 }
71 
72 void QgsCustomLayerOrderWidget::bridgeCustomLayerOrderChanged()
73 {
74  mModel->refreshModel( mBridge->rootGroup()->layerOrder() );
75 }
76 
77 void QgsCustomLayerOrderWidget::nodeVisibilityChanged( QgsLayerTreeNode *node )
78 {
79  if ( QgsLayerTree::isLayer( node ) )
80  {
81  mModel->updateLayerVisibility( QgsLayerTree::toLayer( node )->layerId() );
82  }
83 }
84 
85 void QgsCustomLayerOrderWidget::modelUpdated()
86 {
87  mBridge->rootGroup()->setCustomLayerOrder( mModel->order() );
88 }
89 
90 
91 
93 
94 CustomLayerOrderModel::CustomLayerOrderModel( QgsLayerTreeMapCanvasBridge *bridge, QObject *parent )
95  : QAbstractListModel( parent )
96  , mBridge( bridge )
97 {
98 }
99 
100 int CustomLayerOrderModel::rowCount( const QModelIndex & ) const
101 {
102  return mOrder.count();
103 }
104 
105 QVariant CustomLayerOrderModel::data( const QModelIndex &index, int role ) const
106 {
107  QString id = mOrder.at( index.row() );
108 
109  if ( role == Qt::DisplayRole )
110  {
111  QgsMapLayer *layer = QgsProject::instance()->mapLayer( id );
112  if ( layer )
113  return layer->name();
114  }
115 
116  if ( role == Qt::UserRole + 1 )
117  {
118  QgsMapLayer *layer = QgsProject::instance()->mapLayer( id );
119  if ( layer )
120  return layer->id();
121  }
122 
123  if ( role == Qt::CheckStateRole )
124  {
125  QgsLayerTreeLayer *nodeLayer = mBridge->rootGroup()->findLayer( id );
126  if ( nodeLayer )
127  return nodeLayer->isVisible();
128  }
129 
130  return QVariant();
131 }
132 
133 bool CustomLayerOrderModel::setData( const QModelIndex &index, const QVariant &value, int role )
134 {
135  if ( role == Qt::CheckStateRole )
136  {
137  QString id = mOrder.at( index.row() );
138  QgsLayerTreeLayer *nodeLayer = mBridge->rootGroup()->findLayer( id );
139  if ( nodeLayer )
140  {
141  nodeLayer->setItemVisibilityChecked( static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked );
142  return true;
143  }
144  }
145  return false;
146 }
147 
148 Qt::ItemFlags CustomLayerOrderModel::flags( const QModelIndex &index ) const
149 {
150  if ( !index.isValid() )
151  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
152  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
153 }
154 
155 Qt::DropActions CustomLayerOrderModel::supportedDropActions() const
156 {
157  return Qt::CopyAction | Qt::MoveAction;
158 }
159 
160 QStringList CustomLayerOrderModel::mimeTypes() const
161 {
162  QStringList types;
163  types << QStringLiteral( "application/qgis.layerorderdata" );
164  return types;
165 }
166 
167 QMimeData *CustomLayerOrderModel::mimeData( const QModelIndexList &indexes ) const
168 {
169  QStringList lst;
170  const auto constIndexes = indexes;
171  for ( const QModelIndex &index : constIndexes )
172  lst << data( index, Qt::UserRole + 1 ).toString();
173 
174  QMimeData *mimeData = new QMimeData();
175  mimeData->setData( QStringLiteral( "application/qgis.layerorderdata" ), lst.join( QStringLiteral( "\n" ) ).toUtf8() );
176  return mimeData;
177 }
178 
179 bool CustomLayerOrderModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
180 {
181  Q_UNUSED( parent )
182  Q_UNUSED( column )
183 
184  if ( action == Qt::IgnoreAction )
185  return true;
186 
187  if ( !data->hasFormat( QStringLiteral( "application/qgis.layerorderdata" ) ) )
188  return false;
189 
190  QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layerorderdata" ) );
191  QStringList lst = QString::fromUtf8( encodedData ).split( '\n' );
192 
193  if ( row < 0 )
194  row = mOrder.count();
195 
196  beginInsertRows( QModelIndex(), row, row + lst.count() - 1 );
197  for ( int i = 0; i < lst.count(); ++i )
198  mOrder.insert( row + i, lst[i] );
199  endInsertRows();
200 
201  return true;
202 }
203 
204 bool CustomLayerOrderModel::removeRows( int row, int count, const QModelIndex &parent )
205 {
206  Q_UNUSED( parent )
207  if ( count <= 0 )
208  return false;
209 
210  beginRemoveRows( QModelIndex(), row, row + count - 1 );
211  while ( --count >= 0 )
212  mOrder.removeAt( row );
213  endRemoveRows();
214  return true;
215 }
216 
217 void CustomLayerOrderModel::refreshModel( const QList<QgsMapLayer *> &order )
218 {
219  QStringList orderedIds;
220  const auto constOrder = order;
221  for ( QgsMapLayer *layer : constOrder )
222  {
223  if ( layer )
224  orderedIds.append( layer->id() );
225  }
226 
227  if ( orderedIds != mOrder )
228  {
229  beginResetModel();
230  mOrder = orderedIds;
231  endResetModel();
232  }
233 }
234 
235 void CustomLayerOrderModel::updateLayerVisibility( const QString &layerId )
236 {
237  int row = mOrder.indexOf( layerId );
238  if ( row != -1 )
239  emit dataChanged( index( row ), index( row ) );
240 }
241 
242 
243 
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:78
The QgsLayerTreeMapCanvasBridge class takes care of updates of layer set for QgsMapCanvas from a laye...
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well) ...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
This class is a base class for nodes in a layer tree.
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
QgsCustomLayerOrderWidget(QgsLayerTreeMapCanvasBridge *bridge, QWidget *parent=nullptr)
Constructor for QgsCustomLayerOrderWidget.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
void setCustomLayerOrder(const QList< QgsMapLayer *> &customLayerOrder)
The order in which layers will be rendered on the canvas.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QString name
Definition: qgsmaplayer.h:82
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.