23 #include <QMessageBox>
36 mFieldModel =
new QgsNewVectorTableFieldModel( mConnection->
nativeTypes(),
this );
40 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"
41 "Error message: %1" ).arg( ex.
what() ) );
42 QTimer::singleShot( 0,
this, [ = ] { reject(); } );
46 Q_ASSERT( ! mFieldModel->nativeTypes().isEmpty() );
49 setWindowTitle( tr(
"New Table" ) );
51 auto updateTableNames = [ = ](
const QString &schema = QString( ) )
56 const auto constTables { conn->
tables( schema ) };
57 for (
const auto &tp : constTables )
59 mTableNames.push_back( tp.tableName() );
66 QgsDebugMsg( QStringLiteral(
"Error retrieving tables from connection: %1" ).arg( ex.
what() ) );
71 connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset,
this, [ = ]()
76 mTableName->setText( QStringLiteral(
"new_table_name" ) );
77 mFieldsTableView->setModel( mFieldModel );
78 QgsNewVectorTableDialogFieldsDelegate *delegate {
new QgsNewVectorTableDialogFieldsDelegate( mConnection->
nativeTypes(),
this )};
79 mFieldsTableView->setItemDelegate( delegate );
80 mFieldsTableView->setSelectionBehavior( QAbstractItemView::SelectionBehavior::SelectRows );
81 mFieldsTableView->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
82 mFieldsTableView->setVerticalHeader(
nullptr );
85 mFieldsTableView->horizontalHeader()->setStretchLastSection(
true );
86 mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
89 if ( mConnection->
capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )
91 mSchemaCbo->addItems( mConnection->
schemas() );
92 connect( mSchemaCbo, &QComboBox::currentTextChanged,
this, [ = ](
const QString & schema )
94 updateTableNames( schema );
100 mSchemaLabel->hide();
103 if ( ! mConnection->
capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateSpatialIndex ) )
105 mSpatialIndexChk->setChecked(
false );
106 mSpatialIndexChk->hide();
107 mSpatialIndexLabel->hide();
111 updateTableNames( mSchemaCbo->currentText() );
114 connect( mTableName, &QLineEdit::textChanged,
this, [ = ](
const QString & )
119 connect( mGeomColumn, &QLineEdit::textChanged,
this, [ = ](
const QString & )
125 connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [ = ](
int index )
127 const bool hasGeom { index != 0 };
128 mGeomColumn->setEnabled( hasGeom );
129 mGeomColumnLabel->setEnabled( hasGeom );
130 mSpatialIndexChk->setEnabled( hasGeom );
131 mSpatialIndexLabel->setEnabled( hasGeom );
132 mCrs->setEnabled( hasGeom );
133 mCrsLabel->setEnabled( hasGeom );
134 mDimensionsLabel->setEnabled( hasGeom );
135 mHasMChk->setEnabled( hasGeom );
136 mHasZChk->setEnabled( hasGeom );
140 mCrs->setShowAccuracyWarnings(
true );
143 const bool hasSinglePart { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::SinglePart ) };
150 mGeomTypeCbo->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconTableLayer.svg" ) ), tr(
"No Geometry" ), QgsWkbTypes::Type::NoGeometry );
152 addGeomItem( QgsWkbTypes::Type::Point );
153 addGeomItem( QgsWkbTypes::Type::MultiPoint );
155 addGeomItem( QgsWkbTypes::Type::LineString );
156 addGeomItem( QgsWkbTypes::Type::MultiLineString );
158 addGeomItem( QgsWkbTypes::Type::Polygon );
159 addGeomItem( QgsWkbTypes::Type::MultiPolygon );
163 addGeomItem( QgsWkbTypes::Type::CompoundCurve );
164 addGeomItem( QgsWkbTypes::Type::CurvePolygon );
165 addGeomItem( QgsWkbTypes::Type::MultiCurve );
166 addGeomItem( QgsWkbTypes::Type::MultiSurface );
169 mGeomTypeCbo->setCurrentIndex( 0 );
171 const bool hasZ { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::Z ) };
172 const bool hasM { conn->
geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::M ) };
175 mHasMChk->setEnabled(
false );
176 mHasMChk->setChecked(
false );
180 mHasZChk->setEnabled(
false );
181 mHasZChk->setChecked(
false );
183 if ( ! hasM && ! hasM )
185 mHasZChk->setVisible(
false );
186 mHasMChk->setVisible(
false );
187 mDimensionsLabel->setVisible(
false );
190 connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [ = ](
const QItemSelection & selected,
const QItemSelection & )
192 if ( ! selected.isEmpty() )
194 mCurrentRow = selected.indexes().first().row();
200 const QVariant::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
201 const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
204 connect( mAddFieldBtn, &QPushButton::clicked,
this, [ = ]
207 QgsField newField { QStringLiteral(
"new_field_name" ), defaultFieldType, defaultFieldTypeName };
208 fieldList.append( newField );
209 setFields( fieldList );
210 selectRow( fieldList.count() - 1 );
213 connect( mDeleteFieldBtn, &QPushButton::clicked,
this, [ = ]
216 if ( fieldList.exists( mCurrentRow ) )
218 fieldList.
remove( mCurrentRow );
219 setFields( fieldList );
224 connect( mFieldUpBtn, &QPushButton::clicked,
this, [ = ]
226 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
229 for (
int i = 0; i < fields().count(); ++i )
231 if ( i == mCurrentRow - 1 )
233 fieldList.
append( fields().at( mCurrentRow ) );
234 fieldList.
append( fields().at( mCurrentRow - 1 ) );
236 else if ( i != mCurrentRow )
238 fieldList.
append( fields().at( i ) );
241 setFields( fieldList );
242 selectRow( mCurrentRow - 1 );
246 connect( mFieldDownBtn, &QPushButton::clicked,
this, [ = ]
248 if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
251 for (
int i = 0; i < fields().count(); ++i )
253 if ( i == mCurrentRow )
255 fieldList.
append( fields().at( mCurrentRow + 1 ) );
256 fieldList.
append( fields().at( mCurrentRow ) );
258 else if ( i != mCurrentRow + 1 )
260 fieldList.
append( fields().at( i ) );
263 setFields( fieldList );
264 selectRow( mCurrentRow + 1 );
274 mSchemaCbo->setCurrentText( name );
279 mTableName->setText( name );
284 mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData( type ) );
299 return mTableName->text();
304 return mSchemaCbo->currentText();
309 return mGeomColumn->text();
314 return mFieldModel ? mFieldModel->fields() :
QgsFields();
320 if ( mHasMChk->isChecked() )
324 if ( mHasZChk->isChecked() )
336 mFieldModel->setFields(
fields );
342 return mSpatialIndexChk->isChecked();
347 return mValidationErrors;
350 void QgsNewVectorTableDialog::updateButtons()
352 mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
353 mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
354 mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow !=
fields().count() - 1 );
357 void QgsNewVectorTableDialog::selectRow(
int row )
359 QModelIndex index { mFieldsTableView->model()->index( row, 0 ) };
360 mFieldsTableView->setCurrentIndex( index );
361 QItemSelectionModel::SelectionFlags flags { QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current };
362 mFieldsTableView->selectionModel()->select( index, flags );
363 mFieldsTableView->scrollTo( index );
366 void QgsNewVectorTableDialog::validate()
368 mValidationErrors.clear();
370 const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
371 if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
373 mValidationErrors.push_back( tr(
"Table <b>%1</b> already exists!" ).arg( mTableName->text() ) );
376 if ( isSpatial &&
fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
378 mValidationErrors.push_back( tr(
"Geometry column name <b>%1</b> cannot be equal to an existing field name!" ).arg( mGeomColumn->text() ) );
381 if ( ! isSpatial &&
fields().count() == 0 )
383 mValidationErrors.push_back( tr(
"The table has no geometry column and no fields!" ) );
386 const auto cFields {
fields() };
387 for (
const auto &f : cFields )
389 if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
391 mValidationErrors.push_back( tr(
"Field <b>%1</b>: precision cannot be greater than length!" ).arg( f.name() ) );
395 const bool isValid { mValidationErrors.isEmpty() };
398 mValidationResults->setText( mValidationErrors.join( QLatin1String(
"<br>" ) ) );
401 mValidationFrame->setVisible( ! isValid );
402 mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
407 QDialog::showEvent( event );
408 mTableName->setFocus();
409 mTableName->selectAll();
416 QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
417 : QStyledItemDelegate( parent )
418 , mTypeList( typeList )
423 QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
425 switch ( index.column() )
427 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
429 QComboBox *cbo =
new QComboBox { parent };
430 cbo->setEditable(
false );
431 cbo->setFrame(
false );
432 connect( cbo, qOverload<int>( &QComboBox::currentIndexChanged ),
this, &QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged );
433 for (
const auto &f : std::as_const( mTypeList ) )
435 cbo->addItem( f.mTypeDesc, f.mTypeName );
439 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
441 QSpinBox *sp {
new QSpinBox { parent } };
442 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
446 sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
450 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
452 QSpinBox *sp {
new QSpinBox { parent } };
453 const QgsNewVectorTableFieldModel *model {
static_cast<const QgsNewVectorTableFieldModel *
>( index.model() )};
457 sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
463 return QStyledItemDelegate::createEditor( parent, option, index );
468 void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
470 const auto m { index.model() };
471 switch ( index.column() )
473 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
475 const QString txt = m->data( index, Qt::DisplayRole ).toString();
476 QComboBox *cbo{ qobject_cast<QComboBox *>( editor ) };
479 cbo->setCurrentIndex( cbo->findText( txt ) );
483 case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
484 case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
486 const int value = m->data( index, Qt::DisplayRole ).toInt();
487 QSpinBox *sp{ qobject_cast<QSpinBox *>( editor ) };
490 sp->setValue( value );
496 QStyledItemDelegate::setEditorData( editor, index );
501 void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
503 switch ( index.column() )
505 case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
507 QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
510 model->setData( index, cbo->currentData() );
516 QStyledItemDelegate::setModelData( editor, model, index );
521 void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged(
int index )
524 QComboBox *cb =
static_cast<QComboBox *
>( sender() );
527 emit commitData( cb );
531 QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel(
const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
533 , mNativeTypes( typeList )
538 int QgsNewVectorTableFieldModel::columnCount(
const QModelIndex & )
const
543 QVariant QgsNewVectorTableFieldModel::data(
const QModelIndex &index,
int role )
const
545 if ( mFields.exists( index.row() ) )
550 case Qt::ItemDataRole::DisplayRole:
552 switch (
static_cast<ColumnHeaders
>( index.column() ) )
554 case ColumnHeaders::Name:
558 case ColumnHeaders::Type:
562 case ColumnHeaders::ProviderType:
566 case ColumnHeaders::Comment:
570 case ColumnHeaders::Precision:
574 case ColumnHeaders::Length:
583 case Qt::ItemDataRole::TextAlignmentRole:
585 switch (
static_cast<ColumnHeaders
>( index.column() ) )
587 case ColumnHeaders::Precision:
588 case ColumnHeaders::Length:
590 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
599 if (
static_cast<ColumnHeaders
>( index.column() ) == ColumnHeaders::Name )
609 QVariant QgsNewVectorTableFieldModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
611 if ( orientation == Qt::Orientation::Horizontal )
615 case Qt::ItemDataRole::DisplayRole:
617 switch (
static_cast<ColumnHeaders
>( section ) )
619 case ColumnHeaders::Name:
623 case ColumnHeaders::Type:
627 case ColumnHeaders::Comment:
629 return tr(
"Comment" );
631 case ColumnHeaders::ProviderType:
633 return tr(
"Provider type" );
635 case ColumnHeaders::Length:
637 return tr(
"Length" );
639 case ColumnHeaders::Precision:
641 return tr(
"Precision" );
648 case Qt::ItemDataRole::TextAlignmentRole:
650 switch (
static_cast<ColumnHeaders
>( section ) )
652 case ColumnHeaders::Name:
653 case ColumnHeaders::Comment:
654 case ColumnHeaders::Type:
655 case ColumnHeaders::ProviderType:
657 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
661 return static_cast<Qt::Alignment::Int
>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
668 QgsFieldModel::headerData( section, orientation, role );
675 Qt::ItemFlags QgsNewVectorTableFieldModel::flags(
const QModelIndex &index )
const
677 switch (
static_cast<ColumnHeaders
>( index.column() ) )
679 case ColumnHeaders::Name:
680 case ColumnHeaders::Comment:
681 case ColumnHeaders::Type:
683 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
685 case ColumnHeaders::Length:
687 if ( mFields.exists( index.row( ) ) )
690 if ( nt.mMinLen < nt.mMaxLen )
692 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
697 case ColumnHeaders::Precision:
699 if ( mFields.exists( index.row( ) ) )
702 if ( nt.mMinPrec < nt.mMaxPrec )
704 return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
709 case ColumnHeaders::ProviderType:
711 return QgsFieldModel::flags( index );
714 return QgsFieldModel::flags( index );
717 QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes()
const
722 QString QgsNewVectorTableFieldModel::typeDesc(
const QString &
typeName )
const
724 for (
const auto &t : std::as_const( mNativeTypes ) )
726 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
734 QVariant::Type QgsNewVectorTableFieldModel::type(
const QString &
typeName )
const
736 return nativeType(
typeName ).mType;
741 for (
const auto &t : std::as_const( mNativeTypes ) )
743 if ( t.mTypeName.compare(
typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
750 return mNativeTypes.first();
755 if ( mFields.exists( row ) )
757 return nativeType( mFields.at( row ).typeName() );
760 QgsDebugMsg( QStringLiteral(
"Cannot get field for row: %1" ).arg( row ) );
761 return mNativeTypes.first();
764 bool QgsNewVectorTableFieldModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
766 if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
768 const int fieldIdx { index.row() };
770 switch (
static_cast<ColumnHeaders
>( index.column() ) )
772 case ColumnHeaders::Name:
777 case ColumnHeaders::Type:
780 const auto tp { nativeType( value.toString() ) };
786 case ColumnHeaders::Comment:
791 case ColumnHeaders::ProviderType:
796 case ColumnHeaders::Length:
801 case ColumnHeaders::Precision:
809 for (
int i = 0; i < mFields.count(); ++i )
817 fields.
append( mFields.at( i ) );
822 return QgsFieldModel::setData( index, value, role );