QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
66void QgsCustomLayerOrderWidget::bridgeHasCustomLayerOrderChanged( bool state )
67{
68 mChkOverride->setChecked( state );
69 mView->setEnabled( state );
70}
71
72void QgsCustomLayerOrderWidget::bridgeCustomLayerOrderChanged()
73{
74 mModel->refreshModel( mBridge->rootGroup()->layerOrder() );
75}
76
77void QgsCustomLayerOrderWidget::nodeVisibilityChanged( QgsLayerTreeNode *node )
78{
79 if ( QgsLayerTree::isLayer( node ) )
80 {
81 mModel->updateLayerVisibility( QgsLayerTree::toLayer( node )->layerId() );
82 }
83}
84
85void QgsCustomLayerOrderWidget::modelUpdated()
86{
87 mBridge->rootGroup()->setCustomLayerOrder( mModel->order() );
88}
89
90
91
93
94CustomLayerOrderModel::CustomLayerOrderModel( QgsLayerTreeMapCanvasBridge *bridge, QObject *parent )
95 : QAbstractListModel( parent )
96 , mBridge( bridge )
97{
98}
99
100int CustomLayerOrderModel::rowCount( const QModelIndex & ) const
101{
102 return mOrder.count();
103}
104
105QVariant 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
133bool 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
149Qt::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
156Qt::DropActions CustomLayerOrderModel::supportedDropActions() const
157{
158 return Qt::CopyAction | Qt::MoveAction;
159}
160
161QStringList CustomLayerOrderModel::mimeTypes() const
162{
163 QStringList types;
164 types << QStringLiteral( "application/qgis.layerorderdata" );
165 return types;
166}
167
168QMimeData *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
180bool 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
205bool 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
218void 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
236void 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.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
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:50
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:75
QString name
Definition: qgsmaplayer.h:78
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:481
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.