QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 "qgsdataitem.h"
20 #include "qgslayertree.h"
21 #include "qgslogger.h"
22 #include "qgspluginlayer.h"
23 #include "qgsrasterdataprovider.h"
24 #include "qgsrasterlayer.h"
25 #include "qgsvectordataprovider.h"
26 #include "qgsvectorlayer.h"
27 #include "qgsmeshlayer.h"
28 
29 #include <QRegularExpression>
30 
31 static const char *QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri";
32 
33 QgsMimeDataUtils::Uri::Uri( const QString &encData )
34 {
35  QgsDebugMsgLevel( "encData: " + encData, 4 );
36  const QStringList decoded = decode( encData );
37  if ( decoded.size() < 4 )
38  return;
39 
40  layerType = decoded[0];
41  providerKey = decoded[1];
42  name = decoded[2];
43  uri = decoded[3];
44 
45  if ( layerType == QLatin1String( "raster" ) && decoded.size() >= 6 )
46  {
47  supportedCrs = decode( decoded[4] );
48  supportedFormats = decode( decoded[5] );
49  }
50  else
51  {
52  supportedCrs.clear();
53  supportedFormats.clear();
54  }
55 
56  if ( decoded.size() > 6 )
57  layerId = decoded.at( 6 );
58  if ( decoded.size() > 7 )
59  pId = decoded.at( 7 );
60  if ( decoded.size() > 8 )
61  wkbType = QgsWkbTypes::parseType( decoded.at( 8 ) );
62 
63  QgsDebugMsgLevel( QStringLiteral( "type:%1 key:%2 name:%3 uri:%4 supportedCRS:%5 supportedFormats:%6" )
64  .arg( layerType, providerKey, name, uri,
65  supportedCrs.join( ',' ),
66  supportedFormats.join( ',' ) ), 2 );
67 }
68 
70  : providerKey( layer->providerType() )
71  , name( layer->name() )
72  , uri( layer->dataProvider() ? layer->dataProvider()->dataSourceUri() : layer->source() )
73  , layerId( layer->id() )
74  , pId( QString::number( QCoreApplication::applicationPid() ) )
75 {
76  switch ( layer->type() )
77  {
79  {
80  layerType = QStringLiteral( "vector" );
81  wkbType = qobject_cast< QgsVectorLayer *>( layer )->wkbType();
82  break;
83  }
85  {
86  layerType = QStringLiteral( "raster" );
87  break;
88  }
89 
91  {
92  layerType = QStringLiteral( "mesh" );
93  break;
94  }
96  {
97  layerType = QStringLiteral( "pointcloud" );
98  break;
99  }
101  {
102  layerType = QStringLiteral( "vector-tile" );
103  break;
104  }
105 
108  {
109  // plugin layers do not have a standard way of storing their URI...
110  return;
111  }
112  }
113 }
114 
116 {
117  return encode( QStringList() << layerType << providerKey << name << uri << encode( supportedCrs ) << encode( supportedFormats ) << layerId << pId << QgsWkbTypes::displayString( wkbType ) );
118 }
119 
120 QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
121 {
122  owner = false;
123  error.clear();
124  if ( layerType != QLatin1String( "vector" ) )
125  {
126  error = QObject::tr( "%1: Not a vector layer." ).arg( name );
127  return nullptr;
128  }
129 
130  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
131  {
132  if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
133  {
134  return vectorLayer;
135  }
136  }
137  if ( providerKey == QLatin1String( "memory" ) )
138  {
139  error = QObject::tr( "Cannot get memory layer." );
140  return nullptr;
141  }
142 
143  owner = true;
145  return new QgsVectorLayer( uri, name, providerKey, options );
146 }
147 
148 QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
149 {
150  owner = false;
151  error.clear();
152  if ( layerType != QLatin1String( "raster" ) )
153  {
154  error = QObject::tr( "%1: Not a raster layer." ).arg( name );
155  return nullptr;
156  }
157 
158  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
159  {
160  if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
161  {
162  return rasterLayer;
163  }
164  }
165 
166  owner = true;
167  return new QgsRasterLayer( uri, name, providerKey );
168 }
169 
170 QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
171 {
172  owner = false;
173  error.clear();
174  if ( layerType != QLatin1String( "mesh" ) )
175  {
176  error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
177  return nullptr;
178  }
179 
180  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
181  {
182  if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
183  {
184  return meshLayer;
185  }
186  }
187 
188  owner = true;
189  return new QgsMeshLayer( uri, name, providerKey );
190 }
191 
193 {
194  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
195  {
196  return QgsProject::instance()->mapLayer( layerId );
197  }
198  return nullptr;
199 }
200 
201 // -----
202 
203 bool QgsMimeDataUtils::isUriList( const QMimeData *data )
204 {
205  return data->hasFormat( QGIS_URILIST_MIMETYPE );
206 }
207 
209 {
210  QMimeData *mimeData = new QMimeData();
211 
212  mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
213  return mimeData;
214 }
215 
216 
218 {
219  QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
220  QDataStream stream( &encodedData, QIODevice::ReadOnly );
221  QString xUri; // extended uri: layer_type:provider_key:uri
222  UriList list;
223  while ( !stream.atEnd() )
224  {
225  stream >> xUri;
226  QgsDebugMsgLevel( xUri, 4 );
227  list.append( Uri( xUri ) );
228  }
229  return list;
230 }
231 
232 
233 static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
234 {
235  if ( QgsLayerTree::isGroup( node ) )
236  {
237  const auto constChildren = QgsLayerTree::toGroup( node )->children();
238  for ( QgsLayerTreeNode *child : constChildren )
239  _addLayerTreeNodeToUriList( child, uris );
240  }
241  else if ( QgsLayerTree::isLayer( node ) )
242  {
243  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
244  QgsMapLayer *layer = nodeLayer->layer();
245  if ( !layer )
246  return;
247 
248  if ( layer->type() == QgsMapLayerType::PluginLayer )
249  return; // plugin layers do not have a standard way of storing their URI...
250 
251  uris << QgsMimeDataUtils::Uri( layer );
252  }
253 }
254 
255 QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
256 {
257  UriList uris;
258  const auto constNodes = nodes;
259  for ( QgsLayerTreeNode *node : constNodes )
260  _addLayerTreeNodeToUriList( node, uris );
261  return uriListToByteArray( uris );
262 }
263 
265 {
266  if ( uri.pId.isEmpty() )
267  return false;
268 
269  const qint64 pid = uri.pId.toLongLong();
270  return pid == QCoreApplication::applicationPid();
271 }
272 
273 QString QgsMimeDataUtils::encode( const QStringList &items )
274 {
275  QString encoded;
276  // Do not escape colon twice
277  QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
278  const auto constItems = items;
279  for ( const QString &item : constItems )
280  {
281  QString str = item;
282  str.replace( '\\', QLatin1String( "\\\\" ) );
283  str.replace( re, QStringLiteral( "\\:" ) );
284  encoded += str + ':';
285  }
286  return encoded.left( encoded.length() - 1 );
287 }
288 
289 QStringList QgsMimeDataUtils::decode( const QString &encoded )
290 {
291  QStringList items;
292  QString item;
293  bool inEscape = false;
294  const auto constEncoded = encoded;
295  for ( QChar c : constEncoded )
296  {
297  if ( c == '\\' && inEscape )
298  {
299  item += c;
300  }
301  else if ( c == '\\' )
302  {
303  inEscape = true;
304  }
305  else if ( c == ':' && !inEscape )
306  {
307  items.append( item );
308  item.clear();
309  }
310  else
311  {
312  item += c;
313  inEscape = false;
314  }
315  }
316  items.append( item );
317  return items;
318 }
319 
320 
321 QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
322 {
323  QByteArray encodedData;
324 
325  QDataStream stream( &encodedData, QIODevice::WriteOnly );
326  const auto constLayers = layers;
327  for ( const Uri &u : constLayers )
328  {
329  stream << u.data();
330  }
331  return encodedData;
332 }
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 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
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:85
QgsMapLayerType type
Definition: qgsmaplayer.h:92
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
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:501
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
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
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
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 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 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.