Skip to content

Commit

Permalink
New data item for browser connection roots
Browse files Browse the repository at this point in the history
in order to distinguish it from schema and layer collections.
  • Loading branch information
elpaso committed Jul 16, 2020
1 parent fe69426 commit 4473b2c
Show file tree
Hide file tree
Showing 35 changed files with 170 additions and 106 deletions.
31 changes: 30 additions & 1 deletion python/core/auto_generated/qgsdataitem.sip.in
Expand Up @@ -691,8 +691,37 @@ Returns the standard browser data collection icon.
.. seealso:: :py:func:`iconDir`
%End

protected:
};



class QgsConnectionsRootItem : QgsDataCollectionItem
{
%Docstring
A Collection that represents a root group of connections from a single data provider

.. versionadded:: 3.16
%End

%TypeHeaderCode
#include "qgsdataitem.h"
%End
public:

QgsConnectionsRootItem( QgsDataItem *parent /TransferThis/, const QString &name, const QString &path = QString(), const QString &providerKey = QString() );
%Docstring
Constructor for QgsConnectionsRootItem, with the specified ``parent`` item.

The ``name`` argument specifies the text to show in the model for the item. A translated string should
be used wherever appropriate.

The ``path`` argument gives the item path in the browser tree. The ``path`` string can take any form,
but QgsSchemaItem items pointing to different logical locations should always use a different item ``path``.

The optional ``providerKey`` string can be used to specify the key for the QgsDataItemProvider that created this item.
%End

~QgsConnectionsRootItem();
};


Expand Down
139 changes: 71 additions & 68 deletions src/app/browser/qgsinbuiltdataitemproviders.cpp
Expand Up @@ -849,94 +849,97 @@ QString QgsDatabaseItemGuiProvider::name()
void QgsDatabaseItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
{
Q_UNUSED( selectedItems )
// Add create new table for connections
if ( QgsDataCollectionItem * collectionItem { qobject_cast<QgsDataCollectionItem *>( item ) } )
// Add create new table for collection items but not not if it is a root item
if ( ! qobject_cast<QgsConnectionsRootItem *>( item ) )
{
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( collectionItem->providerKey() ) };
if ( md )
if ( QgsDataCollectionItem * collectionItem { qobject_cast<QgsDataCollectionItem *>( item ) } )
{
const bool isSchema { qobject_cast<QgsDatabaseSchemaItem *>( item ) };
const QString connectionName { isSchema ? collectionItem->parent()->name() : collectionItem->name() };
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) );
if ( conn && conn->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateVectorTable ) )
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( collectionItem->providerKey() ) };
if ( md )
{
QAction *newTableAction = new QAction( QObject::tr( "New Table…" ), menu );
QObject::connect( newTableAction, &QAction::triggered, collectionItem, [ collectionItem, connectionName, md, isSchema, context]
const bool isSchema { qobject_cast<QgsDatabaseSchemaItem *>( item ) };
const QString connectionName { isSchema ? collectionItem->parent()->name() : collectionItem->name() };
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) );
if ( conn && conn->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateVectorTable ) )
{
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn2 { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) };
QgsNewVectorTableDialog dlg { conn2.get(), nullptr };
dlg.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
if ( isSchema )
{
dlg.setSchemaName( collectionItem->name() );
}
if ( dlg.exec() == QgsNewVectorTableDialog::DialogCode::Accepted )
QAction *newTableAction = new QAction( QObject::tr( "New Table…" ), menu );
QObject::connect( newTableAction, &QAction::triggered, collectionItem, [ collectionItem, connectionName, md, isSchema, context]
{
const QgsFields fields { dlg.fields() };
const QString tableName { dlg.tableName() };
const QString schemaName { dlg.schemaName() };
const QString geometryColumn { dlg.geometryColumnName() };
const QgsWkbTypes::Type geometryType { dlg.geometryType() };
const bool createSpatialIndex { dlg.createSpatialIndex() };
const QgsCoordinateReferenceSystem crs { dlg.crs( ) };
// This flag tells to the provider that field types do not need conversion
QMap<QString, QVariant> options { { QStringLiteral( "skipConvertFields" ), true } };

if ( ! geometryColumn.isEmpty() )
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn2 { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) ) };
QgsNewVectorTableDialog dlg { conn2.get(), nullptr };
dlg.setCrs( QgsProject::instance()->defaultCrsForNewLayers() );
if ( isSchema )
{
options[ QStringLiteral( "geometryColumn" ) ] = geometryColumn;
dlg.setSchemaName( collectionItem->name() );
}

