Skip to content

Commit fee3935

Browse files
authored
Merge pull request #8519 from elpaso/browser-gpkg-vector-rename
[feature][needs-docs] Allow gpkg vector rename from browser
2 parents d257cf0 + fe8d419 commit fee3935

File tree

2 files changed

+189
-13
lines changed

2 files changed

+189
-13
lines changed

src/providers/ogr/qgsgeopackagedataitems.cpp

+162-13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "qgstaskmanager.h"
4040
#include "qgsproviderregistry.h"
4141
#include "qgsproxyprogresstask.h"
42+
#include "qgsnewnamedialog.h"
4243

4344
QGISEXTERN bool deleteLayer( const QString &uri, const QString &errCause );
4445

@@ -81,6 +82,13 @@ QList<QAction *> QgsGeoPackageAbstractLayerItem::actions( QWidget * )
8182
QAction *actionDeleteLayer = new QAction( tr( "Delete Layer '%1'…" ).arg( mName ), this );
8283
connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::deleteLayer );
8384
lst.append( actionDeleteLayer );
85+
// Check capabilities: for now rename is only available for vectors
86+
if ( capabilities2() & QgsDataItem::Capability::Rename )
87+
{
88+
QAction *actionRenameLayer = new QAction( tr( "Rename Layer '%1'…" ).arg( mName ), this );
89+
connect( actionRenameLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::renameLayer );
90+
lst.append( actionRenameLayer );
91+
}
8492
return lst;
8593
}
8694

@@ -490,16 +498,7 @@ void QgsGeoPackageCollectionItem::vacuumGeoPackageDbAction()
490498
void QgsGeoPackageAbstractLayerItem::deleteLayer()
491499
{
492500
// Check if the layer(s) are in the registry
493-
QList<QgsMapLayer *> layersList;
494-
const auto mapLayers( QgsProject::instance()->mapLayers() );
495-
for ( QgsMapLayer *layer : mapLayers )
496-
{
497-
if ( layer->publicSource() == mUri )
498-
{
499-
layersList << layer;
500-
}
501-
}
502-
501+
const QList<QgsMapLayer *> layersList( layersInProject( ) );
503502
if ( ! layersList.isEmpty( ) )
504503
{
505504
if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
@@ -515,7 +514,7 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer()
515514
return;
516515
}
517516

518-
if ( layersList.isEmpty() )
517+
if ( ! layersList.isEmpty() )
519518
{
520519
QgsProject::instance()->removeMapLayers( layersList );
521520
}
@@ -534,6 +533,32 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer()
534533
}
535534

536535
}
536+
537+
void QgsGeoPackageAbstractLayerItem::renameLayer( )
538+
{
539+
QMessageBox::warning( nullptr, QObject::tr( "Rename layer" ),
540+
QObject::tr( "The layer <b>%1</b> cannot be renamed because this feature is not yet implemented for this kind of layers." )
541+
.arg( mName ) );
542+
}
543+
544+
void QgsGeoPackageVectorLayerItem::renameLayer()
545+
{
546+
547+
// Get layer name from layer URI
548+
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( providerKey(), mUri ) );
549+
QString baseUri = pieces[QStringLiteral( "path" )].toString();
550+
QString layerName = pieces[QStringLiteral( "layerName" )].toString();
551+
552+
// Collect existing table names
553+
const QRegExp checkRe( QStringLiteral( R"re([A-Za-z_][A-Za-z0-9_\s]+)re" ) );
554+
QgsNewNameDialog dlg( mUri, layerName, QStringList(), tableNames(), checkRe );
555+
dlg.setOverwriteEnabled( false );
556+
557+
if ( dlg.exec() != dlg.Accepted || dlg.name().isEmpty() || dlg.name() == layerName )
558+
return;
559+
560+
rename( dlg.name() );
561+
}
537562
#endif
538563

