• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MerginMaps / input / 4487852520

pending completion
4487852520

push

github

Unknown Committer
Unknown Commit Message

7953 of 13072 relevant lines covered (60.84%)

107.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.85
/app/attributes/attributecontroller.cpp
1
/***************************************************************************
2
 attributecontroller.cpp
3
  --------------------------------------
4
  Date                 : 20.4.2021
5
  Copyright            : (C) 2021 by Peter Petrik
6
  Email                : zilolv@gmail.com
7
 ***************************************************************************
8
 *                                                                         *
9
 *   This program is free software; you can redistribute it and/or modify  *
10
 *   it under the terms of the GNU General Public License as published by  *
11
 *   the Free Software Foundation; either version 2 of the License, or     *
12
 *   (at your option) any later version.                                   *
13
 *                                                                         *
14
 ***************************************************************************/
15

16
#include "attributecontroller.h"
17
#include "attributeformmodel.h"
18
#include "attributetabmodel.h"
19
#include "fieldvalidator.h"
20

21
#include <QDebug>
22
#include <QSet>
23

24
#include "qgsproject.h"
25
#include "qgsvectorlayer.h"
26
#include "qgsattributeeditorfield.h"
27
#include "qgsattributeeditorrelation.h"
28
#include "qgsattributeeditorcontainer.h"
29
#include "qgsvectorlayerutils.h"
30
#include "qgsvectorlayereditbuffer.h"
31
#include "qgsexpressioncontextutils.h"
32
#include "qgsrelation.h"
33
#include "qgsmessagelog.h"
34
#include "inpututils.h"
35
#include "coreutils.h"
36

37
AttributeController::AttributeController( QObject *parent )
90✔
38
  : QObject( parent )
18✔
39
  , mAttributeTabProxyModel( new AttributeTabProxyModel() )
18✔
40
{
54✔
41
}
36✔
42

43
void AttributeController::reset()
×
44
{
45
  setFeatureLayerPair( FeatureLayerPair() );
×
46
}
×
47

48
AttributeController::~AttributeController() = default;
36✔
49

50

51
FeatureLayerPair AttributeController::featureLayerPair() const
958✔
52
{
53
  return mFeatureLayerPair;
958✔
54
}
55

56
void AttributeController::setFeatureLayerPair( const FeatureLayerPair &pair )
22✔
57
{
58
  if ( mFeatureLayerPair != pair )
22✔
59
  {
17✔
60
    // block signals:
17✔
61
    // everything should be invalidated by
17✔
62
    // featureLayerPairChanged & attributeTabProxyModelChanged signals
17✔
63
    blockSignals( true );
39✔
64

65
    bool hasLayerChanged = mFeatureLayerPair.layer() != pair.layer();
39✔
66
    // Set new active pair
67
    mFeatureLayerPair = pair;
39✔
68
    if ( hasLayerChanged )
39✔
69
    {
17✔
70
      // layer changed!
17✔
71
      updateOnLayerChange();
34✔
72
    }
34✔
73

74
    // feature changed!
17✔
75
    updateOnFeatureChange();
22✔
76

77
    // Done, emit signals
78
    blockSignals( false );
22✔
79
    if ( hasLayerChanged )
22✔
80
    {
81
      emit attributeTabProxyModelChanged();
17✔
82
      emit hasTabsChanged();
17✔
83
    }
17✔
84
    emit featureLayerPairChanged();
22✔
85
    emit hasAnyChangesChanged();
22✔
86
    emit hasValidationErrorsChanged();
22✔
87
  }
22✔
88
}
22✔
89

90
QgsAttributeEditorContainer *AttributeController::autoLayoutTabContainer() const  //#spellok
10✔
91
{
92
  QgsVectorLayer *layer = mFeatureLayerPair.layer();
10✔
93
  Q_ASSERT( layer );
10✔
94

95
  std::unique_ptr<QgsAttributeEditorContainer> root( new QgsAttributeEditorContainer( QLatin1String( "AutoLayoutRoot" ), nullptr ) );
10✔
96
  root->setIsGroupBox( false ); //tab!
10✔
97

98
  const QgsFields fields = layer->fields();
10✔
99
  for ( int i = 0; i < fields.size(); ++i )
46✔
100
  {
101
    QgsAttributeEditorField *field = new QgsAttributeEditorField( fields.at( i ).name(), i, root.get() );
36✔
102
    root->addChildElement( field );
36✔
103
  }
36✔
104
  return root.release();
10✔
105
}
10✔
106

107
QgsEditorWidgetSetup AttributeController::getEditorWidgetSetup( QgsVectorLayer *layer, int fieldIndex ) const
81✔
108
{
109
  QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
81✔
110
  if ( setup.type().isEmpty() )
81✔
111
  {
112
    QgsField field = layer->fields().at( fieldIndex );
7✔
113
    return InputUtils::getEditorWidgetSetup( field );
7✔
114
  }
7✔
115
  return setup;
74✔
116
}
81✔
117

118
void AttributeController::prefillRelationReferenceField()
2✔
119
{
120
  if ( !mParentController || !mLinkedRelation.isValid() )
2✔
121
    return;
1✔
122

123
  const QList<QgsRelation::FieldPair> fieldPairs = mLinkedRelation.fieldPairs();
1✔
124
  for ( const QgsRelation::FieldPair &fieldPair : fieldPairs )
2✔
125
  {
126
    QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
1✔
127
    while ( formItemsIterator != mFormItems.end() )
1✔
128
    {
129
      std::shared_ptr<FormItem> itemData = formItemsIterator.value();
1✔
130
      if ( itemData->field().name() == fieldPair.referencingField() )
1✔
131
      {
132
        QVariant fk = mParentController->featureLayerPair().feature().attribute( fieldPair.referencedField() );
1✔
133
        setFormValue( itemData->id(), fk );
1✔
134
        break;
135
      }
1✔
136
      ++formItemsIterator;
×
137
    }
1✔
138
  }
139
}
2✔
140

141
bool AttributeController::allowTabs( QgsAttributeEditorContainer *container )
7✔
142
{
143
  for ( QgsAttributeEditorElement *element : container->children() )
15✔
144
  {
145
    if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
8✔
146
    {
147
      QgsAttributeEditorContainer *elemContainer = static_cast<QgsAttributeEditorContainer *>( element );
3✔
148
      if ( elemContainer->isGroupBox() )
3✔
149
        return false;
1✔
150
    }
2✔
151
    else
152
      return false;
5✔
153
  }
154

155
  return !container->children().isEmpty();
1✔
156
}
7✔
157

158
VariablesManager *AttributeController::variablesManager() const
×
159
{
160
  return mVariablesManager;
×
161
}
162

