17#include "moc_qgsvaluemapconfigdlg.cpp" 
   30#include <QRegularExpression> 
   37  mValueMapErrorsLabel->setVisible( 
false );
 
   38  mValueMapErrorsLabel->setStyleSheet( QStringLiteral( 
"QLabel { color : red; }" ) );
 
   40  tableWidget->insertRow( 0 );
 
   42  tableWidget->horizontalHeader()->setSectionsClickable( 
true );
 
   43  tableWidget->setSortingEnabled( 
true );
 
   45  connect( addNullButton, &QAbstractButton::clicked, 
this, &QgsValueMapConfigDlg::addNullButtonPushed );
 
   46  connect( removeSelectedButton, &QAbstractButton::clicked, 
this, &QgsValueMapConfigDlg::removeSelectedButtonPushed );
 
   47  connect( loadFromLayerButton, &QAbstractButton::clicked, 
this, &QgsValueMapConfigDlg::loadFromLayerButtonPushed );
 
   48  connect( loadFromCSVButton, &QAbstractButton::clicked, 
this, &QgsValueMapConfigDlg::loadFromCSVButtonPushed );
 
   49  connect( tableWidget, &QTableWidget::cellChanged, 
this, &QgsValueMapConfigDlg::vCellChanged );
 
   50  tableWidget->installEventFilter( 
this );
 
 
   55  QList<QVariant> valueList;
 
   58  for ( 
int i = 0; i < tableWidget->rowCount() - 1; i++ )
 
   60    QTableWidgetItem *ki = tableWidget->item( i, 0 );
 
   61    QTableWidgetItem *vi = tableWidget->item( i, 1 );
 
   66    QString ks = ki->text();
 
   72    if ( !vi || vi->text().isNull() )
 
   74      value.insert( ks, ks );
 
   78      value.insert( vi->text(), ks );
 
   80    valueList.append( value );
 
   84  cfg.insert( QStringLiteral( 
"map" ), valueList );
 
 
   90  tableWidget->clearContents();
 
   91  for ( 
int i = tableWidget->rowCount() - 1; i > 0; i-- )
 
   93    tableWidget->removeRow( i );
 
   96  QList<QVariant> valueList = 
config.value( QStringLiteral( 
"map" ) ).toList();
 
   97  QList<QPair<QString, QVariant>> orderedList;
 
   99  if ( valueList.count() > 0 )
 
  101    for ( 
int i = 0, row = 0; i < valueList.count(); i++, row++ )
 
  103      orderedList.append( qMakePair( valueList[i].toMap().constBegin().value().toString(), valueList[i].toMap().constBegin().key() ) );
 
  109    const QVariantMap values = 
