QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsmimedatautils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmimedatautils.cpp
3 ---------------------
4 begin : November 2011
5 copyright : (C) 2011 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#include <QStringList>
16
17#include "qgsmimedatautils.h"
18
19#include "qgslayertree.h"
20#include "qgslogger.h"
21#include "qgsrasterlayer.h"
22#include "qgsvectorlayer.h"
23#include "qgsmeshlayer.h"
24
25#include <QRegularExpression>
26
27static const char *QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri";
28
29QgsMimeDataUtils::Uri::Uri( const QString &encData )
30{
31 QgsDebugMsgLevel( "encData: " + encData, 4 );
32 const QStringList decoded = decode( encData );
33 if ( decoded.size() < 4 )
34 return;
35
36 layerType = decoded[0];
37 providerKey = decoded[1];
38 name = decoded[2];
39 uri = decoded[3];
40
41 if ( layerType == QLatin1String( "raster" ) && decoded.size() >= 6 )
42 {
43 supportedCrs = decode( decoded[4] );
44 supportedFormats = decode( decoded[5] );
45 }
46 else
47 {
48 supportedCrs.clear();
49 supportedFormats.clear();
50 }
51
52 if ( decoded.size() > 6 )
53 layerId = decoded.at( 6 );
54 if ( decoded.size() > 7 )
55 pId = decoded.at( 7 );
56 if ( decoded.size() > 8 )
57 wkbType = QgsWkbTypes::parseType( decoded.at( 8 ) );
58 if ( decoded.size() > 9 )
59 filePath = decoded.at( 9 );
60
61 QgsDebugMsgLevel( QStringLiteral( "type:%1 key:%2 name:%3 uri:%4 supportedCRS:%5 supportedFormats:%6" )
63 supportedCrs.join( ',' ),
64 supportedFormats.join( ',' ) ), 2 );
65}
66
68 : providerKey( layer->providerType() )
69 , name( layer->name() )
70 , uri( layer->dataProvider() ? layer->dataProvider()->dataSourceUri() : layer->source() )
71 , layerId( layer->id() )
72 , pId( QString::number( QCoreApplication::applicationPid() ) )
73{
74 switch ( layer->type() )
75 {
77 {
78 layerType = QStringLiteral( "vector" );
79 wkbType = qobject_cast< QgsVectorLayer *>( layer )->wkbType();
80 break;
81 }
83 {
84 layerType = QStringLiteral( "raster" );
85 break;
86 }
87
89 {
90 layerType = QStringLiteral( "mesh" );
91 break;
92 }
94 {
95 layerType = QStringLiteral( "pointcloud" );
96 break;
97 }
99 {
100 layerType = QStringLiteral( "vector-tile" );
101 break;
102 }
103
107 {
108 // plugin layers do not have a standard way of storing their URI...
109 return;
110 }
111 }
112}
113
115{
116 return encode( { layerType,
117 providerKey,
118 name,
119 uri,
120 encode( supportedCrs ),
121 encode( supportedFormats ),
122 layerId,
123 pId,
125 filePath
126 } );
127}
128
129QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
130{
131 owner = false;
132 error.clear();
133 if ( layerType != QLatin1String( "vector" ) )
134 {
135 error = QObject::tr( "%1: Not a vector layer." ).arg( name );
136 return nullptr;
137 }
138
139 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
140 {
141 if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
142 {
143 return vectorLayer;
144 }
145 }
146 if ( providerKey == QLatin1String( "memory" ) )
147 {
148 error = QObject::tr( "Cannot get memory layer." );
149 return nullptr;
150 }
151
152 owner = true;
154 return new QgsVectorLayer( uri, name, providerKey, options );
155}
156
157QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
158{
159 owner = false;
160 error.clear();
161 if ( layerType != QLatin1String( "raster" ) )
162 {
163 error = QObject::tr( "%1: Not a raster layer." ).arg( name );
164 return nullptr;
165 }
166
167 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
168 {
169 if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
170 {
171 return rasterLayer;
172 }
173 }
174
175 owner = true;
176 return new QgsRasterLayer( uri, name, providerKey );
177}
178
179QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
180{
181 owner = false;
182 error.clear();
183 if ( layerType != QLatin1String( "mesh" ) )
184 {
185 error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
186 return nullptr;
187 }
188
189 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
190 {
191 if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
192 {
193 return meshLayer;
194 }
195 }
196
197 owner = true;
198 return new QgsMeshLayer( uri, name, providerKey );
199}
200
202{
203 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
204 {
205 return QgsProject::instance()->mapLayer( layerId );
206 }
207 return nullptr;
208}
209
210// -----
211
212bool QgsMimeDataUtils::isUriList( const QMimeData *data )
213{
214 return data->hasFormat( QGIS_URILIST_MIMETYPE );
215}
216
218{
219 QMimeData *mimeData = new QMimeData();
220
221 mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
222 return mimeData;
223}
224
225
227{
228 QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
229 QDataStream stream( &encodedData, QIODevice::ReadOnly );
230 QString xUri; // extended uri: layer_type:provider_key:uri
231 UriList list;
232 while ( !stream.atEnd() )
233 {
234 stream >> xUri;
235 QgsDebugMsgLevel( xUri, 4 );
236 list.append( Uri( xUri ) );
237 }
238 return list;
239}
240
241
242static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
243{
244 if ( QgsLayerTree::isGroup( node ) )
245 {
246 const auto constChildren = QgsLayerTree::toGroup( node )->children();
247 for ( QgsLayerTreeNode *child : constChildren )
248 _addLayerTreeNodeToUriList( child, uris );
249 }
250 else if ( QgsLayerTree::isLayer( node ) )
251 {
252 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
253 QgsMapLayer *layer = nodeLayer->layer();
254 if ( !layer )
255 return;
256
257 if ( layer->type() == QgsMapLayerType::PluginLayer )
258 return; // plugin layers do not have a standard way of storing their URI...
259
260 uris << QgsMimeDataUtils::Uri( layer );
261 }
262}
263
264QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
265{
266 UriList uris;
267 const auto constNodes = nodes;
268 for ( QgsLayerTreeNode *node : constNodes )
269 _addLayerTreeNodeToUriList( node, uris );
270 return uriListToByteArray( uris );
271}
272
274{
275 if ( uri.pId.isEmpty() )
276 return false;
277
278 const qint64 pid = uri.pId.toLongLong();
279 return pid == QCoreApplication::applicationPid();
280}
281
282QString QgsMimeDataUtils::encode( const QStringList &items )
283{
284 QString encoded;
285 // Do not escape colon twice
286 const QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
287 const auto constItems = items;
288 for ( const QString &item : constItems )
289 {
290 QString str = item;
291 str.replace( '\\', QLatin1String( "\\\\" ) );
292 str.replace( re, QStringLiteral( "\\:" ) );
293 encoded += str + ':';
294 }
295 return encoded.left( encoded.length() - 1 );
296}
297
298QStringList QgsMimeDataUtils::decode( const QString &encoded )
299{
300 QStringList items;
301 QString item;
302 bool inEscape = false;
303 const auto constEncoded = encoded;
304 for ( const QChar c : constEncoded )
305 {
306 if ( c == '\\' && inEscape )
307 {
308 item += c;
309 }
310 else if ( c == '\\' )
311 {
312 inEscape = true;
313 }
314 else if ( c == ':' && !inEscape )
315 {
316 items.append( item );
317 item.clear();
318 }
319 else
320 {
321 item += c;
322 inEscape = false;
323 }
324 }
325 items.append( item );
326 return items;
327}
328
329
330QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
331{
332 QByteArray encodedData;
333
334 QDataStream stream( &encodedData, QIODevice::WriteOnly );
335 const auto constLayers = layers;
336 for ( const Uri &u : constLayers )
337 {
338 stream << u.data();
339 }
340 return encodedData;
341}
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsMapLayerType type
Definition: qgsmaplayer.h:80
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
static bool isUriList(const QMimeData *data)
QList< QgsMimeDataUtils::Uri > UriList
static QMimeData * encodeUriList(const UriList &layers)
Encodes a URI list to a new QMimeData object.
static UriList decodeUriList(const QMimeData *data)
static bool hasOriginatedFromCurrentAppInstance(const QgsMimeDataUtils::Uri &uri)
Returns true if uri originated from the current QGIS application instance.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
Represents a raster layer.
Represents a vector layer which manages a vector based data sets.
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define str(x)
Definition: qgis.cpp:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsMeshLayer * meshLayer(bool &owner, QString &error) const
Gets mesh layer from uri if possible, otherwise returns nullptr and error is set.
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.
QString name
Human readable name to be used e.g. in layer tree.
QgsMapLayer * mapLayer() const
Returns the layer from the active project corresponding to this uri (if possible),...
QString pId
Unique ID associated with application instance.
QgsRasterLayer * rasterLayer(bool &owner, QString &error) const
Gets raster layer from uri if possible, otherwise returns nullptr and error is set.
QString providerKey
For "vector" / "raster" type: provider id.
QgsVectorLayer * vectorLayer(bool &owner, QString &error) const
Gets vector layer from uri if possible, otherwise returns nullptr and error is set.
QString layerId
Layer ID, if uri is associated with a layer from a QgsProject.
QString data() const
Returns encoded representation of the object.
QgsWkbTypes::Type wkbType
WKB type, if associated with a vector layer, or QgsWkbTypes::Unknown if not yet known.
Uri()=default
Constructs invalid URI.
QString layerType
Type of URI.
Setting options for loading vector layers.