QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 static const char *QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri";
30 
31 QgsMimeDataUtils::Uri::Uri( const QString &encData )
32 {
33  QgsDebugMsgLevel( "encData: " + encData, 4 );
34  const QStringList decoded = decode( encData );
35  if ( decoded.size() < 4 )
36  return;
37 
38  layerType = decoded[0];
39  providerKey = decoded[1];
40  name = decoded[2];
41  uri = decoded[3];
42 
43  if ( layerType == QLatin1String( "raster" ) && decoded.size() >= 6 )
44  {
45  supportedCrs = decode( decoded[4] );
46  supportedFormats = decode( decoded[5] );
47  }
48  else
49  {
50  supportedCrs.clear();
51  supportedFormats.clear();
52  }
53 
54  if ( decoded.size() > 6 )
55  layerId = decoded.at( 6 );
56  if ( decoded.size() > 7 )
57  pId = decoded.at( 7 );
58  if ( decoded.size() > 8 )
59  wkbType = QgsWkbTypes::parseType( decoded.at( 8 ) );
60 
61  QgsDebugMsgLevel( QStringLiteral( "type:%1 key:%2 name:%3 uri:%4 supportedCRS:%5 supportedFormats:%6" )
62  .arg( layerType, providerKey, name, uri,
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  }
93 
95  {
96  layerType = QStringLiteral( "vector-tile" );
97  break;
98  }
99 
102  {
103  // plugin layers do not have a standard way of storing their URI...
104  return;
105  }
106  }
107 }
108 
110 {
111  return encode( QStringList() << layerType << providerKey << name << uri << encode( supportedCrs ) << encode( supportedFormats ) << layerId << pId << QgsWkbTypes::displayString( wkbType ) );
112 }
113 
114 QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
115 {
116  owner = false;
117  error.clear();
118  if ( layerType != QLatin1String( "vector" ) )
119  {
120  error = QObject::tr( "%1: Not a vector layer." ).arg( name );
121  return nullptr;
122  }
123 
124  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
125  {
126  if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
127  {
128  return vectorLayer;
129  }
130  }
131  if ( providerKey == QLatin1String( "memory" ) )
132  {
133  error = QObject::tr( "Cannot get memory layer." );
134  return nullptr;
135  }
136 
137  owner = true;
139  return new QgsVectorLayer( uri, name, providerKey, options );
140 }
141 
142 QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
143 {
144  owner = false;
145  error.clear();
146  if ( layerType != QLatin1String( "raster" ) )
147  {
148  error = QObject::tr( "%1: Not a raster layer." ).arg( name );
149  return nullptr;
150  }
151 
152  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
153  {
154  if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
155  {
156  return rasterLayer;
157  }
158  }
159 
160  owner = true;
161  return new QgsRasterLayer( uri, name, providerKey );
162 }
163 
164 QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
165 {
166  owner = false;
167  error.clear();
168  if ( layerType != QLatin1String( "mesh" ) )
169  {
170  error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
171  return nullptr;
172  }
173 
174  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
175  {
176  if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
177  {
178  return meshLayer;
179  }
180  }
181 
182  owner = true;
183  return new QgsMeshLayer( uri, name, providerKey );
184 }
185 
187 {
188  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
189  {
190  return QgsProject::instance()->mapLayer( layerId );
191  }
192  return nullptr;
193 }
194 
195 // -----
196 
197 bool QgsMimeDataUtils::isUriList( const QMimeData *data )
198 {
199  return data->hasFormat( QGIS_URILIST_MIMETYPE );
200 }
201 
203 {
204  QMimeData *mimeData = new QMimeData();
205 
206  mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
207  return mimeData;
208 }
209 
210 
212 {
213  QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
214  QDataStream stream( &encodedData, QIODevice::ReadOnly );
215  QString xUri; // extended uri: layer_type:provider_key:uri
216  UriList list;
217  while ( !stream.atEnd() )
218  {
219  stream >> xUri;
220  QgsDebugMsgLevel( xUri, 4 );
221  list.append( Uri( xUri ) );
222  }
223  return list;
224 }
225 
226 
227 static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
228 {
229  if ( QgsLayerTree::isGroup( node ) )
230  {
231  const auto constChildren = QgsLayerTree::toGroup( node )->children();
232  for ( QgsLayerTreeNode *child : constChildren )
233  _addLayerTreeNodeToUriList( child, uris );
234  }
235  else if ( QgsLayerTree::isLayer( node ) )
236  {
237  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
238  QgsMapLayer *layer = nodeLayer->layer();
239  if ( !layer )
240  return;
241 
242  if ( layer->type() == QgsMapLayerType::PluginLayer )
243  return; // plugin layers do not have a standard way of storing their URI...
244 
245  uris << QgsMimeDataUtils::Uri( layer );
246  }
247 }
248 
249 QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
250 {
251  UriList uris;
252  const auto constNodes = nodes;
253  for ( QgsLayerTreeNode *node : constNodes )
254  _addLayerTreeNodeToUriList( node, uris );
255  return uriListToByteArray( uris );
256 }
257 
259 {
260  if ( uri.pId.isEmpty() )
261  return false;
262 
263  const qint64 pid = uri.pId.toLongLong();
264  return pid == QCoreApplication::applicationPid();
265 }
266 
267 QString QgsMimeDataUtils::encode( const QStringList &items )
268 {
269  QString encoded;
270  // Do not escape colon twice
271  QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
272  const auto constItems = items;
273  for ( const QString &item : constItems )
274  {
275  QString str = item;
276  str.replace( '\\', QLatin1String( "\\\\" ) );
277  str.replace( re, QStringLiteral( "\\:" ) );
278  encoded += str + ':';
279  }
280  return encoded.left( encoded.length() - 1 );
281 }
282 
283 QStringList QgsMimeDataUtils::decode( const QString &encoded )
284 {
285  QStringList items;
286  QString item;
287  bool inEscape = false;
288  const auto constEncoded = encoded;
289  for ( QChar c : constEncoded )
290  {
291  if ( c == '\\' && inEscape )
292  {
293  item += c;
294  }
295  else if ( c == '\\' )
296  {
297  inEscape = true;
298  }
299  else if ( c == ':' && !inEscape )
300  {
301  items.append( item );
302  item.clear();
303  }
304  else
305  {
306  item += c;
307  inEscape = false;
308  }
309  }
310  items.append( item );
311  return items;
312 }
313 
314 
315 QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
316 {
317  QByteArray encodedData;
318 
319  QDataStream stream( &encodedData, QIODevice::WriteOnly );
320  const auto constLayers = layers;
321  for ( const Uri &u : constLayers )
322  {
323  stream << u.data();
324  }
325  return encodedData;
326 }
QgsMimeDataUtils::Uri::wkbType
QgsWkbTypes::Type wkbType
WKB type, if associated with a vector layer, or QgsWkbTypes::Unknown if not yet known.
Definition: qgsmimedatautils.h:146
QgsMimeDataUtils::Uri::name
QString name
Human readable name to be used e.g. in layer tree.
Definition: qgsmimedatautils.h:121
QgsMimeDataUtils::Uri::uri
QString uri
Identifier of the data source recognized by its providerKey.
Definition: qgsmimedatautils.h:123
QgsLayerTreeNode
This class is a base class for nodes in a layer tree.
Definition: qgslayertreenode.h:75
qgsrasterlayer.h
QgsWkbTypes::displayString
static QString displayString(Type type) SIP_HOLDGIL
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
Definition: qgswkbtypes.cpp:145
QgsMimeDataUtils::Uri::supportedFormats
QStringList supportedFormats
Definition: qgsmimedatautils.h:125
QgsMapLayerType::MeshLayer
@ MeshLayer
Added in 3.2.
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsMimeDataUtils::layerTreeNodesToUriList
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
Definition: qgsmimedatautils.cpp:249
QgsMimeDataUtils::Uri::layerType
QString layerType
Type of URI.
Definition: qgsmimedatautils.h:110
qgsdataitem.h
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:101
QgsMimeDataUtils::Uri::mapLayer
QgsMapLayer * mapLayer() const
Returns the layer from the active project corresponding to this uri (if possible),...
Definition: qgsmimedatautils.cpp:186
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
qgsmimedatautils.h
QgsMimeDataUtils::UriList
QList< QgsMimeDataUtils::Uri > UriList
Definition: qgsmimedatautils.h:156
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3208
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsWkbTypes::parseType
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
QgsMimeDataUtils::Uri::vectorLayer
QgsVectorLayer * vectorLayer(bool &owner, QString &error) const
Gets vector layer from uri if possible, otherwise returns nullptr and error is set.
Definition: qgsmimedatautils.cpp:114
QgsMimeDataUtils::Uri::meshLayer
QgsMeshLayer * meshLayer(bool &owner, QString &error) const
Gets mesh layer from uri if possible, otherwise returns nullptr and error is set.
Definition: qgsmimedatautils.cpp:164
QgsMeshLayer
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
QgsLayerTreeLayer
Layer tree node points to a map layer.
Definition: qgslayertreelayer.h:44
QgsMapLayerType::RasterLayer
@ RasterLayer
qgsvectordataprovider.h
QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance
static bool hasOriginatedFromCurrentAppInstance(const QgsMimeDataUtils::Uri &uri)
Returns true if uri originated from the current QGIS application instance.
Definition: qgsmimedatautils.cpp:258
QgsMimeDataUtils::Uri::pId
QString pId
Unique ID associated with application instance.
Definition: qgsmimedatautils.h:138
QgsMimeDataUtils::Uri::data
QString data() const
Returns encoded representation of the object.
Definition: qgsmimedatautils.cpp:109
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:71
QgsLayerTreeLayer::layer
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Definition: qgslayertreelayer.h:74
QgsMimeDataUtils::Uri
Definition: qgsmimedatautils.h:41
qgslayertree.h
QgsMimeDataUtils::Uri::supportedCrs
QStringList supportedCrs
Definition: qgsmimedatautils.h:124
QgsMimeDataUtils::Uri::rasterLayer
QgsRasterLayer * rasterLayer(bool &owner, QString &error) const
Gets raster layer from uri if possible, otherwise returns nullptr and error is set.
Definition: qgsmimedatautils.cpp:142
qgsmeshlayer.h
QgsMimeDataUtils::Uri::providerKey
QString providerKey
For "vector" / "raster" type: provider id.
Definition: qgsmimedatautils.h:118
qgsvectorlayer.h
QgsMimeDataUtils::Uri::layerId
QString layerId
Layer ID, if uri is associated with a layer from a QgsProject.
Definition: qgsmimedatautils.h:131
QgsVectorLayer::LayerOptions
Setting options for loading vector layers.
Definition: qgsvectorlayer.h:425
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
QgsMimeDataUtils::encodeUriList
static QMimeData * encodeUriList(const UriList &layers)
Encodes a URI list to a new QMimeData object.
Definition: qgsmimedatautils.cpp:202
QgsMimeDataUtils::decodeUriList
static UriList decodeUriList(const QMimeData *data)
Definition: qgsmimedatautils.cpp:211
QgsMimeDataUtils::Uri::Uri
Uri()=default
Constructs invalid URI.
c
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
Definition: porting_processing.dox:1
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:112
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Added in 3.14.
qgspluginlayer.h
qgslogger.h
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QgsMapLayerType::PluginLayer
@ PluginLayer
QgsMimeDataUtils::isUriList
static bool isUriList(const QMimeData *data)
Definition: qgsmimedatautils.cpp:197
qgsrasterdataprovider.h
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:90