539564
bool QgsGeoPackageCollectionItem::vacuumGeoPackageDb( const QString &path, const QString &name, QString &errCause )
@@ -718,11 +743,64 @@ bool QgsGeoPackageAbstractLayerItem::executeDeleteLayer( QString &errCause )
718743
return false;
719744
}
720745

746+
QList<QString> QgsGeoPackageAbstractLayerItem::tableNames()
747+
{
748+
QList<QString> names;
749+
QString errCause;
750+
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( providerKey(), mUri ) );
751+
QString baseUri = pieces[QStringLiteral( "path" )].toString();
752+
if ( !baseUri.isEmpty() )
753+
{
754+
char *errmsg = nullptr;
755+
sqlite3_database_unique_ptr database;
756+
int status = database.open_v2( baseUri, SQLITE_OPEN_READONLY, nullptr );
757+
if ( status == SQLITE_OK )
758+
{
759+
char *sql = sqlite3_mprintf( "SELECT table_name FROM gpkg_contents;" );
760+
status = sqlite3_exec(
761+
database.get(), /* An open database */
762+
sql, /* SQL to be evaluated */
763+
+[]( void *names, int, char **argv, char ** )
764+
{
765+
*static_cast<QList<QString>*>( names ) << QString( argv[ 0 ] );
766+
return 0;
767+
}, /* Callback function */
768+
&names, /* 1st argument to callback */
769+
&errmsg /* Error msg written here */
770+
);
771+
sqlite3_free( sql );
772+
if ( status != SQLITE_OK )
773+
QgsDebugMsg( QStringLiteral( "There was an error reading tables from GPKG layer %1: %2" ).arg( mUri, QString::fromUtf8( errmsg ) ) );
774+
sqlite3_free( errmsg );
775+
}
776+
else
777+
{
778+
QgsDebugMsg( QStringLiteral( "There was an error opening GPKG %1" ).arg( mUri ) );
779+
}
780+
}
781+
return names;
782+
}
783+
784+
QList<QgsMapLayer *> QgsGeoPackageAbstractLayerItem::layersInProject() const
785+
{
786+
// Check if the layer(s) are in the registry
787+
QList<QgsMapLayer *> layersList;
788+
const auto mapLayers( QgsProject::instance()->mapLayers() );
789+
for ( QgsMapLayer *layer : mapLayers )
790+
{
791+
if ( layer->publicSource() == mUri )
792+
{
793+
layersList << layer;
794+
}
795+
}
796+
return layersList;
797+
}
798+
721799

722800
QgsGeoPackageVectorLayerItem::QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType )
723801
: QgsGeoPackageAbstractLayerItem( parent, name, path, uri, layerType, QStringLiteral( "ogr" ) )
724802
{
725-
803+
mCapabilities |= Rename;
726804
}
727805

728806

@@ -737,9 +815,80 @@ bool QgsGeoPackageRasterLayerItem::executeDeleteLayer( QString &errCause )
737815
return QgsGeoPackageCollectionItem::deleteGeoPackageRasterLayer( mUri, errCause );
738816
}
739817

740-
741818
bool QgsGeoPackageVectorLayerItem::executeDeleteLayer( QString &errCause )
742819
{
743820
return ::deleteLayer( mUri, errCause );
744821
}
745822