163
void AttributeController::setVariablesManager( VariablesManager *variablesManager )
×
164
{
165
  if ( mVariablesManager != variablesManager )
×
166
  {
167
    mVariablesManager = variablesManager;
×
168
    emit variablesManagerChanged();
×
169
  }
×
170
}
×
171

172
RememberAttributesController *AttributeController::rememberAttributesController() const
×
173
{
174
  return mRememberAttributesController;
×
175
}
176

177
void AttributeController::setRememberAttributesController( RememberAttributesController *rememberAttributes )
×
178
{
179
  if ( mRememberAttributesController != rememberAttributes )
×
180
  {
181
    mRememberAttributesController = rememberAttributes;
×
182
    if ( mRememberAttributesController && mFeatureLayerPair.layer() )
×
183
      mRememberAttributesController->storeLayerFields( mFeatureLayerPair.layer() );
×
184
    emit rememberAttributesChanged();
×
185
  }
×
186
}
×
187

188
void AttributeController::flatten(
24✔
189
  QgsAttributeEditorContainer *container,
190
  int parentTabRow,
191
  const QString &parentVisibilityExpressions,
192
  QVector<QUuid> &items
193
)
194
{
195
  QgsVectorLayer *layer = mFeatureLayerPair.layer();
24✔
196
  Q_ASSERT( layer );
24✔
197

198
  for ( QgsAttributeEditorElement *element : container->children() )
115✔
199
  {
200
    switch ( element->type() )
91✔
201
    {
202
      case QgsAttributeEditorElement::AeTypeContainer:
203
      {
204
        QString visibilityExpression = parentVisibilityExpressions;
6✔
205
        QgsAttributeEditorContainer *container = static_cast<QgsAttributeEditorContainer *>( element );
6✔
206
        if ( container->visibilityExpression().enabled() )
6✔
207
        {
208
          if ( visibilityExpression.isNull() )
×
209
            visibilityExpression = container->visibilityExpression().data().expression();
×
210
          else
211
            visibilityExpression += " AND " + container->visibilityExpression().data().expression();
×
212
        }
×
213

214
        flatten( container, parentTabRow, visibilityExpression, items );
6✔
215
        break;
216
      }
6✔
217

218
      case QgsAttributeEditorElement::AeTypeField:
219
      {
220
        QUuid fieldUuid = QUuid::createUuid();
81✔
221

222
        QgsAttributeEditorField *editorField = static_cast<QgsAttributeEditorField *>( element );
99✔
223
        int fieldIndex = editorField->idx();
99✔
224
        if ( fieldIndex < 0 || fieldIndex >= layer->fields().size() )
99✔
225
        {
18✔
226
          qDebug() << "Invalid fieldIndex for editorField!";
18✔
227
          continue;
×
228
        }
229
        QgsField field = layer->fields().at( fieldIndex );
81✔
230
        QStringList expressions;
81✔
231
        QString expression = field.constraints().constraintExpression();
81✔
232

233
        if ( !expression.isEmpty() )
81✔
234
        {
235
          expressions << field.constraints().constraintExpression();
19✔
236
        }
19✔
237

238
        bool isReadOnly = ( layer->editFormConfig().readOnly( fieldIndex ) ) ||
180✔
239
                          ( !field.defaultValueDefinition().expression().isEmpty() && field.defaultValueDefinition().applyOnUpdate() );
81✔
240

241
        const QString groupName = container->isGroupBox() ? container->name() : QString();
81✔
242
        std::shared_ptr<FormItem> formItemData =
243
          std::shared_ptr<FormItem>(
81✔
244
            FormItem::createFieldItem(
81✔
245
              fieldUuid,
246
              field,
247
              groupName,
248
              parentTabRow,
81✔
249
              FormItem::Field,
250
              layer->attributeDisplayName( fieldIndex ),
81✔
251
              !isReadOnly,
81✔
252
              getEditorWidgetSetup( layer, fieldIndex ),
81✔
253
              fieldIndex,
81✔
254
              parentVisibilityExpressions // field doesn't have visibility expression itself
81✔
255
            )
256
          );
257

258
        mFormItems[formItemData->id()] = formItemData;
81✔
259

260

261
        items.append( fieldUuid );
81✔
262
        break;
263
      }
81✔
264

265
      case QgsAttributeEditorElement::AeTypeRelation:
266
      {
267
        QUuid widgetUuid = QUuid::createUuid();
4✔
268

269
        QgsAttributeEditorRelation *relationField = static_cast<QgsAttributeEditorRelation *>( element );
4✔
270
        QgsRelation associatedRelation = relationField->relation();
4✔
271

272
        bool isNmRelation = layer->editFormConfig().widgetConfig( associatedRelation.id() )[QStringLiteral( "nm-rel" )].toBool();
4✔
273
        if ( isNmRelation )
4✔
274
        {
275
          CoreUtils::log( "Relations", "Nm relations are not supported" );
×
276
          continue;
×
277
        }
278

279
        const QString groupName = container->isGroupBox() ? container->name() : QString();
4✔
280
        QString label = relationField->label();
4✔
281
        if ( label.isEmpty() )
4✔
282
        {
283
          label = associatedRelation.name();
2✔
284
          if ( label.isEmpty() ) // relation name can be empty
2✔
285
          {
286
            label = associatedRelation.referencingLayer()->name();
×
287
          }
×
288
        }
2✔
289

290
        std::shared_ptr<FormItem> formItemData =
291
          std::shared_ptr<FormItem>(
4✔
292
            FormItem::createRelationItem(
4✔
293
              widgetUuid,
294
              groupName,
295
              parentTabRow,
4✔
296
              FormItem::Relation,
297
              label,
298
              associatedRelation
299
            )
300
          );
301

302
        mFormItems[formItemData->id()] = formItemData;
4✔
303
        items.append( widgetUuid );
4✔
304

305
        break;
306
      }
4✔
307

308
      case QgsAttributeEditorElement::AeTypeInvalid:
309
        // todo
310
        break;
×
311

312
      case QgsAttributeEditorElement::AeTypeAction:
313
        // todo
314
        break;
×
315

316
      case QgsAttributeEditorElement::AeTypeQmlElement:
317
        // todo
318
        break;
×
319

320
      case QgsAttributeEditorElement::AeTypeHtmlElement:
321
        // todo
322
        break;
×
323
    }
324
  }
325
}
24✔
326