config.value( QStringLiteral( 
"map" ) ).toMap();
 
  110    for ( QVariantMap::ConstIterator mit = values.constBegin(); mit != values.constEnd(); mit++, row++ )
 
  113        orderedList.append( qMakePair( mit.key(), QVariant() ) );
 
  115        orderedList.append( qMakePair( mit.value().toString(), mit.key() ) );
 
 
  122void QgsValueMapConfigDlg::vCellChanged( 
int row, 
int column )
 
  125  if ( row == tableWidget->rowCount() - 1 )
 
  127    tableWidget->insertRow( row + 1 );
 
  133    QTableWidgetItem *item = tableWidget->item( row, 0 );
 
  136      const QString validValue = checkValueLength( item->text() );
 
  137      if ( validValue.length() != item->text().length() )
 
  139        const QString errorMessage = tr( 
"Value '%1' has been trimmed (maximum field length: %2)" )
 
  140                                       .arg( item->text(), QString::number( 
layer()->fields().
field( 
field() ).length() ) );
 
  141        item->setText( validValue );
 
  142        mValueMapErrorsLabel->setVisible( 
true );
 
  143        mValueMapErrorsLabel->setText( QStringLiteral( 
"%1<br>%2" ).arg( errorMessage, mValueMapErrorsLabel->text() ) );
 
  151void QgsValueMapConfigDlg::removeSelectedButtonPushed()
 
  153  QList<QTableWidgetItem *> list = tableWidget->selectedItems();
 
  154  QSet<int> rowsToRemove;
 
  157  for ( i = 0; i < list.size(); i++ )
 
  159    if ( list[i]->column() == 0 )
 
  161      const int row = list[i]->row();
 
  162      if ( !rowsToRemove.contains( row ) )
 
  164        rowsToRemove.insert( row );
 
  168  for ( 
const int rowToRemoved : rowsToRemove )
 
  170    tableWidget->removeRow( rowToRemoved - removed );
 
  178  QList<QPair<QString, QVariant>> orderedMap;
 
  179  const auto end = map.constEnd();
 
  180  for ( 
auto it = map.constBegin(); it != end; ++it )
 
  182    orderedMap.append( qMakePair( it.key(), it.value() ) );
 
 
  190  tableWidget->clearContents();
 
  191  mValueMapErrorsLabel->setVisible( 
false );
 
  193  for ( 
int i = tableWidget->rowCount() - 1; i > 0; i-- )
 
  195    tableWidget->removeRow( i );
 
  205  constexpr int maxOverflowErrors { 5 };
 
  206  QStringList reportedErrors;
 
  210  for ( 
const auto &pair : list )
 
  213      setRow( row, pair.first, QString() );
 
  216      const QString value { pair.first };
 
  218      const QString validValue = checkValueLength( value );
 
  220      if ( validValue.length() != value.length() )
 
  222        if ( reportedErrors.length() < maxOverflowErrors )
 
  224          reportedErrors.push_back( tr( 
"Value '%1' has been trimmed (maximum field length: %2)" )
 
  225                                      .arg( value, QString::number( mappedField.length() ) ) );
 
  227        else if ( reportedErrors.length() == maxOverflowErrors )
 
  229          reportedErrors.push_back( tr( 
"Only first %1 errors have been reported." )
 
  230                                      .arg( maxOverflowErrors ) );
 
  234      setRow( row, validValue, pair.second.toString() );
 
  237      if ( !reportedErrors.isEmpty() )
 
  239        mValueMapErrorsLabel->setVisible( 
true );
 
  240        mValueMapErrorsLabel->setText( reportedErrors.join( QLatin1String( 
"<br>" ) ) );
 
 
  247QString QgsValueMapConfigDlg::checkValueLength( 
const QString &value )
 
  257    if ( mappedField.length() > 0 && value.length() > mappedField.length() )
 
  259      return value.mid( 0, mappedField.length() );
 
  267  const QList<QVariant> valueList = 
config.value( QStringLiteral( 
"map" ) ).toList();
 
  269  if ( !valueList.empty() )
 
  271    for ( 
const QVariant &value : valueList )
 
  273      const QVariantMap valueMap = value.toMap();
 
  278      comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
 
  283    const QVariantMap map = 
config.value( QStringLiteral( 
"map" ) ).toMap();
 
  284    for ( 
auto it = map.constBegin(); it != map.constEnd(); ++it )
 
  289      comboBox->addItem( it.key(), it.value() );
 
 
  297  if ( event->type() == QEvent::KeyPress )
 
  299    QKeyEvent *keyEvent = 
static_cast<QKeyEvent *
>( event );
 
  300    if ( keyEvent->matches( QKeySequence::Copy ) )
 
  302      copySelectionToClipboard();
 
 
  310void QgsValueMapConfigDlg::setRow( 
int row, 
const QString &value, 
const QString &description )
 
  312  QTableWidgetItem *valueCell = 
nullptr;
 
  313  QTableWidgetItem *descriptionCell = 
new QTableWidgetItem( description );
 
  314  tableWidget->insertRow( row );
 
  318    cellFont.setItalic( 
true );
 
  321    valueCell->setFont( cellFont );
 
  322    valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
 
  323    descriptionCell->setFont( cellFont );
 
  327    valueCell = 