QString title;
QString message;

try
if ( dlg.exec() == QgsNewVectorTableDialog::DialogCode::Accepted )
{
conn2->createVectorTable( schemaName, tableName, fields, geometryType, crs, true, &options );
if ( createSpatialIndex && conn2->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateSpatialIndex ) )
const QgsFields fields { dlg.fields() };
const QString tableName { dlg.tableName() };
const QString schemaName { dlg.schemaName() };
const QString geometryColumn { dlg.geometryColumnName() };
const QgsWkbTypes::Type geometryType { dlg.geometryType() };
const bool createSpatialIndex { dlg.createSpatialIndex() };
const QgsCoordinateReferenceSystem crs { dlg.crs( ) };
// This flag tells to the provider that field types do not need conversion
QMap<QString, QVariant> options { { QStringLiteral( "skipConvertFields" ), true } };

if ( ! geometryColumn.isEmpty() )
{
conn2->createSpatialIndex( schemaName, tableName );
options[ QStringLiteral( "geometryColumn" ) ] = geometryColumn;
}
// Ok, here is the trick: we cannot refresh the connection item because the refresh is not
// recursive.
// So, we check if the item is a schema or not, if it's not it means we initiated the new table from
// the parent connection item, hence we search for the schema item and refresh it instead of refreshing
// the connection item (the parent) with no effects.
if ( ! isSchema && conn2->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )

QString title;
QString message;

try
{
const auto constChildren { collectionItem->children() };
for ( const auto &c : constChildren )
conn2->createVectorTable( schemaName, tableName, fields, geometryType, crs, true, &options );
if ( createSpatialIndex && conn2->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateSpatialIndex ) )
{
conn2->createSpatialIndex( schemaName, tableName );
}
// Ok, here is the trick: we cannot refresh the connection item because the refresh is not
// recursive.
// So, we check if the item is a schema or not, if it's not it means we initiated the new table from
// the parent connection item, hence we search for the schema item and refresh it instead of refreshing
// the connection item (the parent) with no effects.
if ( ! isSchema && conn2->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )
{
if ( c->name() == schemaName )
const auto constChildren { collectionItem->children() };
for ( const auto &c : constChildren )
{
c->refresh();
if ( c->name() == schemaName )
{
c->refresh();
}
}
}
else
{
collectionItem->refresh( );
}
title = QObject::tr( "New Table Created" );
message = QObject::tr( "Table '%1' was created successfully." ).arg( tableName );
}
else
catch ( QgsProviderConnectionException &ex )
{
collectionItem->refresh( );
title = QObject::tr( "New Table Creation Error" );
message = QObject::tr( "Error creating new table '%1': %2" ).arg( tableName, ex.what() );
}
title = QObject::tr( "New Table Created" );
message = QObject::tr( "Table '%1' was created successfully." ).arg( tableName );
}
catch ( QgsProviderConnectionException &ex )
{
title = QObject::tr( "New Table Creation Error" );
message = QObject::tr( "Error creating new table '%1': %2" ).arg( tableName, ex.what() );
}

if ( context.messageBar() )
{
context.messageBar()->pushSuccess( title, message );
}
else
{
QMessageBox::information( nullptr, title, message );
if ( context.messageBar() )
{
context.messageBar()->pushSuccess( title, message );
}
else
{
QMessageBox::information( nullptr, title, message );
}
}
}
} );
menu->addAction( newTableAction );
} );
menu->addAction( newTableAction );
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/providers/ogr/qgsgeopackagedataitems.cpp
Expand Up @@ -67,7 +67,7 @@ QgsDataItem *QgsGeoPackageDataItemProvider::createDataItem( const QString &path,
}

QgsGeoPackageRootItem::QgsGeoPackageRootItem( QgsDataItem *parent, const QString &name, const QString &path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "GPKG" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "ogr" ) )
{
mCapabilities |= Fast;
mIconName = QStringLiteral( "mGeoPackage.svg" );
Expand Down
2 changes: 1 addition & 1 deletion src/core/providers/ogr/qgsgeopackagedataitems.h
Expand Up @@ -144,7 +144,7 @@ class CORE_EXPORT QgsGeoPackageConnectionItem final: public QgsGeoPackageCollect
};


class CORE_EXPORT QgsGeoPackageRootItem final: public QgsDataCollectionItem
class CORE_EXPORT QgsGeoPackageRootItem final: public QgsConnectionsRootItem
{
Q_OBJECT

Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsbrowsermodel.cpp
Expand Up @@ -777,7 +777,7 @@ QgsDataItem *QgsBrowserModel::addProviderRootItem( QgsDataItemProvider *pr )
if ( item )
{
// make sure the top level key is set always
item->setProviderKey( pr->name() );
item->setProviderKey( pr->dataProviderKey() );
// Forward the signal from the root items to the model (and then to the app)
connect( item, &QgsDataItem::connectionsChanged, this, &QgsBrowserModel::onConnectionsChanged );
QgsDebugMsgLevel( "Add new top level item : " + item->name(), 4 );
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsdataitem.cpp
Expand Up @@ -1758,6 +1758,13 @@ QIcon QgsDatabaseSchemaItem::iconDataCollection()
return QgsApplication::getThemeIcon( QStringLiteral( "/mIconDbSchema.svg" ) );
}


QgsConnectionsRootItem::QgsConnectionsRootItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
: QgsDataCollectionItem( parent, name, path, providerKey )
{
}


///@cond PRIVATE

QgsProjectHomeItem::QgsProjectHomeItem( QgsDataItem *parent, const QString &name, const QString &dirPath, const QString &path )
Expand Down
28 changes: 27 additions & 1 deletion src/core/qgsdataitem.h
Expand Up @@ -699,8 +699,34 @@ class CORE_EXPORT QgsDatabaseSchemaItem : public QgsDataCollectionItem
*/
static QIcon iconDataCollection();

protected:
};



/**
* \ingroup core
* A Collection that represents a root group of connections from a single data provider
* \since QGIS 3.16
*/
class CORE_EXPORT QgsConnectionsRootItem : public QgsDataCollectionItem
{
Q_OBJECT
public:

/**
* Constructor for QgsConnectionsRootItem, with the specified \a parent item.
*
* The \a name argument specifies the text to show in the model for the item. A translated string should
* be used wherever appropriate.
*
* The \a path argument gives the item path in the browser tree. The \a path string can take any form,
* but QgsSchemaItem items pointing to different logical locations should always use a different item \a path.
*
* The optional \a providerKey string can be used to specify the key for the QgsDataItemProvider that created this item.
*/
QgsConnectionsRootItem( QgsDataItem *parent SIP_TRANSFERTHIS, const QString &name, const QString &path = QString(), const QString &providerKey = QString() );

~QgsConnectionsRootItem() override = default;
};


Expand Down
2 changes: 1 addition & 1 deletion src/core/vectortile/qgsvectortiledataitems.cpp
Expand Up @@ -20,7 +20,7 @@
///@cond PRIVATE