327
void AttributeController::createTab( QgsAttributeEditorContainer *container )
18✔
328
{
329
  Q_ASSERT( container );
18✔
330

331
  int tabRow = mTabItems.size();
18✔
332

333
  QgsExpression expr;
18✔
334
  if ( container->visibilityExpression().enabled() )
18✔
335
  {
336
    expr = container->visibilityExpression().data();
×
337
  }
×
338

339
  QVector<QUuid> formItemsUuids;
18✔
340
  flatten( container, tabRow, QString(), formItemsUuids );
18✔
341

342
  std::shared_ptr<TabItem> tabItem( new TabItem( tabRow,
18✔
343
                                    container->name(),
18✔
344
                                    formItemsUuids,
345
                                    expr )
346
                                  );
347

348
  mTabItems.push_back( tabItem );
18✔
349
}
18✔
350

351
void AttributeController::clearAll()
17✔
352
{
353
  mAttributeFormProxyModelForTabItem.clear();
17✔
354
  mAttributeTabProxyModel.reset( new AttributeTabProxyModel() );
17✔
355
  setHasValidationErrors( false );
17✔
356
  mFormItems.clear();
17✔
357
  mTabItems.clear();
17✔
358
  mExpressionFieldsOutsideForm.clear();
17✔
359
  mHasTabs = false;
17✔
360
}
17✔
361

362
void AttributeController::updateOnLayerChange()
17✔
363
{
364
  clearAll();
17✔
365

366
  // 1) DATA
367
  QgsVectorLayer *layer = mFeatureLayerPair.layer();
17✔
368
  if ( layer )
17✔
369
  {
370
    if ( layer->editFormConfig().layout() == QgsEditFormConfig::TabLayout )
17✔
371
    {
372
      QgsAttributeEditorContainer *root = layer->editFormConfig().invisibleRootContainer();
7✔
373
      if ( root->columnCount() > 1 )
7✔
374
      {
375
        qDebug() << "root tab in manual config has multiple columns. not supported on mobile devices!";
×
376
        root->setColumnCount( 1 );
×
377
      }
×
378

379
      mHasTabs = allowTabs( root );
7✔
380
      if ( mHasTabs )
7✔
381
      {
382
        for ( QgsAttributeEditorElement *element : root->children() )
3✔
383
        {
384
          if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
2✔
385
          {
386
            QgsAttributeEditorContainer *container = static_cast<QgsAttributeEditorContainer *>( element );
2✔
387
            if ( container->columnCount() > 1 )
2✔
388
            {
389
              qDebug() << "tab " << container->name() << " in manual config has multiple columns. not supported on mobile devices!";
×
390
              container->setColumnCount( 1 );
×
391
            }
×
392
            createTab( container );
2✔
393
          }
2✔
394
        }
395
      }
1✔
396
      else
397
      {
398
        createTab( root );
6✔
399
      }
400
    }
7✔
401
    else
402
    {
403
      // Auto-Generated Layout
404
      // We create fake root tab
405
      QgsAttributeEditorContainer *tab = autoLayoutTabContainer();
10✔
406

407
      // We need to look for relations and include them into form,
408
      // in auto-generated layout they are not included in form config
409
      discoverRelations( tab );
10✔
410

411
      createTab( tab );
10✔
412
    }
413

414
    if ( mRememberAttributesController )
17✔
415
      mRememberAttributesController->storeLayerFields( layer );
×
416
  }
17✔
417

418
  // 2) MODELS
419
  // for all other models, ownership is managed by Qt parent system
420
  AttributeTabModel *tabModel = new AttributeTabModel( mAttributeTabProxyModel.get(), this, mTabItems.size() );
17✔
421
  mAttributeTabProxyModel->setSourceModel( tabModel );
17✔
422
  mAttributeFormProxyModelForTabItem.resize( mTabItems.size() );
17✔
423
  QVector<std::shared_ptr<TabItem>>::iterator tabItemsIterator = mTabItems.begin();
17✔
424
  while ( tabItemsIterator != mTabItems.end() )
35✔
425
  {
426
    std::shared_ptr<TabItem> item = *tabItemsIterator;
18✔
427
    const QVector<QUuid> &formItems = item->formItems();
18✔
428

429
    AttributeFormProxyModel *proxyFormModel = new AttributeFormProxyModel( mAttributeTabProxyModel.get() );
18✔
430
    AttributeFormModel *formModel = new AttributeFormModel( mAttributeTabProxyModel.get(), this, formItems );
18✔
431
    proxyFormModel->setSourceModel( formModel );
18✔
432
    mAttributeFormProxyModelForTabItem[item->tabIndex()] = proxyFormModel;
18✔
433
    ++tabItemsIterator;
18✔
434
  }
18✔
435

436
  // collect fields which have default value expression and are not in the form
437
  QSet<int> fieldIndexes;
17✔
438
  QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
17✔
439
  while ( formItemsIterator != mFormItems.end() )
102✔
440
  {
441
    std::shared_ptr<FormItem> item = formItemsIterator.value();
85✔
442
    if ( item->type() == FormItem::Field )
85✔
443
    {
444
      fieldIndexes << item->fieldIndex();
81✔
445
    }
81✔
446

447
    ++formItemsIterator;
85✔
448
  }
85✔
449

450
  for ( int i = 0; i < mFeatureLayerPair.layer()->fields().count(); i++ )
102✔
451
  {
452
    if ( !fieldIndexes.contains( i ) )
85✔
453
    {
454
      QgsField f = mFeatureLayerPair.layer()->fields().at( i );
4✔
455
      const QgsDefaultValue defaultDefinition = f.defaultValueDefinition();
4✔
456

457
      if ( !defaultDefinition.expression().isEmpty() )
4✔
458
      {
459
        mExpressionFieldsOutsideForm << i;
3✔
460
      }
3✔
461
    }
4✔
462
  }
85✔
463

464
}
17✔
465

