22 #include <QMessageBox>
34 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->
nativeTypes(),
this );
38 QMessageBox::critical(
nullptr, tr(
"Cannot Create New Tables" ), tr(
"Error retrieving native types from the data provider: creation of new tables is not possible.\n"
39 "Error message: %1" ).arg( ex.
what() ) );
40 QTimer::singleShot( 0, [ = ] { reject(); } );
44 Q_ASSERT( ! mFieldModel->nativeTypes().isEmpty() );
47 setWindowTitle( tr(
"New Table" ) );
49 auto updateTableNames = [ = ](
const QString &schema = QString( ) )
54 const auto constTables { conn->
tables( schema ) };
55 for (
const auto &tp : constTables )
57 mTableNames.push_back( tp.tableName() );
64 QgsDebugMsg( QStringLiteral(
"Error retrieving tables from connection: %1" ).arg( ex.
what() ) );
69 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [ = ]()
74 mTableName->setText( QStringLiteral(
"new_table_name" ) );
75 mFieldsTableView->setModel( mFieldModel );
76 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->
nativeTypes(),
this )};
77 mFieldsTableView->setItemDelegate( delegate );
78 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
79 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
80 mFieldsTableView->setVerticalHeader(
nullptr );
83 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
84 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
87 if ( mConnection->
capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )
89 mSchemaCbo->addItems( mConnection->
schemas() );
90 connect( mSchemaCbo, qgis::overload<const QString &>::of( &QComboBox::currentIndexChanged ),
this, [ = ](
const QString & schema )
92 updateTableNames( schema );
101 if ( ! mConnection->
capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateSpatialIndex ) )
103 mSpatialIndexChk->setChecked(
false );
104 mSpatialIndexChk->hide();
105 mSpatialIndexLabel->hide();
109 updateTableNames( mSchemaCbo->currentText() );
112 connect( mTableName, &QLineEdit::textChanged,
this, [ = ](
const QString & )
117 connect( mGeomColumn, &QLineEdit::textChanged,
this, [ = ](
const QString & )
123 connect( mGeomTypeCbo, qgis::overload<int>::of( &QComboBox::currentIndexChanged ),
this, [ = ](
int index )
125 const bool hasGeom { index != 0 };
126 mGeomColumn->setEnabled( hasGeom );
127 mGeomColumnLabel->setEnabled( hasGeom );
128 mSpatialIndexChk->setEnabled( hasGeom );
129 mSpatialIndexLabel->setEnabled( hasGeom );
130 mCrs->setEnabled( hasGeom );
131 mCrsLabel->setEnabled( hasGeom );
132 mDimensionsLabel->setEnabled( hasGeom );
133 mHasMChk->setEnabled( hasGeom );
134 mHasZChk->setEnabled( hasGeom );
139 const bool hasSinglePart { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::SinglePart ) };
140 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconTableLayer.svg" ) ), tr(
"No Geometry" ), QgsWkbTypes::Type::NoGeometry );
142 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPointLayer.svg" ) ), tr(
"Point" ), QgsWkbTypes::Type::Point );
143 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPointLayer.svg" ) ), tr(
"MultiPoint" ), QgsWkbTypes::Type::MultiPoint );
145 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconLineLayer.svg" ) ), tr(
"Line" ), QgsWkbTypes::Type::LineString );
146 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconLineLayer.svg" ) ), tr(
"MultiLine" ), QgsWkbTypes::Type::MultiLineString );
148 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPolygonLayer.svg" ) ), tr(
"Polygon" ), QgsWkbTypes::Type::Polygon );
149 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPolygonLayer.svg" ) ), tr(
"MultiPolygon" ), QgsWkbTypes::Type::MultiPolygon );
153 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconLineLayer.svg" ) ), tr(
"CompoundCurve" ), QgsWkbTypes::Type::CompoundCurve );
154 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPolygonLayer.svg" ) ), tr(
"CurvePolygon" ), QgsWkbTypes::Type::CurvePolygon );
155 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconLineLayer.svg" ) ), tr(
"MultiCurve" ), QgsWkbTypes::Type::MultiCurve );
156 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconPolygonLayer.svg" ) ), tr(
"MultiSurface" ), QgsWkbTypes::Type::MultiSurface );
159 mGeomTypeCbo->setCurrentIndex( 0 );
161 const bool hasZ { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::Z ) };
162 const bool hasM { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::M ) };
165 mHasMChk->setEnabled(
false );
166 mHasMChk->setChecked(
false );
170 mHasZChk->setEnabled(
false );
171 mHasZChk->setChecked(
false );
173 if ( ! hasM && ! hasM )
175 mHasZChk->setVisible(
false );
176 mHasMChk->setVisible(
false );
177 mDimensionsLabel->setVisible(
false );
180 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [ = ](
const QItemSelection & selected,
const QItemSelection & )
182 if ( ! selected.isEmpty() )
184 mCurrentRow = selected.indexes().first().row();
190 const QVariant::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
191 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
194 connect( mAddFieldBtn, &QPushButton::clicked,
this, [ = ]
197 QgsField newField { QStringLiteral(
"new_field_name" ), defaultFieldType, defaultFieldTypeName };
198 fieldList.append( newField );
199 setFields( fieldList );
200 selectRow( fieldList.count() - 1 );
203 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [ = ]
206 if ( fieldList.exists( mCurrentRow ) )
208 fieldList.
remove( mCurrentRow );
209 setFields( fieldList );
214 connect( mFieldUpBtn, &QPushButton::clicked,
this, [ = ]
216 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
219 for (
int i = 0; i < fields().count(); ++i )
221 if ( i == mCurrentRow - 1 )
223 fieldList.
append( fields().at( mCurrentRow ) );
224 fieldList.
append( fields().at( mCurrentRow - 1 ) );
226 else if ( i != mCurrentRow )
228 fieldList.
append( fields().at( i ) );
231 setFields( fieldList );
232 selectRow( mCurrentRow - 1 );
236 connect( mFieldDownBtn, &QPushButton::clicked,
this, [ = ]
238 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
241 for (
int i = 0; i < fields().count(); ++i )
243 if ( i == mCurrentRow )
245 fieldList.
append( fields().at( mCurrentRow + 1 ) );
246 fieldList.
append( fields().at( mCurrentRow ) );
248 else if ( i != mCurrentRow + 1 )
250 fieldList.
append( fields().at( i ) );
253 setFields( fieldList );
254 selectRow( mCurrentRow + 1 );
264 mSchemaCbo->setCurrentText( name );
269 mTableName->setText( name );
274 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData( type ) );
289 return mTableName->text();
294 return mSchemaCbo->currentText();
299 return mGeomColumn->text();
304 return mFieldModel ? mFieldModel->fields() :
QgsFields();
310 if ( mHasMChk->isChecked() )
314 if ( mHasZChk->isChecked() )
326 mFieldModel->setFields(
fields );
332 return mSpatialIndexChk->isChecked();
337 return mValidationErrors;
340 void QgsNewVectorTableDialog::updateButtons()
342 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
343 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
344 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
347 void QgsNewVectorTableDialog::selectRow(
int row )
349 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
350 mFieldsTableView->setCurrentIndex( index );
351 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
352 mFieldsTableView->selectionModel()->select( index, flags );
353 mFieldsTableView->scrollTo( index );
356 void QgsNewVectorTableDialog::validate()
358 mValidationErrors.clear();
360 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
361 if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
363 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists!" ).arg( mTableName->text() ) );
366 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
368 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name!" ).arg( mGeomColumn->text() ) );
371 if ( ! isSpatial &&
fields().count() == 0 )
373 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields!" ) );
376 const auto cFields {
fields() };
377 for (
const auto &f : cFields )
379 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
381 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length!" ).arg( f.name() ) );
385 const bool isValid { mValidationErrors.isEmpty() };
388 mValidationResults->setText( mValidationErrors.join( QLatin1String(
"<br>" ) ) );
391 mValidationFrame->setVisible( ! isValid );
392 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
397 QDialog::showEvent( event );
398 mTableName->setFocus();
399 mTableName->selectAll();
406 QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
407 : QStyledItemDelegate( parent )
408 , mTypeList( typeList )
413 QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
415 switch ( index.column() )
417 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
419 QComboBox *cbo =
new QComboBox { parent };
420 cbo->setEditable(
false );
421 cbo->setFrame(
false );
422 connect( cbo, qgis::overload<int>::of( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
423 for (
const auto &f : qgis::as_const( mTypeList ) )
425 cbo->addItem( f.mTypeDesc, f.mTypeName );
429 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
431 QSpinBox *sp {
new QSpinBox { parent } };
432 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
436 sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
440 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
442 QSpinBox *sp {
new QSpinBox { parent } };
443 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
447 sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
453 return QStyledItemDelegate::createEditor( parent, option, index );
458 void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
460 const auto m { index.model() };
461 switch ( index.column() )
463 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
465 const QString txt = m->data( index, Qt::DisplayRole ).toString();
466 QComboBox *cbo{ qobject_cast<QComboBox *>( editor ) };
469 cbo->setCurrentIndex( cbo->findText( txt ) );
473 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
474 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
476 const int value = m->data( index, Qt::DisplayRole ).toInt();
477 QSpinBox *sp{ qobject_cast<QSpinBox *>( editor ) };
480 sp->setValue( value );
486 QStyledItemDelegate::setEditorData( editor, index );
491 void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
493 switch ( index.column() )
495 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
497 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
500 model->setData( index, cbo->currentData() );
506 QStyledItemDelegate::setModelData( editor, model, index );
511 void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
514 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
517 emit commitData( cb );
521 QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
523 , mNativeTypes( typeList )
528 int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
533 QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
535 if ( mFields.exists( index.row() ) )
540 case Qt::ItemDataRole::DisplayRole:
542 switch (
static_cast<ColumnHeaders
>( index.column() ) )
544 case ColumnHeaders::Name:
548 case ColumnHeaders::Type:
552 case ColumnHeaders::ProviderType:
556 case ColumnHeaders::Comment:
560 case ColumnHeaders::Precision:
564 case ColumnHeaders::Length:
573 case Qt::ItemDataRole::TextAlignmentRole:
575 switch (
static_cast<ColumnHeaders
>( index.column() ) )
577 case ColumnHeaders::Precision:
578 case ColumnHeaders::Length:
580 return Qt::AlignmentFlag::AlignVCenter + Qt::AlignmentFlag::AlignHCenter;
589 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
599 QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
601 if ( orientation == Qt::Orientation::Horizontal )
605 case Qt::ItemDataRole::DisplayRole:
607 switch (
static_cast<ColumnHeaders
>( section ) )
609 case ColumnHeaders::Name:
613 case ColumnHeaders::Type:
617 case ColumnHeaders::Comment:
619 return tr(
"Comment" );
621 case ColumnHeaders::ProviderType:
623 return tr(
"Provider type" );
625 case ColumnHeaders::Length:
627 return tr(
"Length" );
629 case ColumnHeaders::Precision:
631 return tr(
"Precision" );
638 case Qt::ItemDataRole::TextAlignmentRole:
640 switch (
static_cast<ColumnHeaders
>( section ) )
642 case ColumnHeaders::Name:
643 case ColumnHeaders::Comment:
644 case ColumnHeaders::Type:
645 case ColumnHeaders::ProviderType:
647 return Qt::AlignmentFlag::AlignVCenter + Qt::AlignmentFlag::AlignLeft;
651 return Qt::AlignmentFlag::AlignVCenter + Qt::AlignmentFlag::AlignHCenter;
658 QgsFieldModel::headerData( section, orientation, role );
665 Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
667 switch (
static_cast<ColumnHeaders
>( index.column() ) )
669 case ColumnHeaders::Name:
670 case ColumnHeaders::Comment:
671 case ColumnHeaders::Type:
673 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
675 case ColumnHeaders::Length:
677 if ( mFields.exists( index.row( ) ) )
680 if ( nt.mMinLen < nt.mMaxLen )
682 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
687 case ColumnHeaders::Precision:
689 if ( mFields.exists( index.row( ) ) )
692 if ( nt.mMinPrec < nt.mMaxPrec )
694 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
699 case ColumnHeaders::ProviderType:
701 return QgsFieldModel::flags( index );
704 return QgsFieldModel::flags( index );
707 QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
712 QString QgsNewVectorTableFieldModel::typeDesc(
const QString &
typeName )
const
714 for (
const auto &t : qgis::as_const( mNativeTypes ) )
716 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
724 QVariant::Type QgsNewVectorTableFieldModel::type(
const QString &
typeName )
const
726 return nativeType(
typeName ).mType;
731 for (
const auto &t : qgis::as_const( mNativeTypes ) )
733 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
740 return mNativeTypes.first();
745 if ( mFields.exists( row ) )
747 return nativeType( mFields.at( row ).typeName() );
750 QgsDebugMsg( QStringLiteral(
"Cannot get field for row: %1" ).arg( row ) );
751 return mNativeTypes.first();
754 bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
756 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
758 const int fieldIdx { index.row() };
760 switch (
static_cast<ColumnHeaders
>( index.column() ) )
762 case ColumnHeaders::Name:
767 case ColumnHeaders::Type:
770 const auto tp { nativeType( value.toString() ) };
776 case ColumnHeaders::Comment:
781 case ColumnHeaders::ProviderType:
786 case ColumnHeaders::Length:
791 case ColumnHeaders::Precision:
799 for (
int i = 0; i < mFields.count(); ++i )
807 fields.
append( mFields.at( i ) );
812 return QgsFieldModel::setData( index, value, role );