823+
bool QgsGeoPackageVectorLayerItem::rename( const QString &name )
824+
{
825+
// Checks that name does not exist yet
826+
if ( tableNames().contains( name ) )
827+
{
828+
return false;
829+
}
830+
// Check if the layer(s) are in the registry
831+
const QList<QgsMapLayer *> layersList( layersInProject() );
832+
if ( ! layersList.isEmpty( ) )
833+
{
834+
if ( QMessageBox::question( nullptr, QObject::tr( "Rename Layer" ), QObject::tr( "The layer <b>%1</b> is loaded in the current project with name <b>%2</b>,"
835+
" do you want to remove it from the project and rename it?" ).arg( mName, layersList.at( 0 )->name() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
836+
{
837+
return false;
838+
}
839+
}
840+
if ( ! layersList.isEmpty() )
841+
{
842+
QgsProject::instance()->removeMapLayers( layersList );
843+
}
844+
845+
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mUri );
846+
QString errCause;
847+
if ( parts.empty() || parts.value( QStringLiteral( "path" ) ).isNull() || parts.value( "layerName" ).isNull() )
848+
{
849+
errCause = QObject::tr( "Layer URI %1 is not valid!" ).arg( mUri );
850+
}
851+
else
852+
{
853+
QString filePath = parts.value( QStringLiteral( "path" ) ).toString();
854+
const QList<QgsMapLayer *> layersList( layersInProject() );
855+
if ( ! layersList.isEmpty( ) )
856+
{
857+
if ( QMessageBox::question( nullptr, QObject::tr( "Rename Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
858+
" do you want to remove it from the project and rename it?" ).arg( mName, layersList.at( 0 )->name() ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
859+
{
860+
return false;
861+
}
862+
}
863+
if ( ! layersList.isEmpty() )
864+
{
865+
QgsProject::instance()->removeMapLayers( layersList );
866+
}
867+
868+
// TODO: maybe an index?
869+
QString oldName = parts.value( QStringLiteral( "layerName" ) ).toString();
870+
871+
GDALDatasetH hDS = GDALOpenEx( filePath.toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr );
872+
if ( hDS )
873+
{
874+
QString sql( QStringLiteral( "ALTER TABLE \"%1\" RENAME TO \"%2\"" ).arg( oldName, name ) );
875+
OGRLayerH ogrLayer( GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr ) );
876+
if ( ogrLayer )
877+
GDALDatasetReleaseResultSet( hDS, ogrLayer );
878+
GDALClose( hDS );
879+
errCause = CPLGetLastErrorMsg( );
880+
}
881+
else
882+
{
883+
errCause = QObject::tr( "There was an error opening %1!" ).arg( filePath );
884+
}
885+
}
886+
887+
if ( ! errCause.isEmpty() )
888+
QMessageBox::critical( nullptr, QObject::tr( "Error renaming layer" ), errCause );
889+
else if ( mParent )
890+
mParent->refreshConnections();
891+
892+
return errCause.isEmpty();
893+
}
894+

src/providers/ogr/qgsgeopackagedataitems.h

+27
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,28 @@ class QgsGeoPackageAbstractLayerItem : public QgsLayerItem
3232
QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey );
3333

3434
/**
35+
* Deletes a layer.
3536
* Subclasses need to implement this function with
3637
* the real deletion implementation
3738
*/
3839
virtual bool executeDeleteLayer( QString &errCause );
40+
41+
/**
42+
* Returns a list of all table names for the geopackage
43+
*/
44+
QList<QString> tableNames();
45+
3946
#ifdef HAVE_GUI
47+
48+
//! Checks if the data source has any layer in the current project returns them
49+
QList<QgsMapLayer *> layersInProject() const;
50+
4051
QList<QAction *> actions( QWidget *menu ) override;
52+
4153
public slots:
4254
virtual void deleteLayer();
55+
//! Renames the layer: default implementation does nothing!
56+
virtual void renameLayer();
4357
#endif
4458
};
4559

@@ -61,8 +75,21 @@ class QgsGeoPackageVectorLayerItem : public QgsGeoPackageAbstractLayerItem
6175

6276
public:
6377
QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType );
78+
79+
/**
80+
* Sets a new \a name for the item, and returns true if the item was successfully renamed.
81+
*
82+
* \since QGIS 3.6
83+
*/
84+
virtual bool rename( const QString &name ) override;
85+
6486
protected:
6587
bool executeDeleteLayer( QString &errCause ) override;
88+
#ifdef HAVE_GUI
89+
public slots:
90+
//! Renames the layer
91+
virtual void renameLayer() override;
92+
#endif
6693
};
6794

6895

0 commit comments

Comments
 (0)