466
void AttributeController::updateOnFeatureChange()
22✔
467
{
468
  const QgsFeature feature = mFeatureLayerPair.feature();
22✔
469

470
  QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
22✔
471
  while ( formItemsIterator != mFormItems.end() )
145✔
472
  {
473
    std::shared_ptr<FormItem> itemData = formItemsIterator.value();
123✔
474
    if ( itemData->type() == FormItem::Field )
123✔
475
    {
476
      int fieldIndex = itemData->fieldIndex();
118✔
477
      const QVariant newVal = feature.attribute( fieldIndex );
118✔
478
      mFormItems[itemData->id()]->setOriginalValue( newVal );
118✔
479
      mFormItems[itemData->id()]->setRawValue( newVal ); // we need to set raw value as well, as we use it in form now
118✔
480
      if ( mRememberAttributesController && isNewFeature() ) // this is a new feature
118✔
481
      {
482
        QVariant rememberedValue;
×
483
        bool shouldUseRememberedValue = mRememberAttributesController->rememberedValue(
×
484
                                          mFeatureLayerPair.layer(),
×
485
                                          fieldIndex,
×
486
                                          rememberedValue
487
                                        );
488
        if ( shouldUseRememberedValue )
×
489
        {
490
          mFeatureLayerPair.featureRef().setAttribute( fieldIndex, rememberedValue );
×
491
          itemData->setRawValue( rememberedValue );
×
492
        }
×
493
      }
×
494
    }
118✔
495
    ++formItemsIterator;
123✔
496
  }
123✔
497

498
  bool formValueChange = false;
22✔
499
  // if feature geometry was changed we also need recalculate defaults
500
  // as some attributes may contain expressions which use feature geometry
501
  if ( mFeatureLayerPair.layer()->isEditable() )
22✔
502
  {
503
    formValueChange = mFeatureLayerPair.layer()->editBuffer()->changedGeometries().contains( feature.id() );
×
504
  }
×
505
  recalculateDerivedItems( formValueChange, isNewFeature() );
22✔
506
}
22✔
507

508
bool AttributeController::isNewFeature() const
132✔
509
{
510
  QgsFeatureId id = mFeatureLayerPair.feature().id();
132✔
511
  return FID_IS_NEW( id ) || FID_IS_NULL( id );
132✔
512
}
×
513

514
void AttributeController::acquireId()
×
515
{
516
  if ( !mFeatureLayerPair.layer() )
×
517
    return;
×
518

519
  QgsFeature feat = mFeatureLayerPair.feature();
×
520
  bool featureIsNotYetAdded = FID_IS_NULL( feat.id() );
×
521

522
  startEditing();
×
523

524
  if ( featureIsNotYetAdded )
×
525
  {
526
    if ( !mFeatureLayerPair.layer()->addFeature( feat ) )
×
527
    {
528
      QgsMessageLog::logMessage( tr( "Feature could not be added" ),
×
529
                                 QStringLiteral( "Input" ),
×
530
                                 Qgis::Critical );
531

532
    }
×
533
  }
×
534
  else
535
  {
536
    if ( !mFeatureLayerPair.layer()->updateFeature( feat ) )
×
537
      QgsMessageLog::logMessage( tr( "Cannot update feature" ),
×
538
                                 QStringLiteral( "Input" ),
×
539
                                 Qgis::Warning );
540
  }
541

542
  connect( mFeatureLayerPair.layer(), &QgsVectorLayer::featureAdded, this, &AttributeController::onFeatureAdded );
×
543

544
  if ( !commit() )
×
545
  {
546
    emit commitFailed();
×
547
  }
×
548

549
  disconnect( mFeatureLayerPair.layer(), &QgsVectorLayer::featureAdded, this, &AttributeController::onFeatureAdded );
×
550
}
×
551

552
bool AttributeController::recalculateDefaultValues(
118✔
553
  QSet<QUuid> &changedFormItems,
554
  QgsExpressionContext &expressionContext,
555
  bool isFormValueChange,
556
  bool isFirstUpdateOfNewFeature
557
)
558
{
559
  // update default values for fields which are not in the form
560
  for ( const int idx : mExpressionFieldsOutsideForm )
143✔
561
  {
562
    QgsField f = mFeatureLayerPair.layer()->fields().at( idx );
25✔
563
    const QgsDefaultValue defaultDefinition = f.defaultValueDefinition();
25✔
564

565
    bool shouldApplyDefaultValue =
25✔
566
      !defaultDefinition.expression().isEmpty() &&
50✔
567
      ( isFirstUpdateOfNewFeature || ( isFormValueChange && defaultDefinition.applyOnUpdate() ) );
41✔
568

569
    if ( shouldApplyDefaultValue )
25✔
570
    {
571
      QgsExpression exp( defaultDefinition.expression() );
11✔
572
      exp.prepare( &expressionContext );
11✔
573
      if ( exp.hasParserError() )
11✔
574
        QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has parser error: %3" ).arg(
×
575
                                     mFeatureLayerPair.layer()->name(),
×
576
                                     f.name(),
×
577
                                     exp.parserErrorString() ),
×
578
                                   QStringLiteral( "Input" ),
×
579
                                   Qgis::Warning );
580

581
      QVariant value = exp.evaluate( &expressionContext );
11✔
582

583
      if ( exp.hasEvalError() )
11✔
584
        QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has evaluation error: %3" ).arg(
×
585
                                     mFeatureLayerPair.layer()->name(),
×
586
                                     f.name(),
×
587
                                     exp.evalErrorString() ),
×
588
                                   QStringLiteral( "Input" ),
×
589
                                   Qgis::Warning );
590
      else
591
      {
592
        QVariant val( value );
11✔
593
        if ( !f.convertCompatible( val ) )
11✔
594
        {
595
          QString msg( tr( "Value \"%1\" %4 could not be converted to a compatible value for field %2(%3)." ).arg( value.toString(), f.name(), f.typeName(), value.isNull() ? "NULL" : "NOT NULL" ) );
×
596
          QgsMessageLog::logMessage( msg );
×
597
        }
×
598
        else
599
        {
600
          mFeatureLayerPair.featureRef().setAttribute( idx, val );
11✔
601
          expressionContext.setFeature( featureLayerPair().featureRef() );
11✔
602
        }
603
      }
11✔
604
    }
11✔
605
  }
25✔
606

607
  // evaluate default values for fields in the form
608
  bool hasChanges = false;
118✔
609
  QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
118✔
610
  while ( formItemsIterator != mFormItems.end() )
968✔
611
  {
612
    std::shared_ptr<FormItem> item = formItemsIterator.value();
850✔
613
    const QgsField field = item->field();
850✔
614
    const QgsDefaultValue defaultDefinition = field.defaultValueDefinition();
850✔
615

616
    bool shouldApplyDefaultValue =
850✔
617
      !defaultDefinition.expression().isEmpty() &&
917✔
618
      ( isFirstUpdateOfNewFeature || ( isFormValueChange && defaultDefinition.applyOnUpdate() ) );
110✔
619

620
    if ( shouldApplyDefaultValue )
850✔
621
    {
622
      QgsExpression exp( field.defaultValueDefinition().expression() );
62✔
623
      exp.prepare( &expressionContext );
62✔
624
      if ( exp.hasParserError() )
62✔
625
        QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has parser error: %3" ).arg(
×
626
                                     mFeatureLayerPair.layer()->name(),
×
627
                                     field.name(),
×
628
                                     exp.parserErrorString() ),
×
629
                                   QStringLiteral( "Input" ),
×
630
                                   Qgis::Warning );