new QTableWidgetItem( value );
 
  329  tableWidget->setItem( row, 0, valueCell );
 
  330  tableWidget->setItem( row, 1, descriptionCell );
 
  333void QgsValueMapConfigDlg::copySelectionToClipboard()
 
  335  QAbstractItemModel *model = tableWidget->model();
 
  336  QItemSelectionModel *selection = tableWidget->selectionModel();
 
  337  const QModelIndexList indexes = selection->selectedIndexes();
 
  339  QString clipboardText;
 
  340  QModelIndex previous = indexes.first();
 
  341  auto mimeData = std::make_unique<QMimeData>();
 
  342  for ( 
const QModelIndex ¤t : indexes )
 
  344    const QString text = model->data( current ).toString();
 
  345    if ( current.row() != previous.row() )
 
  347      clipboardText.append( 
'\n' );
 
  349    else if ( current.column() != previous.column() )
 
  351      clipboardText.append( 
'\t' );
 
  353    clipboardText.append( text );
 
  356  mimeData->setData( QStringLiteral( 
"text/plain" ), clipboardText.toUtf8() );
 
  357  QApplication::clipboard()->setMimeData( mimeData.release() );
 
  360void QgsValueMapConfigDlg::addNullButtonPushed()
 
  365void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
 
  368  if ( !layerDialog.exec() )
 
  371  updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
 
  374void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
 
  378  const QString fileName = QFileDialog::getOpenFileName( 
nullptr, tr( 
"Load Value Map from File" ), QDir::homePath() );
 
  379  if ( fileName.isNull() )
 
  388  if ( !f.open( QIODevice::ReadOnly ) )
 
  390    QMessageBox::information( 
nullptr, tr( 
"Load Value Map from File" ), tr( 
"Could not open file %1\nError was: %2" ).arg( filePath, f.errorString() ), QMessageBox::Cancel );
 
  395  s.setAutoDetectUnicode( 
true );
 
  397  const thread_local QRegularExpression re( 
"(?:^\"|[;,]\")(\"\"|[\\w\\W]*?)(?=\"[;,]|\"$)|(?:^(?!\")|[;,](?!\"))([^;,]*?)(?=$|[;,])|(\\r\\n|\\n)" );
 
  398  QList<QPair<QString, QVariant>> map;
 
  401    const QString l = s.readLine().trimmed();
 
  402    QRegularExpressionMatchIterator matches = re.globalMatch( l );
 
  404    while ( matches.hasNext() && ceils.size() < 2 )
 
  406      const QRegularExpressionMatch match = matches.next();
 
  407      ceils << match.capturedTexts().last().trimmed().replace( QLatin1String( 
"\"\"" ), QLatin1String( 
"\"" ) );
 
  410    if ( ceils.size() != 2 )
 
  413    QString key = ceils[0];
 
  414    QString val = ceils[1];
 
  417    map.append( qMakePair( key, val ) );
 
 
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
 
A dialog which allows populating value maps using features present in a vector layer.
 
Encapsulate a field in an attribute table or data source.
 
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
 
Q_INVOKABLE bool exists(int i) const
Returns if a field index is valid.
 
Stores settings for use within QGIS.
 
void loadMapFromCSV(const QString &filePath)
Updates the displayed table with the values from a CSV file.
 
void setConfig(const QVariantMap &config) override
Update the configuration widget to represent the given configuration.
 
bool eventFilter(QObject *watched, QEvent *event) override
 
QgsValueMapConfigDlg(QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
 
void updateMap(const QMap< QString, QVariant > &map, bool insertNull)
Updates the displayed table with the values from map.
 
static void populateComboBox(QComboBox *comboBox, const QVariantMap &configuration, bool skipNull)
Populates a comboBox with the appropriate entries based on a value map configuration.
 
QVariantMap config() override
Create a configuration from the current GUI state.
 
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
 
Represents a vector layer which manages a vector based dataset.