QGIS API Documentation 4.3.0-Master (16eddb2670e)
Loading...
Searching...
No Matches
qgsattributesformmodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformmodel.cpp
3 ---------------------
4 begin : March 2025
5 copyright : (C) 2025 by Germán Carrillo
6 email : german at opengis dot ch
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
17
18#include "qgsactionmanager.h"
19#include "qgsapplication.h"
29#include "qgsgui.h"
30
31#include <QMimeData>
32#include <QString>
33
34#include "moc_qgsattributesformmodel.cpp"
35
36using namespace Qt::StringLiterals;
37
39{
40 if ( !layer || idx < 0 || idx >= layer->fields().count() )
41 return;
42
43 mAlias = layer->fields().at( idx ).alias();
45 mComment = layer->fields().at( idx ).comment();
46 mCustomComment = layer->fields().at( idx ).customComment();
47 mEditable = !layer->editFormConfig().readOnly( idx );
48 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
50 mFieldConstraints = layer->fields().at( idx ).constraints();
51 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
52 mEditorWidgetType = setup.type();
54 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
55 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
56 mMergePolicy = layer->fields().at( idx ).mergePolicy();
59}
60
61QgsAttributesFormData::FieldConfig::operator QVariant()
62{
63 return QVariant::fromValue<QgsAttributesFormData::FieldConfig>( *this );
64}
65
66QgsAttributesFormData::RelationEditorConfiguration::operator QVariant()
67{
68 return QVariant::fromValue<QgsAttributesFormData::RelationEditorConfiguration>( *this );
69}
70
75
80
85
90
92{
93 return mShowLabel;
94}
95
100
105
110
115
120
125
130
135
140
141
146
151
156
161
163{
164 return mBackgroundColor;
165}
166
171
176
181
182
184 : mName( name )
185 , mDisplayName( displayName )
186 , mType( itemType )
187 , mParent( parent )
188{}
189
192)
193 : mName( name )
194 , mDisplayName( displayName )
195 , mType( itemType )
196 , mData( data )
197 , mParent( parent )
198{}
199
201{
202 if ( !mChildren.empty() && row >= 0 && row < childCount() )
203 return mChildren.at( row ).get();
204
205 return nullptr;
206}
207
209{
210 if ( !mChildren.empty() && itemId.trimmed().isEmpty() )
211 return nullptr;
212
213 // Search for first matching item by name
214 const auto it = std::find_if( mChildren.cbegin(), mChildren.cend(), [itemType, itemId]( const std::unique_ptr< QgsAttributesFormItem > &item ) {
215 return item->type() == itemType && item->id() == itemId;
216 } );
217
218 if ( it != mChildren.cend() )
219 return it->get();
220
221 return nullptr;
222}
223
225{
226 if ( !mChildren.empty() && itemId.trimmed().isEmpty() )
227 return nullptr;
228
229 for ( const auto &child : std::as_const( mChildren ) )
230 {
231 if ( child->type() == itemType && child->id() == itemId )
232 return child.get();
233
234 if ( child->childCount() > 0 )
235 {
236 QgsAttributesFormItem *item = child->firstChildRecursive( itemType, itemId );
237 if ( item )
238 return item;
239 }
240 }
241
242 return nullptr;
243}
244
246{
247 return static_cast< int >( mChildren.size() );
248}
249
251{
252 if ( !mParent )
253 return 0;
254
255 const auto it = std::find_if( mParent->mChildren.cbegin(), mParent->mChildren.cend(), [this]( const std::unique_ptr< QgsAttributesFormItem > &item ) { return item.get() == this; } );
256
257 if ( it != mParent->mChildren.cend() )
258 {
259 return static_cast< int >( std::distance( mParent->mChildren.cbegin(), it ) );
260 }
261
262 return -1;
263}
264
265void QgsAttributesFormItem::addChild( std::unique_ptr< QgsAttributesFormItem > &&item )
266{
267 if ( !item )
268 return;
269
270 if ( !item->mParent )
271 item->mParent = this;
272
273 // forward the signal towards the root
275
276 mChildren.push_back( std::move( item ) );
277
278 emit addedChildren( this, mChildren.size() - 1, mChildren.size() - 1 );
279}
280
281void QgsAttributesFormItem::insertChild( int position, std::unique_ptr< QgsAttributesFormItem > &&item )
282{
283 if ( position < 0 || position > static_cast< int >( mChildren.size() ) || !item )
284 return;
285
286 if ( !item->mParent )
287 item->mParent = this;
288
289 // forward the signal towards the root
291
292 mChildren.insert( mChildren.begin() + position, std::move( item ) );
293
294 emit addedChildren( this, position, position );
295}
296
298{
299 if ( index >= 0 && index < static_cast< int >( mChildren.size() ) )
300 mChildren.erase( mChildren.begin() + index );
301}
302
303std::unique_ptr< QgsAttributesFormItem > QgsAttributesFormItem::takeChild( int index )
304{
305 if ( index < 0 || index >= static_cast< int >( mChildren.size() ) )
306 return nullptr;
307
308 std::unique_ptr< QgsAttributesFormItem > child = std::move( mChildren[index] );
309 mChildren.erase( mChildren.begin() + index );
310
311 if ( child )
312 {
314 child->mParent = nullptr;
315 }
316
317 return child;
318}
319
321{
322 mChildren.clear();
323}
324
329
330QVariant QgsAttributesFormItem::data( int role ) const
331{
332 switch ( role )
333 {
335 return mType;
337 return QVariant::fromValue( mData );
339 return mName;
341 return mId;
343 return mDisplayName;
345 return QVariant::fromValue( mFieldConfigData );
346 default:
347 return QVariant();
348 }
349}
350
351bool QgsAttributesFormItem::setData( int role, const QVariant &value )
352{
353 switch ( role )
354 {
356 {
357 mData = value.value< QgsAttributesFormData::AttributeFormItemData >();
358 return true;
359 }
361 {
362 mName = value.toString();
363 return true;
364 }
366 {
367 mDisplayName = value.toString();
368 return true;
369 }
371 {
372 mType = static_cast<QgsAttributesFormData::AttributesFormItemType>( value.toInt() );
373 return true;
374 }
376 {
377 mId = value.toString();
378 return true;
379 }
381 {
382 mFieldConfigData = value.value< QgsAttributesFormData::FieldConfig >();
383 return true;
384 }
385 default:
386 return false;
387 }
388}
389
390
392 : QAbstractItemModel( parent )
393 , mRootItem( std::make_unique< QgsAttributesFormItem >() )
394 , mLayer( layer )
395 , mProject( project )
396{}
397
399
401{
402 if ( index.isValid() )
403 {
404 if ( auto *item = static_cast<QgsAttributesFormItem *>( index.internalPointer() ) )
405 return item;
406 }
407 return mRootItem.get();
408}
409
414
415
416int QgsAttributesFormModel::rowCount( const QModelIndex &parent ) const
417{
418 if ( parent.isValid() && parent.column() > 0 )
419 return 0;
420
421 const QgsAttributesFormItem *parentItem = itemForIndex( parent );
422
423 return parentItem ? parentItem->childCount() : 0;
424}
425
426int QgsAttributesFormModel::columnCount( const QModelIndex & ) const
427{
428 return 1;
429}
430
431bool QgsAttributesFormModel::indexLessThan( const QModelIndex &a, const QModelIndex &b ) const
432{
433 const QVector<int> pathA = rootToLeafPath( itemForIndex( a ) );
434 const QVector<int> pathB = rootToLeafPath( itemForIndex( b ) );
435
436 for ( int i = 0; i < std::min( pathA.size(), pathB.size() ); i++ )
437 {
438 if ( pathA.at( i ) != pathB.at( i ) )
439 {
440 return pathA.at( i ) < pathB.at( i );
441 }
442 }
443
444 return pathA.size() < pathB.size();
445}
446
448{
449 QVector<int> path;
450 if ( item != mRootItem.get() )
451 {
452 path << rootToLeafPath( item->parent() ) << item->row();
453 }
454 return path;
455}
456
457QModelIndex QgsAttributesFormModel::index( int row, int column, const QModelIndex &parent ) const
458{
459 if ( !hasIndex( row, column, parent ) )
460 return QModelIndex();
461
463 if ( !parentItem )
464 return QModelIndex();
465
466 if ( QgsAttributesFormItem *childItem = parentItem->child( row ) )
467 return createIndex( row, column, childItem );
468
469 return QModelIndex();
470}
471
472QModelIndex QgsAttributesFormModel::parent( const QModelIndex &index ) const
473{
474 if ( !index.isValid() )
475 return QModelIndex();
476
478 QgsAttributesFormItem *parentItem = childItem ? childItem->parent() : nullptr;
479
480 return ( parentItem != mRootItem.get() && parentItem != nullptr ) ? createIndex( parentItem->row(), 0, parentItem ) : QModelIndex();
481}
482
484{
485 QgsAttributesFormItem *item = mRootItem->firstTopChild( itemType, itemId );
486 return item ? createIndex( item->row(), 0, item ) : QModelIndex();
487}
488
490{
491 QgsAttributesFormItem *item = mRootItem->firstChildRecursive( itemType, itemId );
492 return item ? createIndex( item->row(), 0, item ) : QModelIndex();
493}
494
495bool QgsAttributesFormModel::setData( const QModelIndex &index, const QVariant &value, int role )
496{
497 if ( !index.isValid() )
498 return false;
499
501 bool result = item->setData( role, value );
502
503 if ( result )
504 {
505 emit dataChanged( index, index, { role } );
506
508 {
509 emit fieldConfigDataChanged( item );
510 }
511 }
512
513 return result;
514}
515
517{
518 return mShowAliases;
519}
520
522{
523 mShowAliases = show;
524
525 emitDataChangedRecursively( QModelIndex(), QVector<int>() << Qt::DisplayRole << Qt::ForegroundRole << Qt::FontRole );
526}
527
528void QgsAttributesFormModel::emitDataChangedRecursively( const QModelIndex &parent, const QVector<int> &roles )
529{
530 const int childCount = rowCount( parent );
531 if ( childCount == 0 )
532 return;
533
534 emit dataChanged( index( 0, 0, parent ), index( childCount - 1, 0, parent ), roles );
535 for ( int i = 0; i < childCount; i++ )
536 {
537 const QModelIndex childIndex = index( i, 0, parent );
538 if ( hasChildren( childIndex ) )
539 {
540 emitDataChangedRecursively( childIndex, roles );
541 }
542 }
543}
544
546{
547 switch ( itemType )
548 {
550 return QgsApplication::getThemeIcon( u"/mEditorWidgetRelationEditor.svg"_s );
551
553 return QgsApplication::getThemeIcon( u"/mEditorWidgetAction.svg"_s );
554
556 return QgsApplication::getThemeIcon( u"/mEditorWidgetQml.svg"_s );
557
559 return QgsApplication::getThemeIcon( u"/mEditorWidgetHtml.svg"_s );
560
562 return QgsApplication::getThemeIcon( u"/mEditorWidgetText.svg"_s );
563
565 return QgsApplication::getThemeIcon( u"/mEditorWidgetSpacer.svg"_s );
566
570 break;
571 }
572 return QIcon();
573}
574
575
579
580Qt::ItemFlags QgsAttributesAvailableWidgetsModel::flags( const QModelIndex &index ) const
581{
582 if ( !index.isValid() )
583 return Qt::NoItemFlags;
584
585 Qt::ItemFlags flags = Qt::ItemIsEnabled;
586
587 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
588 if ( indexType != QgsAttributesFormData::WidgetType )
589 {
590 flags = flags | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable;
591 }
592
593 return flags;
594}
595
596QVariant QgsAttributesAvailableWidgetsModel::headerData( int section, Qt::Orientation orientation, int role ) const
597{
598 Q_UNUSED( section )
599 return orientation == Qt::Horizontal && role == Qt::DisplayRole ? tr( "Available Widgets" ) : QVariant {};
600}
601
603{
604 if ( !mLayer )
605 return;
606
607 beginResetModel();
608 mRootItem->deleteChildren();
609
610 // Load fields
611
612 auto itemFields = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Fields"_s, tr( "Fields" ) );
613
614 const QgsFields fields = mLayer->fields();
615 for ( int i = 0; i < fields.size(); ++i )
616 {
617 const QgsField field = fields.at( i );
619 itemData.setShowLabel( true );
620
622
623 auto item = std::make_unique< QgsAttributesFormItem >();
624 item->setData( ItemFieldConfigRole, cfg );
625 item->setData( ItemNameRole, field.name() );
626 item->setData( ItemIdRole, field.name() ); // Field names act as ids
627 item->setData( ItemDisplayRole, field.alias() );
629 item->setData( ItemDataRole, itemData );
630 item->setIcon( fields.iconForField( i, true ) );
631
632 itemFields->addChild( std::move( item ) );
633 }
634
635 mRootItem->addChild( std::move( itemFields ) );
636
637 // Load relations
638
639 auto itemRelations = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Relations"_s, tr( "Relations" ) );
640
641 const QList<QgsRelation> relations = mProject->relationManager()->referencedRelations( mLayer );
642
643 for ( const QgsRelation &relation : relations )
644 {
645 QString name;
646 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
647 if ( polymorphicRelation.isValid() )
648 {
649 name = u"%1 (%2)"_s.arg( relation.name(), polymorphicRelation.name() );
650 }
651 else
652 {
653 name = relation.name();
654 }
656 itemData.setShowLabel( true );
657
658 auto itemRelation = std::make_unique< QgsAttributesFormItem >();
659 itemRelation->setData( ItemTypeRole, QgsAttributesFormData::Relation );
660 itemRelation->setData( ItemNameRole, name );
661 itemRelation->setData( ItemIdRole, relation.id() );
662 itemRelation->setData( ItemDataRole, itemData );
663 itemRelation->setIcon( iconForItemType( QgsAttributesFormData::Relation ) );
664 itemRelations->addChild( std::move( itemRelation ) );
665 }
666
667 mRootItem->addChild( std::move( itemRelations ) );
668
669 // Load form actions
670
671 auto itemActions = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Actions"_s, tr( "Actions" ) );
672 mRootItem->addChild( std::move( itemActions ) );
673 populateActionItems( mLayer->actions()->actions() );
674
675 // Other widgets
676
677 auto itemOtherWidgets = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Other"_s, tr( "Other Widgets" ) );
678
680 itemData.setShowLabel( true );
681 auto itemQml = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::QmlWidget, itemData, u"QML Widget"_s, tr( "QML Widget" ) );
683 itemOtherWidgets->addChild( std::move( itemQml ) );
684
686 itemHtmlData.setShowLabel( true );
687 auto itemHtml = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::HtmlWidget, itemHtmlData, u"HTML Widget"_s, tr( "HTML Widget" ) );
689 itemOtherWidgets->addChild( std::move( itemHtml ) );
690
692 itemTextData.setShowLabel( true );
693 auto itemText = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::TextWidget, itemTextData, u"Text Widget"_s, tr( "Text Widget" ) );
695 itemOtherWidgets->addChild( std::move( itemText ) );
696
698 itemTextData.setShowLabel( false );
699 auto itemSpacer = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::SpacerWidget, u"Spacer Widget"_s, tr( "Spacer Widget" ) );
701 itemOtherWidgets->addChild( std::move( itemSpacer ) );
702
703 mRootItem->addChild( std::move( itemOtherWidgets ) );
704
705 endResetModel();
706}
707
708void QgsAttributesAvailableWidgetsModel::populateLayerActions( const QList< QgsAction > actions )
709{
710 QModelIndex actionsIndex = actionContainer();
711 QgsAttributesFormItem *itemActions = itemForIndex( actionsIndex );
712
713 beginRemoveRows( actionsIndex, 0, itemActions->childCount() );
714 itemActions->deleteChildren();
715 endRemoveRows();
716
717 int count = 0;
718 for ( const auto &action : std::as_const( actions ) )
719 {
720 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( u"Feature"_s ) || action.actionScopes().contains( u"Layer"_s ) ) )
721 {
722 count++;
723 }
724 }
725
726 if ( count > 0 )
727 {
728 beginInsertRows( actionsIndex, 0, count - 1 );
729 populateActionItems( actions );
730 endInsertRows();
731 }
732}
733
734void QgsAttributesAvailableWidgetsModel::populateActionItems( const QList<QgsAction> actions )
735{
736 QModelIndex actionsIndex = actionContainer();
737 QgsAttributesFormItem *itemActions = itemForIndex( actionsIndex );
738
739 for ( const auto &action : std::as_const( actions ) )
740 {
741 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( u"Feature"_s ) || action.actionScopes().contains( u"Layer"_s ) ) )
742 {
743 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
744
745 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
746 itemData.setShowLabel( true );
747
748 auto itemAction = std::make_unique< QgsAttributesFormItem >();
749 itemAction->setData( ItemIdRole, action.id().toString() );
750 itemAction->setData( ItemTypeRole, QgsAttributesFormData::Action );
751 itemAction->setData( ItemNameRole, actionTitle );
752 itemAction->setData( ItemDataRole, itemData );
753 itemAction->setIcon( iconForItemType( QgsAttributesFormData::Action ) );
754
755 itemActions->addChild( std::move( itemAction ) );
756 }
757 }
758}
759
760QVariant QgsAttributesAvailableWidgetsModel::data( const QModelIndex &index, int role ) const
761{
762 if ( !index.isValid() )
763 return QVariant();
764
766 if ( !item )
767 return QVariant();
768
769 // Relations may be broken due to missing layers or references.
770 // Make those stand out from valid ones.
771 bool invalidRelation = false;
772 if ( ( role == Qt::ToolTipRole || role == Qt::ForegroundRole ) && item->type() == QgsAttributesFormData::Relation )
773 {
774 invalidRelation = !QgsProject::instance()->relationManager()->relation( item->id() ).isValid();
775 }
776
777 switch ( role )
778 {
779 case Qt::DisplayRole:
780 {
781 if ( !showAliases() && item->type() == QgsAttributesFormData::Field )
782 {
783 return item->name();
784 }
785
786 return item->displayName().isEmpty() ? item->name() : item->displayName();
787 }
788
789 case Qt::ToolTipRole:
790 {
792 {
793 const auto cfg = item->data( ItemFieldConfigRole ).value<QgsAttributesFormData::FieldConfig>();
794 if ( !cfg.mAlias.isEmpty() )
795 return tr( "%1 (%2)" ).arg( item->name(), cfg.mAlias );
796 else
797 return item->name();
798 }
799
800 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
801 {
802 // Relation name will be displayed, inform users why it's red via tooltip
803 return tr( "Invalid relation" );
804 }
805
806 return QVariant();
807 }
808
809 case Qt::DecorationRole:
810 return item->icon();
811
812 case Qt::BackgroundRole:
813 {
815 return QBrush( QColor( 140, 140, 140, 50 ) );
816
817 return QVariant();
818 }
819
820 case Qt::ForegroundRole:
821 {
822 if ( item->type() == QgsAttributesFormData::Field )
823 {
824 if ( showAliases() && item->displayName().isEmpty() )
825 {
826 return QBrush( QColor( Qt::lightGray ) );
827 }
828 }
829
830 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
831 {
832 return QBrush( QColor( 255, 0, 0 ) );
833 }
834
835 return QVariant();
836 }
837
838 case Qt::FontRole:
839 {
840 if ( item->type() == QgsAttributesFormData::Field )
841 {
842 if ( showAliases() && item->displayName().isEmpty() )
843 {
844 QFont font = QFont();
845 font.setItalic( true );
846 return font;
847 }
848 }
849 return QVariant();
850 }
851
852 case ItemDataRole:
854 case ItemNameRole:
855 case ItemTypeRole:
856 case ItemIdRole:
857 case ItemDisplayRole:
858 return item->data( role );
859
860 default:
861 return QVariant();
862 }
863}
864
866{
867 return Qt::CopyAction;
868}
869
871{
872 return QStringList() << u"application/x-qgsattributesformavailablewidgetsrelement"_s;
873}
874
875QMimeData *QgsAttributesAvailableWidgetsModel::mimeData( const QModelIndexList &indexes ) const
876{
877 if ( indexes.count() == 0 )
878 return nullptr;
879
880 const QStringList types = mimeTypes();
881 if ( types.isEmpty() )
882 return nullptr;
883
884 QMimeData *data = new QMimeData();
885 const QString format = types.at( 0 );
886 QByteArray encoded;
887 QDataStream stream( &encoded, QIODevice::WriteOnly );
888
889 // Sort indexes since their order reflects selection order
890 QModelIndexList sortedIndexes = indexes;
891
892 std::sort( sortedIndexes.begin(), sortedIndexes.end(), [this]( const QModelIndex &a, const QModelIndex &b ) { return indexLessThan( a, b ); } );
893
894 for ( const QModelIndex &index : std::as_const( sortedIndexes ) )
895 {
896 if ( index.isValid() )
897 {
898 const QString itemId = index.data( QgsAttributesFormModel::ItemIdRole ).toString();
899 const QString itemName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
900 int itemType = index.data( QgsAttributesFormModel::ItemTypeRole ).toInt();
901
902 stream << itemId << itemType << itemName;
903 }
904 }
905
906 data->setData( format, encoded );
907 return data;
908}
909
911{
912 if ( mRootItem->childCount() > 0 )
913 {
914 const int row = 0;
915 QgsAttributesFormItem *item = mRootItem->child( row );
916 if ( item && item->name() == "Fields"_L1 && item->type() == QgsAttributesFormData::WidgetType )
917 return createIndex( row, 0, item );
918 }
919 return QModelIndex();
920}
921
923{
924 if ( mRootItem->childCount() > 1 )
925 {
926 const int row = 1;
927 QgsAttributesFormItem *item = mRootItem->child( row );
928 if ( item && item->name() == "Relations"_L1 && item->type() == QgsAttributesFormData::WidgetType )
929 return createIndex( row, 0, item );
930 }
931 return QModelIndex();
932}
933
935{
936 if ( mRootItem->childCount() > 2 )
937 {
938 const int row = 2;
939 QgsAttributesFormItem *item = mRootItem->child( row );
940 if ( item && item->name() == "Actions"_L1 && item->type() == QgsAttributesFormData::WidgetType )
941 return createIndex( row, 0, item );
942 }
943 return QModelIndex();
944}
945
946QModelIndex QgsAttributesAvailableWidgetsModel::fieldModelIndex( const QString &fieldName ) const
947{
948 if ( mRootItem->childCount() == 0 )
949 return QModelIndex();
950
951 QgsAttributesFormItem *fieldItems = mRootItem->child( 0 );
952 if ( !fieldItems || fieldItems->name() != "Fields"_L1 || fieldItems->type() != QgsAttributesFormData::WidgetType )
953 return QModelIndex();
954
955 QgsAttributesFormItem *item = fieldItems->firstTopChild( QgsAttributesFormData::Field, fieldName );
956 return item ? createIndex( item->row(), 0, item ) : QModelIndex();
957}
958
959
963
964QVariant QgsAttributesFormLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const
965{
966 Q_UNUSED( section )
967 return orientation == Qt::Horizontal && role == Qt::DisplayRole ? tr( "Form Layout" ) : QVariant {};
968}
969
970Qt::ItemFlags QgsAttributesFormLayoutModel::flags( const QModelIndex &index ) const
971{
972 if ( !index.isValid() )
973 return Qt::ItemIsDropEnabled;
974
975 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
976
979 flags |= Qt::ItemIsDropEnabled;
980
981 return flags;
982}
983
985{
986 if ( !mLayer )
987 return;
988
989 beginResetModel();
990 mRootItem->deleteChildren();
991
992 const auto editorElements = mLayer->editFormConfig().tabs();
993 for ( QgsAttributeEditorElement *editorElement : editorElements )
994 {
995 loadAttributeEditorElementItem( editorElement, mRootItem.get() );
996 }
997
998 endResetModel();
999}
1000
1001void QgsAttributesFormLayoutModel::loadAttributeEditorElementItem( QgsAttributeEditorElement *const editorElement, QgsAttributesFormItem *parent, const int position )
1002{
1003 auto setCommonProperties = [editorElement]( QgsAttributesFormData::AttributeFormItemData &itemData ) {
1004 itemData.setShowLabel( editorElement->showLabel() );
1005 itemData.setLabelStyle( editorElement->labelStyle() );
1006 itemData.setHorizontalStretch( editorElement->horizontalStretch() );
1007 itemData.setVerticalStretch( editorElement->verticalStretch() );
1008 };
1009
1010 auto editorItem = std::make_unique< QgsAttributesFormItem >();
1011
1012 switch ( editorElement->type() )
1013 {
1015 {
1016 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1017 setCommonProperties( itemData );
1018
1019 editorItem->setData( ItemNameRole, editorElement->name() );
1020 editorItem->setData( ItemIdRole, editorElement->name() ); // Field names act as ids
1021 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Field );
1022 editorItem->setData( ItemDataRole, itemData );
1023
1024 setFieldItemDataFromLayer( editorItem.get() );
1025
1026 break;
1027 }
1028
1030 {
1031 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( editorElement );
1032 const QgsAction action { actionEditor->action( mLayer ) };
1033 if ( action.isValid() )
1034 {
1035 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1036 setCommonProperties( itemData );
1037
1038 editorItem->setData( ItemIdRole, action.id().toString() );
1039 editorItem->setData( ItemNameRole, action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
1040 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Action );
1041 editorItem->setData( ItemDataRole, itemData );
1042 editorItem->setIcon( iconForItemType( QgsAttributesFormData::Action ) );
1043 }
1044 else
1045 {
1046 QgsDebugError( u"Invalid form action"_s );
1047 }
1048 break;
1049 }
1050
1052 {
1053 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1054 setCommonProperties( itemData );
1055
1056 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( editorElement );
1057 QgsAttributesFormData::RelationEditorConfiguration relationEditorConfig;
1058 relationEditorConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
1059 relationEditorConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
1060 relationEditorConfig.nmRelationId = relationEditor->nmRelationId();
1061 relationEditorConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
1062 relationEditorConfig.label = relationEditor->label();
1063 itemData.setRelationEditorConfiguration( relationEditorConfig );
1064
1065 QgsRelation relation = relationEditor->relation();
1066 if ( relation.id().isEmpty() )
1067 {
1068 // If relation is coming from an internal move, we lose the id.
1069 // Go to relation manager and bring relation properties.
1070 relation = mProject->relationManager()->relation( editorElement->name() );
1071 }
1072
1073 editorItem->setData( ItemIdRole, relation.id() );
1074 editorItem->setData( ItemNameRole, relation.name() );
1075 editorItem->setData( ItemDisplayRole, relationEditorConfig.label );
1076 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Relation );
1077 editorItem->setData( ItemDataRole, itemData );
1078 editorItem->setIcon( iconForItemType( QgsAttributesFormData::Relation ) );
1079
1080 break;
1081 }
1082
1084 {
1085 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1086 setCommonProperties( itemData );
1087
1088 editorItem->setData( ItemNameRole, editorElement->name() );
1089 editorItem->setData( ItemIdRole, editorElement->name() ); // Containers don't have id, use name to make them searchable
1090 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Container );
1091
1092 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( editorElement );
1093 if ( !container )
1094 break;
1095
1096 itemData.setColumnCount( container->columnCount() );
1097 itemData.setContainerType( container->type() );
1098 itemData.setBackgroundColor( container->backgroundColor() );
1099 itemData.setVisibilityExpression( container->visibilityExpression() );
1100 itemData.setCollapsedExpression( container->collapsedExpression() );
1101 itemData.setCollapsed( container->collapsed() );
1102
1103 editorItem->setData( ItemDataRole, itemData );
1104
1105 const QList<QgsAttributeEditorElement *> children = container->children();
1106 for ( QgsAttributeEditorElement *childElement : children )
1107 {
1108 loadAttributeEditorElementItem( childElement, editorItem.get() );
1109 }
1110 break;
1111 }
1112
1114 {
1115 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( editorElement );
1116 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1117 setCommonProperties( itemData );
1118
1119 QgsAttributesFormData::QmlElementEditorConfiguration qmlEdConfig;
1120 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
1121 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
1122
1123 editorItem->setData( ItemNameRole, editorElement->name() );
1124 editorItem->setData( ItemTypeRole, QgsAttributesFormData::QmlWidget );
1125 editorItem->setData( ItemDataRole, itemData );
1126 editorItem->setIcon( iconForItemType( QgsAttributesFormData::QmlWidget ) );
1127 break;
1128 }
1129
1131 {
1132 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( editorElement );
1133 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1134 setCommonProperties( itemData );
1135
1136 QgsAttributesFormData::HtmlElementEditorConfiguration htmlEdConfig;
1137 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
1138 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
1139
1140 editorItem->setData( ItemNameRole, editorElement->name() );
1141 editorItem->setData( ItemTypeRole, QgsAttributesFormData::HtmlWidget );
1142 editorItem->setData( ItemDataRole, itemData );
1143 editorItem->setIcon( iconForItemType( QgsAttributesFormData::HtmlWidget ) );
1144 break;
1145 }
1146
1148 {
1149 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( editorElement );
1150 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1151 setCommonProperties( itemData );
1152
1153 QgsAttributesFormData::TextElementEditorConfiguration textEdConfig;
1154 textEdConfig.text = textElementEditor->text();
1155 itemData.setTextElementEditorConfiguration( textEdConfig );
1156
1157 editorItem->setData( ItemNameRole, editorElement->name() );
1158 editorItem->setData( ItemTypeRole, QgsAttributesFormData::TextWidget );
1159 editorItem->setData( ItemDataRole, itemData );
1160 editorItem->setIcon( iconForItemType( QgsAttributesFormData::TextWidget ) );
1161 break;
1162 }
1163
1165 {
1166 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( editorElement );
1167 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1168 setCommonProperties( itemData );
1169 itemData.setShowLabel( false );
1170
1171 QgsAttributesFormData::SpacerElementEditorConfiguration spacerEdConfig;
1172 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
1173 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
1174
1175 editorItem->setData( ItemNameRole, editorElement->name() );
1176 editorItem->setData( ItemTypeRole, QgsAttributesFormData::SpacerWidget );
1177 editorItem->setData( ItemDataRole, itemData );
1178 editorItem->setIcon( iconForItemType( QgsAttributesFormData::SpacerWidget ) );
1179 break;
1180 }
1181
1183 {
1184 QgsDebugError( u"Not loading invalid attribute editor type..."_s );
1185 break;
1186 }
1187 }
1188
1189 if ( position >= 0 && position < parent->childCount() )
1190 {
1191 parent->insertChild( position, std::move( editorItem ) );
1192 }
1193 else
1194 {
1195 parent->addChild( std::move( editorItem ) );
1196 }
1197}
1198
1199QVariant QgsAttributesFormLayoutModel::data( const QModelIndex &index, int role ) const
1200{
1201 if ( !index.isValid() )
1202 return QVariant();
1203
1204 if ( role == ItemFieldConfigRole ) // This model doesn't store data for that role
1205 return false;
1206
1208 if ( !item )
1209 return QVariant();
1210
1211 // Fields may be present in the form layout configuration
1212 // even if their corresponding layer fields were deleted.
1213 // Make those stand out from existent ones.
1214 const int fieldIndex = mLayer->fields().indexOf( item->name() );
1215 const bool invalidField = fieldIndex == -1;
1216
1217 // Relations may be broken due to missing layers or references.
1218 // Make those stand out from valid ones.
1219 bool invalidRelation = false;
1220 if ( ( role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::ForegroundRole ) && item->type() == QgsAttributesFormData::Relation )
1221 {
1222 invalidRelation = !QgsProject::instance()->relationManager()->relation( item->id() ).isValid();
1223 }
1224
1225 switch ( role )
1226 {
1227 case Qt::DisplayRole:
1228 {
1229 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
1230 {
1231 // Invalid relations can have an id, if that's the case, we have a name.
1232 // Only set a new name if id is missing.
1233 if ( item->id().isEmpty() )
1234 {
1235 return tr( "Invalid relation" );
1236 }
1237 }
1238
1239 if ( !showAliases() && ( item->type() == QgsAttributesFormData::Field || item->type() == QgsAttributesFormData::Relation ) )
1240 {
1241 return item->name();
1242 }
1243
1244 return item->displayName().isEmpty() ? item->name() : item->displayName();
1245 }
1246
1247 case Qt::ToolTipRole:
1248 {
1249 if ( item->type() == QgsAttributesFormData::Field )
1250 {
1251 if ( invalidField )
1252 {
1253 return tr( "Invalid field" );
1254 }
1255 else
1256 {
1257 return item->name();
1258 }
1259 }
1260
1261 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
1262 {
1263 if ( !item->id().isEmpty() )
1264 {
1265 // The relation name is shown, let's inform users via tooltip why it's red
1266 return tr( "Invalid relation" );
1267 }
1268 }
1269
1270 return QVariant();
1271 }
1272
1273 case Qt::DecorationRole:
1274 return item->icon();
1275
1276 case Qt::BackgroundRole:
1277 {
1278 if ( item->type() == QgsAttributesFormData::Container )
1279 return QBrush( QColor( 140, 140, 140, 50 ) );
1280
1281 return QVariant();
1282 }
1283
1284 case Qt::ForegroundRole:
1285 {
1286 if ( item->type() == QgsAttributesFormData::Field )
1287 {
1288 if ( invalidField )
1289 {
1290 return QBrush( QColor( 255, 0, 0 ) );
1291 }
1292 else if ( showAliases() && item->displayName().isEmpty() )
1293 {
1294 return QBrush( QColor( Qt::lightGray ) );
1295 }
1296 }
1297
1298 if ( item->type() == QgsAttributesFormData::Relation )
1299 {
1300 if ( invalidRelation )
1301 {
1302 return QBrush( QColor( 255, 0, 0 ) );
1303 }
1304 else if ( showAliases() && item->displayName().isEmpty() )
1305 {
1306 return QBrush( QColor( Qt::lightGray ) );
1307 }
1308 }
1309
1310 return QVariant();
1311 }
1312
1313 case Qt::FontRole:
1314 {
1315 if ( item->type() == QgsAttributesFormData::Field )
1316 {
1317 if ( !invalidField && showAliases() && item->displayName().isEmpty() )
1318 {
1319 QFont font = QFont();
1320 font.setItalic( true );
1321 return font;
1322 }
1323 }
1324
1325 if ( item->type() == QgsAttributesFormData::Relation )
1326 {
1327 if ( !invalidRelation && showAliases() && item->displayName().isEmpty() )
1328 {
1329 QFont font = QFont();
1330 font.setItalic( true );
1331 return font;
1332 }
1333 }
1334
1335 return QVariant();
1336 }
1337
1338 case ItemDataRole:
1339 case ItemNameRole:
1340 case ItemIdRole:
1341 case ItemTypeRole:
1342 case ItemDisplayRole:
1343 return item->data( role );
1344
1345 default:
1346 return QVariant();
1347 }
1348}
1349
1350bool QgsAttributesFormLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
1351{
1352 if ( row < 0 )
1353 return false;
1354
1356
1357 if ( row > item->childCount() - count )
1358 return false;
1359
1360 beginRemoveRows( parent, row, row + count - 1 );
1361 for ( int r = 0; r < count; ++r )
1362 item->deleteChildAtIndex( row );
1363 endRemoveRows();
1364 return true;
1365}
1366
1367bool QgsAttributesFormLayoutModel::removeRow( int row, const QModelIndex &parent )
1368{
1369 beginRemoveRows( parent, row, row );
1371 item->deleteChildAtIndex( row );
1372 endRemoveRows();
1373 return true;
1374}
1375
1377{
1378 // For internal moves, QAbstractItemView::startDrag() will delete the dragged item after a successful drop if we don't support Qt::CopyAction
1379 // See QgsAttributesFormLayoutView::dropEvent where we force it to be a CopyAction.
1380 return Qt::CopyAction | Qt::MoveAction;
1381}
1382
1384{
1385 return Qt::DropAction::CopyAction | Qt::DropAction::MoveAction;
1386}
1387
1389{
1390 return QStringList() << u"application/x-qgsattributesformlayoutelement"_s << u"application/x-qgsattributesformavailablewidgetsrelement"_s;
1391}
1392
1393QModelIndexList QgsAttributesFormLayoutModel::curateIndexesForMimeData( const QModelIndexList &indexes ) const
1394{
1395 QModelIndexList containerList;
1396 for ( const auto index : indexes )
1397 {
1398 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1399 if ( indexType == QgsAttributesFormData::Container )
1400 {
1401 containerList << index;
1402 }
1403 }
1404
1405 if ( containerList.size() == 0 )
1406 return indexes;
1407
1408 QModelIndexList curatedIndexes;
1409
1410 // Iterate searching if current index is child of any container in containerList (recursively)
1411 for ( const auto index : indexes )
1412 {
1413 QModelIndex parent = index.parent();
1414 bool redundantChild = false;
1415
1416 while ( parent.isValid() )
1417 {
1418 if ( containerList.contains( parent ) )
1419 {
1420 redundantChild = true;
1421 break;
1422 }
1423
1424 parent = parent.parent();
1425 }
1426
1427 if ( !redundantChild )
1428 curatedIndexes << index;
1429 }
1430
1431 return curatedIndexes;
1432}
1433
1434QMimeData *QgsAttributesFormLayoutModel::mimeData( const QModelIndexList &indexes ) const
1435{
1436 if ( indexes.count() == 0 )
1437 return nullptr;
1438
1439 // Discard redundant indexes
1440 QModelIndexList curatedIndexes;
1441 if ( indexes.count() > 1 )
1442 {
1443 curatedIndexes = curateIndexesForMimeData( indexes );
1444 }
1445 else
1446 {
1447 curatedIndexes = indexes;
1448 }
1449
1450 const QStringList types = mimeTypes();
1451 if ( types.isEmpty() )
1452 return nullptr;
1453
1454 QMimeData *data = new QMimeData();
1455 const QString format = types.at( 0 );
1456 QByteArray encoded;
1457 QDataStream stream( &encoded, QIODevice::WriteOnly );
1458
1459 // Sort indexes since their order reflects selection order
1460 std::sort( curatedIndexes.begin(), curatedIndexes.end(), [this]( const QModelIndex &a, const QModelIndex &b ) { return indexLessThan( a, b ); } );
1461
1462 // Remember the dragged source items in the same order they are serialized below
1463 // so an internal move can be performed without a rebuild of the dragged item subtree
1464 mDraggedLayoutIndexes.clear();
1465 for ( const QModelIndex &index : std::as_const( curatedIndexes ) )
1466 mDraggedLayoutIndexes << QPersistentModelIndex( index );
1467
1468 for ( const QModelIndex &index : std::as_const( curatedIndexes ) )
1469 {
1470 if ( index.isValid() )
1471 {
1472 QDomDocument doc;
1473
1474 QDomElement rootElem = doc.createElement( u"form_layout_mime"_s );
1475 std::unique_ptr< QgsAttributeEditorElement > editor( createAttributeEditorWidget( index, nullptr ) );
1476 QDomElement editorElem = editor->toDomElement( doc );
1477 rootElem.appendChild( editorElem );
1478
1479 doc.appendChild( rootElem );
1480 stream << doc.toString( -1 );
1481 }
1482 }
1483
1484 data->setData( format, encoded );
1485 return data;
1486}
1487
1488bool QgsAttributesFormLayoutModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1489{
1490 Q_UNUSED( column )
1491 bool isDropSuccessful = false;
1492 int rows = 0;
1493
1494 if ( row == -1 ) // Dropped at invalid index
1495 row = rowCount( parent ); // Let's append the item
1496
1497 if ( action == Qt::IgnoreAction )
1498 {
1499 isDropSuccessful = true;
1500 }
1501 else if ( data->hasFormat( u"application/x-qgsattributesformavailablewidgetsrelement"_s ) )
1502 {
1503 Q_ASSERT( action == Qt::CopyAction ); // External drop
1504 QByteArray itemData = data->data( u"application/x-qgsattributesformavailablewidgetsrelement"_s );
1505 QDataStream stream( &itemData, QIODevice::ReadOnly );
1506 QModelIndexList addedIndexes;
1507
1508 while ( !stream.atEnd() )
1509 {
1510 QString itemId;
1511 int itemTypeInt;
1512 QString itemName;
1513 stream >> itemId >> itemTypeInt >> itemName;
1514
1515 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( itemTypeInt );
1516 insertChild( parent, row + rows, itemId, itemType, itemName );
1517
1518 isDropSuccessful = true;
1519 addedIndexes << index( row + rows, 0, parent );
1520
1521 rows++;
1522 }
1523
1524 if ( !addedIndexes.isEmpty() )
1525 emit externalItemsDropped( addedIndexes );
1526 }
1527 else if ( data->hasFormat( u"application/x-qgsattributesformlayoutelement"_s ) )
1528 {
1529 Q_ASSERT( action == Qt::MoveAction ); // Internal move
1530
1531 const QList< QPersistentModelIndex > draggedIndexes = mDraggedLayoutIndexes;
1532 mDraggedLayoutIndexes.clear();
1533
1534 if ( draggedIndexes.isEmpty() )
1535 return false;
1536
1537 for ( const QPersistentModelIndex &source : draggedIndexes )
1538 {
1539 if ( !source.isValid() )
1540 return false;
1541 }
1542
1543 // Defer the actual relocation until after the drag's modal event loop has
1544 // exited. Mutating the model while the drag is still in
1545 // progress corrupts the proxy's incremental source-to-proxy mapping
1546 const QPersistentModelIndex persistentParent( parent );
1547 QMetaObject::invokeMethod( this, [this, draggedIndexes, persistentParent, row]() { performInternalMove( draggedIndexes, persistentParent, row ); }, Qt::QueuedConnection );
1548
1549 isDropSuccessful = true;
1550 }
1551
1552 return isDropSuccessful;
1553}
1554
1555void QgsAttributesFormLayoutModel::performInternalMove( const QList< QPersistentModelIndex > &draggedIndexes, const QModelIndex &parent, int row )
1556{
1557 for ( const QPersistentModelIndex &source : draggedIndexes )
1558 {
1559 if ( !source.isValid() )
1560 return;
1561 }
1562
1563 QgsAttributesFormItem *destParentItem = itemForIndex( parent );
1564
1565 // Capture the dragged items as raw pointers: they survive the move (only the
1566 // owning unique_ptr is relocated), so they stay valid across the mutations
1567 // below and across the model reset.
1568 QList< QgsAttributesFormItem * > draggedItems;
1569 draggedItems.reserve( draggedIndexes.size() );
1570 for ( const QPersistentModelIndex &source : draggedIndexes )
1571 draggedItems << itemForIndex( source );
1572
1573 // Returns true if ancestor is item itself or one of its ancestors.
1574 const auto isSelfOrAncestorOf = []( const QgsAttributesFormItem *ancestor, QgsAttributesFormItem *item ) {
1575 for ( QgsAttributesFormItem *walk = item; walk; walk = walk->parent() )
1576 {
1577 if ( walk == ancestor )
1578 return true;
1579 }
1580 return false;
1581 };
1582
1583 // We signal the move as a full model reset rather than via fine-grained
1584 // beginInsertRows() and friends.
1585 // This is safe here because dropMimeData() defers this call out of the drag's modal
1586 // loop, so no drag indexes are invalidated. The view restores the
1587 // expanded state and selection afterwards (see internalItemsDropped()).
1588 beginResetModel();
1589
1590 int rows = 0;
1591 QList< QgsAttributesFormItem * > movedItems;
1592 for ( QgsAttributesFormItem *item : std::as_const( draggedItems ) )
1593 {
1594 QgsAttributesFormItem *sourceParentItem = item->parent();
1595 if ( !sourceParentItem )
1596 continue;
1597
1598 const int sourceRow = item->row();
1599 const int destRow = row + rows;
1600
1601 // Moving an item onto its current position is a no-op.
1602 if ( sourceParentItem == destParentItem && ( destRow == sourceRow || destRow == sourceRow + 1 ) )
1603 {
1604 movedItems << item;
1605 rows++;
1606 continue;
1607 }
1608
1609 // A container cannot be moved into itself or one of its own descendants.
1610 if ( isSelfOrAncestorOf( item, destParentItem ) )
1611 continue;
1612
1613 std::unique_ptr< QgsAttributesFormItem > movedItem = sourceParentItem->takeChild( sourceRow );
1614
1615 int insertPosition = destRow;
1616 if ( sourceParentItem == destParentItem && sourceRow < destRow )
1617 insertPosition = destRow - 1; // account for the just-removed source row
1618 insertPosition = std::clamp( insertPosition, 0, destParentItem->childCount() );
1619
1620 destParentItem->insertChild( insertPosition, std::move( movedItem ) );
1621 movedItems << item;
1622
1623 // Removing a source row placed above the drop point shifts all rows below
1624 if ( sourceParentItem == destParentItem && sourceRow < row )
1625 row--;
1626
1627 rows++;
1628 }
1629
1630 endResetModel();
1631
1632 if ( !movedItems.isEmpty() )
1633 {
1634 QModelIndexList indexes;
1635 indexes.reserve( movedItems.size() );
1636 for ( QgsAttributesFormItem *item : std::as_const( movedItems ) )
1637 indexes << createIndex( item->row(), 0, item );
1638 emit internalItemsDropped( indexes );
1639 }
1640}
1641
1642void QgsAttributesFormLayoutModel::updateFieldConfigForFieldItemsRecursive( QgsAttributesFormItem *parent, const QString &fieldName, const QgsAttributesFormData::FieldConfig &config )
1643{
1644 for ( int i = 0; i < parent->childCount(); i++ )
1645 {
1646 QgsAttributesFormItem *child = parent->child( i );
1647 if ( child->name() == fieldName && child->type() == QgsAttributesFormData::Field )
1648 {
1649 child->setData( ItemFieldConfigRole, QVariant::fromValue( config ) );
1650 child->setIcon( QgsGui::instance()->editorWidgetRegistry()->icon( config.mEditorWidgetType ) );
1651 emit fieldConfigDataChanged( child ); // Item's field config has changed, let views know about it
1652 }
1653
1654 if ( child->childCount() > 0 )
1655 {
1656 updateFieldConfigForFieldItemsRecursive( child, fieldName, config );
1657 }
1658 }
1659}
1660
1662{
1663 updateFieldConfigForFieldItemsRecursive( mRootItem.get(), fieldName, config );
1664}
1665
1666void QgsAttributesFormLayoutModel::updateAliasForFieldItemsRecursive( QgsAttributesFormItem *parent, const QString &fieldName, const QString &fieldAlias )
1667{
1668 for ( int i = 0; i < parent->childCount(); i++ )
1669 {
1670 QgsAttributesFormItem *child = parent->child( i );
1671 if ( child->name() == fieldName && child->type() == QgsAttributesFormData::Field )
1672 {
1673 child->setData( ItemDisplayRole, fieldAlias );
1674 const QModelIndex index = createIndex( child->row(), 0, child );
1675 emit dataChanged( index, index ); // Item's alias has changed, let views know about it
1676 }
1677
1678 if ( child->childCount() > 0 )
1679 {
1680 updateAliasForFieldItemsRecursive( child, fieldName, fieldAlias );
1681 }
1682 }
1683}
1684
1685void QgsAttributesFormLayoutModel::updateAliasForFieldItems( const QString &fieldName, const QString &fieldAlias )
1686{
1687 updateAliasForFieldItemsRecursive( mRootItem.get(), fieldName, fieldAlias );
1688}
1689
1690QList< QgsAddAttributeFormContainerDialog::ContainerPair > QgsAttributesFormLayoutModel::recursiveListOfContainers( QgsAttributesFormItem *parent ) const
1691{
1692 QList< QgsAddAttributeFormContainerDialog::ContainerPair > containerList;
1693 for ( int i = 0; i < parent->childCount(); i++ )
1694 {
1695 QgsAttributesFormItem *child = parent->child( i );
1696 if ( child->type() == QgsAttributesFormData::Container )
1697 {
1698 containerList << QgsAddAttributeFormContainerDialog::ContainerPair( child->name(), createIndex( child->row(), 0, child ) );
1699 }
1700
1701 if ( child->childCount() > 0 )
1702 {
1703 containerList.append( recursiveListOfContainers( child ) );
1704 }
1705 }
1706
1707 return containerList;
1708}
1709
1711{
1712 QgsAttributeEditorElement *widgetDef = nullptr;
1713
1715 const int indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1716 const QString indexName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1717 const QString indexId = index.data( QgsAttributesFormModel::ItemIdRole ).toString();
1718
1719 switch ( indexType )
1720 {
1722 {
1723 const int fieldIndex = mLayer->fields().lookupField( indexName );
1724 widgetDef = new QgsAttributeEditorField( indexName, fieldIndex, parent );
1725 break;
1726 }
1727
1729 {
1730 const QgsAction action { mLayer->actions()->action( indexId ) };
1731 widgetDef = new QgsAttributeEditorAction( action, parent );
1732 break;
1733 }
1734
1736 {
1737 const QgsRelation relation = mProject->relationManager()->relation( indexId );
1738
1741 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
1742 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
1743 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
1744 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
1745 relDef->setLabel( relationEditorConfig.label );
1746 widgetDef = relDef;
1747 break;
1748 }
1749
1751 {
1752 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( indexName, parent, itemData.backgroundColor() );
1753 container->setColumnCount( itemData.columnCount() );
1754 // only top-level containers can be tabs
1756 bool isTopLevel = !index.parent().isValid();
1757 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
1758 {
1759 // a tab container found which isn't at the top level -- reset it to a group box instead
1761 }
1762 container->setType( type );
1763 container->setCollapsed( itemData.collapsed() );
1764 container->setCollapsedExpression( itemData.collapsedExpression() );
1765 container->setVisibilityExpression( itemData.visibilityExpression() );
1766 container->setBackgroundColor( itemData.backgroundColor() );
1767
1768 QModelIndex childIndex;
1769 for ( int t = 0; t < rowCount( index ); t++ )
1770 {
1771 childIndex = this->index( t, 0, index );
1772 QgsAttributeEditorElement *element { createAttributeEditorWidget( childIndex, container ) };
1773 if ( element )
1774 container->addChildElement( element );
1775 }
1776 widgetDef = container;
1777 break;
1778 }
1779
1781 {
1783 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
1784 widgetDef = element;
1785 break;
1786 }
1787
1789 {
1792 widgetDef = element;
1793 break;
1794 }
1795
1797 {
1799 element->setText( itemData.textElementEditorConfiguration().text );
1800 widgetDef = element;
1801 break;
1802 }
1803
1805 {
1808 widgetDef = element;
1809 break;
1810 }
1811
1813 default:
1814 break;
1815 }
1816
1817 if ( widgetDef )
1818 {
1819 widgetDef->setShowLabel( itemData.showLabel() );
1820 widgetDef->setLabelStyle( itemData.labelStyle() );
1821 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
1822 widgetDef->setVerticalStretch( itemData.verticalStretch() );
1823 }
1824
1825 return widgetDef;
1826}
1827
1828QList< QgsAddAttributeFormContainerDialog::ContainerPair > QgsAttributesFormLayoutModel::listOfContainers() const
1829{
1830 return recursiveListOfContainers( mRootItem.get() );
1831}
1832
1834{
1835 beginInsertRows( parent, rowCount( parent ), rowCount( parent ) );
1836
1837 QgsAttributesFormItem *parentItem = itemForIndex( parent );
1838
1839 auto containerItem = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::Container, name, QString(), parentItem );
1840
1842 itemData.setColumnCount( columnCount );
1844
1845 containerItem->setData( QgsAttributesFormModel::ItemDataRole, itemData );
1846 containerItem->setData( QgsAttributesFormModel::ItemIdRole, name ); // Make it searchable
1847 parentItem->addChild( std::move( containerItem ) );
1848
1849 endInsertRows();
1850}
1851
1852void QgsAttributesFormLayoutModel::setFieldItemDataFromLayer( QgsAttributesFormItem *item )
1853{
1854 const int fieldIndex = mLayer->fields().indexOf( item->name() );
1855 if ( fieldIndex == -1 )
1856 return;
1857
1858 item->setData( ItemDisplayRole, mLayer->fields().field( fieldIndex ).alias() );
1859
1860 const QgsAttributesFormData::FieldConfig config( mLayer, fieldIndex );
1861 item->setData( ItemFieldConfigRole, QVariant::fromValue( config ) );
1862 item->setIcon( QgsGui::instance()->editorWidgetRegistry()->icon( config.mEditorWidgetType ) );
1863}
1864
1865void QgsAttributesFormLayoutModel::insertChild( const QModelIndex &parent, int row, const QString &itemId, QgsAttributesFormData::AttributesFormItemType itemType, const QString &itemName )
1866{
1867 if ( row < 0 )
1868 return;
1869
1870 beginInsertRows( parent, row, row );
1871 auto item = std::make_unique< QgsAttributesFormItem >();
1872
1876
1877 // Set the same icon (and, for fields, the same data) the item has in the available widgets tree
1878 if ( itemType == QgsAttributesFormData::Field )
1879 {
1880 setFieldItemDataFromLayer( item.get() );
1881 }
1882 else
1883 {
1884 item->setIcon( iconForItemType( itemType ) );
1885 }
1886
1887 itemForIndex( parent )->insertChild( row, std::move( item ) );
1888 endInsertRows();
1889}
1890
1891
1893 : QSortFilterProxyModel( parent )
1894{}
1895
1897{
1898 mModel = model;
1899 QSortFilterProxyModel::setSourceModel( mModel );
1900}
1901
1906
1908{
1909 return mFilterText;
1910}
1911
1913{
1914 // Since we want to allow refreshing the filter when, e.g.,
1915 // users switch to aliases, then we allow this method to be
1916 // executed even if previous and new filters are equal
1917
1918 mFilterText = filterText.trimmed();
1919 invalidate();
1920}
1921
1922bool QgsAttributesFormProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
1923{
1924 if ( mFilterText.isEmpty() )
1925 return true;
1926
1927 QModelIndex sourceIndex = sourceModel()->index( sourceRow, 0, sourceParent );
1928 if ( !sourceIndex.isValid() )
1929 return false;
1930
1931 // If name or alias match, accept it before any other checks
1932 if ( sourceIndex.data( QgsAttributesFormModel::ItemNameRole ).toString().contains( mFilterText, Qt::CaseInsensitive )
1933 || sourceIndex.data( QgsAttributesFormModel::ItemDisplayRole ).toString().contains( mFilterText, Qt::CaseInsensitive ) )
1934 return true;
1935
1936 // Child is accepted if any of its parents is accepted
1937 QModelIndex parent = sourceIndex.parent();
1938 while ( parent.isValid() )
1939 {
1940 if ( parent.data( QgsAttributesFormModel::ItemNameRole ).toString().contains( mFilterText, Qt::CaseInsensitive )
1941 || parent.data( QgsAttributesFormModel::ItemDisplayRole ).toString().contains( mFilterText, Qt::CaseInsensitive ) )
1942 return true;
1943
1944 parent = parent.parent();
1945 }
1946
1947 return false;
1948}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:6290
@ Action
A layer action element.
Definition qgis.h:6277
@ Container
A container.
Definition qgis.h:6272
@ QmlElement
A QML element.
Definition qgis.h:6275
@ Relation
A relation.
Definition qgis.h:6274
@ HtmlElement
A HTML element.
Definition qgis.h:6276
@ TextElement
A text element.
Definition qgis.h:6278
@ SpacerElement
A spacer element.
Definition qgis.h:6279
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:38
QString name() const
The name of the action. This may be a longer description.
Definition qgsaction.h:136
bool isValid() const
Returns true if this action was a default constructed one.
Definition qgsaction.h:151
QString shortTitle() const
The short title is used to label user interface elements like buttons.
Definition qgsaction.h:139
QUuid id() const
Returns a unique id for this action.
Definition qgsaction.h:145
QPair< QString, QModelIndex > ContainerPair
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This element will load a layer action onto the form.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
A container for attribute editors, used to group them visually in the attribute form if it is set to ...
virtual void addChildElement(QgsAttributeEditorElement *element)
Add a child element to this container.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
void setColumnCount(int columnCount)
Set the number of columns in this group.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
The visibility expression is used in the attribute form to show or hide this container based on an ex...
QgsOptionalExpression collapsedExpression() const
The collapsed expression is used in the attribute form to set the collapsed status of the group box c...
bool collapsed() const
For group box containers returns true if this group box is collapsed.
Qgis::AttributeEditorContainerType type() const
Returns the container type.
void setType(Qgis::AttributeEditorContainerType type)
Sets the container type.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
The collapsed expression is used in the attribute form to set the collapsed status of the group box o...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
Returns the background color of the container.
void setCollapsed(bool collapsed)
For group box containers sets if this group box is collapsed.
int columnCount() const
Gets the number of columns in this group.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color to backgroundColor.
An abstract base class for any elements of a drag and drop form.
void setHorizontalStretch(int stretch)
Sets the horizontal stretch factor for the element.
LabelStyle labelStyle() const
Returns the label style.
void setLabelStyle(const LabelStyle &labelStyle)
Sets the labelStyle.
Qgis::AttributeEditorType type() const
The type of this element.
int verticalStretch() const
Returns the vertical stretch factor for the element.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString name() const
Returns the name of this element.
void setVerticalStretch(int stretch)
Sets the vertical stretch factor for the element.
void setShowLabel(bool showLabel)
Controls if this element should be labeled with a title (field, relation or groupname).
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
This element will load a field's widget onto the form.
An attribute editor widget that will represent arbitrary HTML code.
QString htmlCode() const
The Html code that will be represented within this widget.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code that will be represented within this widget to htmlCode.
An attribute editor widget that will represent arbitrary QML code.
QString qmlCode() const
The QML code that will be represented within this widget.
void setQmlCode(const QString &qmlCode)
Sets the QML code that will be represented within this widget to qmlCode.
This element will load a relation editor onto the form.
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
void setRelationWidgetTypeId(const QString &relationWidgetTypeId)
Sets the relation widget type.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
QVariantMap relationEditorConfiguration() const
Returns the relation editor widget configuration.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
QString relationWidgetTypeId() const
Returns the current relation widget type id.
void setRelationEditorConfiguration(const QVariantMap &config)
Sets the relation editor configuration.
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
QString label() const
Determines the label of this element.
An attribute editor widget that will represent a spacer.
void setDrawLine(bool drawLine)
Sets a flag to define if the spacer element will contain an horizontal line.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
An attribute editor widget that will represent arbitrary text code.
void setText(const QString &text)
Sets the text that will be represented within this widget to text.
QString text() const
The Text that will be represented within this widget.
QgsAttributesAvailableWidgetsModel(QgsVectorLayer *layer, QgsProject *project, QObject *parent=nullptr)
Constructor for QgsAttributesAvailableWidgetsModel, with the given parent.
Qt::DropActions supportedDragActions() const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QModelIndex fieldModelIndex(const QString &fieldName) const
Returns the model index that corresponds to the field with the given fieldName.
QModelIndex actionContainer() const
Returns the action container in this model, expected to be placed at the third top-level row.
void populateLayerActions(const QList< QgsAction > actions)
Refresh layer actions in the model to keep an updated action list.
QModelIndex fieldContainer() const
Returns the field container in this model, expected to be placed at the first top-level row.
QModelIndex relationContainer() const
Returns the relation container in this model, expected to be placed at the second top-level row.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Main class to store and transfer editor data contained in a QgsAttributesFormModel.
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
Returns the HTML editor configuration.
QgsOptionalExpression collapsedExpression() const
Returns the optional expression that dynamically controls the collapsed status of a group box contain...
TextElementEditorConfiguration textElementEditorConfiguration() const
Returns the editor configuration for text element.
bool collapsed() const
For group box containers returns if this group box is collapsed.
SpacerElementEditorConfiguration spacerElementEditorConfiguration() const
Returns the spacer element configuration.
const QgsAttributeEditorElement::LabelStyle labelStyle() const
Returns the label style.
void setColumnCount(int count)
Sets the number of columns for a container.
void setHtmlElementEditorConfiguration(const HtmlElementEditorConfiguration &htmlElementEditorConfiguration)
Sets the HTML editor configuration.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color of a container.
int columnCount() const
Returns the number of columns in a container.
bool showLabel() const
Returns whether the widget's label is to be shown.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
Sets the optional collapsedExpression that dynamically controls the collapsed status of a group box c...
void setShowLabel(bool showLabel)
Sets whether the label for the widget should be shown.
void setQmlElementEditorConfiguration(const QmlElementEditorConfiguration &qmlElementEditorConfiguration)
Sets the QML editor configuration.
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
void setHorizontalStretch(int stretch)
Sets the horizontal stretch factor for the element.
void setContainerType(Qgis::AttributeEditorContainerType type)
Sets the container type.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
void setRelationEditorConfiguration(const RelationEditorConfiguration &relationEditorConfiguration)
Sets the relation editor configuration.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
Sets the optional visibilityExpression that dynamically controls the visibility status of a container...
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
void setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
int verticalStretch() const
Returns the vertical stretch factor for the element.
void setVerticalStretch(int stretch)
Sets the vertical stretch factor for the element.
void setCollapsed(bool collapsed)
For group box containers sets if this group box is collapsed.
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
Returns the QML editor configuration.
QColor backgroundColor() const
Returns the background color of a container.
void setTextElementEditorConfiguration(const TextElementEditorConfiguration &textElementEditorConfiguration)
Sets the editor configuration for text element to textElementEditorConfiguration.
QgsOptionalExpression visibilityExpression() const
Returns the expression to control the visibility status of a container.
AttributesFormItemType
Custom item types.
@ Container
Container for the form, which may be tab, group or row.
@ Relation
Relation between two vector layers.
@ Field
Vector layer field.
@ SpacerWidget
Spacer widget type,.
@ WidgetType
In the available widgets tree, the type of widget.
@ TextWidget
Text widget type,.
Holds parent-child relations as well as item data contained in a QgsAttributesFormModel.
void insertChild(int position, std::unique_ptr< QgsAttributesFormItem > &&item)
Inserts a child item to the item at a given position.
QIcon icon() const
Returns the icon of the item.
void setIcon(const QIcon &icon)
Sets an icon for the item.
QgsAttributesFormItem()=default
QString name() const
Returns the name of the item.
int childCount() const
Returns the number of children items for the given item.
QgsAttributesFormItem * child(int row)
Access the child item located at row position.
static bool isGroup(QgsAttributesFormItem *item)
Returns whether the item is a group.
bool setData(int role, const QVariant &value)
Stores a data value in a given role inside the item.
QgsAttributesFormItem * parent()
Returns the parent object of the item.
QgsAttributesFormItem * firstChildRecursive(const QgsAttributesFormData::AttributesFormItemType &itemType, const QString &itemId) const
Access the first child item that matches itemType and itemId, recursively.
int row() const
Returns the position of the item regarding its parent.
QgsAttributesFormData::AttributesFormItemType type() const
Returns the type of the item.
QString id() const
Returns the id of the item.
QVariant data(int role) const
Returns the data stored in the item, corresponding to the given role.
QString displayName() const
Returns the display name of the item.
void addedChildren(QgsAttributesFormItem *item, int indexFrom, int indexTo)
Notifies other objects when children have been added to the item, informing the indices where added c...
void addChild(std::unique_ptr< QgsAttributesFormItem > &&child)
Appends a child to this item.
QgsAttributesFormItem * firstTopChild(const QgsAttributesFormData::AttributesFormItemType itemType, const QString &itemId) const
Access the first top-level child item that matches itemType and itemId.
std::unique_ptr< QgsAttributesFormItem > takeChild(int index)
Removes the child item placed at the given index from this item without deleting it.
void deleteChildAtIndex(int index)
Deletes the child of the item placed at the given index.
void deleteChildren()
Deletes all child items from this item.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Qt::DropActions supportedDragActions() const override
void updateAliasForFieldItems(const QString &fieldName, const QString &fieldAlias)
Updates the aliases of all matching fields in the model.
QStringList mimeTypes() const override
QgsAttributesFormLayoutModel(QgsVectorLayer *layer, QgsProject *project, QObject *parent=nullptr)
Constructor for QgsAttributesFormLayoutModel, with the given parent.
QMimeData * mimeData(const QModelIndexList &indexes) const override
bool removeRow(int row, const QModelIndex &parent=QModelIndex())
Removes the index located at row within the given parent.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
Qt::DropActions supportedDropActions() const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
void updateFieldConfigForFieldItems(const QString &fieldName, const QgsAttributesFormData::FieldConfig &config)
Updates the field config of all matching fields in the model.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void addContainer(QModelIndex &parent, const QString &name, int columnCount, Qgis::AttributeEditorContainerType type)
Adds a new container to parent.
QList< QgsAddAttributeFormContainerDialog::ContainerPair > listOfContainers() const
Returns a list of containers stored in the model, structured as pairs (name, container model index).
QgsAttributeEditorElement * createAttributeEditorWidget(const QModelIndex &index, QgsAttributeEditorElement *parent) const
Creates a new attribute editor element based on the definition stored in a form layout model index.
Qt::ItemFlags flags(const QModelIndex &index) const override
void internalItemsDropped(const QModelIndexList &indexes)
Informs that items were moved (via drop) in the model from the same model.
void externalItemsDropped(const QModelIndexList &indexes)
Informs that items were inserted (via drop) in the model from another model.
void insertChild(const QModelIndex &parent, int row, const QString &itemId, QgsAttributesFormData::AttributesFormItemType itemType, const QString &itemName)
Inserts a new child to parent model index at the given row position.
Abstract class for tree models allowing for configuration of attributes forms.
void emitDataChangedRecursively(const QModelIndex &parent=QModelIndex(), const QVector< int > &roles=QVector< int >())
Emits dataChanged signal for all parent items in a model.
static QIcon iconForItemType(QgsAttributesFormData::AttributesFormItemType itemType)
Returns the icon used for items of the given itemType, both in the available widgets tree and in the ...
int rowCount(const QModelIndex &parent=QModelIndex()) const override
std::unique_ptr< QgsAttributesFormItem > mRootItem
bool showAliases() const
Returns whether field aliases are preferred over field names as item text.
@ ItemFieldConfigRole
Prior to QGIS 3.44, this was available as FieldConfigRole.
@ ItemDisplayRole
Display text for the item.
@ ItemNameRole
Prior to QGIS 3.44, this was available as FieldNameRole.
@ ItemDataRole
Prior to QGIS 3.44, this was available as DnDTreeRole.
@ ItemIdRole
Items may have ids to ease comparison. Used by Relations, fields, actions and containers.
void setShowAliases(bool show)
Sets whether field aliases should be preferred over field names as item text.
QModelIndex firstRecursiveMatchingModelIndex(const QgsAttributesFormData::AttributesFormItemType &itemType, const QString &itemId) const
Returns the first model index that matches the given itemType and itemId, recursively.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QgsAttributesFormModel(QgsVectorLayer *layer, QgsProject *project, QObject *parent=nullptr)
Constructor for QgsAttributesFormModel, with the given parent.
QModelIndex parent(const QModelIndex &index) const override
~QgsAttributesFormModel() override
QModelIndex firstTopMatchingModelIndex(const QgsAttributesFormData::AttributesFormItemType &itemType, const QString &itemId) const
Returns the first top-level model index that matches the given itemType and itemId.
bool indexLessThan(const QModelIndex &a, const QModelIndex &b) const
Auxiliary function to sort indexes, returning true if index a is less than index b.
void fieldConfigDataChanged(QgsAttributesFormItem *item)
Notifies other objects that the field config data has changed in the item.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QgsAttributesFormItem * rootItem() const
Returns the root item in this model.
QVector< int > rootToLeafPath(QgsAttributesFormItem *item) const
Returns a QVector of iterative positions from root item to the given item.
QgsAttributesFormItem * itemForIndex(const QModelIndex &index) const
Returns the underlying item that corresponds to the given index.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
QgsAttributesFormModel * sourceAttributesFormModel() const
Returns the source model.
void setFilterText(const QString &filterText=QString())
Sets the filter text.
const QString filterText() const
Returns the text used to filter source model items.
QgsAttributesFormProxyModel(QObject *parent=nullptr)
Constructor for QgsAttributesFormProxyModel, with the given parent.
void setAttributesFormSourceModel(QgsAttributesFormModel *model)
Sets the source model for the proxy model.
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
Qgis::AttributeFormReuseLastValuePolicy reuseLastValuePolicy(int index) const
Returns the reuse of last value policy for an attribute index.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the field's split policy, which indicates how field values should be handled during a split o...
Definition qgsfield.cpp:769
QString alias
Definition qgsfield.h:66
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:779
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:67
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the field's merge policy, which indicates how field values should be handled during a merge o...
Definition qgsfield.cpp:789
QString customComment
Definition qgsfield.h:71
QString comment
Definition qgsfield.h:64
QgsFieldConstraints constraints
Definition qgsfield.h:68
Container of fields for a vector layer.
Definition qgsfields.h:45
int count
Definition qgsfields.h:49
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:110
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition qgsgui.cpp:94
An expression with an additional enabled flag.
A relation where the referenced (parent) layer is calculated based on fields from the referencing (ch...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsRelationManager * relationManager
Definition qgsproject.h:125
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QString name
Definition qgsrelation.h:52
QString id
Definition qgsrelation.h:45
Represents a vector layer which manages a vector based dataset.
QgsEditFormConfig editFormConfig
#define QgsDebugError(str)
Definition qgslogger.h:71
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.
Holds the configuration for a field.
Qgis::FieldDuplicatePolicy mDuplicatePolicy
QMap< QString, QVariant > mEditorWidgetConfig
Qgis::FieldDomainSplitPolicy mSplitPolicy
Qgis::FieldDomainMergePolicy mMergePolicy
Qgis::AttributeFormReuseLastValuePolicy mReuseLastValuePolicy