631

632
      QVariant value = exp.evaluate( &expressionContext );
62✔
633

634
      if ( exp.hasEvalError() )
62✔
635
        QgsMessageLog::logMessage( tr( "Default value expression for %1:%2 has evaluation error: %3" ).arg(
×
636
                                     mFeatureLayerPair.layer()->name(),
×
637
                                     field.name(),
×
638
                                     exp.evalErrorString() ),
×
639
                                   QStringLiteral( "Input" ),
×
640
                                   Qgis::Warning );
641
      else
642
      {
643
        QVariant val( value );
62✔
644
        if ( !field.convertCompatible( val ) )
62✔
645
        {
646
          QString msg( tr( "Value \"%1\" %4 could not be converted to a compatible value for field %2(%3)." ).arg( value.toString(), field.name(), field.typeName(), value.isNull() ? "NULL" : "NOT NULL" ) );
×
647
          QgsMessageLog::logMessage( msg );
×
648
        }
×
649
        else
650
        {
651
          QVariant oldVal = mFeatureLayerPair.feature().attribute( item->fieldIndex() );
62✔
652

653
          if ( val != oldVal )
62✔
654
          {
655
            mFeatureLayerPair.featureRef().setAttribute( item->fieldIndex(), val );
14✔
656
            item->setRawValue( val );
14✔
657
            emit formDataChanged( item->id(), { AttributeFormModel::RawValue, AttributeFormModel::RawValueIsNull } );
14✔
658
            // Update also expression context after an attribute change
659
            expressionContext.setFeature( featureLayerPair().featureRef() );
14✔
660
            changedFormItems.insert( item->id() );
14✔
661
            hasChanges = true;
14✔
662
          }
14✔
663
        }
62✔
664
      }
62✔
665
    }
62✔
666

667
    if ( isFirstUpdateOfNewFeature )
850✔
668
    {
669
      // check if item is a slider, if so and it does not have default value,
670
      // set it's initial value from NULL to zero or minimum
671
      bool isSlider = item->editorWidgetType() == QStringLiteral( "Range" ) &&
93✔
672
                      item->editorWidgetConfig().value( QStringLiteral( "Style" ) ) == QStringLiteral( "Slider" );
9✔
673
      bool hasDefaultValueDefinition = !defaultDefinition.expression().isEmpty();
84✔
674

675
      if ( isSlider && !hasDefaultValueDefinition )
84✔
676
      {
677
        double min = item->editorWidgetConfig().value( "Min" ).toDouble();
1✔
678
        double max = item->editorWidgetConfig().value( "Max" ).toDouble();
1✔
679
        double valueToSet = 0;
1✔
680

681
        if ( !( min <= valueToSet && valueToSet <= max ) )
1✔
682
        {
683
          valueToSet = qMin( min, max );
×
684
        }
×
685

686
        mFeatureLayerPair.featureRef().setAttribute( item->fieldIndex(), valueToSet );
1✔
687
        item->setRawValue( valueToSet );
1✔
688
        emit formDataChanged( item->id(), { AttributeFormModel::RawValue } );
1✔
689
        changedFormItems.insert( item->id() );
1✔
690
      }
1✔
691
    }
84✔
692

693
    ++formItemsIterator;
850✔
694
  }
850✔
695
  return hasChanges;
118✔
696
}
×
697

698
void AttributeController::recalculateDerivedItems( bool isFormValueChange, bool isFirstUpdateOfNewFeature )
114✔
699
{
700
  QSet<QUuid> changedFormItems;
114✔
701

702
  QgsVectorLayer *layer = mFeatureLayerPair.layer();
114✔
703
  if ( !layer || !layer->isValid() )
114✔
704
    return;
×
705

706
  if ( !mFeatureLayerPair.feature().isValid() )
114✔
707
    return;
4✔
708

709
  // Create context
710
  QgsFields fields = mFeatureLayerPair.feature().fields();
110✔
711
  QgsExpressionContext expressionContext = layer->createExpressionContext();
110✔
712
  expressionContext << QgsExpressionContextUtils::formScope( mFeatureLayerPair.feature() );
110✔
713
  if ( mVariablesManager )
110✔
714
    expressionContext << mVariablesManager->positionScope();
×
715

716
  expressionContext.setFields( fields );
110✔
717
  expressionContext.setFeature( featureLayerPair().featureRef() );
110✔
718

719
  // Evaluate default values
720
  // it could be recursive, so
721
  // let say try few times
722
  const int LIMIT = 3;
110✔
723
  int tryNumber = 0;
110✔
724
  bool anyValueChanged = true;
110✔
725
  while ( anyValueChanged && tryNumber < LIMIT )
228✔
726
  {
727
    anyValueChanged = recalculateDefaultValues( changedFormItems, expressionContext, isFormValueChange, isFirstUpdateOfNewFeature );
118✔
728
    ++tryNumber;
118✔
729
  }
730
  if ( anyValueChanged )
110✔
731
  {
732
    // ok we cut the loop on limit...
733
    qDebug() << "Evaluation of default values was not finished in " << LIMIT << " tries. Giving up, sorry!";
×
734
  }
×
735

736
  // Evaluate tab items visiblity
737
  {
738
    QVector<std::shared_ptr<TabItem>>::iterator tabItemsIterator = mTabItems.begin();
110✔
739
    while ( tabItemsIterator != mTabItems.end() )
222✔
740
    {
741
      std::shared_ptr<TabItem> item = *tabItemsIterator;
112✔
742
      QgsExpression exp = item->visibilityExpression();
112✔
743
      exp.prepare( &expressionContext );
112✔
744
      bool visible = true;
112✔
745
      if ( exp.isValid() )
112✔
746
      {
747
        visible = exp.evaluate( &expressionContext ).toBool();
×
748
      }
×
749

750
      if ( item->isVisible() != visible )
112✔
751
      {
752
        item->setVisible( visible );
16✔
753
        emit tabDataChanged( item->tabIndex() );
16✔
754
      }
16✔
755
      ++tabItemsIterator;
112✔
756
    }
112✔
757
  }
758

759
  // Evaluate form items visibility
760
  {
761
    QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
110✔
762
    while ( formItemsIterator != mFormItems.end() )
900✔
763
    {
764
      std::shared_ptr<FormItem> item = formItemsIterator.value();
790✔
765
      bool visible = true;
790✔
766
      if ( item->editorWidgetType() == QLatin1String( "Hidden" ) )
790✔
767
      {
768
        visible = false;
4✔
769
      }
4✔
770
      else
771
      {
772
        QgsExpression exp = item->visibilityExpression();
786✔
773
        exp.prepare( &expressionContext );
786✔
774

775
        if ( exp.isValid() )
786✔
776
          visible = exp.evaluate( &expressionContext ).toInt();
×
777
      }
786✔
778

779
      if ( item->visible() != visible )
790✔
780
      {
781
        item->setVisible( visible );
76✔
782
        changedFormItems << item->id();
76✔
783
      }
76✔
784
      ++formItemsIterator;
790✔
785
    }
790✔
786
  }
787

788
  // Evaluate form items value state - hard/soft constraints, value validity
789
  {
790
    bool containsValidationError = false;
110✔
791
    {
792
      QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
110✔
793
      while ( formItemsIterator != mFormItems.end() )
900✔
794
      {
795
        std::shared_ptr<FormItem> item = formItemsIterator.value();
790✔
796
        if ( item->type() == FormItem::Field )
790✔
797
        {
798
          QString validationMessage;
787✔
799
          FieldValidator::ValidationStatus validationStatus;
800

801
          validationStatus = FieldValidator::validate( featureLayerPair(), *item, validationMessage );
787✔
802

803
          if ( validationStatus == FieldValidator::Error )
787✔
804
          {
805
            containsValidationError = true;
109✔
806
          }
109✔
807

808
          if ( validationMessage != item->validationMessage() )
787✔
809
          {
810
            item->setValidationStatus( validationStatus );
50✔
811
            item->setValidationMessage( validationMessage );
50✔
812
            changedFormItems.insert( item->id() );
50✔
813
          }
50✔
814
        }
787✔
815
        ++formItemsIterator;
790✔
816
      }
790✔
817
    }
818
    setHasValidationErrors( containsValidationError );
110✔
819
  }
820

821
  // Check if we have any changes
822
  bool anyChanges = isNewFeature();
110✔
823
  if ( !anyChanges )
110✔
824
  {
825
    QMap<QUuid, std::shared_ptr<FormItem>>::iterator formItemsIterator = mFormItems.begin();
12✔
826
    while ( formItemsIterator != mFormItems.end() )
67✔
827
    {
828
      std::shared_ptr<FormItem> item = formItemsIterator.value();
56✔
829
      if ( item->type() == FormItem::Field )
56✔
830
      {
831
        if ( item->originalValue() != mFeatureLayerPair.feature().attribute( item->fieldIndex() ) )
54✔
832
        {
833
          anyChanges = true;
1✔
834
          break;
1✔
835
        }
836
      }
53✔
837

838
      ++formItemsIterator;
55✔
839
    }
56✔
840
  }
12✔
841
  setHasAnyChanges( anyChanges );
110✔
842

843
  // Emit all signals
844
  QSet<QUuid>::const_iterator i = changedFormItems.constBegin();
110✔
845
  while ( i != changedFormItems.constEnd() )
238✔
846
  {
847
    emit formDataChanged( *i );
128✔
848
    ++i;
128✔
849
  }
850

851
  if ( anyChanges )
110✔
852
    emit formRecalculated();
99✔
853
}
114✔
854

