QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 }
104 {
105 layerType = QStringLiteral( "tiled-scene" );
106 break;
107 }
108
112 {
113 // plugin layers do not have a standard way of storing their URI...
114 return;
115 }
116 }
117}
118
120{
121 return encode( { layerType,
122 providerKey,
123 name,
124 uri,
125 encode( supportedCrs ),
126 encode( supportedFormats ),
127 layerId,
128 pId,
130 filePath
131 } );
132}
133
134QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
135{
136 owner = false;
137 error.clear();
138 if ( layerType != QLatin1String( "vector" ) )
139 {
140 error = QObject::tr( "%1: Not a vector layer." ).arg( name );
141 return nullptr;
142 }
143
144 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
145 {
146 if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
147 {
148 return vectorLayer;
149 }
150 }
151 if ( providerKey == QLatin1String( "memory" ) )
152 {
153 error = QObject::tr( "Cannot get memory layer." );
154 return nullptr;
155 }
156
157 owner = true;
159 return new QgsVectorLayer( uri, name, providerKey, options );
160}
161
162QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
163{
164 owner = false;
165 error.clear();
166 if ( layerType != QLatin1String( "raster" ) )
167 {
168 error = QObject::tr( "%1: Not a raster layer." ).arg( name );
169 return nullptr;
170 }
171
172 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
173 {
174 if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
175 {
176 return rasterLayer;
177 }
178 }
179
180 owner = true;
181 return new QgsRasterLayer( uri, name, providerKey );
182}
183
184QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
185{
186 owner = false;
187 error.clear();
188 if ( layerType != QLatin1String( "mesh" ) )
189 {
190 error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
191 return nullptr;
192 }
193
194 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
195 {
196 if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
197 {
198 return meshLayer;
199 }
200 }
201
202 owner = true;
203 return new QgsMeshLayer( uri, name, providerKey );
204}
205
207{
208 if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
209 {
210 return QgsProject::instance()->mapLayer( layerId );
211 }
212 return nullptr;
213}
214
215// -----
216
217bool QgsMimeDataUtils::isUriList( const QMimeData *data )
218{
219 return data->hasFormat( QGIS_URILIST_MIMETYPE );
220}
221
223{
224 QMimeData *mimeData = new QMimeData();
225
226 mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
227 return mimeData;
228}
229
230
232{
233 QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
234 QDataStream stream( &encodedData, QIODevice::ReadOnly );
235 QString xUri; // extended uri: layer_type:provider_key:uri
236 UriList list;
237 while ( !stream.atEnd() )
238 {
239 stream >> xUri;
240 QgsDebugMsgLevel( xUri, 4 );
241 list.append( Uri( xUri ) );
242 }
243 return list;
244}
245
246
247static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
248{
249 if ( QgsLayerTree::isGroup( node ) )
250 {
251 const auto constChildren = QgsLayerTree::toGroup( node )->children();
252 for ( QgsLayerTreeNode *child : constChildren )
253 _addLayerTreeNodeToUriList( child, uris );
254 }
255 else if ( QgsLayerTree::isLayer( node ) )
256 {
257 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
258 QgsMapLayer *layer = nodeLayer->layer();
259 if ( !layer )
260 return;
261
262 if ( layer->type() == Qgis::LayerType::Plugin )
263 return; // plugin layers do not have a standard way of storing their URI...
264
265 uris << QgsMimeDataUtils::Uri( layer );
266 }
267}
268
269QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
270{
271 UriList uris;
272 const auto constNodes = nodes;
273 for ( QgsLayerTreeNode *node : constNodes )
274 _addLayerTreeNodeToUriList( node, uris );
275 return uriListToByteArray( uris );
276}
277
279{
280 if ( uri.pId.isEmpty() )
281 return false;
282
283 const qint64 pid = uri.pId.toLongLong();
284 return pid == QCoreApplication::applicationPid();
285}
286
287QString QgsMimeDataUtils::encode( const QStringList &items )
288{
289 QString encoded;
290 // Do not escape colon twice
291 const thread_local QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
292 const auto constItems = items;
293 for ( const QString &item : constItems )
294 {
295 QString str = item;
296 str.replace( '\\', QLatin1String( "\\\\" ) );
297 str.replace( re, QStringLiteral( "\\:" ) );
298 encoded += str + ':';
299 }
300 return encoded.left( encoded.length() - 1 );
301}
302
303QStringList QgsMimeDataUtils::decode( const QString &encoded )
304{
305 QStringList items;
306 QString item;
307 bool inEscape = false;
308 const auto constEncoded = encoded;
309 for ( const QChar c : constEncoded )
310 {
311 if ( c == '\\' && inEscape )
312 {
313 item += c;
314 }
315 else if ( c == '\\' )
316 {
317 inEscape = true;
318 }
319 else if ( c == ':' && !inEscape )
320 {
321 items.append( item );
322 item.clear();
323 }
324 else
325 {
326 item += c;
327 inEscape = false;
328 }
329 }
330 items.append( item );
331 return items;
332}
333
334
335QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
336{
337 QByteArray encodedData;
338
339 QDataStream stream( &encodedData, QIODevice::WriteOnly );
340 const auto constLayers = layers;
341 for ( const Uri &u : constLayers )
342 {
343 stream << u.data();
344 }
345 return encodedData;
346}
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
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:70
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:41
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:60
Base class for all map layer types.
Definition: qgsmaplayer.h:75
Qgis::LayerType type
Definition: qgsmaplayer.h:82
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:101
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:481
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:113
Represents a raster layer.
Represents a vector layer which manages a vector based data sets.
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
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:38
#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.
Qgis::WkbType wkbType
WKB type, if associated with a vector layer, or QgsWkbTypes::Unknown if not yet known.
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.
Uri()=default
Constructs invalid URI.
QString layerType
Type of URI.
Setting options for loading vector layers.