57 #include <QTextStream> 
   60 #include <QFormLayout> 
   61 #include <QGridLayout> 
   64 #include <QPushButton> 
   66 #include <QMessageBox> 
   67 #include <QToolButton> 
   71 int QgsAttributeForm::sFormCounter = 0;
 
   76   , mOwnsMessageBar( true )
 
   78   , mFormNr( sFormCounter++ )
 
   80   , mPreventFeatureRefresh( false )
 
   81   , mIsSettingMultiEditFeatures( false )
 
   82   , mUnsavedMultiEditChanges( false )
 
   83   , mEditCommandMessage( tr( 
"Attributes changed" ) )
 
   96   updateContainersVisibility();
 
  104   qDeleteAll( mInterfaces );
 
  131   mInterfaces.append( iface );
 
  147     if ( mUnsavedMultiEditChanges )
 
  150       int res = QMessageBox::question( 
this, tr( 
"Multiedit Attributes" ),
 
  151                                        tr( 
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
 
  152       if ( res == QMessageBox::Yes )
 
  157     clearMultiEditMessages();
 
  159   mUnsavedMultiEditChanges = 
false;
 
  211     w->setContext( newContext );
 
  217     w->setVisible( relationWidgetsVisible );
 
  224       mSearchButtonBox->setVisible( 
false );
 
  229       mSearchButtonBox->setVisible( 
false );
 
  234       mSearchButtonBox->setVisible( 
false );
 
  238       resetMultiEdit( 
false );
 
  240       mSearchButtonBox->setVisible( 
false );
 
  244       mSearchButtonBox->setVisible( 
true );
 
  250       mSearchButtonBox->setVisible( 
false );
 
  258       mSearchButtonBox->setVisible( 
false );
 
  267   const auto constMWidgets = mWidgets;
 
  282         QVariant mainValue = eww->
value();
 
  284         additionalFieldValues[index] = value;
 
  285         eww->
setValues( mainValue, additionalFieldValues );
 
  294   mIsSettingFeature = 
true;
 
  311       mIsSettingFeature = 
false;
 
  312       const auto constMInterfaces = mInterfaces;
 
  315         iface->featureChanged();
 
  331   mIsSettingFeature = 
false;
 
  334 bool QgsAttributeForm::saveEdits( QString *error )
 
  337   bool changedLayer = 
false;
 
  342     bool doUpdate = 
false;
 
  362             *error = tr( 
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
 
  365         QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
 
  366         QVariantList srcVars = QVariantList() << eww->
value();
 
  367         QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
  371         for ( 
const QString &fieldName : additionalFields )
 
  375           dstVars << dst.at( idx );
 
  379         Q_ASSERT( dstVars.count() == srcVars.count() );
 
  381         for ( 
int i = 0; i < dstVars.count(); i++ )
 
  384           if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
 
  386             dst[fieldIndexes[i]] = srcVars[i];
 
  396     const auto constMInterfaces = mInterfaces;
 
  399       if ( !iface->acceptChanges( updatedFeature ) )
 
  409         mFeature = updatedFeature;
 
  415         bool res = mLayer->
addFeature( updatedFeature );
 
  434         for ( 
int i = 0; i < dst.count(); ++i )
 
  437                || !dst.at( i ).isValid()                 
 
  438                || !fieldIsEditable( i ) )                
 
  444           QgsDebugMsgLevel( QStringLiteral( 
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
 
  445                             .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
 
  446           QgsDebugMsgLevel( QStringLiteral( 
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
 
  447                             .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
 
  449           newValues[i] = dst.at( i );
 
  450           oldValues[i] = src.at( i );
 
  457         if ( success && n > 0 )
 
  484 QgsFeature QgsAttributeForm::getUpdatedFeature()
 const 
  496     QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
 
  497     QVariantList srcVars = QVariantList() << eww->
value();
 
  498     QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
  502     for ( 
const QString &fieldName : additionalFields )
 
  506       dstVars << featureAttributes.at( idx );
 
  510     Q_ASSERT( dstVars.count() == srcVars.count() );
 
  512     for ( 
int i = 0; i < dstVars.count(); i++ )
 
  514       if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
 
  515         featureAttributes[fieldIndexes[i]] = srcVars[i];
 
  520   return updatedFeature;
 
  523 void QgsAttributeForm::updateValuesDependencies( 
const int originIdx )
 
  525   updateFieldDependencies();
 
  527   updateValuesDependenciesDefaultValues( originIdx );
 
  528   updateValuesDependenciesVirtualFields( originIdx );
 
  531 void QgsAttributeForm::updateValuesDependenciesDefaultValues( 
const int originIdx )
 
  533   if ( !mDefaultValueDependencies.contains( originIdx ) )
 
  541   QgsFeature updatedFeature = getUpdatedFeature();
 
  544   QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
 
  557       if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
 
  568 void QgsAttributeForm::updateValuesDependenciesVirtualFields( 
const int originIdx )
 
  570   if ( !mVirtualFieldsDependencies.contains( originIdx ) )
 
  577   QgsFeature updatedFeature = getUpdatedFeature();
 
  580   const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
 
  588     if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
 
  594     const QVariant value = exp.evaluate( &context );
 
  600 void QgsAttributeForm::updateRelatedLayerFields()
 
  603   updateRelatedLayerFieldsDependencies();
 
  605   if ( mRelatedLayerFieldsDependencies.isEmpty() )
 
  612   QgsFeature updatedFeature = getUpdatedFeature();
 
  615   const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
 
  619     if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
 
  625     QVariant value = exp.evaluate( &context );
 
  630 void QgsAttributeForm::resetMultiEdit( 
bool promptToSave )
 
  635   mUnsavedMultiEditChanges = 
false;
 
  639 void QgsAttributeForm::multiEditMessageClicked( 
const QString &link )
 
  641   clearMultiEditMessages();
 
  642   resetMultiEdit( link == QLatin1String( 
"#apply" ) );
 
  645 void QgsAttributeForm::filterTriggered()
 
  647   QString filter = createFilterExpression();
 
  653 void QgsAttributeForm::searchZoomTo()
 
  655   QString filter = createFilterExpression();
 
  656   if ( filter.isEmpty() )
 
  662 void QgsAttributeForm::searchFlash()
 
  664   QString filter = createFilterExpression();
 
  665   if ( filter.isEmpty() )
 
  671 void QgsAttributeForm::filterAndTriggered()
 
  673   QString filter = createFilterExpression();
 
  674   if ( filter.isEmpty() )
 
  682 void QgsAttributeForm::filterOrTriggered()
 
  684   QString filter = createFilterExpression();
 
  685   if ( filter.isEmpty() )
 
  693 void QgsAttributeForm::pushSelectedFeaturesMessage()
 
  699                               tr( 
"%n matching feature(s) selected", 
"matching features", count ),
 
  700                               Qgis::MessageLevel::Info );
 
  705                               tr( 
"No matching features found" ),
 
  706                               Qgis::MessageLevel::Info );
 
  714                             Qgis::MessageLevel::Warning );
 
  719   QString filter = createFilterExpression();
 
  720   if ( filter.isEmpty() )
 
  724   pushSelectedFeaturesMessage();
 
  729 void QgsAttributeForm::searchSetSelection()
 
  734 void QgsAttributeForm::searchAddToSelection()
 
  739 void QgsAttributeForm::searchRemoveFromSelection()
 
  744 void QgsAttributeForm::searchIntersectSelection()
 
  749 bool QgsAttributeForm::saveMultiEdits()
 
  753   QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
 
  754   for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
 
  761          || !fieldIsEditable( wIt.key() ) ) 
 
  769     newAttributeValues.insert( wIt.key(), w->
currentValue() );
 
  772   if ( newAttributeValues.isEmpty() )
 
  780   int res = QMessageBox::information( 
this, tr( 
"Multiedit Attributes" ),
 
  781                                       tr( 
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
 
  782   if ( res != QMessageBox::Ok )
 
  793   const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
 
  796     QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
 
  797     for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
 
  803   clearMultiEditMessages();
 
  808     mMultiEditMessageBarItem = 
new QgsMessageBarItem( tr( 
"Attribute changes for multiple features applied." ), Qgis::MessageLevel::Success, -1 );
 
  813     mMultiEditMessageBarItem = 
new QgsMessageBarItem( tr( 
"Changes could not be applied." ), Qgis::MessageLevel::Warning, 0 );
 
  816   if ( !mButtonBox->isVisible() )
 
  817     mMessageBar->
pushItem( mMultiEditMessageBarItem );
 
  843     wrapper->notifyAboutToSave();
 
  883       success = saveEdits( error );
 
  887       success = saveMultiEdits();
 
  892   mUnsavedMultiEditChanges = 
false;
 
  901   mValuesInitialized = 
false;
 
  902   const auto constMWidgets = mWidgets;
 
  905     ww->setFeature( mFeature );
 
  907   mValuesInitialized = 
true;
 
  913   const auto widgets { findChildren<  QgsAttributeFormEditorWidget * >() };
 
  920 void QgsAttributeForm::clearMultiEditMessages()
 
  922   if ( mMultiEditUnsavedMessageBarItem )
 
  924     if ( !mButtonBox->isVisible() )
 
  925       mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
 
  926     mMultiEditUnsavedMessageBarItem = 
nullptr;
 
  928   if ( mMultiEditMessageBarItem )
 
  930     if ( !mButtonBox->isVisible() )
 
  931       mMessageBar->
popWidget( mMultiEditMessageBarItem );
 
  932     mMultiEditMessageBarItem = 
nullptr;
 
  936 QString QgsAttributeForm::createFilterExpression()
 const 
  942     if ( !filter.isEmpty() )
 
  946   if ( filters.isEmpty() )
 
  949   QString filter = filters.join( QLatin1String( 
") AND (" ) ).prepend( 
'(' ).append( 
')' );
 
  958   if ( mExtraContextScope )
 
  965 void QgsAttributeForm::onAttributeChanged( 
const QVariant &value, 
const QVariantList &additionalFieldValues )
 
  970   bool signalEmitted = 
false;
 
  972   if ( mValuesInitialized )
 
  991       for ( 
int i = 0; i < additionalFields.count(); i++ )
 
  993         const QString fieldName = additionalFields.at( i );
 
  994         const QVariant value = additionalFieldValues.at( i );
 
  998       signalEmitted = 
true;
 
 1000       if ( mValuesInitialized )
 
 1001         updateJoinedFields( *eww );
 
 1007       if ( !mIsSettingMultiEditFeatures )
 
 1009         mUnsavedMultiEditChanges = 
true;
 
 1011         QLabel *msgLabel = 
new QLabel( tr( 
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
 
 1012         msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
 
 1013         msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
 
 1014         connect( msgLabel, &QLabel::linkActivated, 
this, &QgsAttributeForm::multiEditMessageClicked );
 
 1015         clearMultiEditMessages();
 
 1017         mMultiEditUnsavedMessageBarItem = 
new QgsMessageBarItem( msgLabel, Qgis::MessageLevel::Warning );
 
 1018         if ( !mButtonBox->isVisible() )
 
 1019           mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
 
 1022         signalEmitted = 
true;
 
 1032   updateConstraints( eww );
 
 1035   if ( mValuesInitialized )
 
 1038     mAlreadyUpdatedFields.append( eww->
fieldIdx() );
 
 1039     updateValuesDependencies( eww->
fieldIdx() );
 
 1040     mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
 
 1046   if ( !signalEmitted )
 
 1051     bool attributeHasChanged = !mIsSettingFeature;
 
 1053       attributeHasChanged &= !mIsSettingMultiEditFeatures;
 
 1059 void QgsAttributeForm::updateAllConstraints()
 
 1061   const auto constMWidgets = mWidgets;
 
 1066       updateConstraints( eww );
 
 1074   if ( currentFormValuesFeature( ft ) )
 
 1086     updateConstraint( ft, eww );
 
 1089     const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
 
 1092       updateConstraint( ft, depsEww );
 
 1100     const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
 
 1101     for ( ContainerInformation *info : infos )
 
 1103       info->apply( &context );
 
 1108 void QgsAttributeForm::updateContainersVisibility()
 
 1112   const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
 
 1114   for ( ContainerInformation *info : infos )
 
 1116     info->apply( &context );
 
 1120   updateAllConstraints();
 
 1126   if ( mContext.
attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode )
 
 1138         if ( mJoinedFeatures.contains( info ) )
 
 1156 void QgsAttributeForm::updateLabels()
 
 1158   if ( ! mLabelDataDefinedProperties.isEmpty() )
 
 1161     if ( currentFormValuesFeature( currentFeature ) )
 
 1165       for ( 
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
 
 1167         QLabel *label { it.key() };
 
 1169         const QString value { it->valueAsString( context, QString(), &ok ) };
 
 1170         if ( ok && ! value.isEmpty() )
 
 1172           label->setText( value );
 
 1179 bool QgsAttributeForm::currentFormValuesFeature( 
QgsFeature &feature )
 
 1192     if ( dst.count() > eww->
fieldIdx() )
 
 1194       QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
 
 1195       QVariantList srcVars = QVariantList() << eww->
value();
 
 1196       QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
 1200       for ( 
const QString &fieldName : additionalFields )
 
 1203         fieldIndexes << idx;
 
 1204         dstVars << dst.at( idx );
 
 1208       Q_ASSERT( dstVars.count() == srcVars.count() );
 
 1210       for ( 
int i = 0; i < dstVars.count(); i++ )
 
 1214         if ( ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
 
 1216           dst[fieldIndexes[i]] = srcVars[i];
 
 1233 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
 
 1235   mContainerVisibilityCollapsedInformation.append( info );
 
 1237   const QSet<QString> referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() );
 
 1239   for ( 
const QString &col : referencedColumns )
 
 1241     mContainerInformationDependency[ col ].append( info );
 
 1245 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
 const 
 1269 bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
 const 
 1290 void QgsAttributeForm::onAttributeAdded( 
int idx )
 
 1292   mPreventFeatureRefresh = 
false;
 
 1296     attrs.insert( idx, QVariant( 
layer()->fields().at( idx ).type() ) );
 
 1304 void QgsAttributeForm::onAttributeDeleted( 
int idx )
 
 1306   mPreventFeatureRefresh = 
false;
 
 1310     attrs.remove( idx );
 
 1318 void QgsAttributeForm::onRelatedFeaturesChanged()
 
 1320   updateRelatedLayerFields();
 
 1323 void QgsAttributeForm::onUpdatedFields()
 
 1325   mPreventFeatureRefresh = 
false;
 
 1342         attrs[i] = QVariant( 
layer()->fields().at( i ).type() );
 
 1352 void QgsAttributeForm::onConstraintStatusChanged( 
const QString &constraint,
 
 1360   if ( formEditorWidget )
 
 1366   QList<QgsEditorWidgetWrapper *> wDeps;
 
 1378       if ( name != ewwName )
 
 1385         for ( 
const QString &colName : referencedColumns )
 
 1387           if ( name == colName )
 
 1389             wDeps.append( eww );
 
 1402   return setupRelationWidgetWrapper( QString(), rel, context );
 
 1415 void QgsAttributeForm::preventFeatureRefresh()
 
 1417   mPreventFeatureRefresh = 
true;
 
 1448   return mNeedsGeometry;
 
 1451 void QgsAttributeForm::synchronizeState()
 
 1453   bool isEditable = ( mFeature.
isValid()
 
 1470       bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
 
 1471       ww->setEnabled( enabled );
 
 1477       ww->setEnabled( isEditable );
 
 1485     QStringList invalidFields, descriptions;
 
 1486     mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
 
 1490       if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
 
 1492         mConstraintsFailMessageBarItem = 
new QgsMessageBarItem( tr( 
"Changes to this form will not be saved. %n field(s) don't meet their constraints.", 
"invalid fields", invalidFields.size() ), Qgis::MessageLevel::Warning, -1 );
 
 1493         mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
 
 1495       else if ( mValidConstraints && mConstraintsFailMessageBarItem )
 
 1497         mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
 
 1498         mConstraintsFailMessageBarItem = 
nullptr;
 
 1501     else if ( mConstraintsFailMessageBarItem )
 
 1503       mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
 
 1504       mConstraintsFailMessageBarItem = 
nullptr;
 
 1507     isEditable = isEditable & mValidConstraints;
 
 1511   QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
 
 1513     okButton->setEnabled( isEditable );
 
 1516 void QgsAttributeForm::init()
 
 1518   QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
 
 1521   QWidget *formWidget = 
nullptr;
 
 1522   mNeedsGeometry = 
false;
 
 1524   bool buttonBoxVisible = 
true;
 
 1528     buttonBoxVisible = mButtonBox->isVisible();
 
 1530     mButtonBox = 
nullptr;
 
 1533   if ( mSearchButtonBox )
 
 1535     delete mSearchButtonBox;
 
 1536     mSearchButtonBox = 
nullptr;
 
 1539   qDeleteAll( mWidgets );
 
 1542   while ( QWidget *w = this->findChild<QWidget *>() )
 
 1548   QVBoxLayout *vl = 
new QVBoxLayout();
 
 1549   vl->setContentsMargins( 0, 0, 0, 0 );
 
 1551   mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
 
 1552   vl->addWidget( mMessageBar );
 
 1557   QGridLayout *layout = 
new QGridLayout();
 
 1558   QWidget *container = 
new QWidget();
 
 1559   container->setLayout( layout );
 
 1560   vl->addWidget( container );
 
 1562   mFormEditorWidgets.clear();
 
 1563   mFormWidgets.clear();
 
 1566   setContentsMargins( 0, 0, 0, 0 );
 
 1575     if ( file && file->open( QFile::ReadOnly ) )
 
 1579       QFileInfo fi( file->fileName() );
 
 1580       loader.setWorkingDirectory( fi.dir() );
 
 1581       formWidget = loader.load( file, 
this );
 
 1584         formWidget->setWindowFlags( Qt::Widget );
 
 1585         layout->addWidget( formWidget );
 
 1588         mButtonBox = findChild<QDialogButtonBox *>();
 
 1591         formWidget->installEventFilter( 
this );
 
 1603     int columnCount = 1;
 
 1604     bool hasRootFields = 
false;
 
 1605     bool addSpacer = 
true;
 
 1614         if ( !containerDef )
 
 1619           tabWidget = 
nullptr;
 
 1620           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
 
 1621           if ( widgetInfo.labelStyle.overrideColor )
 
 1623             if ( widgetInfo.labelStyle.color.isValid() )
 
 1625               widgetInfo.widget->setStyleSheet( QStringLiteral( 
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
 
 1628           if ( widgetInfo.labelStyle.overrideFont )
 
 1630             widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
 
 1632           layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
 
 1644             layout->addWidget( tabWidget, row, column, 1, 2 );
 
 1648           QWidget *tabPage = 
new QWidget( tabWidget );
 
 1650           tabWidget->addTab( tabPage, widgDef->name() );
 
 1651           tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
 
 1655             registerContainerInformation( 
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
 
 1657           QGridLayout *tabPageLayout = 
new QGridLayout();
 
 1658           tabPage->setLayout( tabPageLayout );
 
 1660           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
 
 1661           tabPageLayout->addWidget( widgetInfo.widget );
 
 1666         hasRootFields = 
true;
 
 1667         tabWidget = 
nullptr;
 
 1668         WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
 
 1671         if ( widgetInfo.showLabel )
 
 1673           if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
 
 1675             collapsibleGroupBox->
setStyleSheet( QStringLiteral( 
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
 
 1678           if ( widgetInfo.labelStyle.overrideFont )
 
 1680             collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
 
 1683           collapsibleGroupBox->setTitle( widgetInfo.labelText );
 
 1686         QVBoxLayout *collapsibleGroupBoxLayout = 
new QVBoxLayout();
 
 1687         collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
 
 1688         collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
 
 1690         QVBoxLayout *
c = 
new QVBoxLayout();
 
 1691         c->addWidget( collapsibleGroupBox );
 
 1692         layout->addLayout( 
c, row, column, 1, 2 );
 
 1700         hasRootFields = 
true;
 
 1701         tabWidget = 
nullptr;
 
 1702         WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
 
 1703         QLabel *label = 
new QLabel( widgetInfo.labelText );
 
 1705         if ( widgetInfo.labelStyle.overrideColor )
 
 1707           if ( widgetInfo.labelStyle.color.isValid() )
 
 1709             label->setStyleSheet( QStringLiteral( 
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
 
 1713         if ( widgetInfo.labelStyle.overrideFont )
 
 1715           label->setFont( widgetInfo.labelStyle.font );
 
 1718         label->setToolTip( widgetInfo.toolTip );
 
 1719         if ( columnCount > 1 && !widgetInfo.labelOnTop )
 
 1721           label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
 
 1724         label->setBuddy( widgetInfo.widget );
 
 1727         if ( widgetInfo.widget
 
 1728              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
 
 1729              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
 
 1730              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
 
 1733         if ( !widgetInfo.showLabel )
 
 1735           QVBoxLayout *
c = 
new QVBoxLayout();
 
 1736           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 1737           c->addWidget( widgetInfo.widget );
 
 1738           layout->addLayout( 
c, row, column, 1, 2 );
 
 1741         else if ( widgetInfo.labelOnTop )
 
 1743           QVBoxLayout *
c = 
new QVBoxLayout();
 
 1744           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 1745           c->addWidget( label );
 
 1746           c->addWidget( widgetInfo.widget );
 
 1747           layout->addLayout( 
c, row, column, 1, 2 );
 
 1752           layout->addWidget( label, row, column++ );
 
 1753           layout->addWidget( widgetInfo.widget, row, column++ );
 
 1757         if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
 
 1760           const int fieldIdx = fieldElement->
idx();
 
 1761           if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
 
 1763             const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
 
 1767               if ( property.isActive() && ! 
property.expressionString().isEmpty() )
 
 1769                 mLabelDataDefinedProperties[ label ] = property;
 
 1776       if ( column >= columnCount * 2 )
 
 1783     if ( hasRootFields && addSpacer )
 
 1785       QSpacerItem *spacerItem = 
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
 
 1786       layout->addItem( spacerItem, row, 0 );
 
 1787       layout->setRowStretch( row, 1 );
 
 1790     formWidget = container;
 
 1799     formWidget = 
new QWidget( 
this );
 
 1800     QGridLayout *gridLayout = 
new QGridLayout( formWidget );
 
 1801     formWidget->setLayout( gridLayout );
 
 1807       scrollArea->setWidget( formWidget );
 
 1808       scrollArea->setWidgetResizable( 
true );
 
 1809       scrollArea->setFrameShape( QFrame::NoFrame );
 
 1810       scrollArea->setFrameShadow( QFrame::Plain );
 
 1811       scrollArea->setFocusProxy( 
this );
 
 1812       layout->addWidget( scrollArea );
 
 1816       layout->addWidget( formWidget );
 
 1831       QString labelText = fieldName;
 
 1832       labelText.replace( 
'&', QLatin1String( 
"&&" ) ); 
 
 1836       if ( widgetSetup.
type() == QLatin1String( 
"Hidden" ) )
 
 1842       QLabel *label = 
new QLabel( labelText );
 
 1844       QSvgWidget *i = 
new QSvgWidget();
 
 1845       i->setFixedSize( 18, 18 );
 
 1850         if ( property.isActive() && ! property.expressionString().isEmpty() )
 
 1852           mLabelDataDefinedProperties[ label ] = property;
 
 1858       QWidget *w = 
nullptr;
 
 1863         mFormEditorWidgets.insert( idx, formWidget );
 
 1864         mFormWidgets.append( formWidget );
 
 1867         label->setBuddy( eww->
widget() );
 
 1871         w = 
new QLabel( QStringLiteral( 
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr( 
"Failed to create widget with type '%1'" ).arg( widgetSetup.
type() ) ) );
 
 1880         addWidgetWrapper( eww );
 
 1881         mIconMap[eww->
widget()] = i;
 
 1886         gridLayout->addWidget( label, row++, 0, 1, 2 );
 
 1887         gridLayout->addWidget( w, row++, 0, 1, 2 );
 
 1888         gridLayout->addWidget( i, row++, 0, 1, 2 );
 
 1892         gridLayout->addWidget( label, row, 0 );
 
 1893         gridLayout->addWidget( w, row, 1 );
 
 1894         gridLayout->addWidget( i, row++, 2 );
 
 1908       QVBoxLayout *collapsibleGroupBoxLayout = 
new QVBoxLayout();
 
 1909       collapsibleGroupBoxLayout->addWidget( formWidget );
 
 1910       collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
 
 1912       gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
 
 1914       mWidgets.append( rww );
 
 1915       mFormWidgets.append( formWidget );
 
 1920       QSpacerItem *spacerItem = 
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
 
 1921       gridLayout->addItem( spacerItem, row, 0 );
 
 1922       gridLayout->setRowStretch( row, 1 );
 
 1927   updateFieldDependencies();
 
 1931     mButtonBox = 
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
 
 1932     mButtonBox->setObjectName( QStringLiteral( 
"buttonBox" ) );
 
 1933     layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
 
 1935   mButtonBox->setVisible( buttonBoxVisible );
 
 1937   if ( !mSearchButtonBox )
 
 1939     mSearchButtonBox = 
new QWidget();
 
 1940     QHBoxLayout *boxLayout = 
new QHBoxLayout();
 
 1941     boxLayout->setContentsMargins( 0, 0, 0, 0 );
 
 1942     mSearchButtonBox->setLayout( boxLayout );
 
 1943     mSearchButtonBox->setObjectName( QStringLiteral( 
"searchButtonBox" ) );
 
 1945     QPushButton *clearButton = 
new QPushButton( tr( 
"&Reset Form" ), mSearchButtonBox );
 
 1947     boxLayout->addWidget( clearButton );
 
 1948     boxLayout->addStretch( 1 );
 
 1950     QPushButton *flashButton = 
new QPushButton();
 
 1951     flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1952     flashButton->setText( tr( 
"&Flash Features" ) );
 
 1953     connect( flashButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchFlash );
 
 1954     boxLayout->addWidget( flashButton );
 
 1956     QPushButton *openAttributeTableButton = 
new QPushButton();
 
 1957     openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1958     openAttributeTableButton->setText( tr( 
"Show in &Table" ) );
 
 1959     openAttributeTableButton->setToolTip( tr( 
"Open the attribute table editor with the filtered features" ) );
 
 1960     connect( openAttributeTableButton, &QToolButton::clicked, 
this, [ = ]
 
 1964     boxLayout->addWidget( openAttributeTableButton );
 
 1966     QPushButton *zoomButton = 
new QPushButton();
 
 1967     zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1968     zoomButton->setText( tr( 
"&Zoom to Features" ) );
 
 1969     connect( zoomButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchZoomTo );
 
 1970     boxLayout->addWidget( zoomButton );
 
 1972     QToolButton *selectButton = 
new QToolButton();
 
 1973     selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1974     selectButton->setText( tr( 
"&Select Features" ) );
 
 1976     selectButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 1977     selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
 
 1978     connect( selectButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchSetSelection );
 
 1979     QMenu *selectMenu = 
new QMenu( selectButton );
 
 1980     QAction *selectAction = 
new QAction( tr( 
"Select Features" ), selectMenu );
 
 1982     connect( selectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchSetSelection );
 
 1983     selectMenu->addAction( selectAction );
 
 1984     QAction *addSelectAction = 
new QAction( tr( 
"Add to Current Selection" ), selectMenu );
 
 1986     connect( addSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchAddToSelection );
 
 1987     selectMenu->addAction( addSelectAction );
 
 1988     QAction *deselectAction = 
new QAction( tr( 
"Remove from Current Selection" ), selectMenu );
 
 1990     connect( deselectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchRemoveFromSelection );
 
 1991     selectMenu->addAction( deselectAction );
 
 1992     QAction *filterSelectAction = 
new QAction( tr( 
"Filter Current Selection" ), selectMenu );
 
 1994     connect( filterSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchIntersectSelection );
 
 1995     selectMenu->addAction( filterSelectAction );
 
 1996     selectButton->setMenu( selectMenu );
 
 1997     boxLayout->addWidget( selectButton );
 
 2001       QToolButton *filterButton = 
new QToolButton();
 
 2002       filterButton->setText( tr( 
"Filter Features" ) );
 
 2003       filterButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 2004       filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 2005       connect( filterButton, &QToolButton::clicked, 
this, &QgsAttributeForm::filterTriggered );
 
 2006       QMenu *filterMenu = 
new QMenu( filterButton );
 
 2007       QAction *filterAndAction = 
new QAction( tr( 
"Filter Within (\"AND\")" ), filterMenu );
 
 2008       connect( filterAndAction, &QAction::triggered, 
this, &QgsAttributeForm::filterAndTriggered );
 
 2009       filterMenu->addAction( filterAndAction );
 
 2010       QAction *filterOrAction = 
new QAction( tr( 
"Extend Filter (\"OR\")" ), filterMenu );
 
 2011       connect( filterOrAction, &QAction::triggered, 
this, &QgsAttributeForm::filterOrTriggered );
 
 2012       filterMenu->addAction( filterOrAction );
 
 2013       filterButton->setMenu( filterMenu );
 
 2014       boxLayout->addWidget( filterButton );
 
 2018       QPushButton *closeButton = 
new QPushButton( tr( 
"Close" ), mSearchButtonBox );
 
 2020       closeButton->setShortcut( Qt::Key_Escape );
 
 2021       boxLayout->addWidget( closeButton );
 
 2024     layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
 
 2042   const auto constMInterfaces = mInterfaces;
 
 2053   QApplication::restoreOverrideCursor();
 
 2056 void QgsAttributeForm::cleanPython()
 
 2058   if ( !mPyFormVarName.isNull() )
 
 2060     QString expr = QStringLiteral( 
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
 
 2065 void QgsAttributeForm::initPython()
 
 2082         if ( !initFilePath.isEmpty() )
 
 2086           if ( inputFile && inputFile->open( QFile::ReadOnly ) )
 
 2089             QTextStream inf( inputFile );
 
 2090 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 
 2091             inf.setCodec( 
"UTF-8" );
 
 2093             initCode = inf.readAll();
 
 2098             QgsLogger::warning( QStringLiteral( 
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
 
 2109         if ( initCode.isEmpty() )
 
 2111           QgsLogger::warning( QStringLiteral( 
"The python code provided in the dialog is empty!" ) );
 
 2122     if ( !initCode.isEmpty() )
 
 2128                                   tr( 
"Python macro could not be run due to missing permissions." ),
 
 2129                                   Qgis::MessageLevel::Warning );
 
 2136     if ( 
QgsPythonRunner::eval( QStringLiteral( 
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
 
 2138       static int sFormId = 0;
 
 2139       mPyFormVarName = QStringLiteral( 
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
 
 2141       QString form = QStringLiteral( 
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
 
 2142                      .arg( mPyFormVarName )
 
 2143                      .arg( ( quint64 ) 
this );
 
 2147       QgsDebugMsg( QStringLiteral( 
"running featureForm init: %1" ).arg( mPyFormVarName ) );
 
 2150       if ( numArgs == QLatin1String( 
"3" ) )
 
 2158         msgBox.setText( tr( 
"The python init function (<code>%1</code>) does not accept three arguments as expected!<br>Please check the function name in the <b>Fields</b> tab of the layer properties." ).arg( initFunction ) );
 
 2161         QString expr = QString( 
"%1(%2)" )
 
 2162                        .arg( mLayer->editFormInit() )
 
 2163                        .arg( mPyFormVarName );
 
 2164         QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr, 
"QgsAttributeFormInterface" );
 
 2174       msgBox.setText( tr( 
"The python init function (<code>%1</code>) could not be found!<br>Please check the function name in the <b>Fields</b> tab of the layer properties." ).arg( initFunction ) );
 
 2182   WidgetInfo newWidgetInfo;
 
 2184   newWidgetInfo.labelStyle = widgetDef->
labelStyle();
 
 2186   switch ( widgetDef->
type() )
 
 2198       mWidgets.append( actionWrapper );
 
 2199       newWidgetInfo.widget = actionWrapper->
widget();
 
 2200       newWidgetInfo.showLabel = 
false;
 
 2213       if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2219         mFormEditorWidgets.insert( fldIdx, formWidget );
 
 2220         mFormWidgets.append( formWidget );
 
 2224         newWidgetInfo.widget = formWidget;
 
 2225         addWidgetWrapper( eww );
 
 2227         newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
 
 2228         newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
 
 2233       newWidgetInfo.labelText.replace( 
'&', QLatin1String( 
"&&" ) ); 
 
 2234       newWidgetInfo.toolTip = QStringLiteral( 
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
 
 2235       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2256       mWidgets.append( rww );
 
 2257       mFormWidgets.append( formWidget );
 
 2259       newWidgetInfo.widget = formWidget;
 
 2260       newWidgetInfo.showLabel = relDef->
showLabel();
 
 2261       newWidgetInfo.labelText = relDef->
label();
 
 2262       if ( newWidgetInfo.labelText.isEmpty() )
 
 2264       newWidgetInfo.labelOnTop = 
true;
 
 2276       if ( columnCount <= 0 )
 
 2280       QWidget *myContainer = 
nullptr;
 
 2284         widgetName = QStringLiteral( 
"QGroupBox" );
 
 2287           groupBox->setTitle( container->
name() );
 
 2288           if ( newWidgetInfo.labelStyle.overrideColor )
 
 2290             if ( newWidgetInfo.labelStyle.color.isValid() )
 
 2292               groupBox->
setStyleSheet( QStringLiteral( 
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
 
 2295           if ( newWidgetInfo.labelStyle.overrideFont )
 
 2297             groupBox->setFont( newWidgetInfo.labelStyle.font );
 
 2300         myContainer = groupBox;
 
 2301         newWidgetInfo.widget = myContainer;
 
 2306         myContainer = 
new QWidget();
 
 2310         scrollArea->setWidget( myContainer );
 
 2311         scrollArea->setWidgetResizable( 
true );
 
 2312         scrollArea->setFrameShape( QFrame::NoFrame );
 
 2313         widgetName = QStringLiteral( 
"QScrollArea QWidget" );
 
 2315         newWidgetInfo.widget = scrollArea;
 
 2320         QString style {QStringLiteral( 
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
 
 2321         newWidgetInfo.widget->setStyleSheet( style );
 
 2324       QGridLayout *gbLayout = 
new QGridLayout();
 
 2325       myContainer->setLayout( gbLayout );
 
 2329       bool addSpacer = 
true;
 
 2331       const QList<QgsAttributeEditorElement *> children = container->
children();
 
 2335         WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
 
 2346         if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
 
 2348           gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
 
 2353           QLabel *mypLabel = 
new QLabel( widgetInfo.labelText );
 
 2355           if ( widgetInfo.labelStyle.overrideColor )
 
 2357             if ( widgetInfo.labelStyle.color.isValid() )
 
 2359               mypLabel->setStyleSheet( QStringLiteral( 
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
 
 2363           if ( widgetInfo.labelStyle.overrideFont )
 
 2365             mypLabel->setFont( widgetInfo.labelStyle.font );
 
 2373             const int fldIdx = fieldDef->
idx();
 
 2374             if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2376               const QString fieldName { fields.
at( fldIdx ).
name() };
 
 2380                 if ( property.isActive() && ! 
property.expressionString().isEmpty() )
 
 2382                   mLabelDataDefinedProperties[ mypLabel ] = property;
 
 2388           mypLabel->setToolTip( widgetInfo.toolTip );
 
 2389           if ( columnCount > 1 && !widgetInfo.labelOnTop )
 
 2391             mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
 
 2394           mypLabel->setBuddy( widgetInfo.widget );
 
 2396           if ( widgetInfo.labelOnTop )
 
 2398             QVBoxLayout *
c = 
new QVBoxLayout();
 
 2399             mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 2400             c->layout()->addWidget( mypLabel );
 
 2401             c->layout()->addWidget( widgetInfo.widget );
 
 2402             gbLayout->addLayout( 
c, row, column, 1, 2 );
 
 2407             gbLayout->addWidget( mypLabel, row, column++ );
 
 2408             gbLayout->addWidget( widgetInfo.widget, row, column++ );
 
 2412         if ( column >= columnCount * 2 )
 
 2418         if ( widgetInfo.widget
 
 2419              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
 
 2420              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
 
 2421              && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
 
 2425         if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
 
 2431         QWidget *spacer = 
new QWidget();
 
 2432         spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
 
 2433         gbLayout->addWidget( spacer, ++row, 0 );
 
 2434         gbLayout->setRowStretch( row, 1 );
 
 2437       newWidgetInfo.labelText = QString();
 
 2438       newWidgetInfo.labelOnTop = 
true;
 
 2439       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2452       mWidgets.append( qmlWrapper );
 
 2454       newWidgetInfo.widget = qmlWrapper->
widget();
 
 2455       newWidgetInfo.labelText = elementDef->
name();
 
 2456       newWidgetInfo.labelOnTop = 
true;
 
 2457       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2469       mWidgets.append( htmlWrapper );
 
 2471       newWidgetInfo.widget = htmlWrapper->
widget();
 
 2472       newWidgetInfo.labelText = elementDef->
name();
 
 2473       newWidgetInfo.labelOnTop = 
true;
 
 2474       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2480       QgsDebugMsg( QStringLiteral( 
"Unknown attribute editor widget type encountered..." ) );
 
 2484   return newWidgetInfo;
 
 2505   mWidgets.append( eww );
 
 2508 void QgsAttributeForm::createWrappers()
 
 2510   QList<QWidget *> myWidgets = findChildren<QWidget *>();
 
 2511   const QList<QgsField> fields = mLayer->
fields().
toList();
 
 2513   const auto constMyWidgets = myWidgets;
 
 2514   for ( QWidget *myWidget : constMyWidgets )
 
 2517     QVariant vRel = myWidget->property( 
"qgisRelation" );
 
 2518     if ( vRel.isValid() )
 
 2528         mWidgets.append( rww );
 
 2533       const auto constFields = fields;
 
 2536         if ( 
field.
name() == myWidget->objectName() )
 
 2541           addWidgetWrapper( eww );
 
 2548 void QgsAttributeForm::afterWidgetInit()
 
 2550   bool isFirstEww = 
true;
 
 2552   const auto constMWidgets = mWidgets;
 
 2561         setFocusProxy( eww->
widget() );
 
 2571       if ( relationWidgetWrapper )
 
 2584   if ( e->type() == QEvent::KeyPress )
 
 2586     QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( e );
 
 2587     if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
 
 2599     QSet< int > &mixedValueFields,
 
 2600     QHash< int, QVariant > &fieldSharedValues )
 const 
 2602   mixedValueFields.clear();
 
 2603   fieldSharedValues.clear();
 
 2609     for ( 
int i = 0; i < mLayer->
fields().count(); ++i )
 
 2611       if ( mixedValueFields.contains( i ) )
 
 2616         fieldSharedValues[i] = f.
attribute( i );
 
 2620         if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
 
 2622           fieldSharedValues.remove( i );
 
 2623           mixedValueFields.insert( i );
 
 2629     if ( mixedValueFields.count() == mLayer->
fields().
count() )
 
 2638 void QgsAttributeForm::layerSelectionChanged()
 
 2651       resetMultiEdit( 
true );
 
 2658   mIsSettingMultiEditFeatures = 
true;
 
 2659   mMultiEditFeatureIds = fids;
 
 2661   if ( fids.isEmpty() )
 
 2664     QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
 
 2665     for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
 
 2667       wIt.value()->initialize( QVariant() );
 
 2669     mIsSettingMultiEditFeatures = 
false;
 
 2676   QSet< int > mixedValueFields;
 
 2677   QHash< int, QVariant > fieldSharedValues;
 
 2678   scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
 
 2685   const auto constMixedValueFields = mixedValueFields;
 
 2686   for ( 
int fieldIndex : std::as_const( mixedValueFields ) )
 
 2690       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2691       QVariantList additionalFieldValues;
 
 2692       for ( 
const QString &additionalField : additionalFields )
 
 2693         additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2694       w->initialize( firstFeature.
attribute( fieldIndex ), 
true, additionalFieldValues );
 
 2697   QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
 
 2698   for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
 
 2703       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2704       for ( 
const QString &additionalField : additionalFields )
 
 2707         if ( constMixedValueFields.contains( index ) )
 
 2714       QVariantList additionalFieldValues;
 
 2717         for ( 
const QString &additionalField : additionalFields )
 
 2718           additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2719         w->initialize( firstFeature.
attribute( sharedValueIt.key() ), 
true, additionalFieldValues );
 
 2723         for ( 
const QString &additionalField : additionalFields )
 
 2726           Q_ASSERT( fieldSharedValues.contains( index ) );
 
 2727           additionalFieldValues << fieldSharedValues.value( index );
 
 2729         w->initialize( sharedValueIt.value(), 
false, additionalFieldValues );
 
 2734   setMultiEditFeatureIdsRelations( fids );
 
 2736   mIsSettingMultiEditFeatures = 
false;
 
 2741   if ( mOwnsMessageBar )
 
 2743   mOwnsMessageBar = 
false;
 
 2744   mMessageBar = messageBar;
 
 2754   QStringList filters;
 
 2757     QString filter = widget->currentFilterExpression();
 
 2758     if ( !filter.isNull() )
 
 2759       filters << 
'(' + filter + 
')';
 
 2762   return filters.join( QLatin1String( 
" AND " ) );
 
 2767   mExtraContextScope.reset( extraScope );
 
 2773   const bool newVisibility = expression.evaluate( expressionContext ).toBool();
 
 2775   if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible )
 
 2783       widget->setVisible( newVisibility );
 
 2786     isVisible = newVisibility;
 
 2789   const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
 
 2791   if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
 
 2796       collapsibleGroupBox->
setCollapsed( newCollapsedState );
 
 2797       isCollapsed = newCollapsedState;
 
 2811   if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
 
 2814   const QString hint = tr( 
"No feature joined" );
 
 2815   const auto constInfos = infos;
 
 2818     if ( !info->isDynamicFormEnabled() )
 
 2823     mJoinedFeatures[info] = joinFeature;
 
 2825     if ( info->hasSubset() )
 
 2829       const auto constSubsetNames = subsetNames;
 
 2830       for ( 
const QString &
field : constSubsetNames )
 
 2832         QString prefixedName = info->prefixedFieldName( 
field );
 
 2834         QString hintText = hint;
 
 2850         QString prefixedName = info->prefixedFieldName( 
field );
 
 2852         QString hintText = hint;
 
 2866 bool QgsAttributeForm::fieldIsEditable( 
int fieldIndex )
 const 
 2871 void QgsAttributeForm::updateFieldDependencies()
 
 2873   mDefaultValueDependencies.clear();
 
 2874   mVirtualFieldsDependencies.clear();
 
 2875   mRelatedLayerFieldsDependencies.clear();
 
 2884     updateFieldDependenciesDefaultValue( eww );
 
 2885     updateFieldDependenciesVirtualFields( eww );
 
 2886     updateRelatedLayerFieldsDependencies( eww );
 
 2894   if ( exp.needsGeometry() )
 
 2895     mNeedsGeometry = 
true;
 
 2897   const QSet<QString> referencedColumns = exp.referencedColumns();
 
 2898   for ( 
const QString &referencedColumn : referencedColumns )
 
 2904       for ( 
const int id : allAttributeIds )
 
 2906         mDefaultValueDependencies.insertMulti( 
id, eww );
 
 2911       mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
 
 2919   if ( expressionField.isEmpty() )
 
 2924   if ( exp.needsGeometry() )
 
 2925     mNeedsGeometry = 
true;
 
 2927   const QSet<QString> referencedColumns = exp.referencedColumns();
 
 2928   for ( 
const QString &referencedColumn : referencedColumns )
 
 2933       for ( 
const int id : allAttributeIds )
 
 2935         mVirtualFieldsDependencies.insertMulti( 
id, eww );
 
 2940       mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
 
 2950     if ( expressionField.contains( QStringLiteral( 
"relation_aggregate" ) )
 
 2951          || expressionField.contains( QStringLiteral( 
"get_features" ) ) )
 
 2952       mRelatedLayerFieldsDependencies.insert( eww );
 
 2956     mRelatedLayerFieldsDependencies.clear();
 
 2961       if ( ! editorWidgetWrapper )
 
 2964       updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
 
 2969 void QgsAttributeForm::setMultiEditFeatureIdsRelations( 
const QgsFeatureIds &fids )
 
 2974     if ( !relationEditorWidget )
 
 2987   mIconMap[eww->
widget()]->hide();
 
 3001         const QString file = QStringLiteral( 
"/mIconJoinNotEditable.svg" );
 
 3002         const QString tooltip = tr( 
"Join settings do not allow editing" );
 
 3003         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 3007         const QString file = QStringLiteral( 
"mIconJoinHasNotUpsertOnEdit.svg" );
 
 3008         const QString tooltip = tr( 
"Join settings do not allow upsert on edit" );
 
 3009         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 3013         const QString file = QStringLiteral( 
"/mIconJoinedLayerNotEditable.svg" );
 
 3014         const QString tooltip = tr( 
"Joined layer is not toggled editable" );
 
 3015         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 3021 void QgsAttributeForm::reloadIcon( 
const QString &file, 
const QString &tooltip, QSvgWidget *sw )
 
 3024   sw->setToolTip( tooltip );