855
bool AttributeController::hasValidationErrors() const
10✔
856
{
857
  return mHasValidationErrors;
10✔
858
}
859

860
bool AttributeController::hasTabs() const
5✔
861
{
862
  return mHasTabs;
5✔
863
}
864

865
AttributeTabProxyModel *AttributeController::attributeTabProxyModel() const
4✔
866
{
867
  Q_ASSERT( mAttributeTabProxyModel );
4✔
868
  return mAttributeTabProxyModel.get();
4✔
869
}
870

871
int AttributeController::tabCount() const
51✔
872
{
873
  return mTabItems.size();
51✔
874
}
875

876
AttributeFormProxyModel *AttributeController::attributeFormProxyModelForTab( int tabRow ) const
4✔
877
{
878
  if ( isValidTabId( tabRow ) )
4✔
879
  {
880
    return mAttributeFormProxyModelForTabItem[tabRow];
4✔
881
  }
882
  else
883
  {
884
    return nullptr;
×
885
  }
886
}
4✔
887

888
bool AttributeController::deleteFeature()
×
889
{
890
  if ( !mFeatureLayerPair.layer() )
×
891
    return false;
×
892

893
  bool rv = true;
×
894

895
  if ( !startEditing() )
×
896
  {
897
    rv = false;
×
898
  }
×
899

900
  bool isDeleted = mFeatureLayerPair.layer()->deleteFeature( mFeatureLayerPair.feature().id() );
×
901
  rv = commit();
×
902

903
  if ( !isDeleted || !rv )
×
904
  {
905
    QgsMessageLog::logMessage( tr( "Cannot delete feature" ),
×
906
                               QStringLiteral( "Input" ),
×
907
                               Qgis::Warning );
908
    emit commitFailed();
×
909
  }
×
910
  else
911
  {
912
    mFeatureLayerPair = FeatureLayerPair();
×
913
    emit featureLayerPairChanged();
×
914
    emit changesCommited();
×
915
  }
916

917
  return rv;
×
918
}
×
919

920
bool AttributeController::rollback()
×
921
{
922
  if ( !mFeatureLayerPair.layer() )
×
923
    return false;
×
924

925
  if ( !mFeatureLayerPair.layer()->isEditable() )
×
926
  {
927
    return false;
×
928
  }
929

930
  if ( !mFeatureLayerPair.layer()->rollBack() )
×
931
  {
932
    CoreUtils::log( QStringLiteral( "Attribute Controller" ), QStringLiteral( "Could not rollback the changes in form" ) );
×
933
  }
×
934

935
  mFeatureLayerPair.layer()->triggerRepaint();
×
936
  return true;
×
937
}
×
938

