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