Skip to content

Commit

Permalink
Support F2 for layer rename action
Browse files Browse the repository at this point in the history
Also refactored some internal methods to
avoid duplication of code and use parent
class virtual rename() instead of a new
method for gpkg only.
  • Loading branch information
elpaso committed Nov 21, 2018
1 parent e44d620 commit fe8d419
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 63 deletions.
177 changes: 117 additions & 60 deletions src/providers/ogr/qgsgeopackagedataitems.cpp
Expand Up @@ -39,6 +39,7 @@
#include "qgstaskmanager.h"
#include "qgsproviderregistry.h"
#include "qgsproxyprogresstask.h"
#include "qgsnewnamedialog.h"

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

Expand Down Expand Up @@ -81,8 +82,8 @@ QList<QAction *> QgsGeoPackageAbstractLayerItem::actions( QWidget * )
QAction *actionDeleteLayer = new QAction( tr( "Delete Layer '%1'…" ).arg( mName ), this );
connect( actionDeleteLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::deleteLayer );
lst.append( actionDeleteLayer );
// For now, rename is only available for vectors
if ( mapLayerType() == QgsMapLayer::LayerType::VectorLayer )
// Check capabilities: for now rename is only available for vectors
if ( capabilities2() & QgsDataItem::Capability::Rename )
{
QAction *actionRenameLayer = new QAction( tr( "Rename Layer '%1'…" ).arg( mName ), this );
connect( actionRenameLayer, &QAction::triggered, this, &QgsGeoPackageAbstractLayerItem::renameLayer );
Expand Down Expand Up @@ -497,16 +498,7 @@ void QgsGeoPackageCollectionItem::vacuumGeoPackageDbAction()
void QgsGeoPackageAbstractLayerItem::deleteLayer()
{
// Check if the layer(s) are in the registry
QList<QgsMapLayer *> layersList;
const auto mapLayers( QgsProject::instance()->mapLayers() );
for ( QgsMapLayer *layer : mapLayers )
{
if ( layer->publicSource() == mUri )
{
layersList << layer;
}
}

const QList<QgsMapLayer *> layersList( layersInProject( ) );
if ( ! layersList.isEmpty( ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Delete Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
Expand Down Expand Up @@ -542,52 +534,30 @@ void QgsGeoPackageAbstractLayerItem::deleteLayer()

}

void QgsGeoPackageAbstractLayerItem::renameLayer()
void QgsGeoPackageAbstractLayerItem::renameLayer( )
{
// Check if the layer(s) are in the registry
QList<QgsMapLayer *> layersList;
const auto mapLayers( QgsProject::instance()->mapLayers() );
for ( QgsMapLayer *layer : mapLayers )
{
if ( layer->publicSource() == mUri )
{
layersList << layer;
}
}
QMessageBox::warning( nullptr, QObject::tr( "Rename layer" ),
QObject::tr( "The layer <b>%1</b> cannot be renamed because this feature is not yet implemented for this kind of layers." )
.arg( mName ) );
}

if ( ! layersList.isEmpty( ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Rename Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
" 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 )
{
return;
}
}
if ( ! layersList.isEmpty() )
{
QgsProject::instance()->removeMapLayers( layersList );
}
void QgsGeoPackageVectorLayerItem::renameLayer()
{

bool ok;
QString newName = QInputDialog::getText( nullptr, QObject::tr( "Rename %1" ).arg( mName ),
QObject::tr( "New name:" ), QLineEdit::Normal,
QString(), &ok );
if ( ! ok || newName.isEmpty() )
return;
// Get layer name from layer URI
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( providerKey(), mUri ) );
QString baseUri = pieces[QStringLiteral( "path" )].toString();
QString layerName = pieces[QStringLiteral( "layerName" )].toString();

QString errCause;
bool res = executeRenameLayer( newName, errCause );
if ( !res )
{
QMessageBox::warning( nullptr, tr( "Rename Layer" ), errCause );
}
else
{
QMessageBox::information( nullptr, tr( "Rename Layer" ), tr( "Layer <b>%1</b> renamed successfully." ).arg( mName ) );
if ( mParent )
mParent->refreshConnections();
}
// Collect existing table names
const QRegExp checkRe( QStringLiteral( R"re([A-Za-z_][A-Za-z0-9_\s]+)re" ) );
QgsNewNameDialog dlg( mUri, layerName, QStringList(), tableNames(), checkRe );
dlg.setOverwriteEnabled( false );

if ( dlg.exec() != dlg.Accepted || dlg.name().isEmpty() || dlg.name() == layerName )
return;

rename( dlg.name() );
}
#endif

Expand Down Expand Up @@ -773,18 +743,64 @@ bool QgsGeoPackageAbstractLayerItem::executeDeleteLayer( QString &errCause )
return false;
}

bool QgsGeoPackageAbstractLayerItem::executeRenameLayer( QString &newName, QString &errCause )
QList<QString> QgsGeoPackageAbstractLayerItem::tableNames()
{
Q_UNUSED( newName );
errCause = QObject::tr( "The layer <b>%1</b> cannot be renamed because this feature is not yet implemented for this kind of layers." ).arg( mName );
return false;
QList<QString> names;
QString errCause;
QVariantMap pieces( QgsProviderRegistry::instance()->decodeUri( providerKey(), mUri ) );
QString baseUri = pieces[QStringLiteral( "path" )].toString();
if ( !baseUri.isEmpty() )
{
char *errmsg = nullptr;
sqlite3_database_unique_ptr database;
int status = database.open_v2( baseUri, SQLITE_OPEN_READONLY, nullptr );
if ( status == SQLITE_OK )
{
char *sql = sqlite3_mprintf( "SELECT table_name FROM gpkg_contents;" );
status = sqlite3_exec(
database.get(), /* An open database */
sql, /* SQL to be evaluated */
+[]( void *names, int, char **argv, char ** )
{
*static_cast<QList<QString>*>( names ) << QString( argv[ 0 ] );
return 0;
}, /* Callback function */
&names, /* 1st argument to callback */
&errmsg /* Error msg written here */
);
sqlite3_free( sql );
if ( status != SQLITE_OK )
QgsDebugMsg( QStringLiteral( "There was an error reading tables from GPKG layer %1: %2" ).arg( mUri, QString::fromUtf8( errmsg ) ) );
sqlite3_free( errmsg );
}
else
{
QgsDebugMsg( QStringLiteral( "There was an error opening GPKG %1" ).arg( mUri ) );
}
}
return names;
}

QList<QgsMapLayer *> QgsGeoPackageAbstractLayerItem::layersInProject() const
{
// Check if the layer(s) are in the registry
QList<QgsMapLayer *> layersList;
const auto mapLayers( QgsProject::instance()->mapLayers() );
for ( QgsMapLayer *layer : mapLayers )
{
if ( layer->publicSource() == mUri )
{
layersList << layer;
}
}
return layersList;
}


QgsGeoPackageVectorLayerItem::QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType )
: QgsGeoPackageAbstractLayerItem( parent, name, path, uri, layerType, QStringLiteral( "ogr" ) )
{

mCapabilities |= Rename;
}


Expand All @@ -804,23 +820,58 @@ bool QgsGeoPackageVectorLayerItem::executeDeleteLayer( QString &errCause )
return ::deleteLayer( mUri, errCause );
}

bool QgsGeoPackageVectorLayerItem::executeRenameLayer( QString &newName, QString &errCause )
bool QgsGeoPackageVectorLayerItem::rename( const QString &name )
{
// Checks that name does not exist yet
if ( tableNames().contains( name ) )
{
return false;
}
// Check if the layer(s) are in the registry
const QList<QgsMapLayer *> layersList( layersInProject() );
if ( ! layersList.isEmpty( ) )
{
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>,"
" 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 )
{
return false;
}
}
if ( ! layersList.isEmpty() )
{
QgsProject::instance()->removeMapLayers( layersList );
}

const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mUri );
QString errCause;
if ( parts.empty() || parts.value( QStringLiteral( "path" ) ).isNull() || parts.value( "layerName" ).isNull() )
{
errCause = QObject::tr( "Layer URI %1 is not valid!" ).arg( mUri );
}
else
{
QString filePath = parts.value( QStringLiteral( "path" ) ).toString();
const QList<QgsMapLayer *> layersList( layersInProject() );
if ( ! layersList.isEmpty( ) )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Rename Layer" ), QObject::tr( "The layer <b>%1</b> exists in the current project <b>%2</b>,"
" 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 )
{
return false;
}
}
if ( ! layersList.isEmpty() )
{
QgsProject::instance()->removeMapLayers( layersList );
}

// TODO: maybe an index?
QString oldName = parts.value( QStringLiteral( "layerName" ) ).toString();

GDALDatasetH hDS = GDALOpenEx( filePath.toUtf8().constData(), GDAL_OF_VECTOR | GDAL_OF_UPDATE, nullptr, nullptr, nullptr );
if ( hDS )
{
QString sql( QStringLiteral( "ALTER TABLE \"%1\" RENAME TO \"%2\"" ).arg( oldName, newName ) );
QString sql( QStringLiteral( "ALTER TABLE \"%1\" RENAME TO \"%2\"" ).arg( oldName, name ) );
OGRLayerH ogrLayer( GDALDatasetExecuteSQL( hDS, sql.toUtf8().constData(), nullptr, nullptr ) );
if ( ogrLayer )
GDALDatasetReleaseResultSet( hDS, ogrLayer );
Expand All @@ -832,6 +883,12 @@ bool QgsGeoPackageVectorLayerItem::executeRenameLayer( QString &newName, QString
errCause = QObject::tr( "There was an error opening %1!" ).arg( filePath );
}
}

if ( ! errCause.isEmpty() )
QMessageBox::critical( nullptr, QObject::tr( "Error renaming layer" ), errCause );
else if ( mParent )
mParent->refreshConnections();

return errCause.isEmpty();
}

30 changes: 27 additions & 3 deletions src/providers/ogr/qgsgeopackagedataitems.h
Expand Up @@ -32,16 +32,28 @@ class QgsGeoPackageAbstractLayerItem : public QgsLayerItem
QgsGeoPackageAbstractLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey );

/**
* Deletes a layer.
* Subclasses need to implement this function with
* the real deletion implementation
*/
virtual bool executeDeleteLayer( QString &errCause );
virtual bool executeRenameLayer( QString &newName, QString &errCause );

/**
* Returns a list of all table names for the geopackage
*/
QList<QString> tableNames();

#ifdef HAVE_GUI

//! Checks if the data source has any layer in the current project returns them
QList<QgsMapLayer *> layersInProject() const;

QList<QAction *> actions( QWidget *menu ) override;

public slots:
virtual void deleteLayer();
void renameLayer();
//! Renames the layer: default implementation does nothing!
virtual void renameLayer();
#endif
};

Expand All @@ -63,9 +75,21 @@ class QgsGeoPackageVectorLayerItem : public QgsGeoPackageAbstractLayerItem

public:
QgsGeoPackageVectorLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType );

/**
* Sets a new \a name for the item, and returns true if the item was successfully renamed.
*
* \since QGIS 3.6
*/
virtual bool rename( const QString &name ) override;

protected:
bool executeDeleteLayer( QString &errCause ) override;
bool executeRenameLayer( QString &newName, QString &errCause ) override;
#ifdef HAVE_GUI
public slots:
//! Renames the layer
virtual void renameLayer() override;
#endif
};


Expand Down

0 comments on commit fe8d419

Please sign in to comment.