QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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->setContentsMargins( 0, 0, 0, 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  const 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  Q_UNUSED( value ); // Toggle
136  if ( role == Qt::CheckStateRole )
137  {
138  const QString id = mOrder.at( index.row() );
139  QgsLayerTreeLayer *nodeLayer = mBridge->rootGroup()->findLayer( id );
140  if ( nodeLayer )
141  {
142  nodeLayer->setItemVisibilityChecked( ! nodeLayer->itemVisibilityChecked() );
143  return true;
144  }
145  }
146  return false;
147 }
148 
149 Qt::ItemFlags CustomLayerOrderModel::flags( const QModelIndex &index ) const
150 {
151  if ( !index.isValid() )
152  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
153  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
154 }
155 
156 Qt::DropActions CustomLayerOrderModel::supportedDropActions() const
157 {
158  return Qt::CopyAction | Qt::MoveAction;
159 }
160 
161 QStringList CustomLayerOrderModel::mimeTypes() const
162 {
163  QStringList types;
164  types << QStringLiteral( "application/qgis.layerorderdata" );
165  return types;
166 }
167 
168 QMimeData *CustomLayerOrderModel::mimeData( const QModelIndexList &indexes ) const
169 {
170  QStringList lst;
171  const auto constIndexes = indexes;
172  for ( const QModelIndex &index : constIndexes )
173  lst << data( index, Qt::UserRole + 1 ).toString();
174 
175  QMimeData *mimeData = new QMimeData();
176  mimeData->setData( QStringLiteral( "application/qgis.layerorderdata" ), lst.join( QLatin1Char( '\n' ) ).toUtf8() );
177  return mimeData;
178 }
179 
180 bool CustomLayerOrderModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
181 {
182  Q_UNUSED( parent )
183  Q_UNUSED( column )
184 
185  if ( action == Qt::IgnoreAction )
186  return true;
187 
188  if ( !data->hasFormat( QStringLiteral( "application/qgis.layerorderdata" ) ) )
189  return false;
190 
191  const QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layerorderdata" ) );
192  QStringList lst = QString::fromUtf8( encodedData ).split( '\n' );
193 
194  if ( row < 0 )
195  row = mOrder.count();
196 
197  beginInsertRows( QModelIndex(), row, row + lst.count() - 1 );
198  for ( int i = 0; i < lst.count(); ++i )
199  mOrder.insert( row + i, lst[i] );
200  endInsertRows();
201 
202  return true;
203 }
204 
205 bool CustomLayerOrderModel::removeRows( int row, int count, const QModelIndex &parent )
206 {
207  Q_UNUSED( parent )
208  if ( count <= 0 )
209  return false;
210 
211  beginRemoveRows( QModelIndex(), row, row + count - 1 );
212  while ( --count >= 0 )
213  mOrder.removeAt( row );
214  endRemoveRows();
215  return true;
216 }
217 
218 void CustomLayerOrderModel::refreshModel( const QList<QgsMapLayer *> &order )
219 {
220  QStringList orderedIds;
221  const auto constOrder = order;
222  for ( QgsMapLayer *layer : constOrder )
223  {
224  if ( layer )
225  orderedIds.append( layer->id() );
226  }
227 
228  if ( orderedIds != mOrder )
229  {
230  beginResetModel();
231  mOrder = orderedIds;
232  endResetModel();
233  }
234 }
235 
236 void CustomLayerOrderModel::updateLayerVisibility( const QString &layerId )
237 {
238  const int row = mOrder.indexOf( layerId );
239  if ( row != -1 )
240  emit dataChanged( index( row ), index( row ) );
241 }
242 
243 
244 
QgsCustomLayerOrderWidget(QgsLayerTreeMapCanvasBridge *bridge, QWidget *parent=nullptr)
Constructor for QgsCustomLayerOrderWidget.
Layer tree node points to a map layer.
The QgsLayerTreeMapCanvasBridge class takes care of updates of layer set for QgsMapCanvas from a laye...
This class is a base class for nodes in a layer tree.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void customLayerOrderChanged()
Emitted when the custom layer order has changed.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
void hasCustomLayerOrderChanged(bool hasCustomLayerOrder)
Emitted when the hasCustomLayerOrder flag changes.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
void setHasCustomLayerOrder(bool hasCustomLayerOrder)
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
void setCustomLayerOrder(const QList< QgsMapLayer * > &customLayerOrder)
The order in which layers will be rendered on the canvas.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.