939
bool AttributeController::save()
×
940
{
941
  if ( !mFeatureLayerPair.layer() )
×
942
    return false;
×
943

944
  if ( !startEditing() )
×
945
  {
946
    return false;
×
947
  }
948

949
  bool rv = true;
×
950

951
  QgsFeature feat = mFeatureLayerPair.feature();
×
952

953
  bool featureIsNotYetAdded = FID_IS_NULL( feat.id() );
×
954

955
  if ( featureIsNotYetAdded )
×
956
  {
957
    if ( !mFeatureLayerPair.layer()->addFeature( feat ) )
×
958
    {
959
      QgsMessageLog::logMessage( tr( "Feature could not be added" ),
×
960
                                 QStringLiteral( "Input" ),
×
961
                                 Qgis::Critical );
962

963
    }
×
964
    connect( mFeatureLayerPair.layer(), &QgsVectorLayer::featureAdded, this, &AttributeController::onFeatureAdded );
×
965
  }
×
966
  else
967
  {
968
    // update it instead of adding
969
    if ( !mFeatureLayerPair.layer()->updateFeature( feat, true ) )
×
970
      QgsMessageLog::logMessage( tr( "Cannot update feature" ),
×
971
                                 QStringLiteral( "Input" ),
×
972
                                 Qgis::Warning );
973
  }
974

975
  bool featureIsNew = isNewFeature();
×
976

977
  // This calls lower-level I/O functions which shouldn't be used
978
  // in a Q_INVOKABLE because they can make the UI unresponsive.
979
  rv = commit();
×
980

981
  if ( rv )
×
982
  {
983
    emit changesCommited();
×
984
  }
×
985
  else
986
  {
987
    emit commitFailed();
×
988
  }
989

990
  if ( featureIsNotYetAdded )
×
991
  {
992
    disconnect( mFeatureLayerPair.layer(), &QgsVectorLayer::featureAdded, this, &AttributeController::onFeatureAdded );
×
993
  }
×
994

995
  // Store the feature attributes for future use
996
  if ( featureIsNew && mRememberAttributesController )
×
997
  {
998
    mRememberAttributesController->storeFeature( mFeatureLayerPair );
×
999
  }
×
1000

1001
  return rv;
×
1002
}
×
1003

1004
bool AttributeController::startEditing()
×
1005
{
1006
  Q_ASSERT( mFeatureLayerPair.layer() );
×
1007

1008
  // Already an edit session active
1009
  if ( mFeatureLayerPair.layer()->editBuffer() )
×
1010
    return true;
×
1011

1012
  if ( !mFeatureLayerPair.layer()->startEditing() )
×
1013
  {
1014
    QgsMessageLog::logMessage( tr( "Cannot start editing" ),
×
1015
                               QStringLiteral( "Input" ),
×
1016
                               Qgis::Warning );
1017
    return false;
×
1018
  }
1019
  else
1020
  {
1021
    return true;
×
1022
  }
1023
}
×
1024

1025
bool AttributeController::commit()
×
1026
{
1027
  Q_ASSERT( mFeatureLayerPair.layer() );
×
1028

1029

1030
  if ( !mFeatureLayerPair.layer()->commitChanges() )
×
1031
  {
1032
    CoreUtils::log( QStringLiteral( "CommitChanges" ),
×
1033
                    QStringLiteral( "Failed to commit changes:\n%1" )
×
1034
                    .arg( mFeatureLayerPair.layer()->commitErrors().join( QLatin1Char( '\n' ) ) ) );
×
1035
    mFeatureLayerPair.layer()->rollBack();
×
1036
    return false;
×
1037
  }
1038
  else
1039
  {
1040
    mFeatureLayerPair.layer()->triggerRepaint();
×
1041
    return true;
×
1042
  }
1043
}
×
1044

1045
bool AttributeController::hasAnyChanges() const
×
1046
{
1047
  return mHasAnyChanges;
×
1048
}
1049

1050
void AttributeController::setHasAnyChanges( bool hasChanges )
110✔
1051
{
1052
  if ( hasChanges != mHasAnyChanges )
110✔
1053
  {
1054
    mHasAnyChanges = hasChanges;
8✔
1055
    emit hasAnyChangesChanged();
8✔
1056
  }
8✔
1057
}
110✔
1058

1059
void AttributeController::setHasValidationErrors( bool hasErrors )
127✔
1060
{
1061
  if ( mHasValidationErrors != hasErrors )
127✔
1062
  {
1063
    mHasValidationErrors = hasErrors;
23✔
1064
    emit hasValidationErrorsChanged();
23✔
1065
  }
23✔
1066
}
127✔
1067

1068
void AttributeController::discoverRelations( QgsAttributeEditorContainer *container )
10✔
1069
{
1070
  QgsRelationManager *rManager = QgsProject::instance()->relationManager();
10✔
1071

1072
  if ( !rManager || !mFeatureLayerPair.layer() )
10✔
1073
    return;
×
1074

1075
  // find relation references (add relation reference widgets)
1076
  const QList<QgsRelation> childRelations = rManager->referencingRelations( mFeatureLayerPair.layer() );
10✔
1077

1078
  for ( const QgsRelation &relation : childRelations )
14✔
1079
  {
1080
    for ( QgsAttributeEditorElement *child : container->children() )
18✔
1081
    {
1082
      if ( child->name() == relation.fieldPairs().at( 0 ).first ) // we are using only one fieldPair!
14✔
1083
      {
1084
        QgsAttributeEditorField *editorField = static_cast<QgsAttributeEditorField *>( child );
4✔
1085

1086
        if ( !editorField )
4✔
1087
          continue;
×
1088

1089
        int fieldIdx = editorField->idx();
4✔
1090
        QgsField field = mFeatureLayerPair.layer()->fields().at( fieldIdx );
4✔
1091

1092
        QVariantMap config = mFeatureLayerPair.layer()->editorWidgetSetup( fieldIdx ).config();
4✔
1093

1094
        // relation reference fields in autogenerated fields might have an empty config, we need to set needed properties here
1095
        if ( config.isEmpty() )
4✔
1096
        {
1097
          QVariantMap neededArgs =
1098
          {
3✔
1099
            { QStringLiteral( "ReferencedLayerId" ), relation.referencedLayerId() },
1✔
1100
            { QStringLiteral( "Relation" ), relation.id() }
1✔
1101
          };
1102

1103
          const QgsEditorWidgetSetup newWidgetSetup = InputUtils::getEditorWidgetSetup( field, QStringLiteral( "RelationReference" ), neededArgs );
1✔
1104
          mFeatureLayerPair.layer()->setEditorWidgetSetup( fieldIdx, newWidgetSetup );
1✔
1105

1106
          CoreUtils::log( QStringLiteral( "DiscoverRelations" ), QStringLiteral( "Changed field %1 to RelationReference" ).arg( field.name() ) );
1✔
1107
        }
1✔
1108
        else
1109
        {
1110
          config.insert( QStringLiteral( "Relation" ), relation.id() );
3✔
1111

1112
          const QgsEditorWidgetSetup newWidgetSetup = QgsEditorWidgetSetup( QStringLiteral( "RelationReference" ), config );
3✔
1113
          mFeatureLayerPair.layer()->setEditorWidgetSetup( fieldIdx, newWidgetSetup );
3✔
1114
        }
3✔
1115
      }
4✔
1116
    }
1117
  }
1118

1119
  // find relations (add relation widgets)
1120
  const QList<QgsRelation> referencingRelations = rManager->referencedRelations( mFeatureLayerPair.layer() );
10✔
1121

1122
  for ( const QgsRelation &relation : referencingRelations )
14✔
1123
  {
1124
    std::unique_ptr<QgsAttributeEditorRelation> relationEditor = std::unique_ptr<QgsAttributeEditorRelation>( new QgsAttributeEditorRelation( relation, container ) );
4✔
1125
    if ( relationEditor->label().isEmpty() && relation.name().isEmpty() )
8✔
1126
    {
1127
      // relation does not have a name nor field have label, set label based on child layer
1128
      QString label = relation.referencingLayer()->name();
2✔
1129
      relationEditor->setLabel( label );
2✔
1130
    }
2✔
1131
    container->addChildElement( relationEditor.release() );
4✔
1132
  }
4✔
1133
}
10✔
1134

