23 #include <QMessageBox> 
   24 #include <QPushButton> 
   25 #include <QStandardItemModel> 
   26 #include <QToolButton> 
   36   : QAbstractTableModel( parent )
 
   37   , mExpressionContextGenerator( new 
QgsFieldMappingModel::ExpressionContextGenerator( sourceFields ) )
 
   44   if ( role == Qt::DisplayRole )
 
   46     switch ( orientation )
 
   54             return tr( 
"Source Expression" );
 
   58             return tr( 
"Aggregate Function" );
 
   62             return tr( 
"Delimiter" );
 
   74             return tr( 
"Length" );
 
   78             return tr( 
"Precision" );
 
   99   if ( parent.isValid() )
 
  101   return mMapping.count();
 
  106   if ( parent.isValid() )
 
  113   if ( index.isValid() )
 
  116     const Aggregate &agg { mMapping.at( index.row() ) };
 
  120       case Qt::DisplayRole:
 
  131             return agg.aggregate;
 
  135             return agg.delimiter;
 
  139             return agg.field.displayName();
 
  143             return agg.field.typeName();
 
  147             return agg.field.length();
 
  151             return agg.field.precision();
 
  163   if ( index.isValid() )
 
  165     return Qt::ItemFlags( Qt::ItemIsSelectable |
 
  169   return Qt::ItemFlags();
 
  174   if ( index.isValid() )
 
  176     if ( role == Qt::EditRole )
 
  204           QgsFieldMappingModel::setFieldTypeFromName( f.
field, value.toString() );
 
  210           const int length { value.toInt( &ok ) };
 
  218           const int precision { value.toInt( &ok ) };
 
  224       emit dataChanged( index, index );
 
  235 bool QgsAggregateMappingModel::moveUpOrDown( 
const QModelIndex &index, 
bool up )
 
  237   if ( ! index.isValid() && index.model() == 
this )
 
  241   const int row { up ? index.row() - 1 : index.row() };
 
  243   if ( row < 0 || row + 1 >= 
rowCount( QModelIndex() ) )
 
  247   beginMoveRows( QModelIndex( ), row, row, QModelIndex(), row + 2 );
 
  248 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) 
  249   mMapping.swap( row, row + 1 );
 
  251   mMapping.swapItemsAt( row, row + 1 );
 
  260   if ( mExpressionContextGenerator )
 
  261     mExpressionContextGenerator->setSourceFields( mSourceFields );
 
  263   const QStringList usedFields;
 
  271     aggregate.
field.
setTypeName( QgsFieldMappingModel::qgsFieldToTypeName( f ) );
 
  275       aggregate.
aggregate = QStringLiteral( 
"sum" );
 
  276     else if ( f.type() == QVariant::String || ( f.type() == QVariant::List && f.subType() == QVariant::String ) )
 
  277       aggregate.
aggregate = QStringLiteral( 
"concatenate" );
 
  281     mMapping.push_back( aggregate );
 
  288   return mExpressionContextGenerator.get();
 
  293   mExpressionContextGenerator->setBaseExpressionContextGenerator( generator );
 
  305   for ( 
auto &agg : mMapping )
 
  307     agg.field.setTypeName( QgsFieldMappingModel::qgsFieldToTypeName( agg.field ) );
 
  314   const int lastRow { 
rowCount( QModelIndex( ) ) };
 
  315   beginInsertRows( QModelIndex(), lastRow, lastRow );
 
  322   mMapping.push_back( agg );
 
  328   if ( index.isValid() && index.model() == 
this && index.row() < 
rowCount( QModelIndex() ) )
 
  330     beginRemoveRows( QModelIndex(), index.row(), index.row() );
 
  331     mMapping.removeAt( index.row() );
 
  343   return moveUpOrDown( index );
 
  348   return moveUpOrDown( index, 
false );
 
  360   QVBoxLayout *verticalLayout = 
new QVBoxLayout();
 
  361   verticalLayout->setContentsMargins( 0, 0, 0, 0 );
 
  362   mTableView = 
new QTableView();
 
  363   verticalLayout->addWidget( mTableView );
 
  364   setLayout( verticalLayout );
 
  367   mTableView->setModel( mModel );
 
  373   connect( mModel, &QgsAggregateMappingModel::rowsInserted, 
this, [ = ] { updateColumns(); } );
 
  374   connect( mModel, &QgsAggregateMappingModel::modelReset, 
this, [ = ] { updateColumns(); } );
 
  383   return qobject_cast<QgsAggregateMappingModel *>( mModel );
 
  398   return mTableView->selectionModel();
 
  408   mSourceLayer = layer;
 
  418   mTableView->scrollTo( index );
 
  433   if ( ! mTableView->selectionModel()->hasSelection() )
 
  436   std::list<int> rowsToRemove { selectedRows() };
 
  437   rowsToRemove.reverse();
 
  438   for ( 
const int row : rowsToRemove )
 
  440     if ( ! 
model()->removeField( 
model()->index( row, 0, QModelIndex() ) ) )
 
  450   if ( ! mTableView->selectionModel()->hasSelection() )
 
  453   const std::list<int> rowsToMoveUp { selectedRows() };
 
  454   for ( 
const int row : rowsToMoveUp )
 
  456     if ( ! 
model()->moveUp( 
model()->index( row, 0, QModelIndex() ) ) )
 
  466   if ( ! mTableView->selectionModel()->hasSelection() )
 
  469   std::list<int> rowsToMoveDown { selectedRows() };
 
  470   rowsToMoveDown.reverse();
 
  471   for ( 
const int row : rowsToMoveDown )
 
  473     if ( ! 
model()->moveDown( 
model()->index( row, 0, QModelIndex() ) ) )
 
  481 void QgsAggregateMappingWidget::updateColumns()
 
  483   for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  490   for ( 
int i = 0; i < mModel->columnCount(); ++i )
 
  492     mTableView->resizeColumnToContents( i );
 
  496 std::list<int> QgsAggregateMappingWidget::selectedRows()
 
  499   if ( mTableView->selectionModel()->hasSelection() )
 
  501     const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
 
  502     for ( 
const QModelIndex &index : constSelection )
 
  504       rows.push_back( index.row() );
 
  518 QgsAggregateMappingWidget::AggregateDelegate::AggregateDelegate( QObject *parent )
 
  519   : QStyledItemDelegate( parent )
 
  523 QWidget *QgsAggregateMappingWidget::AggregateDelegate::createEditor( QWidget *parent, 
const QStyleOptionViewItem &option, 
const QModelIndex & )
 const 
  526   QComboBox *editor = 
new QComboBox( parent );
 
  528   const QStringList aggregateList { QgsAggregateMappingWidget::AggregateDelegate::aggregates() };
 
  530   for ( 
const QString &aggregate : aggregateList )
 
  532     editor->addItem( aggregate );
 
  533     editor->setItemData( i, aggregate, Qt::UserRole );
 
  538            qOverload<int >( &QComboBox::currentIndexChanged ),
 
  540            [ = ]( 
int currentIndex )
 
  542     Q_UNUSED( currentIndex )
 
  543     const_cast< QgsAggregateMappingWidget::AggregateDelegate *
>( this )->emit commitData( editor );
 
  549 void QgsAggregateMappingWidget::AggregateDelegate::setEditorData( QWidget *editor, 
const QModelIndex &index )
 const 
  551   QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
 
  552   if ( ! editorWidget )
 
  555   const QVariant value = index.model()->data( index, Qt::EditRole );
 
  556   editorWidget->setCurrentIndex( editorWidget->findData( value ) );
 
  559 void QgsAggregateMappingWidget::AggregateDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, 
const QModelIndex &index )
 const 
  561   QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
 
  562   if ( ! editorWidget )
 
  565   const QVariant currentValue = editorWidget->currentData( );
 
  566   model->setData( index, currentValue, Qt::EditRole );
 
  569 const QStringList QgsAggregateMappingWidget::AggregateDelegate::aggregates()
 
  571   static QStringList sAggregates;
 
  572   static std::once_flag initialized;
 
  573   std::call_once( initialized, [ = ]( )
 
  575     sAggregates << QStringLiteral( 
"first_value" )
 
  576                 << QStringLiteral( 
"last_value" );
 
  581       if ( !
function || function->isDeprecated() || function->name().isEmpty() || function->name().at( 0 ) == 
'_' )
 
  584       if ( function->groups().contains( QLatin1String( 
"Aggregates" ) ) )
 
  586         if ( function->name() == QLatin1String( 
"aggregate" )
 
  587              || function->name() == QLatin1String( 
"relation_aggregate" ) )
 
  590         sAggregates.append( function->name() );
 
  593       std::sort( sAggregates.begin(), sAggregates.end() );