#include "qgssourcefieldsproperties.h" QgsSourceFieldsProperties::QgsSourceFieldsProperties( QgsVectorLayer *layer, QWidget *parent ) : QWidget( parent ) , mLayer( layer ) { if ( !layer ) return; setupUi( this ); //button appearance mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) ); mDeleteAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) ); mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) ); mCalculateFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCalculateField.svg" ) ) ); //button signals connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsSourceFieldsProperties::toggleEditing ); connect( mAddAttributeButton, &QAbstractButton::clicked, this, &QgsSourceFieldsProperties::addAttributeClicked ); connect( mDeleteAttributeButton, &QAbstractButton::clicked, this, &QgsSourceFieldsProperties::deleteAttributeClicked ); connect( mCalculateFieldButton, &QAbstractButton::clicked, this, &QgsSourceFieldsProperties::calculateFieldClicked ); //slots connect( mLayer, &QgsVectorLayer::editingStarted, this, &QgsSourceFieldsProperties::editingToggled ); connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsSourceFieldsProperties::editingToggled ); connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsSourceFieldsProperties::attributeAdded ); connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsSourceFieldsProperties::attributeDeleted ); //field list appearance mFieldsList->setColumnCount( AttrColCount ); mFieldsList->setSelectionBehavior( QAbstractItemView::SelectRows ); mFieldsList->setDragDropMode( QAbstractItemView::DragOnly ); mFieldsList->setHorizontalHeaderItem( AttrIdCol, new QTableWidgetItem( tr( "Id" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrNameCol, new QTableWidgetItem( tr( "Name" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrTypeCol, new QTableWidgetItem( tr( "Type" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrTypeNameCol, new QTableWidgetItem( tr( "Type name" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrLengthCol, new QTableWidgetItem( tr( "Length" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrPrecCol, new QTableWidgetItem( tr( "Precision" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrCommentCol, new QTableWidgetItem( tr( "Comment" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrWMSCol, new QTableWidgetItem( QStringLiteral( "WMS" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrWFSCol, new QTableWidgetItem( QStringLiteral( "WFS" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrAliasCol, new QTableWidgetItem( tr( "Alias" ) ) ); mFieldsList->setSortingEnabled( true ); mFieldsList->sortByColumn( 0, Qt::AscendingOrder ); mFieldsList->setSelectionBehavior( QAbstractItemView::SelectRows ); mFieldsList->setSelectionMode( QAbstractItemView::ExtendedSelection ); mFieldsList->verticalHeader()->hide(); //load buttons and field list updateButtons(); } QgsSourceFieldsProperties::~QgsSourceFieldsProperties() { } void QgsSourceFieldsProperties::init() { loadRows(); //not used here: mEditorLayoutComboBox->setCurrentIndex( mLayer->editFormConfig().layout() ); //used here????? mFormSuppressCmbBx->setCurrentIndex( mLayer->editFormConfig().suppress() ); //not used here: loadAttributeEditorTree(); } void QgsSourceFieldsProperties::loadRows() { disconnect( mFieldsList, &QTableWidget::cellChanged, this, &QgsSourceFieldsProperties::attributesListCellChanged ); const QgsFields &fields = mLayer->fields(); mIndexedWidgets.clear(); mFieldsList->setRowCount( 0 ); for ( int i = 0; i < fields.count(); ++i ) attributeAdded( i ); mFieldsList->resizeColumnsToContents(); connect( mFieldsList, &QTableWidget::cellChanged, this, &QgsSourceFieldsProperties::attributesListCellChanged ); updateFieldRenamingStatus(); } void QgsSourceFieldsProperties::updateFieldRenamingStatus() { bool canRenameFields = mLayer->isEditable() && ( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::RenameAttributes ) && !mLayer->readOnly(); for ( int row = 0; row < mFieldsList->rowCount(); ++row ) { if ( canRenameFields ) mFieldsList->item( row, AttrNameCol )->setFlags( mFieldsList->item( row, AttrNameCol )->flags() | Qt::ItemIsEditable ); else mFieldsList->item( row, AttrNameCol )->setFlags( mFieldsList->item( row, AttrNameCol )->flags() & ~Qt::ItemIsEditable ); } } void QgsSourceFieldsProperties::updateExpression() { QToolButton *btn = qobject_cast( sender() ); Q_ASSERT( btn ); int index = btn->property( "Index" ).toInt(); const QString exp = mLayer->expressionField( index ); QgsExpressionContext context; context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope( QgsProject::instance() ); QgsExpressionBuilderDialog dlg( mLayer, exp, nullptr, QStringLiteral( "generic" ), context ); if ( dlg.exec() ) { mLayer->updateExpressionField( index, dlg.expressionText() ); loadRows(); } } void QgsSourceFieldsProperties::attributeAdded( int idx ) { bool sorted = mFieldsList->isSortingEnabled(); if ( sorted ) mFieldsList->setSortingEnabled( false ); const QgsFields &fields = mLayer->fields(); int row = mFieldsList->rowCount(); mFieldsList->insertRow( row ); setRow( row, idx, fields.at( idx ) ); mFieldsList->setCurrentCell( row, idx ); for ( int i = idx + 1; i < mIndexedWidgets.count(); i++ ) mIndexedWidgets.at( i )->setData( Qt::DisplayRole, i ); if ( sorted ) mFieldsList->setSortingEnabled( true ); for( int i = 0; i < 7; i++ ){ // mFieldsList->columnCount() switch ( mLayer->fields().fieldOrigin( idx ) ) { case QgsFields::OriginExpression: mFieldsList->item( idx, i )->setBackgroundColor( QColor( 200, 200, 255 ) ); break; case QgsFields::OriginJoin: mFieldsList->item( idx, i )->setBackgroundColor( QColor( 200, 255, 200 ) ); break; default: mFieldsList->item( idx, i )->setBackgroundColor( QColor( 255, 255, 200 ) ); break; } } } void QgsSourceFieldsProperties::attributeDeleted( int idx ) { mFieldsList->removeRow( mIndexedWidgets.at( idx )->row() ); mIndexedWidgets.removeAt( idx ); for ( int i = idx; i < mIndexedWidgets.count(); i++ ) { mIndexedWidgets.at( i )->setData( Qt::DisplayRole, i ); } } void QgsSourceFieldsProperties::setRow( int row, int idx, const QgsField &field ) { QTableWidgetItem *dataItem = new QTableWidgetItem(); dataItem->setData( Qt::DisplayRole, idx ); switch ( mLayer->fields().fieldOrigin( idx ) ) { case QgsFields::OriginExpression: dataItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) ); break; case QgsFields::OriginJoin: dataItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/join.png" ) ) ); break; default: dataItem->setIcon( mLayer->fields().iconForField( idx ) ); break; } mFieldsList->setItem( row, AttrIdCol, dataItem ); mIndexedWidgets.insert( idx, mFieldsList->item( row, 0 ) ); mFieldsList->setItem( row, AttrNameCol, new QTableWidgetItem( field.name() ) ); mFieldsList->setItem( row, AttrTypeCol, new QTableWidgetItem( QVariant::typeToName( field.type() ) ) ); mFieldsList->setItem( row, AttrTypeNameCol, new QTableWidgetItem( field.typeName() ) ); mFieldsList->setItem( row, AttrLengthCol, new QTableWidgetItem( QString::number( field.length() ) ) ); mFieldsList->setItem( row, AttrPrecCol, new QTableWidgetItem( QString::number( field.precision() ) ) ); if ( mLayer->fields().fieldOrigin( idx ) == QgsFields::OriginExpression ) { QWidget *expressionWidget = new QWidget; expressionWidget->setLayout( new QHBoxLayout ); QToolButton *editExpressionButton = new QToolButton; editExpressionButton->setProperty( "Index", idx ); editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) ); connect( editExpressionButton, &QAbstractButton::clicked, this, &QgsSourceFieldsProperties::updateExpression ); expressionWidget->layout()->setContentsMargins( 0, 0, 0, 0 ); expressionWidget->layout()->addWidget( editExpressionButton ); expressionWidget->layout()->addWidget( new QLabel( mLayer->expressionField( idx ) ) ); mFieldsList->setCellWidget( row, AttrCommentCol, expressionWidget ); } else { mFieldsList->setItem( row, AttrCommentCol, new QTableWidgetItem( field.comment() ) ); } QList notEditableCols = QList() << AttrIdCol << AttrNameCol << AttrTypeCol << AttrTypeNameCol << AttrLengthCol << AttrPrecCol; Q_FOREACH ( int i, notEditableCols ) mFieldsList->item( row, i )->setFlags( mFieldsList->item( row, i )->flags() & ~Qt::ItemIsEditable ); bool canRenameFields = mLayer->isEditable() && ( mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::RenameAttributes ) && !mLayer->readOnly(); if ( canRenameFields ) mFieldsList->item( row, AttrNameCol )->setFlags( mFieldsList->item( row, AttrNameCol )->flags() | Qt::ItemIsEditable ); else mFieldsList->item( row, AttrNameCol )->setFlags( mFieldsList->item( row, AttrNameCol )->flags() & ~Qt::ItemIsEditable ); //set the alias for the attribute mFieldsList->setItem( row, AttrAliasCol, new QTableWidgetItem( field.alias() ) ); //published WMS/WFS attributes QTableWidgetItem *wmsAttrItem = new QTableWidgetItem(); wmsAttrItem->setCheckState( mLayer->excludeAttributesWms().contains( field.name() ) ? Qt::Unchecked : Qt::Checked ); wmsAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); mFieldsList->setItem( row, AttrWMSCol, wmsAttrItem ); QTableWidgetItem *wfsAttrItem = new QTableWidgetItem(); wfsAttrItem->setCheckState( mLayer->excludeAttributesWfs().contains( field.name() ) ? Qt::Unchecked : Qt::Checked ); wfsAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); mFieldsList->setItem( row, AttrWFSCol, wfsAttrItem ); } bool QgsSourceFieldsProperties::addAttribute( const QgsField &field ) { QgsDebugMsg( "inserting attribute " + field.name() + " of type " + field.typeName() ); mLayer->beginEditCommand( tr( "Added attribute" ) ); if ( mLayer->addAttribute( field ) ) { mLayer->endEditCommand(); return true; } else { mLayer->destroyEditCommand(); QMessageBox::critical( this, tr( "Failed to add field" ), tr( "Failed to add field '%1' of type '%2'. Is the field name unique?" ).arg( field.name(), field.typeName() ) ); return false; } } //SLOTS void QgsSourceFieldsProperties::editingToggled() { updateButtons(); updateFieldRenamingStatus(); } void QgsSourceFieldsProperties::addAttributeClicked() { QgsAddAttrDialog dialog( mLayer, this ); if ( dialog.exec() == QDialog::Accepted ) { addAttribute( dialog.field() ); } } void QgsSourceFieldsProperties::deleteAttributeClicked() { QSet providerFields; QSet expressionFields; Q_FOREACH ( QTableWidgetItem *item, mFieldsList->selectedItems() ) { if ( item->column() == 0 ) { int idx = mIndexedWidgets.indexOf( item ); if ( idx < 0 ) continue; if ( mLayer->fields().fieldOrigin( idx ) == QgsFields::OriginExpression ) expressionFields << idx; else providerFields << idx; } } if ( !expressionFields.isEmpty() ) mLayer->deleteAttributes( expressionFields.toList() ); if ( !providerFields.isEmpty() ) { mLayer->beginEditCommand( tr( "Deleted attributes" ) ); if ( mLayer->deleteAttributes( providerFields.toList() ) ) mLayer->endEditCommand(); else mLayer->destroyEditCommand(); } } void QgsSourceFieldsProperties::calculateFieldClicked() { if ( !mLayer ) { return; } QgsFieldCalculator calc( mLayer, this ); calc.exec(); } void QgsSourceFieldsProperties::attributesListCellChanged( int row, int column ) { if ( column == AttrAliasCol && mLayer ) { int idx = mFieldsList->item( row, AttrIdCol )->text().toInt(); const QgsFields &fields = mLayer->fields(); if ( idx >= fields.count() ) { return; // index must be wrong } QTableWidgetItem *aliasItem = mFieldsList->item( row, column ); if ( aliasItem ) { if ( !aliasItem->text().trimmed().isEmpty() ) { mLayer->setFieldAlias( idx, aliasItem->text() ); } else { mLayer->removeFieldAlias( idx ); } } } else if ( column == AttrNameCol && mLayer && mLayer->isEditable() ) { QTableWidgetItem *nameItem = mFieldsList->item( row, column ); if ( !nameItem || nameItem->text().isEmpty() || !mLayer->fields().exists( row ) || mLayer->fields().at( row ).name() == nameItem->text() ) return; mLayer->beginEditCommand( tr( "Rename attribute" ) ); if ( mLayer->renameAttribute( row, nameItem->text() ) ) { mLayer->endEditCommand(); } else { mLayer->destroyEditCommand(); QMessageBox::critical( this, tr( "Failed to rename field" ), tr( "Failed to rename field to '%1'. Is the field name unique?" ).arg( nameItem->text() ) ); } } } //NICE FUNCTIONS void QgsSourceFieldsProperties::updateButtons() { int cap = mLayer->dataProvider()->capabilities(); mToggleEditingButton->setEnabled( ( cap & QgsVectorDataProvider::ChangeAttributeValues ) && !mLayer->readOnly() ); if ( mLayer->isEditable() ) { mDeleteAttributeButton->setEnabled( cap & QgsVectorDataProvider::DeleteAttributes ); mAddAttributeButton->setEnabled( cap & QgsVectorDataProvider::AddAttributes ); mToggleEditingButton->setChecked( true ); } else { mToggleEditingButton->setChecked( false ); mAddAttributeButton->setEnabled( false ); // Enable delete button if items are selected mDeleteAttributeButton->setEnabled( !mFieldsList->selectedItems().isEmpty() ); // and only if all selected items have their origin in an expression Q_FOREACH ( QTableWidgetItem *item, mFieldsList->selectedItems() ) { if ( item->column() == 0 ) { int idx = mIndexedWidgets.indexOf( item ); if ( mLayer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression ) { mDeleteAttributeButton->setEnabled( false ); break; } } } } }