1135
bool AttributeController::isValidFormId( const QUuid &id ) const
566✔
1136
{
1137
  return mFormItems.contains( id );
566✔
1138
}
1139

1140
bool AttributeController::isValidTabId( int id ) const
49✔
1141
{
1142
  return ( id >= 0 ) && ( id < tabCount() );
49✔
1143
}
1144

1145
const FormItem *AttributeController::formItem( const QUuid &id ) const
474✔
1146
{
1147
  if ( isValidFormId( id ) )
474✔
1148
    return mFormItems[id].get();
474✔
1149
  else
1150
    return nullptr;
×
1151
}
474✔
1152

1153
const TabItem *AttributeController::tabItem( int tabRow ) const
45✔
1154
{
1155
  if ( isValidTabId( tabRow ) )
45✔
1156
    return mTabItems[tabRow].get();
44✔
1157
  else
1158
    return nullptr;
1✔
1159
}
45✔
1160

1161
bool AttributeController::setFormShouldRememberValue( const QUuid &id, bool shouldRememberValue )
×
1162
{
1163
  if ( !mRememberAttributesController )
×
1164
    return true; //noop
×
1165

1166
  if ( isValidFormId( id ) )
×
1167
  {
1168
    std::shared_ptr<FormItem> data = mFormItems[id];
×
1169
    bool changed = mRememberAttributesController->setShouldRememberValue( mFeatureLayerPair.layer(), data->fieldIndex(), shouldRememberValue );
×
1170
    if ( changed )
×
1171
    {
1172
      emit formDataChanged( id );
×
1173
    }
×
1174
    return true;
×
1175
  }
×
1176
  else
1177
  {
1178
    return false;
×
1179
  }
1180
}
×
1181

1182
bool AttributeController::formShouldRememberValue( int fieldIndex ) const
×
1183
{
1184
  const QgsFeature feat = mFeatureLayerPair.feature();
×
1185
  const QgsVectorLayer *layer = mFeatureLayerPair.layer();
×
1186

1187
  if ( !mRememberAttributesController )
×
1188
    return false;
×
1189

1190
  return mRememberAttributesController->shouldRememberValue( layer, fieldIndex );
×
1191
}
×
1192

1193
bool AttributeController::setFormValue( const QUuid &id, QVariant value )
92✔
1194
{
1195
  if ( isValidFormId( id ) )
92✔
1196
  {
1197
    std::shared_ptr<FormItem> item = mFormItems[id];
92✔
1198
    QgsField field = item->field();
92✔
1199
    QVariant val( value );
92✔
1200

1201
    item->setRawValue( val );
92✔
1202
    emit formDataChanged( item->id(), { AttributeFormModel::RawValue } );
92✔
1203

1204
    if ( !field.convertCompatible( val ) )
92✔
1205
    {
1206
      QString msg( tr( "Value \"%1\" %4 could not be converted to a compatible value for field %2(%3)." ).arg( value.toString(), field.name(), field.typeName(), value.isNull() ? "NULL" : "NOT NULL" ) );
17✔
1207
      QgsMessageLog::logMessage( msg );
17✔
1208
    }
17✔
1209
    else
1210
    {
1211
      mFeatureLayerPair.featureRef().setAttribute( item->fieldIndex(), val );
75✔
1212
      emit formDataChanged( item->id(), { AttributeFormModel::AttributeValue, AttributeFormModel::RawValueIsNull } );
75✔
1213
    }
1214
    recalculateDerivedItems( true, false );
92✔
1215
    return true;
92✔
1216
  }
92✔
1217
  else
1218
  {
1219
    return false;
×
1220
  }
1221
}
92✔
1222

1223
QVariant AttributeController::formValue( int fieldIndex ) const
11✔
1224
{
1225
  const QgsFeature feat = mFeatureLayerPair.feature();
11✔
1226
  if ( !feat.isValid() ||
22✔
1227
       fieldIndex < 0 ||
11✔
1228
       fieldIndex >= feat.attributeCount()
11✔
1229
     )
1230
    return QVariant();
×
1231

1232
  return mFeatureLayerPair.feature().attribute( fieldIndex );
11✔
1233
}
11✔
1234

1235
AttributeController *AttributeController::parentController() const
×
1236
{
1237
  return mParentController;
×
1238
}
1239

1240
void AttributeController::setParentController( AttributeController *newParentController )
1✔
1241
{
1242
  if ( !newParentController || mParentController == newParentController )
1✔
1243
    return;
×
1244

1245
  mParentController = newParentController;
1✔
1246
  emit parentControllerChanged();
1✔
1247

1248
  prefillRelationReferenceField();
1✔
1249

1250
  if ( mParentController )
1✔
1251
  {
1252
    // When parent's feature Id is changed, we want to update the relation reference field
1253
    connect( mParentController, &AttributeController::featureIdChanged, this, &AttributeController::prefillRelationReferenceField );
1✔
1254
  }
1✔
1255
}
1✔
1256

1257
const QgsRelation &AttributeController::linkedRelation() const
×
1258
{
1259
  return mLinkedRelation;
×
1260
}
1261

1262
void AttributeController::setLinkedRelation( const QgsRelation &newLinkedRelation )
1✔
1263
{
1264
  if ( mLinkedRelation.id() == newLinkedRelation.id() )
1✔
1265
    return;
×
1266

1267
  mLinkedRelation = newLinkedRelation;
1✔
1268
  emit linkedRelationChanged();
1✔
1269

1270
  prefillRelationReferenceField();
1✔
1271
}
1✔
1272

1273
void AttributeController::onFeatureAdded( QgsFeatureId newFeatureId )
×
1274
{
1275
  QgsFeature f = mFeatureLayerPair.layer()->getFeature( newFeatureId );
×
1276
  setFeatureLayerPair( FeatureLayerPair( f, mFeatureLayerPair.layer() ) );
×
1277
  emit featureIdChanged();
×
1278
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc