From 95aa80aec9418d6d0a0341321ba3f2d34f2422a9 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 19 Feb 2024 12:18:19 +0100 Subject: [PATCH 1/4] Fix sublayers not added to legend after drag and drop Fixes #56403 --- src/app/layers/qgsapplayerhandling.cpp | 57 +++++++++++++++++--------- src/app/layers/qgsapplayerhandling.h | 17 ++++++-- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/app/layers/qgsapplayerhandling.cpp b/src/app/layers/qgsapplayerhandling.cpp index 2ccae77c17a1..e486100b9fb6 100644 --- a/src/app/layers/qgsapplayerhandling.cpp +++ b/src/app/layers/qgsapplayerhandling.cpp @@ -1073,17 +1073,17 @@ QList< QgsMapLayer * > QgsAppLayerHandling::openLayer( const QString &fileName, return openedLayers; } -QgsVectorLayer *QgsAppLayerHandling::addVectorLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) +QListQgsAppLayerHandling::addVectorLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) { return addLayerPrivate< QgsVectorLayer >( Qgis::LayerType::Vector, uri, baseName, !provider.isEmpty() ? provider : QLatin1String( "ogr" ), true, addToLegend ); } -QgsRasterLayer *QgsAppLayerHandling::addRasterLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) +QListQgsAppLayerHandling::addRasterLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) { return addLayerPrivate< QgsRasterLayer >( Qgis::LayerType::Raster, uri, baseName, !provider.isEmpty() ? provider : QLatin1String( "gdal" ), true, addToLegend ); } -QgsMeshLayer *QgsAppLayerHandling::addMeshLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) +QListQgsAppLayerHandling::addMeshLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend ) { return addLayerPrivate< QgsMeshLayer >( Qgis::LayerType::Mesh, uri, baseName, provider, true, addToLegend ); } @@ -1148,10 +1148,15 @@ QList QgsAppLayerHandling::addGdalRasterLayers( const QStringList // try to create the layer cursorOverride.reset(); - QgsRasterLayer *layer = addLayerPrivate< QgsRasterLayer >( Qgis::LayerType::Raster, uri, layerName, QStringLiteral( "gdal" ), showWarningOnInvalid ); - res << layer; + const QList layersList { addLayerPrivate< QgsRasterLayer >( Qgis::LayerType::Raster, uri, layerName, QStringLiteral( "gdal" ), showWarningOnInvalid ) }; - if ( layer && layer->isValid() ) + // loop and cast + for ( QgsRasterLayer *layer : std::as_const( layersList ) ) + { + res.append( layer ); + } + + if ( ! layersList.isEmpty() && layersList.first()->isValid() ) { //only allow one copy of a ai grid file to be loaded at a //time to prevent the user selecting all adfs in 1 dir which @@ -1342,7 +1347,7 @@ QList< QgsMapLayer * > QgsAppLayerHandling::addDatabaseLayers( const QStringList } template -T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &uri, const QString &name, const QString &providerKey, bool guiWarnings, bool addToLegend ) +QListQgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &uri, const QString &name, const QString &providerKey, bool guiWarnings, bool addToLegend ) { QgsSettings settings; @@ -1375,7 +1380,7 @@ T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &ur const bool canQuerySublayers = providerMetadata && ( providerMetadata->capabilities() & QgsProviderMetadata::QuerySublayers ); - T *result = nullptr; + QList result; if ( canQuerySublayers ) { // query sublayers @@ -1398,7 +1403,7 @@ T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &ur } // since the layer is bad, stomp on it - return nullptr; + return QList(); } else if ( sublayers.size() > 1 || QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount ) ) { @@ -1416,14 +1421,22 @@ T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &ur const QList< QgsProviderSublayerDetails > selectedLayers = dlg.selectedLayers(); if ( !selectedLayers.isEmpty() ) { - result = qobject_cast< T * >( addSublayers( selectedLayers, baseName, dlg.groupName(), addToLegend ).value( 0 ) ); + const QList layers { addSublayers( selectedLayers, baseName, dlg.groupName(), addToLegend ) }; + for ( QgsMapLayer *layer : std::as_const( layers ) ) + { + result << qobject_cast( layer ); + } } } break; } case SublayerHandling::LoadAll: { - result = qobject_cast< T * >( addSublayers( sublayers, baseName, QString(), addToLegend ).value( 0 ) ); + const QList layers { addSublayers( sublayers, baseName, QString(), addToLegend ) }; + for ( QgsMapLayer *layer : std::as_const( layers ) ) + { + result << qobject_cast( layer ); + } break; } case SublayerHandling::AbortLoading: @@ -1432,16 +1445,20 @@ T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &ur } else { - result = qobject_cast< T * >( addSublayers( sublayers, name, QString(), addToLegend ).value( 0 ) ); + const QList layers { addSublayers( sublayers, name, QString(), addToLegend ) }; - if ( result ) + if ( ! layers.isEmpty() ) { QString base( baseName ); if ( settings.value( QStringLiteral( "qgis/formatLayerName" ), false ).toBool() ) { base = QgsMapLayer::formatLayerName( base ); } - result->setName( base ); + for ( QgsMapLayer *layer : std::as_const( layers ) ) + { + layer->setName( base ); + result << qobject_cast( layer ); + } } } } @@ -1449,19 +1466,19 @@ T *QgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QString &ur { QgsMapLayerFactory::LayerOptions options( QgsProject::instance()->transformContext() ); options.loadDefaultStyle = false; - result = qobject_cast< T * >( QgsMapLayerFactory::createLayer( uri, name, type, options, providerKey ) ); - if ( result ) + result.push_back( qobject_cast< T * >( QgsMapLayerFactory::createLayer( uri, name, type, options, providerKey ) ) ); + if ( ! result.isEmpty() ) { QString base( baseName ); if ( settings.value( QStringLiteral( "qgis/formatLayerName" ), false ).toBool() ) { base = QgsMapLayer::formatLayerName( base ); } - result->setName( base ); - QgsProject::instance()->addMapLayer( result, addToLegend ); + result.first()->setName( base ); + QgsProject::instance()->addMapLayer( result.first(), addToLegend ); - QgisApp::instance()->askUserForDatumTransform( result->crs(), QgsProject::instance()->crs(), result ); - QgsAppLayerHandling::postProcessAddedLayer( result ); + QgisApp::instance()->askUserForDatumTransform( result.first()->crs(), QgsProject::instance()->crs(), result.first() ); + QgsAppLayerHandling::postProcessAddedLayer( result.first() ); } } diff --git a/src/app/layers/qgsapplayerhandling.h b/src/app/layers/qgsapplayerhandling.h index 6785f1517fd6..7e16fbbcf729 100644 --- a/src/app/layers/qgsapplayerhandling.h +++ b/src/app/layers/qgsapplayerhandling.h @@ -69,10 +69,13 @@ class APP_EXPORT QgsAppLayerHandling * * The \a baseName parameter will be used as the layer name (and shown in the map legend). * + * \returns the list of layers added, which may contain more than one layer if the + * datasource contains multiple sublayers. + * * \note This may trigger a dialog asking users to select from available sublayers in the datasource, * depending on the contents of the datasource and the user's current QGIS settings. */ - static QgsVectorLayer *addVectorLayer( const QString &uri, const QString &baseName, const QString &provider = QLatin1String( "ogr" ), bool addToLegend = true ); + static QList< QgsVectorLayer * >addVectorLayer( const QString &uri, const QString &baseName, const QString &provider = QLatin1String( "ogr" ), bool addToLegend = true ); /** * Adds a list of vector layers from a list of layer \a uris supported by the OGR provider. @@ -90,10 +93,13 @@ class APP_EXPORT QgsAppLayerHandling * * The \a baseName parameter will be used as the layer name (and shown in the map legend). * + * \returns the list of layers added, which may contain more than one layer if the + * datasource contains multiple sublayers. + * * \note This may trigger a dialog asking users to select from available sublayers in the datasource, * depending on the contents of the datasource and the user's current QGIS settings. */ - static QgsRasterLayer *addRasterLayer( QString const &uri, const QString &baseName, const QString &provider = QLatin1String( "gdal" ), bool addToLegend = true ); + static QListaddRasterLayer( QString const &uri, const QString &baseName, const QString &provider = QLatin1String( "gdal" ), bool addToLegend = true ); /** * Adds a list of raster layers from a list of layer \a uris supported by the GDAL provider. @@ -111,10 +117,13 @@ class APP_EXPORT QgsAppLayerHandling * * The \a baseName parameter will be used as the layer name (and shown in the map legend). * + * \returns the list of layers added, which may contain more than one layer if the + * datasource contains multiple sublayers. + * * \note This may trigger a dialog asking users to select from available sublayers in the datasource, * depending on the contents of the datasource and the user's current QGIS settings. */ - static QgsMeshLayer *addMeshLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend = true ); + static QList< QgsMeshLayer *>addMeshLayer( const QString &uri, const QString &baseName, const QString &provider, bool addToLegend = true ); /** * Post processes an entire group of added \a layers. @@ -204,7 +213,7 @@ class APP_EXPORT QgsAppLayerHandling private: - template static T *addLayerPrivate( Qgis::LayerType type, const QString &uri, const QString &baseName, const QString &providerKey, bool guiWarnings = true, bool addToLegend = true ); + template static QListaddLayerPrivate( Qgis::LayerType type, const QString &uri, const QString &baseName, const QString &providerKey, bool guiWarnings = true, bool addToLegend = true ); /** * Post processes a single added \a layer, applying any default behavior which should From 573e461b9d5af1a4e260e3f7cf990e962e143ac4 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 19 Feb 2024 13:52:12 +0100 Subject: [PATCH 2/4] Update client code --- src/app/qgisapp.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index a381808accc4..18c51896218e 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -2463,18 +2463,27 @@ QList< QgsMapLayer * > QgisApp::handleDropUriList( const QgsMimeDataUtils::UriLi } else if ( u.layerType == QLatin1String( "vector" ) ) { - if ( QgsMapLayer *layer = QgsAppLayerHandling::addVectorLayer( uri, u.name, u.providerKey, addToLegend ) ) + const QList layerList { QgsAppLayerHandling::addVectorLayer( uri, u.name, u.providerKey, addToLegend ) }; + for ( QgsVectorLayer *layer : std::as_const( layerList ) ) + { addedLayers << layer; + } } else if ( u.layerType == QLatin1String( "raster" ) ) { - if ( QgsMapLayer *layer = QgsAppLayerHandling::addRasterLayer( uri, u.name, u.providerKey, addToLegend ) ) + const QList layerList { QgsAppLayerHandling::addRasterLayer( uri, u.name, u.providerKey, addToLegend ) }; + for ( QgsRasterLayer *layer : std::as_const( layerList ) ) + { addedLayers << layer; + } } else if ( u.layerType == QLatin1String( "mesh" ) ) { - if ( QgsMapLayer *layer = QgsAppLayerHandling::addMeshLayer( uri, u.name, u.providerKey, addToLegend ) ) + const QList layerList { QgsAppLayerHandling::addMeshLayer( uri, u.name, u.providerKey, addToLegend ) }; + for ( QgsMeshLayer *layer : std::as_const( layerList ) ) + { addedLayers << layer; + } } else if ( u.layerType == QLatin1String( "pointcloud" ) ) { From 0c543514c12d6e4baee0ccbb38a440103ad017f8 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 19 Feb 2024 14:23:31 +0100 Subject: [PATCH 3/4] Add comment about single layers --- src/app/layers/qgsapplayerhandling.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/layers/qgsapplayerhandling.cpp b/src/app/layers/qgsapplayerhandling.cpp index e486100b9fb6..4d8730c7d908 100644 --- a/src/app/layers/qgsapplayerhandling.cpp +++ b/src/app/layers/qgsapplayerhandling.cpp @@ -1464,6 +1464,8 @@ QListQgsAppLayerHandling::addLayerPrivate( Qgis::LayerType type, const QStr } else { + // Handle single layers (no sublayers available for this provider): result will + // contain at most one single layer QgsMapLayerFactory::LayerOptions options( QgsProject::instance()->transformContext() ); options.loadDefaultStyle = false; result.push_back( qobject_cast< T * >( QgsMapLayerFactory::createLayer( uri, name, type, options, providerKey ) ) ); From a9def5d0411a59be4db8574bb8c9bcde10638b00 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Mon, 19 Feb 2024 15:05:17 +0100 Subject: [PATCH 4/4] Update app client code --- src/app/qgisapp.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 18c51896218e..b0e6fa39130c 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -5701,7 +5701,8 @@ QString QgisApp::crsAndFormatAdjustedLayerUri( const QString &uri, const QString QgsMeshLayer *QgisApp::addMeshLayer( const QString &url, const QString &baseName, const QString &providerKey ) { - return QgsAppLayerHandling::addMeshLayer( url, baseName, providerKey ); + const QList layers { QgsAppLayerHandling::addMeshLayer( url, baseName, providerKey ) }; + return layers.isEmpty() ? nullptr : layers.first(); } template @@ -13127,7 +13128,8 @@ void QgisApp::show3DMapViewsManager() QgsVectorLayer *QgisApp::addVectorLayer( const QString &vectorLayerPath, const QString &name, const QString &providerKey ) { - return QgsAppLayerHandling::addVectorLayer( vectorLayerPath, name, providerKey ); + const QList layers { QgsAppLayerHandling::addVectorLayer( vectorLayerPath, name, providerKey ) }; + return layers.isEmpty() ? nullptr : layers.first(); } void QgisApp::embedLayers() @@ -15913,7 +15915,8 @@ void QgisApp::renameView() QgsRasterLayer *QgisApp::addRasterLayer( QString const &uri, QString const &baseName, QString const &providerKey ) { - return QgsAppLayerHandling::addRasterLayer( uri, baseName, providerKey ); + const QList layers { QgsAppLayerHandling::addRasterLayer( uri, baseName, providerKey ) }; + return layers.isEmpty() ? nullptr : layers.first(); } #ifdef ANDROID