QgsVectorTileRootItem::QgsVectorTileRootItem( QgsDataItem *parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "vectortile" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "vectortile" ) )
{
mCapabilities |= Fast;
mIconName = QStringLiteral( "mIconVectorTileLayer.svg" );
Expand Down
2 changes: 1 addition & 1 deletion src/core/vectortile/qgsvectortiledataitems.h
Expand Up @@ -22,7 +22,7 @@
#define SIP_NO_FILE

//! Root item for XYZ tile layers
class CORE_EXPORT QgsVectorTileRootItem : public QgsDataCollectionItem
class CORE_EXPORT QgsVectorTileRootItem : public QgsConnectionsRootItem
{
Q_OBJECT
public:
Expand Down
2 changes: 1 addition & 1 deletion src/gui/qgsnewvectortabledialog.cpp
Expand Up @@ -365,7 +365,7 @@ void QgsNewVectorTableDialog::validate()
// No geometry and no fields? No party!
if ( ! isSpatial && mFieldModel->fields().count() == 0 )
{
mValidationErrors.push_back( tr( "The table has no geometry column and no fields: cannot create an empty table!" ) );
mValidationErrors.push_back( tr( "The table has no geometry column and no fields!" ) );
}

const bool isValid { mValidationErrors.isEmpty() };
Expand Down
2 changes: 1 addition & 1 deletion src/providers/arcgisrest/qgsafsdataitems.cpp
Expand Up @@ -29,7 +29,7 @@


QgsAfsRootItem::QgsAfsRootItem( QgsDataItem *parent, const QString &name, const QString &path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "AFS" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "AFS" ) )
{
mCapabilities |= Fast;
mIconName = QStringLiteral( "mIconAfs.svg" );
Expand Down
2 changes: 1 addition & 1 deletion src/providers/arcgisrest/qgsafsdataitems.h
Expand Up @@ -21,7 +21,7 @@
#include "qgsdataitemprovider.h"


class QgsAfsRootItem : public QgsDataCollectionItem
class QgsAfsRootItem : public QgsConnectionsRootItem
{
Q_OBJECT
public:
Expand Down
2 changes: 1 addition & 1 deletion src/providers/arcgisrest/qgsamsdataitems.cpp
Expand Up @@ -27,7 +27,7 @@
#include <QImageReader>

QgsAmsRootItem::QgsAmsRootItem( QgsDataItem *parent, const QString &name, const QString &path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "AMS" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "AMS" ) )
{
mCapabilities |= Fast;
mIconName = QStringLiteral( "mIconAms.svg" );
Expand Down
2 changes: 1 addition & 1 deletion src/providers/arcgisrest/qgsamsdataitems.h
Expand Up @@ -23,7 +23,7 @@
#include "qgsdataitemprovider.h"


class QgsAmsRootItem : public QgsDataCollectionItem
class QgsAmsRootItem : public QgsConnectionsRootItem
{
Q_OBJECT
public:
Expand Down
2 changes: 1 addition & 1 deletion src/providers/db2/qgsdb2dataitems.cpp
Expand Up @@ -334,7 +334,7 @@ bool QgsDb2ConnectionItem::handleDrop( const QMimeData *data, const QString &toS
}

QgsDb2RootItem::QgsDb2RootItem( QgsDataItem *parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "DB2" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "DB2" ) )
{
mIconName = QStringLiteral( "mIconDb2.svg" );
populate();
Expand Down
2 changes: 1 addition & 1 deletion src/providers/db2/qgsdb2dataitems.h
Expand Up @@ -30,7 +30,7 @@ class QgsDb2LayerItem;
* \class QgsDb2RootItem
* \brief Browser Panel DB2 root object.
*/
class QgsDb2RootItem : public QgsDataCollectionItem
class QgsDb2RootItem : public QgsConnectionsRootItem
{
Q_OBJECT

Expand Down
2 changes: 1 addition & 1 deletion src/providers/geonode/qgsgeonodedataitems.cpp
Expand Up @@ -186,7 +186,7 @@ void QgsGeoNodeServiceItem::replacePath( QgsDataItem *item, const QString &befor
}

QgsGeoNodeRootItem::QgsGeoNodeRootItem( QgsDataItem *parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "GeoNode" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "GeoNode" ) )
{
mCapabilities |= Fast;
{
Expand Down
2 changes: 1 addition & 1 deletion src/providers/geonode/qgsgeonodedataitems.h
Expand Up @@ -56,7 +56,7 @@ class QgsGeoNodeServiceItem : public QgsDataCollectionItem
bool layerCollection() const override;
};

class QgsGeoNodeRootItem : public QgsDataCollectionItem
class QgsGeoNodeRootItem : public QgsConnectionsRootItem
{
Q_OBJECT
public:
Expand Down
2 changes: 1 addition & 1 deletion src/providers/mssql/qgsmssqldataitems.cpp
Expand Up @@ -610,7 +610,7 @@ QVector<QgsDataItem *> QgsMssqlLayerItem::createChildren()

// ---------------------------------------------------------------------------
QgsMssqlRootItem::QgsMssqlRootItem( QgsDataItem *parent, const QString &name, const QString &path )
: QgsDataCollectionItem( parent, name, path, QStringLiteral( "MSSQL" ) )
: QgsConnectionsRootItem( parent, name, path, QStringLiteral( "MSSQL" ) )
{
mIconName = QStringLiteral( "mIconMssql.svg" );
populate();
Expand Down
2 changes: 1 addition & 1 deletion src/providers/mssql/qgsmssqldataitems.h
Expand Up @@ -31,7 +31,7 @@ class QgsMssqlConnectionItem;
class QgsMssqlSchemaItem;
class QgsMssqlLayerItem;

class QgsMssqlRootItem : public QgsDataCollectionItem
class QgsMssqlRootItem : public QgsConnectionsRootItem
{
Q_OBJECT
public:
Expand Down

0 comments on commit 4473b2c

Please sign in to comment.