QGIS/src/app/qgsattributesformproperties.cpp
2018-09-05 11:01:32 +02:00

1151 lines
43 KiB
C++

/***************************************************************************
qgsattributesformproperties.cpp
---------------------
begin : August 2017
copyright : (C) 2017 by David Signer
email : david at opengis dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsattributesformproperties.h"
#include "qgsattributetypedialog.h"
#include "qgsattributerelationedit.h"
#include "qgsattributesforminitcode.h"
#include "qgisapp.h"
QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, QWidget *parent )
: QWidget( parent )
, mLayer( layer )
{
if ( !layer )
return;
setupUi( this );
// available widgets tree
QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
mAvailableWidgetsTree = new DnDTree( mLayer );
availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
availableWidgetsWidgetLayout->setMargin( 0 );
mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
mAvailableWidgetsTree->setType( DnDTree::Type::Drag );
// form layout tree
QGridLayout *formLayoutWidgetLayout = new QGridLayout;
mFormLayoutTree = new DnDTree( mLayer );
mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
formLayoutWidgetLayout->addWidget( mFormLayoutTree );
formLayoutWidgetLayout->setMargin( 0 );
mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
mFormLayoutTree->setType( DnDTree::Type::Drop );
// AttributeTypeDialog
mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, -1, mAttributeTypeFrame );
mAttributeTypeDialog->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
// AttributeRelationEdit
mAttributeRelationEdit = new QgsAttributeRelationEdit( "", mAttributeTypeFrame );
mAttributeRelationEdit->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->addWidget( mAttributeRelationEdit );
connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addTabOrGroupButton );
connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
}
void QgsAttributesFormProperties::init()
{
initAvailableWidgetsTree();
initFormLayoutTree();
initLayoutConfig();
initInitPython();
initSuppressCombo();
mAttributeTypeDialog->setEnabled( false );
mAttributeRelationEdit->setEnabled( false );
}
void QgsAttributesFormProperties::initAvailableWidgetsTree()
{
mAvailableWidgetsTree->clear();
mAvailableWidgetsTree->setSortingEnabled( false );
mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
mAvailableWidgetsTree->setAcceptDrops( false );
mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
//load Fields
DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::Container, "Fields", "Fields" );
QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
const QgsFields fields = mLayer->fields();
for ( int i = 0; i < fields.size(); ++i )
{
const QgsField field = fields.at( i );
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
itemData.setShowLabel( true );
FieldConfig cfg( mLayer, i );
QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
item->setData( 0, FieldConfigRole, cfg );
item->setData( 0, FieldNameRole, field.name() );
QString tooltip;
if ( !field.alias().isEmpty() )
tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
else
tooltip = field.name();
item->setToolTip( 0, tooltip );
}
catitem->setExpanded( true );
//load Relations
catItemData = DnDTreeItemData( DnDTreeItemData::Container, QStringLiteral( "Relations" ), tr( "Relations" ) );
catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
for ( const QgsRelation &relation : relations )
{
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, QStringLiteral( "%1" ).arg( relation.id() ), QStringLiteral( "%1" ).arg( relation.name() ) );
itemData.setShowLabel( true );
RelationConfig cfg( mLayer, relation.id() );
QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
item->setData( 0, RelationConfigRole, cfg );
item->setData( 0, FieldNameRole, relation.id() );
}
catitem->setExpanded( true );
}
void QgsAttributesFormProperties::initFormLayoutTree()
{
// tabs and groups info
mFormLayoutTree->clear();
mFormLayoutTree->setSortingEnabled( false );
mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
mFormLayoutTree->setAcceptDrops( true );
mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
Q_FOREACH ( QgsAttributeEditorElement *wdg, mLayer->editFormConfig().tabs() )
{
loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
}
}
void QgsAttributesFormProperties::initSuppressCombo()
{
QgsSettings settings;
if ( settings.value( QStringLiteral( "qgis/digitizing/disable_enter_attribute_values_dialog" ), false ).toBool() )
{
mFormSuppressCmbBx->addItem( tr( "Hide form on add feature (global settings)" ) );
}
else
{
mFormSuppressCmbBx->addItem( tr( "Show form on add feature (global settings)" ) );
}
mFormSuppressCmbBx->addItem( tr( "Hide form on add feature" ) );
mFormSuppressCmbBx->addItem( tr( "Show form on add feature" ) );
mFormSuppressCmbBx->setCurrentIndex( mLayer->editFormConfig().suppress() );
}
void QgsAttributesFormProperties::initLayoutConfig()
{
mEditorLayoutComboBox->setCurrentIndex( mLayer->editFormConfig().layout() );
mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
QgsEditFormConfig cfg = mLayer->editFormConfig();
mEditFormLineEdit->setText( cfg.uiForm() );
}
void QgsAttributesFormProperties::initInitPython()
{
QgsEditFormConfig cfg = mLayer->editFormConfig();
mInitCodeSource = cfg.initCodeSource();
mInitFunction = cfg.initFunction();
mInitFilePath = cfg.initFilePath();
mInitCode = cfg.initCode();
if ( mInitCode.isEmpty() )
{
mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
"QGIS forms can have a Python function that is called when the form is\n"
"opened.\n"
"\n"
"Use this function to add extra logic to your forms.\n"
"\n"
"Enter the name of the function in the \"Python Init function\"\n"
"field.\n"
"An example follows:\n"
"\"\"\"\n"
"from qgis.PyQt.QtWidgets import QWidget\n\n"
"def my_form_open(dialog, layer, feature):\n"
"\tgeom = feature.geometry()\n"
"\tcontrol = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
}
}
void QgsAttributesFormProperties::loadAttributeTypeDialog()
{
//check if item or field with the items name for some reason not available anymore
if ( !mAvailableWidgetsTree->currentItem() ||
mLayer->fields().indexOf( mAvailableWidgetsTree->currentItem()->data( 0, FieldNameRole ).toString() ) < 0 )
mAttributeTypeDialog->setEnabled( false );
else
{
FieldConfig cfg = mAvailableWidgetsTree->currentItem()->data( 0, FieldConfigRole ).value<FieldConfig>();
QString fieldName = mAvailableWidgetsTree->currentItem()->data( 0, FieldNameRole ).toString();
int index = mLayer->fields().indexOf( fieldName );
mAttributeTypeDialog->setEnabled( true );
// AttributeTypeDialog delete and recreate
mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
delete mAttributeTypeDialog;
mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
mAttributeTypeDialog->setAlias( cfg.mAlias );
mAttributeTypeDialog->setComment( cfg.mComment );
mAttributeTypeDialog->setFieldEditable( cfg.mEditable );
mAttributeTypeDialog->setLabelOnTop( cfg.mLabelOnTop );
mAttributeTypeDialog->setNotNull( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull );
mAttributeTypeDialog->setNotNullEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
mAttributeTypeDialog->setUnique( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique );
mAttributeTypeDialog->setUniqueEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
QgsFieldConstraints constraints = cfg.mFieldConstraints;
QgsFieldConstraints::Constraints providerConstraints = nullptr;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintUnique ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintUnique;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintExpression ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintExpression;
mAttributeTypeDialog->setProviderConstraints( providerConstraints );
mAttributeTypeDialog->setConstraintExpression( cfg.mConstraint );
mAttributeTypeDialog->setConstraintExpressionDescription( cfg.mConstraintDescription );
mAttributeTypeDialog->setConstraintExpressionEnforced( cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintStrengthHard ) == QgsFieldConstraints::ConstraintStrengthHard );
mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
mAttributeTypeDialog->setEditorWidgetConfig( cfg.mEditorWidgetConfig );
mAttributeTypeDialog->setEditorWidgetType( cfg.mEditorWidgetType );
mAttributeTypeDialog->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
}
}
void QgsAttributesFormProperties::storeAttributeTypeDialog()
{
if ( mAttributeTypeDialog->fieldIdx() < 0 )
return;
FieldConfig cfg;
cfg.mEditable = mAttributeTypeDialog->fieldEditable();
cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
cfg.mAlias = mAttributeTypeDialog->alias();
//confustion (will be removed): wir laden teilweise sachen einfach beim store anstelle des applys auf die mLayer - eingie Sachen laden wir auch vom layer anstatt über das cfg. wieso
QgsFieldConstraints constraints = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).constraints();
QgsFieldConstraints::Constraints providerConstraints = nullptr;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintUnique ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintUnique;
if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintExpression ) == QgsFieldConstraints::ConstraintOriginProvider )
providerConstraints |= QgsFieldConstraints::ConstraintExpression;
cfg.mConstraints = nullptr;
if ( mAttributeTypeDialog->notNull() && !( providerConstraints & QgsFieldConstraints::ConstraintNotNull ) )
{
cfg.mConstraints |= QgsFieldConstraints::ConstraintNotNull;
}
if ( mAttributeTypeDialog->unique() && !( providerConstraints & QgsFieldConstraints::ConstraintUnique ) )
{
cfg.mConstraints |= QgsFieldConstraints::ConstraintUnique;
}
if ( !mAttributeTypeDialog->constraintExpression().isEmpty() && !( providerConstraints & QgsFieldConstraints::ConstraintExpression ) )
{
cfg.mConstraints |= QgsFieldConstraints::ConstraintExpression;
}
cfg.mConstraintDescription = mAttributeTypeDialog->constraintExpressionDescription();
cfg.mConstraint = mAttributeTypeDialog->constraintExpression();
mLayer->setDefaultValueDefinition( mAttributeTypeDialog->fieldIdx(), QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, mAttributeTypeDialog->notNullEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, mAttributeTypeDialog->uniqueEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
cfg.mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, mAttributeTypeDialog->constraintExpressionEnforced() ?
QgsFieldConstraints::ConstraintStrengthHard : QgsFieldConstraints::ConstraintStrengthSoft );
QString fieldName = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).name();
QTreeWidgetItemIterator itemIt( mAvailableWidgetsTree );
while ( *itemIt )
{
QTreeWidgetItem *item = *itemIt;
if ( item->data( 0, FieldNameRole ).toString() == fieldName )
item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
++itemIt;
}
}
void QgsAttributesFormProperties::loadAttributeRelationEdit()
{
QTreeWidgetItem *currentItem = mAvailableWidgetsTree->currentItem();
if ( !currentItem )
mAttributeRelationEdit->setEnabled( false );
else
{
mAttributeRelationEdit->setEnabled( true );
mAttributeTypeFrame->layout()->removeWidget( mAttributeRelationEdit );
delete mAttributeRelationEdit;
RelationConfig cfg = currentItem->data( 0, RelationConfigRole ).value<RelationConfig>();
mAttributeRelationEdit = new QgsAttributeRelationEdit( currentItem->data( 0, FieldNameRole ).toString(), mAttributeTypeFrame );
mAttributeRelationEdit->setCardinalityCombo( tr( "Many to one relation" ) );
QgsRelation relation = QgsProject::instance()->relationManager()->relation( currentItem->data( 0, FieldNameRole ).toString() );
Q_FOREACH ( const QgsRelation &nmrel, QgsProject::instance()->relationManager()->referencingRelations( relation.referencingLayer() ) )
{
if ( nmrel.fieldPairs().at( 0 ).referencingField() != relation.fieldPairs().at( 0 ).referencingField() )
mAttributeRelationEdit->setCardinalityCombo( QStringLiteral( "%1 (%2)" ).arg( nmrel.referencedLayer()->name(), nmrel.fieldPairs().at( 0 ).referencedField() ), nmrel.id() );
}
mAttributeRelationEdit->setCardinality( cfg.mCardinality );
mAttributeRelationEdit->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->setMargin( 0 );
mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
mAttributeTypeFrame->layout()->addWidget( mAttributeRelationEdit );
}
}
void QgsAttributesFormProperties::storeAttributeRelationEdit()
{
RelationConfig cfg;
cfg.mCardinality = mAttributeRelationEdit->cardinality();
QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
for ( int i = 0; i < relationContainer->childCount(); i++ )
{
QTreeWidgetItem *relationItem = relationContainer->child( i );
DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
if ( itemData.name() == mAttributeRelationEdit->mRelationId )
{
relationItem->setData( 0, RelationConfigRole, QVariant::fromValue<RelationConfig>( cfg ) );
}
}
}
QgsAttributesFormProperties::RelationConfig QgsAttributesFormProperties::configForRelation( const QString &relationId )
{
QTreeWidgetItemIterator itemIt( mAvailableWidgetsTree );
while ( *itemIt )
{
QTreeWidgetItem *item = *itemIt;
if ( item->data( 0, FieldNameRole ).toString() == relationId )
return item->data( 0, RelationConfigRole ).value<RelationConfig>();
++itemIt;
}
// Should never get here
Q_ASSERT( false );
return RelationConfig();
}
QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, DnDTree *tree )
{
QTreeWidgetItem *newWidget = nullptr;
switch ( widgetDef->type() )
{
case QgsAttributeEditorElement::AeTypeField:
{
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
itemData.setShowLabel( widgetDef->showLabel() );
newWidget = tree->addItem( parent, itemData );
break;
}
case QgsAttributeEditorElement::AeTypeRelation:
{
const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
itemData.setShowLabel( widgetDef->showLabel() );
RelationEditorConfiguration relEdConfig;
relEdConfig.showLinkButton = relationEditor->showLinkButton();
relEdConfig.showUnlinkButton = relationEditor->showUnlinkButton();
itemData.setRelationEditorConfiguration( relEdConfig );
newWidget = tree->addItem( parent, itemData );
break;
}
case QgsAttributeEditorElement::AeTypeContainer:
{
DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
itemData.setShowLabel( widgetDef->showLabel() );
const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
if ( !container )
break;
itemData.setColumnCount( container->columnCount() );
itemData.setShowAsGroupBox( container->isGroupBox() );
itemData.setVisibilityExpression( container->visibilityExpression() );
newWidget = tree->addItem( parent, itemData );
const QList<QgsAttributeEditorElement *> children = container->children();
for ( QgsAttributeEditorElement *wdg : children )
{
loadAttributeEditorTreeItem( wdg, newWidget, tree );
}
}
break;
default:
//should not happen
break;
}
return newWidget;
}
void QgsAttributesFormProperties::onAttributeSelectionChanged()
{
storeAttributeTypeDialog();
storeAttributeRelationEdit();
switch ( mAvailableWidgetsTree->currentItem()->data( 0, DnDTreeRole ).value<DnDTreeItemData>().type() )
{
case DnDTreeItemData::Relation:
{
mAttributeTypeDialog->setVisible( false );
loadAttributeRelationEdit();
break;
}
case DnDTreeItemData::Field:
{
mAttributeRelationEdit->setVisible( false );
loadAttributeTypeDialog();
break;
}
case DnDTreeItemData::Container:
{
mAttributeRelationEdit->setVisible( false );
mAttributeTypeDialog->setVisible( false );
break;
}
}
}
void QgsAttributesFormProperties::addTabOrGroupButton()
{
QList<QgsAddTabOrGroup::TabPair> tabList;
for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
{
DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
if ( itemData.type() == DnDTreeItemData::Container )
{
tabList.append( QgsAddTabOrGroup::TabPair( itemData.name(), *it ) );
}
}
QgsAddTabOrGroup addTabOrGroup( mLayer, tabList, this );
if ( !addTabOrGroup.exec() )
return;
QString name = addTabOrGroup.name();
if ( addTabOrGroup.tabButtonIsChecked() )
{
mFormLayoutTree->addContainer( mFormLayoutTree->invisibleRootItem(), name, addTabOrGroup.columnCount() );
}
else
{
QTreeWidgetItem *tabItem = addTabOrGroup.tab();
mFormLayoutTree->addContainer( tabItem, name, addTabOrGroup.columnCount() );
}
}
void QgsAttributesFormProperties::removeTabOrGroupButton()
{
qDeleteAll( mFormLayoutTree->selectedItems() );
}
QgsAttributeEditorElement *QgsAttributesFormProperties::createAttributeEditorWidget( QTreeWidgetItem *item, QgsAttributeEditorElement *parent, bool forceGroup )
{
QgsAttributeEditorElement *widgetDef = nullptr;
DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
switch ( itemData.type() )
{
//indexed here?
case DnDTreeItemData::Field:
{
int idx = mLayer->fields().lookupField( itemData.name() );
widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
break;
}
case DnDTreeItemData::Relation:
{
QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
relDef->setShowLinkButton( itemData.relationEditorConfiguration().showLinkButton );
relDef->setShowUnlinkButton( itemData.relationEditorConfiguration().showUnlinkButton );
widgetDef = relDef;
break;
}
case DnDTreeItemData::Container:
{
QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent );
container->setColumnCount( itemData.columnCount() );
container->setIsGroupBox( forceGroup ? true : itemData.showAsGroupBox() );
container->setVisibilityExpression( itemData.visibilityExpression() );
for ( int t = 0; t < item->childCount(); t++ )
{
container->addChildElement( createAttributeEditorWidget( item->child( t ), container ) );
}
widgetDef = container;
break;
}
}
widgetDef->setShowLabel( itemData.showLabel() );
return widgetDef;
}
void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int index )
{
switch ( index )
{
case 0:
mFormLayoutWidget->setVisible( false );
mUiFileFrame->setVisible( false );
mAddTabOrGroupButton->setVisible( false );
mRemoveTabOrGroupButton->setVisible( false );
break;
case 1:
mFormLayoutWidget->setVisible( true );
mUiFileFrame->setVisible( false );
mAddTabOrGroupButton->setVisible( true );
mRemoveTabOrGroupButton->setVisible( true );
break;
case 2:
mFormLayoutWidget->setVisible( false );
mUiFileFrame->setVisible( true );
mAddTabOrGroupButton->setVisible( false );
mRemoveTabOrGroupButton->setVisible( false );
break;
}
}
void QgsAttributesFormProperties::mTbInitCode_clicked()
{
QgsAttributesFormInitCode attributesFormInitCode;
attributesFormInitCode.setCodeSource( mInitCodeSource );
attributesFormInitCode.setInitCode( mInitCode );
attributesFormInitCode.setInitFilePath( mInitFilePath );
attributesFormInitCode.setInitFunction( mInitFunction );
if ( !attributesFormInitCode.exec() )
return;
mInitCodeSource = attributesFormInitCode.codeSource();
mInitCode = attributesFormInitCode.initCode();
mInitFilePath = attributesFormInitCode.initFilePath();
mInitFunction = attributesFormInitCode.initFunction();
}
void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
{
QgsSettings myQSettings;
QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
if ( uifilename.isNull() )
return;
QFileInfo fi( uifilename );
myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
mEditFormLineEdit->setText( uifilename );
}
void QgsAttributesFormProperties::apply()
{
storeAttributeTypeDialog();
storeAttributeRelationEdit();
QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
for ( int i = 0; i < fieldContainer->childCount(); i++ )
{
QTreeWidgetItem *fieldItem = fieldContainer->child( i );
FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
int idx = mLayer->fields().indexOf( fieldItem->data( 0, FieldNameRole ).toString() );
//continue in case field does not exist anymore
if ( idx < 0 )
continue;
editFormConfig.setReadOnly( idx, !cfg.mEditable );
editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
mLayer->setConstraintExpression( idx, cfg.mConstraint, cfg.mConstraintDescription );
mLayer->setEditorWidgetSetup( idx, QgsEditorWidgetSetup( cfg.mEditorWidgetType, cfg.mEditorWidgetConfig ) );
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintNotNull )
{
mLayer->setFieldConstraint( idx, QgsFieldConstraints::ConstraintNotNull, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintStrengthHard ) );
}
else
{
mLayer->removeFieldConstraint( idx, QgsFieldConstraints::ConstraintNotNull );
}
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintUnique )
{
mLayer->setFieldConstraint( idx, QgsFieldConstraints::ConstraintUnique, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintStrengthHard ) );
}
else
{
mLayer->removeFieldConstraint( idx, QgsFieldConstraints::ConstraintUnique );
}
if ( cfg.mConstraints & QgsFieldConstraints::ConstraintExpression )
{
mLayer->setFieldConstraint( idx, QgsFieldConstraints::ConstraintExpression, cfg.mConstraintStrength.value( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintStrengthHard ) );
}
else
{
mLayer->removeFieldConstraint( idx, QgsFieldConstraints::ConstraintExpression );
}
mLayer->setFieldAlias( idx, cfg.mAlias );
}
// tabs and groups
editFormConfig.clearTabs();
for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
{
QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
editFormConfig.addTab( createAttributeEditorWidget( tabItem, nullptr, false ) );
}
editFormConfig.setUiForm( mEditFormLineEdit->text() );
editFormConfig.setLayout( ( QgsEditFormConfig::EditorLayout ) mEditorLayoutComboBox->currentIndex() );
editFormConfig.setInitCodeSource( mInitCodeSource );
editFormConfig.setInitFunction( mInitFunction );
editFormConfig.setInitFilePath( mInitFilePath );
editFormConfig.setInitCode( mInitCode );
editFormConfig.setSuppress( ( QgsEditFormConfig::FeatureFormSuppress )mFormSuppressCmbBx->currentIndex() );
// relations
QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
for ( int i = 0; i < relationContainer->childCount(); i++ )
{
QTreeWidgetItem *relationItem = relationContainer->child( i );
DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
RelationConfig relCfg = configForRelation( itemData.name() );
QVariantMap cfg;
cfg[QStringLiteral( "nm-rel" )] = relCfg.mCardinality.toString();
editFormConfig.setWidgetConfig( itemData.name(), cfg );
}
mLayer->setEditFormConfig( editFormConfig );
}
/*
* FieldConfig implementation
*/
QgsAttributesFormProperties::FieldConfig::FieldConfig( QgsVectorLayer *layer, int idx )
{
mAlias = layer->fields().at( idx ).alias();
mComment = layer->fields().at( idx ).comment();
mEditable = !layer->editFormConfig().readOnly( idx );
mEditableEnabled = layer->fields().fieldOrigin( idx ) != QgsFields::OriginJoin
&& layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
mFieldConstraints = layer->fields().at( idx ).constraints();
mConstraints = mFieldConstraints.constraints();
mConstraint = mFieldConstraints.constraintExpression();
mConstraintStrength.insert( QgsFieldConstraints::ConstraintNotNull, mFieldConstraints.constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
mConstraintStrength.insert( QgsFieldConstraints::ConstraintUnique, mFieldConstraints.constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
mConstraintStrength.insert( QgsFieldConstraints::ConstraintExpression, mFieldConstraints.constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
mConstraintDescription = mFieldConstraints.constraintDescription();
const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
mEditorWidgetType = setup.type();
mEditorWidgetConfig = setup.config();
}
QgsAttributesFormProperties::FieldConfig::operator QVariant()
{
return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
}
/*
* RelationConfig implementation
*/
QgsAttributesFormProperties::RelationConfig::RelationConfig() = default;
QgsAttributesFormProperties::RelationConfig::RelationConfig( QgsVectorLayer *layer, const QString &relationId )
{
const QVariant nmrelcfg = layer->editFormConfig().widgetConfig( relationId ).value( QStringLiteral( "nm-rel" ) );
mCardinality = nmrelcfg;
}
QgsAttributesFormProperties::RelationConfig::operator QVariant()
{
return QVariant::fromValue<QgsAttributesFormProperties::RelationConfig>( *this );
}
/*
* DnDTree implementation
*/
QTreeWidgetItem *DnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount )
{
QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
newItem->setBackground( 0, QBrush( Qt::lightGray ) );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
QgsAttributesFormProperties::DnDTreeItemData itemData( QgsAttributesFormProperties::DnDTreeItemData::Container, title, title );
itemData.setColumnCount( columnCount );
newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
parent->addChild( newItem );
newItem->setExpanded( true );
return newItem;
}
DnDTree::DnDTree( QgsVectorLayer *layer, QWidget *parent )
: QTreeWidget( parent )
, mLayer( layer )
{
connect( this, &QTreeWidget::itemDoubleClicked, this, &DnDTree::onItemDoubleClicked );
}
QTreeWidgetItem *DnDTree::addItem( QTreeWidgetItem *parent, QgsAttributesFormProperties::DnDTreeItemData data, int index )
{
QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
if ( data.type() == QgsAttributesFormProperties::DnDTreeItemData::Container )
{
newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
newItem->setBackground( 0, QBrush( Qt::lightGray ) );
switch ( data.type() )
{
case QgsAttributesFormProperties::DnDTreeItemData::Field:
newItem->setIcon( 0, QgsApplication::getThemeIcon( "/mFieldIcon.svg" ) );
break;
case QgsAttributesFormProperties::DnDTreeItemData::Relation:
newItem->setIcon( 0, QgsApplication::getThemeIcon( "/mRelationIcon.svg" ) );
break;
case QgsAttributesFormProperties::DnDTreeItemData::Container:
newItem->setIcon( 0, QgsApplication::getThemeIcon( "/mContainerIcon.svg" ) );
break;
}
}
newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, data );
newItem->setText( 0, data.displayName() );
if ( index < 0 )
parent->addChild( newItem );
else
parent->insertChild( index, newItem );
return newItem;
}
/**
* Is called when mouse is moved over attributes tree before a
* drop event. Used to inhibit dropping fields onto the root item.
*/
void DnDTree::dragMoveEvent( QDragMoveEvent *event )
{
const QMimeData *data = event->mimeData();
if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
{
QgsAttributesFormProperties::DnDTreeItemData itemElement;
QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
QDataStream stream( &itemData, QIODevice::ReadOnly );
stream >> itemElement;
// Inner drag and drop actions are always MoveAction
if ( event->source() == this )
{
event->setDropAction( Qt::MoveAction );
}
}
else
{
event->ignore();
}
QTreeWidget::dragMoveEvent( event );
}
bool DnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
{
bool bDropSuccessful = false;
if ( action == Qt::IgnoreAction )
{
bDropSuccessful = true;
}
else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
{
QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
QDataStream stream( &itemData, QIODevice::ReadOnly );
QgsAttributesFormProperties::DnDTreeItemData itemElement;
while ( !stream.atEnd() )
{
stream >> itemElement;
if ( parent )
{
addItem( parent, itemElement, index );
bDropSuccessful = true;
}
else
{
addItem( invisibleRootItem(), itemElement, index );
bDropSuccessful = true;
}
}
}
return bDropSuccessful;
}
void DnDTree::dropEvent( QDropEvent *event )
{
if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
return;
if ( event->source() == this )
{
event->setDropAction( Qt::MoveAction );
}
QTreeWidget::dropEvent( event );
}
QStringList DnDTree::mimeTypes() const
{
return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
}
QMimeData *DnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
{
if ( items.count() <= 0 )
return nullptr;
QStringList types = mimeTypes();
if ( types.isEmpty() )
return nullptr;
QMimeData *data = new QMimeData();
QString format = types.at( 0 );
QByteArray encoded;
QDataStream stream( &encoded, QIODevice::WriteOnly );
Q_FOREACH ( const QTreeWidgetItem *item, items )
{
if ( item )
{
// Relevant information is always in the DnDTreeRole of the first column
QgsAttributesFormProperties::DnDTreeItemData itemData = item->data( 0, QgsAttributesFormProperties::DnDTreeRole ).value<QgsAttributesFormProperties::DnDTreeItemData>();
stream << itemData;
}
}
data->setData( format, encoded );
return data;
}
void DnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
{
Q_UNUSED( column )
QgsAttributesFormProperties::DnDTreeItemData itemData = item->data( 0, QgsAttributesFormProperties::DnDTreeRole ).value<QgsAttributesFormProperties::DnDTreeItemData>();
QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
QFormLayout *baseLayout = new QFormLayout();
baseData->setLayout( baseLayout );
QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
showLabelCheckbox->setChecked( itemData.showLabel() );
baseLayout->addRow( showLabelCheckbox );
QWidget *baseWidget = new QWidget();
baseWidget->setLayout( baseLayout );
if ( itemData.type() == QgsAttributesFormProperties::DnDTreeItemData::Container )
{
QDialog dlg;
dlg.setWindowTitle( tr( "Configure Container" ) );
QFormLayout *layout = new QFormLayout() ;
dlg.setLayout( layout );
layout->addRow( baseWidget );
QCheckBox *showAsGroupBox = nullptr;
QLineEdit *title = new QLineEdit( itemData.name() );
QSpinBox *columnCount = new QSpinBox();
QGroupBox *visibilityExpressionGroupBox = new QGroupBox( tr( "Control visibility by expression" ) );
visibilityExpressionGroupBox->setCheckable( true );
visibilityExpressionGroupBox->setChecked( itemData.visibilityExpression().enabled() );
visibilityExpressionGroupBox->setLayout( new QGridLayout );
QgsFieldExpressionWidget *visibilityExpressionWidget = new QgsFieldExpressionWidget;
visibilityExpressionWidget->setLayer( mLayer );
visibilityExpressionWidget->setExpressionDialogTitle( tr( "Visibility Expression" ) );
visibilityExpressionWidget->setExpression( itemData.visibilityExpression()->expression() );
visibilityExpressionGroupBox->layout()->addWidget( visibilityExpressionWidget );
columnCount->setRange( 1, 5 );
columnCount->setValue( itemData.columnCount() );
layout->addRow( tr( "Title" ), title );
layout->addRow( tr( "Column count" ), columnCount );
layout->addRow( visibilityExpressionGroupBox );
if ( !item->parent() )
{
showAsGroupBox = new QCheckBox( tr( "Show as group box" ) );
showAsGroupBox->setChecked( itemData.showAsGroupBox() );
layout->addRow( showAsGroupBox );
}
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok
| QDialogButtonBox::Cancel );
connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
layout->addWidget( buttonBox );
if ( dlg.exec() )
{
itemData.setColumnCount( columnCount->value() );
itemData.setShowAsGroupBox( showAsGroupBox ? showAsGroupBox->isChecked() : true );
itemData.setName( title->text() );
itemData.setShowLabel( showLabelCheckbox->isChecked() );
QgsOptionalExpression visibilityExpression;
visibilityExpression.setData( QgsExpression( visibilityExpressionWidget->expression() ) );
visibilityExpression.setEnabled( visibilityExpressionGroupBox->isChecked() );
itemData.setVisibilityExpression( visibilityExpression );
item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
item->setText( 0, title->text() );
}
}
else if ( itemData.type() == QgsAttributesFormProperties::DnDTreeItemData::Relation )
{
QDialog dlg;
dlg.setWindowTitle( tr( "Configure Relation Editor" ) );
QFormLayout *layout = new QFormLayout() ;
dlg.setLayout( layout );
layout->addWidget( baseWidget );
QCheckBox *showLinkButton = new QCheckBox( tr( "Show link button" ) );
showLinkButton->setChecked( itemData.relationEditorConfiguration().showLinkButton );
QCheckBox *showUnlinkButton = new QCheckBox( tr( "Show unlink button" ) );
showUnlinkButton->setChecked( itemData.relationEditorConfiguration().showUnlinkButton );
layout->addRow( showLinkButton );
layout->addRow( showUnlinkButton );
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
dlg.layout()->addWidget( buttonBox );
if ( dlg.exec() )
{
QgsAttributesFormProperties::RelationEditorConfiguration relEdCfg;
relEdCfg.showLinkButton = showLinkButton->isChecked();
relEdCfg.showUnlinkButton = showUnlinkButton->isChecked();
itemData.setShowLabel( showLabelCheckbox->isChecked() );
itemData.setRelationEditorConfiguration( relEdCfg );
item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
}
}
else
{
QDialog dlg;
dlg.setWindowTitle( tr( "Configure Field" ) );
dlg.setLayout( new QGridLayout() );
dlg.layout()->addWidget( baseWidget );
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok
| QDialogButtonBox::Cancel );
connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
dlg.layout()->addWidget( buttonBox );
if ( dlg.exec() )
{
itemData.setShowLabel( showLabelCheckbox->isChecked() );
item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
}
}
}
DnDTree::Type DnDTree::type() const
{
return mType;
}
void DnDTree::setType( const Type &value )
{
mType = value;
}
/*
* Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
*/
QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
{
stream << ( quint32 )data.type() << data.name() << data.displayName();
return stream;
}
QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
{
QString name;
QString displayName;
quint32 type;
stream >> type >> name >> displayName;
data.setType( ( QgsAttributesFormProperties::DnDTreeItemData::Type )type );
data.setName( name );
data.setDisplayName( displayName );
return stream;
}
bool QgsAttributesFormProperties::DnDTreeItemData::showAsGroupBox() const
{
return mShowAsGroupBox;
}
void QgsAttributesFormProperties::DnDTreeItemData::setShowAsGroupBox( bool showAsGroupBox )
{
mShowAsGroupBox = showAsGroupBox;
}
bool QgsAttributesFormProperties::DnDTreeItemData::showLabel() const
{
return mShowLabel;
}
void QgsAttributesFormProperties::DnDTreeItemData::setShowLabel( bool showLabel )
{
mShowLabel = showLabel;
}
QgsOptionalExpression QgsAttributesFormProperties::DnDTreeItemData::visibilityExpression() const
{
return mVisibilityExpression;
}
void QgsAttributesFormProperties::DnDTreeItemData::setVisibilityExpression( const QgsOptionalExpression &visibilityExpression )
{
mVisibilityExpression = visibilityExpression;
}
QgsAttributesFormProperties::RelationEditorConfiguration QgsAttributesFormProperties::DnDTreeItemData::relationEditorConfiguration() const
{
return mRelationEditorConfiguration;
}
void QgsAttributesFormProperties::DnDTreeItemData::setRelationEditorConfiguration( QgsAttributesFormProperties::RelationEditorConfiguration relationEditorConfiguration )
{
mRelationEditorConfiguration = relationEditorConfiguration;
}