QGIS API Documentation  3.23.0-Master (eb871beae0)
qgsnewvectortabledialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnewvectortabledialog.cpp - QgsNewVectorTableDialog
3 
4  ---------------------
5  begin : 12.7.2020
6  copyright : (C) 2020 by Alessandro Pasotti
7  email : elpaso at itopen dot it
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 #include "qgsvectorlayer.h"
18 #include "qgslogger.h"
19 #include "qgsgui.h"
20 #include "qgsapplication.h"
21 #include "qgsiconutils.h"
22 #include <QSpinBox>
23 #include <QMessageBox>
24 #include <QTimer>
25 
27  : QDialog( parent )
28  , mConnection( conn )
29 {
30 
31  setupUi( this );
32 
33  // This is a precondition for the dialog to work correctly
34  try
35  {
36  mFieldModel = new QgsNewVectorTableFieldModel( mConnection->nativeTypes(), this );
37  }
38  catch ( QgsProviderConnectionException &ex )
39  {
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, [ = ] { reject(); } );
43  return;
44  }
45 
46  Q_ASSERT( ! mFieldModel->nativeTypes().isEmpty() );
47 
49  setWindowTitle( tr( "New Table" ) );
50 
51  auto updateTableNames = [ = ]( const QString &schema = QString( ) )
52  {
53  mTableNames.clear();
54  try
55  {
56  const auto constTables { conn->tables( schema ) };
57  for ( const auto &tp : constTables )
58  {
59  mTableNames.push_back( tp.tableName() );
60  }
61  validate();
62  }
63  catch ( QgsProviderConnectionException &ex )
64  {
65  // This should never happen but it's not critical, we can safely continue.
66  QgsDebugMsg( QStringLiteral( "Error retrieving tables from connection: %1" ).arg( ex.what() ) );
67  }
68  };
69 
70  // Validate on data changed
71  connect( mFieldModel, &QgsNewVectorTableFieldModel::modelReset, this, [ = ]()
72  {
73  validate();
74  } );
75 
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 );
83 
84  // Cosmetics
85  mFieldsTableView->horizontalHeader()->setStretchLastSection( true );
86  mFieldsTableView->setColumnWidth( QgsNewVectorTableFieldModel::ColumnHeaders::Type, 300 );
87 
88  // Schema is not supported by all providers
89  if ( mConnection->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) )
90  {
91  mSchemaCbo->addItems( mConnection->schemas() );
92  connect( mSchemaCbo, &QComboBox::currentTextChanged, this, [ = ]( const QString & schema )
93  {
94  updateTableNames( schema );
95  } );
96  }
97  else
98  {
99  mSchemaCbo->hide();
100  mSchemaLabel->hide();
101  }
102 
103  if ( ! mConnection->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::CreateSpatialIndex ) )
104  {
105  mSpatialIndexChk->setChecked( false );
106  mSpatialIndexChk->hide();
107  mSpatialIndexLabel->hide();
108  }
109 
110  // Initial load of table names
111  updateTableNames( mSchemaCbo->currentText() );
112 
113  // Validators
114  connect( mTableName, &QLineEdit::textChanged, this, [ = ]( const QString & )
115  {
116  validate();
117  } );
118 
119  connect( mGeomColumn, &QLineEdit::textChanged, this, [ = ]( const QString & )
120  {
121  validate();
122  } );
123 
124  // Enable/disable geometry options and call validate
125  connect( mGeomTypeCbo, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
126  {
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 );
137  validate();
138  } );
139 
140  mCrs->setShowAccuracyWarnings( true );
141 
142  // geometry types
143  const bool hasSinglePart { conn->geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::SinglePart ) };
144 
145  const auto addGeomItem = [this]( QgsWkbTypes::Type type )
146  {
147  mGeomTypeCbo->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), type );
148  };
149 
150  mGeomTypeCbo->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconTableLayer.svg" ) ), tr( "No Geometry" ), QgsWkbTypes::Type::NoGeometry );
151  if ( hasSinglePart )
152  addGeomItem( QgsWkbTypes::Type::Point );
153  addGeomItem( QgsWkbTypes::Type::MultiPoint );
154  if ( hasSinglePart )
155  addGeomItem( QgsWkbTypes::Type::LineString );
156  addGeomItem( QgsWkbTypes::Type::MultiLineString );
157  if ( hasSinglePart )
158  addGeomItem( QgsWkbTypes::Type::Polygon );
159  addGeomItem( QgsWkbTypes::Type::MultiPolygon );
160 
161  if ( conn->geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::Curves ) )
162  {
163  addGeomItem( QgsWkbTypes::Type::CompoundCurve );
164  addGeomItem( QgsWkbTypes::Type::CurvePolygon );
165  addGeomItem( QgsWkbTypes::Type::MultiCurve );
166  addGeomItem( QgsWkbTypes::Type::MultiSurface );
167  }
168 
169  mGeomTypeCbo->setCurrentIndex( 0 );
170 
171  const bool hasZ { conn->geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::Z ) };
172  const bool hasM { conn->geometryColumnCapabilities().testFlag( QgsAbstractDatabaseProviderConnection::GeometryColumnCapability::M ) };
173  if ( ! hasM )
174  {
175  mHasMChk->setEnabled( false );
176  mHasMChk->setChecked( false );
177  }
178  if ( ! hasZ )
179  {
180  mHasZChk->setEnabled( false );
181  mHasZChk->setChecked( false );
182  }
183  if ( ! hasM && ! hasM )
184  {
185  mHasZChk->setVisible( false );
186  mHasMChk->setVisible( false );
187  mDimensionsLabel->setVisible( false );
188  }
189 
190  connect( mFieldsTableView->selectionModel(), &QItemSelectionModel::selectionChanged, mFieldsTableView, [ = ]( const QItemSelection & selected, const QItemSelection & )
191  {
192  if ( ! selected.isEmpty() )
193  {
194  mCurrentRow = selected.indexes().first().row();
195  }
196  updateButtons();
197  } );
198 
199  // Get a default type for new fields
200  const QVariant::Type defaultFieldType { mFieldModel->nativeTypes().first().mType };
201  const QString defaultFieldTypeName { mFieldModel->nativeTypes().first().mTypeName };
202 
203  // Actions
204  connect( mAddFieldBtn, &QPushButton::clicked, this, [ = ]
205  {
206  QgsFields fieldList { fields() };
207  QgsField newField { QStringLiteral( "new_field_name" ), defaultFieldType, defaultFieldTypeName };
208  fieldList.append( newField );
209  setFields( fieldList );
210  selectRow( fieldList.count() - 1 );
211  } );
212 
213  connect( mDeleteFieldBtn, &QPushButton::clicked, this, [ = ]
214  {
215  QgsFields fieldList { fields() };
216  if ( fieldList.exists( mCurrentRow ) )
217  {
218  fieldList.remove( mCurrentRow );
219  setFields( fieldList );
220  mCurrentRow = -1;
221  }
222  } );
223 
224  connect( mFieldUpBtn, &QPushButton::clicked, this, [ = ]
225  {
226  if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow - 1 ) )
227  {
228  QgsFields fieldList;
229  for ( int i = 0; i < fields().count(); ++i )
230  {
231  if ( i == mCurrentRow - 1 )
232  {
233  fieldList.append( fields().at( mCurrentRow ) );
234  fieldList.append( fields().at( mCurrentRow - 1 ) );
235  }
236  else if ( i != mCurrentRow )
237  {
238  fieldList.append( fields().at( i ) );
239  }
240  }
241  setFields( fieldList );
242  selectRow( mCurrentRow - 1 );
243  }
244  } );
245 
246  connect( mFieldDownBtn, &QPushButton::clicked, this, [ = ]
247  {
248  if ( fields().exists( mCurrentRow ) && fields().exists( mCurrentRow + 1 ) )
249  {
250  QgsFields fieldList;
251  for ( int i = 0; i < fields().count(); ++i )
252  {
253  if ( i == mCurrentRow )
254  {
255  fieldList.append( fields().at( mCurrentRow + 1 ) );
256  fieldList.append( fields().at( mCurrentRow ) );
257  }
258  else if ( i != mCurrentRow + 1 )
259  {
260  fieldList.append( fields().at( i ) );
261  }
262  }
263  setFields( fieldList );
264  selectRow( mCurrentRow + 1 );
265  }
266  } );
267 
268  updateButtons();
269  validate();
270 }
271 
272 void QgsNewVectorTableDialog::setSchemaName( const QString &name )
273 {
274  mSchemaCbo->setCurrentText( name );
275 }
276 
277 void QgsNewVectorTableDialog::setTableName( const QString &name )
278 {
279  mTableName->setText( name );
280 }
281 
283 {
284  mGeomTypeCbo->setCurrentIndex( mGeomTypeCbo->findData( type ) );
285 }
286 
288 {
289  mCrs->setCrs( crs );
290 }
291 
293 {
294  return mCrs->crs( );
295 }
296 
298 {
299  return mTableName->text();
300 }
301 
303 {
304  return mSchemaCbo->currentText();
305 }
306 
308 {
309  return mGeomColumn->text();
310 }
311 
313 {
314  return mFieldModel ? mFieldModel->fields() : QgsFields();
315 }
316 
318 {
319  QgsWkbTypes::Type type { static_cast<QgsWkbTypes::Type>( mGeomTypeCbo->currentData( ).toInt() ) };
320  if ( mHasMChk->isChecked() )
321  {
322  type = QgsWkbTypes::addM( type );
323  }
324  if ( mHasZChk->isChecked() )
325  {
326  type = QgsWkbTypes::addZ( type );
327  }
328  return type;
329 }
330 
331 
333 {
334  if ( mFieldModel )
335  {
336  mFieldModel->setFields( fields );
337  }
338 }
339 
341 {
342  return mSpatialIndexChk->isChecked();
343 }
344 
346 {
347  return mValidationErrors;
348 }
349 
350 void QgsNewVectorTableDialog::updateButtons()
351 {
352  mDeleteFieldBtn->setEnabled( mCurrentRow != -1 );
353  mFieldUpBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != 0 );
354  mFieldDownBtn->setEnabled( mCurrentRow != -1 && mCurrentRow != fields().count() - 1 );
355 }
356 
357 void QgsNewVectorTableDialog::selectRow( int row )
358 {
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 );
364 }
365 
366 void QgsNewVectorTableDialog::validate()
367 {
368  mValidationErrors.clear();
369 
370  const bool isSpatial { mGeomTypeCbo->currentIndex() > 0 };
371  if ( mTableNames.contains( mTableName->text(), Qt::CaseSensitivity::CaseInsensitive ) )
372  {
373  mValidationErrors.push_back( tr( "Table <b>%1</b> already exists!" ).arg( mTableName->text() ) );
374  }
375  // Check for field names and geom col name
376  if ( isSpatial && fields().names().contains( mGeomColumn->text(), Qt::CaseSensitivity::CaseInsensitive ) )
377  {
378  mValidationErrors.push_back( tr( "Geometry column name <b>%1</b> cannot be equal to an existing field name!" ).arg( mGeomColumn->text() ) );
379  }
380  // No geometry and no fields? No party!
381  if ( ! isSpatial && fields().count() == 0 )
382  {
383  mValidationErrors.push_back( tr( "The table has no geometry column and no fields!" ) );
384  }
385  // Check if precision is <= length
386  const auto cFields { fields() };
387  for ( const auto &f : cFields )
388  {
389  if ( f.isNumeric() && f.length() >= 0 && f.precision() >= 0 && f.precision() > f.length() )
390  {
391  mValidationErrors.push_back( tr( "Field <b>%1</b>: precision cannot be greater than length!" ).arg( f.name() ) );
392  }
393  }
394 
395  const bool isValid { mValidationErrors.isEmpty() };
396  if ( ! isValid )
397  {
398  mValidationResults->setText( mValidationErrors.join( QLatin1String( "<br>" ) ) );
399  }
400 
401  mValidationFrame->setVisible( ! isValid );
402  mButtonBox->button( QDialogButtonBox::StandardButton::Ok )->setEnabled( isValid );
403 }
404 
405 void QgsNewVectorTableDialog::showEvent( QShowEvent *event )
406 {
407  QDialog::showEvent( event );
408  mTableName->setFocus();
409  mTableName->selectAll();
410 }
411 
412 
414 
415 
416 QgsNewVectorTableDialogFieldsDelegate::QgsNewVectorTableDialogFieldsDelegate( const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
417  : QStyledItemDelegate( parent )
418  , mTypeList( typeList )
419 {
420 
421 }
422 
423 QWidget *QgsNewVectorTableDialogFieldsDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
424 {
425  switch ( index.column() )
426  {
427  case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
428  {
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 ) )
434  {
435  cbo->addItem( f.mTypeDesc, f.mTypeName );
436  }
437  return cbo;
438  }
439  case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
440  {
441  QSpinBox *sp { new QSpinBox { parent } };
442  const QgsNewVectorTableFieldModel *model { static_cast<const QgsNewVectorTableFieldModel *>( index.model() )};
443  if ( model )
444  {
445  const QgsVectorDataProvider::NativeType nt { model->nativeType( index.row() ) };
446  sp->setRange( nt.mMinPrec, std::min<int>( nt.mMaxPrec, index.model()->data( index.model()->index( index.row(), index.column() - 1 ) ).toInt() ) );
447  }
448  return sp;
449  }
450  case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
451  {
452  QSpinBox *sp { new QSpinBox { parent } };
453  const QgsNewVectorTableFieldModel *model { static_cast<const QgsNewVectorTableFieldModel *>( index.model() )};
454  if ( model )
455  {
456  const QgsVectorDataProvider::NativeType nt { model->nativeType( index.row() ) };
457  sp->setRange( std::max<int>( nt.mMinLen, index.model()->data( index.model()->index( index.row(), index.column() + 1 ) ).toInt() ), nt.mMaxLen );
458  }
459  return sp;
460  }
461  default:
462  {
463  return QStyledItemDelegate::createEditor( parent, option, index );
464  }
465  }
466 }
467 
468 void QgsNewVectorTableDialogFieldsDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
469 {
470  const auto m { index.model() };
471  switch ( index.column() )
472  {
473  case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
474  {
475  const QString txt = m->data( index, Qt::DisplayRole ).toString();
476  QComboBox *cbo{ qobject_cast<QComboBox *>( editor ) };
477  if ( cbo )
478  {
479  cbo->setCurrentIndex( cbo->findText( txt ) );
480  }
481  break;
482  }
483  case QgsNewVectorTableFieldModel::ColumnHeaders::Precision:
484  case QgsNewVectorTableFieldModel::ColumnHeaders::Length:
485  {
486  const int value = m->data( index, Qt::DisplayRole ).toInt();
487  QSpinBox *sp{ qobject_cast<QSpinBox *>( editor ) };
488  if ( sp )
489  {
490  sp->setValue( value );
491  }
492  break;
493  }
494  default:
495  {
496  QStyledItemDelegate::setEditorData( editor, index );
497  }
498  }
499 }
500 
501 void QgsNewVectorTableDialogFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
502 {
503  switch ( index.column() )
504  {
505  case QgsNewVectorTableFieldModel::ColumnHeaders::Type:
506  {
507  QComboBox *cbo { qobject_cast<QComboBox *>( editor ) };
508  if ( cbo )
509  {
510  model->setData( index, cbo->currentData() );
511  }
512  break;
513  }
514  default:
515  {
516  QStyledItemDelegate::setModelData( editor, model, index );
517  }
518  }
519 }
520 
521 void QgsNewVectorTableDialogFieldsDelegate::onFieldTypeChanged( int index )
522 {
523  Q_UNUSED( index )
524  QComboBox *cb = static_cast<QComboBox *>( sender() );
525  if ( cb )
526  {
527  emit commitData( cb );
528  }
529 }
530 
531 QgsNewVectorTableFieldModel::QgsNewVectorTableFieldModel( const QList<QgsVectorDataProvider::NativeType> &typeList, QObject *parent )
532  : QgsFieldModel( parent )
533  , mNativeTypes( typeList )
534 {
535 
536 }
537 
538 int QgsNewVectorTableFieldModel::columnCount( const QModelIndex & ) const
539 {
540  return 6;
541 }
542 
543 QVariant QgsNewVectorTableFieldModel::data( const QModelIndex &index, int role ) const
544 {
545  if ( mFields.exists( index.row() ) )
546  {
547  const QgsField field { mFields.at( index.row() ) };
548  switch ( role )
549  {
550  case Qt::ItemDataRole::DisplayRole:
551  {
552  switch ( static_cast<ColumnHeaders>( index.column() ) )
553  {
554  case ColumnHeaders::Name:
555  {
556  return QgsFieldModel::data( index, role );
557  }
558  case ColumnHeaders::Type:
559  {
560  return typeDesc( field.typeName() );
561  }
562  case ColumnHeaders::ProviderType:
563  {
564  return field.typeName();
565  }
566  case ColumnHeaders::Comment:
567  {
568  return field.comment();
569  }
570  case ColumnHeaders::Precision:
571  {
572  return field.precision();
573  }
574  case ColumnHeaders::Length:
575  {
576  return field.length();
577  }
578  default:
579  break;
580  }
581  return QgsFieldModel::data( index, role );
582  }
583  case Qt::ItemDataRole::TextAlignmentRole:
584  {
585  switch ( static_cast<ColumnHeaders>( index.column() ) )
586  {
587  case ColumnHeaders::Precision:
588  case ColumnHeaders::Length:
589  {
590  return static_cast<Qt::Alignment::Int>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
591  }
592  default:
593  break;
594  }
595  return QgsFieldModel::data( index, role );
596  }
597  default:
598  {
599  if ( static_cast<ColumnHeaders>( index.column() ) == ColumnHeaders::Name )
600  {
601  return QgsFieldModel::data( index, role );
602  }
603  }
604  }
605  }
606  return QVariant();
607 }
608 
609 QVariant QgsNewVectorTableFieldModel::headerData( int section, Qt::Orientation orientation, int role ) const
610 {
611  if ( orientation == Qt::Orientation::Horizontal )
612  {
613  switch ( role )
614  {
615  case Qt::ItemDataRole::DisplayRole:
616  {
617  switch ( static_cast<ColumnHeaders>( section ) )
618  {
619  case ColumnHeaders::Name:
620  {
621  return tr( "Name" );
622  }
623  case ColumnHeaders::Type:
624  {
625  return tr( "Type" );
626  }
627  case ColumnHeaders::Comment:
628  {
629  return tr( "Comment" );
630  }
631  case ColumnHeaders::ProviderType:
632  {
633  return tr( "Provider type" );
634  }
635  case ColumnHeaders::Length:
636  {
637  return tr( "Length" );
638  }
639  case ColumnHeaders::Precision:
640  {
641  return tr( "Precision" );
642  }
643  default:
644  return QVariant();
645  }
646  break;
647  }
648  case Qt::ItemDataRole::TextAlignmentRole:
649  {
650  switch ( static_cast<ColumnHeaders>( section ) )
651  {
652  case ColumnHeaders::Name:
653  case ColumnHeaders::Comment:
654  case ColumnHeaders::Type:
655  case ColumnHeaders::ProviderType:
656  {
657  return static_cast<Qt::Alignment::Int>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft );
658  }
659  default:
660  {
661  return static_cast<Qt::Alignment::Int>( Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter );
662  }
663  }
664  break;
665  }
666  default:
667  {
668  QgsFieldModel::headerData( section, orientation, role );
669  }
670  }
671  }
672  return QVariant();
673 }
674 
675 Qt::ItemFlags QgsNewVectorTableFieldModel::flags( const QModelIndex &index ) const
676 {
677  switch ( static_cast<ColumnHeaders>( index.column() ) )
678  {
679  case ColumnHeaders::Name:
680  case ColumnHeaders::Comment:
681  case ColumnHeaders::Type:
682  {
683  return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
684  }
685  case ColumnHeaders::Length:
686  {
687  if ( mFields.exists( index.row( ) ) )
688  {
689  const QgsVectorDataProvider::NativeType nt { nativeType( mFields.at( index.row( ) ).typeName() ) };
690  if ( nt.mMinLen < nt.mMaxLen )
691  {
692  return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
693  }
694  }
695  break;
696  }
697  case ColumnHeaders::Precision:
698  {
699  if ( mFields.exists( index.row( ) ) )
700  {
701  const QgsVectorDataProvider::NativeType nt { nativeType( mFields.at( index.row( ) ).typeName() ) };
702  if ( nt.mMinPrec < nt.mMaxPrec )
703  {
704  return QgsFieldModel::flags( index ) | Qt::ItemIsEditable;
705  }
706  }
707  break;
708  }
709  case ColumnHeaders::ProviderType:
710  {
711  return QgsFieldModel::flags( index );
712  }
713  }
714  return QgsFieldModel::flags( index );
715 }
716 
717 QList<QgsVectorDataProvider::NativeType> QgsNewVectorTableFieldModel::nativeTypes() const
718 {
719  return mNativeTypes;
720 }
721 
722 QString QgsNewVectorTableFieldModel::typeDesc( const QString &typeName ) const
723 {
724  for ( const auto &t : std::as_const( mNativeTypes ) )
725  {
726  if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
727  {
728  return t.mTypeDesc;
729  }
730  }
731  return typeName;
732 }
733 
734 QVariant::Type QgsNewVectorTableFieldModel::type( const QString &typeName ) const
735 {
736  return nativeType( typeName ).mType;
737 }
738 
739 QgsVectorDataProvider::NativeType QgsNewVectorTableFieldModel::nativeType( const QString &typeName ) const
740 {
741  for ( const auto &t : std::as_const( mNativeTypes ) )
742  {
743  if ( t.mTypeName.compare( typeName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
744  {
745  return t;
746  }
747  }
748  // This should never happen!
749  QgsDebugMsg( QStringLiteral( "Cannot get field native type for: %1" ).arg( typeName ) );
750  return mNativeTypes.first();
751 }
752 
753 QgsVectorDataProvider::NativeType QgsNewVectorTableFieldModel::nativeType( int row ) const
754 {
755  if ( mFields.exists( row ) )
756  {
757  return nativeType( mFields.at( row ).typeName() );
758  }
759  // This should never happen!
760  QgsDebugMsg( QStringLiteral( "Cannot get field for row: %1" ).arg( row ) );
761  return mNativeTypes.first();
762 }
763 
764 bool QgsNewVectorTableFieldModel::setData( const QModelIndex &index, const QVariant &value, int role )
765 {
766  if ( role == Qt::ItemDataRole::EditRole && mFields.exists( index.row() ) && index.column() < 6 )
767  {
768  const int fieldIdx { index.row() };
769  QgsField field {mFields.at( fieldIdx )};
770  switch ( static_cast<ColumnHeaders>( index.column() ) )
771  {
772  case ColumnHeaders::Name:
773  {
774  field.setName( value.toString() );
775  break;
776  }
777  case ColumnHeaders::Type:
778  {
779  field.setTypeName( value.toString() );
780  const auto tp { nativeType( value.toString() ) };
781  field.setType( tp.mType );
782  field.setLength( std::max( std::min<int>( field.length(), tp.mMaxLen ), tp.mMinLen ) );
783  field.setPrecision( std::max( std::min<int>( field.precision(), tp.mMaxPrec ), tp.mMinPrec ) );
784  break;
785  }
786  case ColumnHeaders::Comment:
787  {
788  field.setComment( value.toString() );
789  break;
790  }
791  case ColumnHeaders::ProviderType:
792  {
793  field.setTypeName( value.toString() );
794  break;
795  }
796  case ColumnHeaders::Length:
797  {
798  field.setLength( value.toInt() );
799  break;
800  }
801  case ColumnHeaders::Precision:
802  {
803  field.setPrecision( value.toInt() );
804  break;
805  }
806  }
807 
808  QgsFields fields;
809  for ( int i = 0; i < mFields.count(); ++i )
810  {
811  if ( i == fieldIdx )
812  {
813  fields.append( field );
814  }
815  else
816  {
817  fields.append( mFields.at( i ) );
818  }
819  }
820  setFields( fields );
821  }
822  return QgsFieldModel::setData( index, value, role );
823 }
824 
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
virtual GeometryColumnCapabilities geometryColumnCapabilities()
Returns connection geometry column capabilities (Z, M, SinglePart, Curves).
virtual QList< QgsVectorDataProvider::NativeType > nativeTypes() const SIP_THROW(QgsProviderConnectionException)=0
Returns a list of native types supported by the connection.
virtual QStringList schemas() const SIP_THROW(QgsProviderConnectionException)
Returns information about the existing schemas.
virtual QList< QgsAbstractDatabaseProviderConnection::TableProperty > tables(const QString &schema=QString(), const QgsAbstractDatabaseProviderConnection::TableFlags &flags=QgsAbstractDatabaseProviderConnection::TableFlags()) const
Returns information on the tables in the given schema.
Capabilities capabilities() const
Returns connection capabilities.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class represents a coordinate reference system (CRS).
QString what() const
Definition: qgsexception.h:48
The QgsFieldModel class is a model to display the list of fields in widgets (optionally associated wi...
Definition: qgsfieldmodel.h:39
QVariant data(const QModelIndex &index, int role) const override
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
void setPrecision(int precision)
Set the field precision.
Definition: qgsfield.cpp:199
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:175
void setComment(const QString &comment)
Set the field comment.
Definition: qgsfield.cpp:204
void setLength(int len)
Set the field length.
Definition: qgsfield.cpp:195
void setType(QVariant::Type type)
Set variant type.
Definition: qgsfield.cpp:180
QString comment
Definition: qgsfield.h:59
void setTypeName(const QString &typeName)
Set the field type.
Definition: qgsfield.cpp:190
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
void remove(int fieldIdx)
Removes the field with the given index.
Definition: qgsfields.cpp:101
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:168
static QIcon iconForWkbType(QgsWkbTypes::Type type)
Returns the icon for a vector layer whose geometry type is provided.
QString schemaName() const
Returns the schema name.
bool createSpatialIndex()
Returns true if spatialindex checkbox is cheched.
QgsFields fields() const
Returns the fields.
QStringList validationErrors() const
Returns the validation errors or an empty list if the dialog is valid.
void setGeometryType(QgsWkbTypes::Type type)
Sets the geometry type.
void setFields(const QgsFields &fields)
Sets the fields to fields.
QgsCoordinateReferenceSystem crs() const
Returns the CRS.
QString tableName() const
Returns the table name.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS to crs.
QString geometryColumnName() const
Returns the geometry column name.
void setSchemaName(const QString &name)
Sets the schema name.
QgsWkbTypes::Type geometryType() const
Returns the geometry type.
QgsNewVectorTableDialog(QgsAbstractDatabaseProviderConnection *conn, QWidget *parent=nullptr)
QgsNewVectorTableDialog constructor.
void setTableName(const QString &name)
Sets the table name.
void showEvent(QShowEvent *event) override
Custom exception class for provider connection related exceptions.
Definition: qgsexception.h:101
static QString translatedDisplayString(Type type) SIP_HOLDGIL
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
const QString & typeName