QGIS API Documentation 4.3.0-Master (9ff14a2eeba)
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 emit dataChanged( index( 0, 0, parent ), index( rowCount( parent ) - 1, 0, parent ), roles );
531 for ( int i = 0; i < rowCount( parent ); i++ )
532 {
533 const QModelIndex childIndex = index( i, 0, parent );
534 if ( hasChildren( childIndex ) )
535 {
536 emitDataChangedRecursively( childIndex, roles );
537 }
538 }
539}
540
542{
543 switch ( itemType )
544 {
546 return QgsApplication::getThemeIcon( u"/mEditorWidgetRelationEditor.svg"_s );
547
549 return QgsApplication::getThemeIcon( u"/mEditorWidgetAction.svg"_s );
550
552 return QgsApplication::getThemeIcon( u"/mEditorWidgetQml.svg"_s );
553
555 return QgsApplication::getThemeIcon( u"/mEditorWidgetHtml.svg"_s );
556
558 return QgsApplication::getThemeIcon( u"/mEditorWidgetText.svg"_s );
559
561 return QgsApplication::getThemeIcon( u"/mEditorWidgetSpacer.svg"_s );
562
566 break;
567 }
568 return QIcon();
569}
570
571
575
576Qt::ItemFlags QgsAttributesAvailableWidgetsModel::flags( const QModelIndex &index ) const
577{
578 if ( !index.isValid() )
579 return Qt::NoItemFlags;
580
581 Qt::ItemFlags flags = Qt::ItemIsEnabled;
582
583 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
584 if ( indexType != QgsAttributesFormData::WidgetType )
585 {
586 flags = flags | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable;
587 }
588
589 return flags;
590}
591
592QVariant QgsAttributesAvailableWidgetsModel::headerData( int section, Qt::Orientation orientation, int role ) const
593{
594 Q_UNUSED( section )
595 return orientation == Qt::Horizontal && role == Qt::DisplayRole ? tr( "Available Widgets" ) : QVariant {};
596}
597
599{
600 if ( !mLayer )
601 return;
602
603 beginResetModel();
604 mRootItem->deleteChildren();
605
606 // Load fields
607
608 auto itemFields = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Fields"_s, tr( "Fields" ) );
609
610 const QgsFields fields = mLayer->fields();
611 for ( int i = 0; i < fields.size(); ++i )
612 {
613 const QgsField field = fields.at( i );
615 itemData.setShowLabel( true );
616
618
619 auto item = std::make_unique< QgsAttributesFormItem >();
620 item->setData( ItemFieldConfigRole, cfg );
621 item->setData( ItemNameRole, field.name() );
622 item->setData( ItemIdRole, field.name() ); // Field names act as ids
623 item->setData( ItemDisplayRole, field.alias() );
625 item->setData( ItemDataRole, itemData );
626 item->setIcon( fields.iconForField( i, true ) );
627
628 itemFields->addChild( std::move( item ) );
629 }
630
631 mRootItem->addChild( std::move( itemFields ) );
632
633 // Load relations
634
635 auto itemRelations = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Relations"_s, tr( "Relations" ) );
636
637 const QList<QgsRelation> relations = mProject->relationManager()->referencedRelations( mLayer );
638
639 for ( const QgsRelation &relation : relations )
640 {
641 QString name;
642 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
643 if ( polymorphicRelation.isValid() )
644 {
645 name = u"%1 (%2)"_s.arg( relation.name(), polymorphicRelation.name() );
646 }
647 else
648 {
649 name = relation.name();
650 }
652 itemData.setShowLabel( true );
653
654 auto itemRelation = std::make_unique< QgsAttributesFormItem >();
655 itemRelation->setData( ItemTypeRole, QgsAttributesFormData::Relation );
656 itemRelation->setData( ItemNameRole, name );
657 itemRelation->setData( ItemIdRole, relation.id() );
658 itemRelation->setData( ItemDataRole, itemData );
659 itemRelation->setIcon( iconForItemType( QgsAttributesFormData::Relation ) );
660 itemRelations->addChild( std::move( itemRelation ) );
661 }
662
663 mRootItem->addChild( std::move( itemRelations ) );
664
665 // Load form actions
666
667 auto itemActions = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Actions"_s, tr( "Actions" ) );
668 mRootItem->addChild( std::move( itemActions ) );
669 populateActionItems( mLayer->actions()->actions() );
670
671 // Other widgets
672
673 auto itemOtherWidgets = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::WidgetType, u"Other"_s, tr( "Other Widgets" ) );
674
676 itemData.setShowLabel( true );
677 auto itemQml = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::QmlWidget, itemData, u"QML Widget"_s, tr( "QML Widget" ) );
679 itemOtherWidgets->addChild( std::move( itemQml ) );
680
682 itemHtmlData.setShowLabel( true );
683 auto itemHtml = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::HtmlWidget, itemHtmlData, u"HTML Widget"_s, tr( "HTML Widget" ) );
685 itemOtherWidgets->addChild( std::move( itemHtml ) );
686
688 itemTextData.setShowLabel( true );
689 auto itemText = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::TextWidget, itemTextData, u"Text Widget"_s, tr( "Text Widget" ) );
691 itemOtherWidgets->addChild( std::move( itemText ) );
692
694 itemTextData.setShowLabel( false );
695 auto itemSpacer = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::SpacerWidget, u"Spacer Widget"_s, tr( "Spacer Widget" ) );
697 itemOtherWidgets->addChild( std::move( itemSpacer ) );
698
699 mRootItem->addChild( std::move( itemOtherWidgets ) );
700
701 endResetModel();
702}
703
704void QgsAttributesAvailableWidgetsModel::populateLayerActions( const QList< QgsAction > actions )
705{
706 QModelIndex actionsIndex = actionContainer();
707 QgsAttributesFormItem *itemActions = itemForIndex( actionsIndex );
708
709 beginRemoveRows( actionsIndex, 0, itemActions->childCount() );
710 itemActions->deleteChildren();
711 endRemoveRows();
712
713 int count = 0;
714 for ( const auto &action : std::as_const( actions ) )
715 {
716 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( u"Feature"_s ) || action.actionScopes().contains( u"Layer"_s ) ) )
717 {
718 count++;
719 }
720 }
721
722 if ( count > 0 )
723 {
724 beginInsertRows( actionsIndex, 0, count - 1 );
725 populateActionItems( actions );
726 endInsertRows();
727 }
728}
729
730void QgsAttributesAvailableWidgetsModel::populateActionItems( const QList<QgsAction> actions )
731{
732 QModelIndex actionsIndex = actionContainer();
733 QgsAttributesFormItem *itemActions = itemForIndex( actionsIndex );
734
735 for ( const auto &action : std::as_const( actions ) )
736 {
737 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( u"Feature"_s ) || action.actionScopes().contains( u"Layer"_s ) ) )
738 {
739 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
740
741 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
742 itemData.setShowLabel( true );
743
744 auto itemAction = std::make_unique< QgsAttributesFormItem >();
745 itemAction->setData( ItemIdRole, action.id().toString() );
746 itemAction->setData( ItemTypeRole, QgsAttributesFormData::Action );
747 itemAction->setData( ItemNameRole, actionTitle );
748 itemAction->setData( ItemDataRole, itemData );
749 itemAction->setIcon( iconForItemType( QgsAttributesFormData::Action ) );
750
751 itemActions->addChild( std::move( itemAction ) );
752 }
753 }
754}
755
756QVariant QgsAttributesAvailableWidgetsModel::data( const QModelIndex &index, int role ) const
757{
758 if ( !index.isValid() )
759 return QVariant();
760
762 if ( !item )
763 return QVariant();
764
765 // Relations may be broken due to missing layers or references.
766 // Make those stand out from valid ones.
767 bool invalidRelation = false;
768 if ( ( role == Qt::ToolTipRole || role == Qt::ForegroundRole ) && item->type() == QgsAttributesFormData::Relation )
769 {
770 invalidRelation = !QgsProject::instance()->relationManager()->relation( item->id() ).isValid();
771 }
772
773 switch ( role )
774 {
775 case Qt::DisplayRole:
776 {
777 if ( !showAliases() && item->type() == QgsAttributesFormData::Field )
778 {
779 return item->name();
780 }
781
782 return item->displayName().isEmpty() ? item->name() : item->displayName();
783 }
784
785 case Qt::ToolTipRole:
786 {
788 {
789 const auto cfg = item->data( ItemFieldConfigRole ).value<QgsAttributesFormData::FieldConfig>();
790 if ( !cfg.mAlias.isEmpty() )
791 return tr( "%1 (%2)" ).arg( item->name(), cfg.mAlias );
792 else
793 return item->name();
794 }
795
796 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
797 {
798 // Relation name will be displayed, inform users why it's red via tooltip
799 return tr( "Invalid relation" );
800 }
801
802 return QVariant();
803 }
804
805 case Qt::DecorationRole:
806 return item->icon();
807
808 case Qt::BackgroundRole:
809 {
811 return QBrush( QColor( 140, 140, 140, 50 ) );
812
813 return QVariant();
814 }
815
816 case Qt::ForegroundRole:
817 {
818 if ( item->type() == QgsAttributesFormData::Field )
819 {
820 if ( showAliases() && item->displayName().isEmpty() )
821 {
822 return QBrush( QColor( Qt::lightGray ) );
823 }
824 }
825
826 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
827 {
828 return QBrush( QColor( 255, 0, 0 ) );
829 }
830
831 return QVariant();
832 }
833
834 case Qt::FontRole:
835 {
836 if ( item->type() == QgsAttributesFormData::Field )
837 {
838 if ( showAliases() && item->displayName().isEmpty() )
839 {
840 QFont font = QFont();
841 font.setItalic( true );
842 return font;
843 }
844 }
845 return QVariant();
846 }
847
848 case ItemDataRole:
850 case ItemNameRole:
851 case ItemTypeRole:
852 case ItemIdRole:
853 case ItemDisplayRole:
854 return item->data( role );
855
856 default:
857 return QVariant();
858 }
859}
860
862{
863 return Qt::CopyAction;
864}
865
867{
868 return QStringList() << u"application/x-qgsattributesformavailablewidgetsrelement"_s;
869}
870
871QMimeData *QgsAttributesAvailableWidgetsModel::mimeData( const QModelIndexList &indexes ) const
872{
873 if ( indexes.count() == 0 )
874 return nullptr;
875
876 const QStringList types = mimeTypes();
877 if ( types.isEmpty() )
878 return nullptr;
879
880 QMimeData *data = new QMimeData();
881 const QString format = types.at( 0 );
882 QByteArray encoded;
883 QDataStream stream( &encoded, QIODevice::WriteOnly );
884
885 // Sort indexes since their order reflects selection order
886 QModelIndexList sortedIndexes = indexes;
887
888 std::sort( sortedIndexes.begin(), sortedIndexes.end(), [this]( const QModelIndex &a, const QModelIndex &b ) { return indexLessThan( a, b ); } );
889
890 for ( const QModelIndex &index : std::as_const( sortedIndexes ) )
891 {
892 if ( index.isValid() )
893 {
894 const QString itemId = index.data( QgsAttributesFormModel::ItemIdRole ).toString();
895 const QString itemName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
896 int itemType = index.data( QgsAttributesFormModel::ItemTypeRole ).toInt();
897
898 stream << itemId << itemType << itemName;
899 }
900 }
901
902 data->setData( format, encoded );
903 return data;
904}
905
907{
908 if ( mRootItem->childCount() > 0 )
909 {
910 const int row = 0;
911 QgsAttributesFormItem *item = mRootItem->child( row );
912 if ( item && item->name() == "Fields"_L1 && item->type() == QgsAttributesFormData::WidgetType )
913 return createIndex( row, 0, item );
914 }
915 return QModelIndex();
916}
917
919{
920 if ( mRootItem->childCount() > 1 )
921 {
922 const int row = 1;
923 QgsAttributesFormItem *item = mRootItem->child( row );
924 if ( item && item->name() == "Relations"_L1 && item->type() == QgsAttributesFormData::WidgetType )
925 return createIndex( row, 0, item );
926 }
927 return QModelIndex();
928}
929
931{
932 if ( mRootItem->childCount() > 2 )
933 {
934 const int row = 2;
935 QgsAttributesFormItem *item = mRootItem->child( row );
936 if ( item && item->name() == "Actions"_L1 && item->type() == QgsAttributesFormData::WidgetType )
937 return createIndex( row, 0, item );
938 }
939 return QModelIndex();
940}
941
942QModelIndex QgsAttributesAvailableWidgetsModel::fieldModelIndex( const QString &fieldName ) const
943{
944 if ( mRootItem->childCount() == 0 )
945 return QModelIndex();
946
947 QgsAttributesFormItem *fieldItems = mRootItem->child( 0 );
948 if ( !fieldItems || fieldItems->name() != "Fields"_L1 || fieldItems->type() != QgsAttributesFormData::WidgetType )
949 return QModelIndex();
950
951 QgsAttributesFormItem *item = fieldItems->firstTopChild( QgsAttributesFormData::Field, fieldName );
952 return item ? createIndex( item->row(), 0, item ) : QModelIndex();
953}
954
955
959
960QVariant QgsAttributesFormLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const
961{
962 Q_UNUSED( section )
963 return orientation == Qt::Horizontal && role == Qt::DisplayRole ? tr( "Form Layout" ) : QVariant {};
964}
965
966Qt::ItemFlags QgsAttributesFormLayoutModel::flags( const QModelIndex &index ) const
967{
968 if ( !index.isValid() )
969 return Qt::ItemIsDropEnabled;
970
971 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
972
975 flags |= Qt::ItemIsDropEnabled;
976
977 return flags;
978}
979
981{
982 if ( !mLayer )
983 return;
984
985 beginResetModel();
986 mRootItem->deleteChildren();
987
988 const auto editorElements = mLayer->editFormConfig().tabs();
989 for ( QgsAttributeEditorElement *editorElement : editorElements )
990 {
991 loadAttributeEditorElementItem( editorElement, mRootItem.get() );
992 }
993
994 endResetModel();
995}
996
997void QgsAttributesFormLayoutModel::loadAttributeEditorElementItem( QgsAttributeEditorElement *const editorElement, QgsAttributesFormItem *parent, const int position )
998{
999 auto setCommonProperties = [editorElement]( QgsAttributesFormData::AttributeFormItemData &itemData ) {
1000 itemData.setShowLabel( editorElement->showLabel() );
1001 itemData.setLabelStyle( editorElement->labelStyle() );
1002 itemData.setHorizontalStretch( editorElement->horizontalStretch() );
1003 itemData.setVerticalStretch( editorElement->verticalStretch() );
1004 };
1005
1006 auto editorItem = std::make_unique< QgsAttributesFormItem >();
1007
1008 switch ( editorElement->type() )
1009 {
1011 {
1012 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1013 setCommonProperties( itemData );
1014
1015 editorItem->setData( ItemNameRole, editorElement->name() );
1016 editorItem->setData( ItemIdRole, editorElement->name() ); // Field names act as ids
1017 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Field );
1018 editorItem->setData( ItemDataRole, itemData );
1019
1020 setFieldItemDataFromLayer( editorItem.get() );
1021
1022 break;
1023 }
1024
1026 {
1027 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( editorElement );
1028 const QgsAction action { actionEditor->action( mLayer ) };
1029 if ( action.isValid() )
1030 {
1031 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1032 setCommonProperties( itemData );
1033
1034 editorItem->setData( ItemIdRole, action.id().toString() );
1035 editorItem->setData( ItemNameRole, action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
1036 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Action );
1037 editorItem->setData( ItemDataRole, itemData );
1038 editorItem->setIcon( iconForItemType( QgsAttributesFormData::Action ) );
1039 }
1040 else
1041 {
1042 QgsDebugError( u"Invalid form action"_s );
1043 }
1044 break;
1045 }
1046
1048 {
1049 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1050 setCommonProperties( itemData );
1051
1052 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( editorElement );
1053 QgsAttributesFormData::RelationEditorConfiguration relationEditorConfig;
1054 relationEditorConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
1055 relationEditorConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
1056 relationEditorConfig.nmRelationId = relationEditor->nmRelationId();
1057 relationEditorConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
1058 relationEditorConfig.label = relationEditor->label();
1059 itemData.setRelationEditorConfiguration( relationEditorConfig );
1060
1061 QgsRelation relation = relationEditor->relation();
1062 if ( relation.id().isEmpty() )
1063 {
1064 // If relation is coming from an internal move, we lose the id.
1065 // Go to relation manager and bring relation properties.
1066 relation = mProject->relationManager()->relation( editorElement->name() );
1067 }
1068
1069 editorItem->setData( ItemIdRole, relation.id() );
1070 editorItem->setData( ItemNameRole, relation.name() );
1071 editorItem->setData( ItemDisplayRole, relationEditorConfig.label );
1072 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Relation );
1073 editorItem->setData( ItemDataRole, itemData );
1074 editorItem->setIcon( iconForItemType( QgsAttributesFormData::Relation ) );
1075
1076 break;
1077 }
1078
1080 {
1081 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1082 setCommonProperties( itemData );
1083
1084 editorItem->setData( ItemNameRole, editorElement->name() );
1085 editorItem->setData( ItemIdRole, editorElement->name() ); // Containers don't have id, use name to make them searchable
1086 editorItem->setData( ItemTypeRole, QgsAttributesFormData::Container );
1087
1088 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( editorElement );
1089 if ( !container )
1090 break;
1091
1092 itemData.setColumnCount( container->columnCount() );
1093 itemData.setContainerType( container->type() );
1094 itemData.setBackgroundColor( container->backgroundColor() );
1095 itemData.setVisibilityExpression( container->visibilityExpression() );
1096 itemData.setCollapsedExpression( container->collapsedExpression() );
1097 itemData.setCollapsed( container->collapsed() );
1098
1099 editorItem->setData( ItemDataRole, itemData );
1100
1101 const QList<QgsAttributeEditorElement *> children = container->children();
1102 for ( QgsAttributeEditorElement *childElement : children )
1103 {
1104 loadAttributeEditorElementItem( childElement, editorItem.get() );
1105 }
1106 break;
1107 }
1108
1110 {
1111 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( editorElement );
1112 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1113 setCommonProperties( itemData );
1114
1115 QgsAttributesFormData::QmlElementEditorConfiguration qmlEdConfig;
1116 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
1117 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
1118
1119 editorItem->setData( ItemNameRole, editorElement->name() );
1120 editorItem->setData( ItemTypeRole, QgsAttributesFormData::QmlWidget );
1121 editorItem->setData( ItemDataRole, itemData );
1122 editorItem->setIcon( iconForItemType( QgsAttributesFormData::QmlWidget ) );
1123 break;
1124 }
1125
1127 {
1128 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( editorElement );
1129 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1130 setCommonProperties( itemData );
1131
1132 QgsAttributesFormData::HtmlElementEditorConfiguration htmlEdConfig;
1133 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
1134 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
1135
1136 editorItem->setData( ItemNameRole, editorElement->name() );
1137 editorItem->setData( ItemTypeRole, QgsAttributesFormData::HtmlWidget );
1138 editorItem->setData( ItemDataRole, itemData );
1139 editorItem->setIcon( iconForItemType( QgsAttributesFormData::HtmlWidget ) );
1140 break;
1141 }
1142
1144 {
1145 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( editorElement );
1146 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1147 setCommonProperties( itemData );
1148
1149 QgsAttributesFormData::TextElementEditorConfiguration textEdConfig;
1150 textEdConfig.text = textElementEditor->text();
1151 itemData.setTextElementEditorConfiguration( textEdConfig );
1152
1153 editorItem->setData( ItemNameRole, editorElement->name() );
1154 editorItem->setData( ItemTypeRole, QgsAttributesFormData::TextWidget );
1155 editorItem->setData( ItemDataRole, itemData );
1156 editorItem->setIcon( iconForItemType( QgsAttributesFormData::TextWidget ) );
1157 break;
1158 }
1159
1161 {
1162 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( editorElement );
1163 QgsAttributesFormData::AttributeFormItemData itemData = QgsAttributesFormData::AttributeFormItemData();
1164 setCommonProperties( itemData );
1165 itemData.setShowLabel( false );
1166
1167 QgsAttributesFormData::SpacerElementEditorConfiguration spacerEdConfig;
1168 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
1169 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
1170
1171 editorItem->setData( ItemNameRole, editorElement->name() );
1172 editorItem->setData( ItemTypeRole, QgsAttributesFormData::SpacerWidget );
1173 editorItem->setData( ItemDataRole, itemData );
1174 editorItem->setIcon( iconForItemType( QgsAttributesFormData::SpacerWidget ) );
1175 break;
1176 }
1177
1179 {
1180 QgsDebugError( u"Not loading invalid attribute editor type..."_s );
1181 break;
1182 }
1183 }
1184
1185 if ( position >= 0 && position < parent->childCount() )
1186 {
1187 parent->insertChild( position, std::move( editorItem ) );
1188 }
1189 else
1190 {
1191 parent->addChild( std::move( editorItem ) );
1192 }
1193}
1194
1195QVariant QgsAttributesFormLayoutModel::data( const QModelIndex &index, int role ) const
1196{
1197 if ( !index.isValid() )
1198 return QVariant();
1199
1200 if ( role == ItemFieldConfigRole ) // This model doesn't store data for that role
1201 return false;
1202
1204 if ( !item )
1205 return QVariant();
1206
1207 // Fields may be present in the form layout configuration
1208 // even if their corresponding layer fields were deleted.
1209 // Make those stand out from existent ones.
1210 const int fieldIndex = mLayer->fields().indexOf( item->name() );
1211 const bool invalidField = fieldIndex == -1;
1212
1213 // Relations may be broken due to missing layers or references.
1214 // Make those stand out from valid ones.
1215 bool invalidRelation = false;
1216 if ( ( role == Qt::DisplayRole || role == Qt::ToolTipRole || role == Qt::ForegroundRole ) && item->type() == QgsAttributesFormData::Relation )
1217 {
1218 invalidRelation = !QgsProject::instance()->relationManager()->relation( item->id() ).isValid();
1219 }
1220
1221 switch ( role )
1222 {
1223 case Qt::DisplayRole:
1224 {
1225 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
1226 {
1227 // Invalid relations can have an id, if that's the case, we have a name.
1228 // Only set a new name if id is missing.
1229 if ( item->id().isEmpty() )
1230 {
1231 return tr( "Invalid relation" );
1232 }
1233 }
1234
1235 if ( !showAliases() && ( item->type() == QgsAttributesFormData::Field || item->type() == QgsAttributesFormData::Relation ) )
1236 {
1237 return item->name();
1238 }
1239
1240 return item->displayName().isEmpty() ? item->name() : item->displayName();
1241 }
1242
1243 case Qt::ToolTipRole:
1244 {
1245 if ( item->type() == QgsAttributesFormData::Field )
1246 {
1247 if ( invalidField )
1248 {
1249 return tr( "Invalid field" );
1250 }
1251 else
1252 {
1253 return item->name();
1254 }
1255 }
1256
1257 if ( item->type() == QgsAttributesFormData::Relation && invalidRelation )
1258 {
1259 if ( !item->id().isEmpty() )
1260 {
1261 // The relation name is shown, let's inform users via tooltip why it's red
1262 return tr( "Invalid relation" );
1263 }
1264 }
1265
1266 return QVariant();
1267 }
1268
1269 case Qt::DecorationRole:
1270 return item->icon();
1271
1272 case Qt::BackgroundRole:
1273 {
1274 if ( item->type() == QgsAttributesFormData::Container )
1275 return QBrush( QColor( 140, 140, 140, 50 ) );
1276
1277 return QVariant();
1278 }
1279
1280 case Qt::ForegroundRole:
1281 {
1282 if ( item->type() == QgsAttributesFormData::Field )
1283 {
1284 if ( invalidField )
1285 {
1286 return QBrush( QColor( 255, 0, 0 ) );
1287 }
1288 else if ( showAliases() && item->displayName().isEmpty() )
1289 {
1290 return QBrush( QColor( Qt::lightGray ) );
1291 }
1292 }
1293
1294 if ( item->type() == QgsAttributesFormData::Relation )
1295 {
1296 if ( invalidRelation )
1297 {
1298 return QBrush( QColor( 255, 0, 0 ) );
1299 }
1300 else if ( showAliases() && item->displayName().isEmpty() )
1301 {
1302 return QBrush( QColor( Qt::lightGray ) );
1303 }
1304 }
1305
1306 return QVariant();
1307 }
1308
1309 case Qt::FontRole:
1310 {
1311 if ( item->type() == QgsAttributesFormData::Field )
1312 {
1313 if ( !invalidField && showAliases() && item->displayName().isEmpty() )
1314 {
1315 QFont font = QFont();
1316 font.setItalic( true );
1317 return font;
1318 }
1319 }
1320
1321 if ( item->type() == QgsAttributesFormData::Relation )
1322 {
1323 if ( !invalidRelation && showAliases() && item->displayName().isEmpty() )
1324 {
1325 QFont font = QFont();
1326 font.setItalic( true );
1327 return font;
1328 }
1329 }
1330
1331 return QVariant();
1332 }
1333
1334 case ItemDataRole:
1335 case ItemNameRole:
1336 case ItemIdRole:
1337 case ItemTypeRole:
1338 case ItemDisplayRole:
1339 return item->data( role );
1340
1341 default:
1342 return QVariant();
1343 }
1344}
1345
1346bool QgsAttributesFormLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
1347{
1348 if ( row < 0 )
1349 return false;
1350
1352
1353 if ( row > item->childCount() - count )
1354 return false;
1355
1356 beginRemoveRows( parent, row, row + count - 1 );
1357 for ( int r = 0; r < count; ++r )
1358 item->deleteChildAtIndex( row );
1359 endRemoveRows();
1360 return true;
1361}
1362
1363bool QgsAttributesFormLayoutModel::removeRow( int row, const QModelIndex &parent )
1364{
1365 beginRemoveRows( parent, row, row );
1367 item->deleteChildAtIndex( row );
1368 endRemoveRows();
1369 return true;
1370}
1371
1373{
1374 // For internal moves, QAbstractItemView::startDrag() will delete the dragged item after a successful drop if we don't support Qt::CopyAction
1375 // See QgsAttributesFormLayoutView::dropEvent where we force it to be a CopyAction.
1376 return Qt::CopyAction | Qt::MoveAction;
1377}
1378
1380{
1381 return Qt::DropAction::CopyAction | Qt::DropAction::MoveAction;
1382}
1383
1385{
1386 return QStringList() << u"application/x-qgsattributesformlayoutelement"_s << u"application/x-qgsattributesformavailablewidgetsrelement"_s;
1387}
1388
1389QModelIndexList QgsAttributesFormLayoutModel::curateIndexesForMimeData( const QModelIndexList &indexes ) const
1390{
1391 QModelIndexList containerList;
1392 for ( const auto index : indexes )
1393 {
1394 const auto indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1395 if ( indexType == QgsAttributesFormData::Container )
1396 {
1397 containerList << index;
1398 }
1399 }
1400
1401 if ( containerList.size() == 0 )
1402 return indexes;
1403
1404 QModelIndexList curatedIndexes;
1405
1406 // Iterate searching if current index is child of any container in containerList (recursively)
1407 for ( const auto index : indexes )
1408 {
1409 QModelIndex parent = index.parent();
1410 bool redundantChild = false;
1411
1412 while ( parent.isValid() )
1413 {
1414 if ( containerList.contains( parent ) )
1415 {
1416 redundantChild = true;
1417 break;
1418 }
1419
1420 parent = parent.parent();
1421 }
1422
1423 if ( !redundantChild )
1424 curatedIndexes << index;
1425 }
1426
1427 return curatedIndexes;
1428}
1429
1430QMimeData *QgsAttributesFormLayoutModel::mimeData( const QModelIndexList &indexes ) const
1431{
1432 if ( indexes.count() == 0 )
1433 return nullptr;
1434
1435 // Discard redundant indexes
1436 QModelIndexList curatedIndexes;
1437 if ( indexes.count() > 1 )
1438 {
1439 curatedIndexes = curateIndexesForMimeData( indexes );
1440 }
1441 else
1442 {
1443 curatedIndexes = indexes;
1444 }
1445
1446 const QStringList types = mimeTypes();
1447 if ( types.isEmpty() )
1448 return nullptr;
1449
1450 QMimeData *data = new QMimeData();
1451 const QString format = types.at( 0 );
1452 QByteArray encoded;
1453 QDataStream stream( &encoded, QIODevice::WriteOnly );
1454
1455 // Sort indexes since their order reflects selection order
1456 std::sort( curatedIndexes.begin(), curatedIndexes.end(), [this]( const QModelIndex &a, const QModelIndex &b ) { return indexLessThan( a, b ); } );
1457
1458 // Remember the dragged source items in the same order they are serialized below
1459 // so an internal move can be performed without a rebuild of the dragged item subtree
1460 mDraggedLayoutIndexes.clear();
1461 for ( const QModelIndex &index : std::as_const( curatedIndexes ) )
1462 mDraggedLayoutIndexes << QPersistentModelIndex( index );
1463
1464 for ( const QModelIndex &index : std::as_const( curatedIndexes ) )
1465 {
1466 if ( index.isValid() )
1467 {
1468 QDomDocument doc;
1469
1470 QDomElement rootElem = doc.createElement( u"form_layout_mime"_s );
1472 QDomElement editorElem = editor->toDomElement( doc );
1473 rootElem.appendChild( editorElem );
1474
1475 doc.appendChild( rootElem );
1476 stream << doc.toString( -1 );
1477 }
1478 }
1479
1480 data->setData( format, encoded );
1481 return data;
1482}
1483
1484bool QgsAttributesFormLayoutModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1485{
1486 Q_UNUSED( column )
1487 bool isDropSuccessful = false;
1488 int rows = 0;
1489
1490 if ( row == -1 ) // Dropped at invalid index
1491 row = rowCount( parent ); // Let's append the item
1492
1493 if ( action == Qt::IgnoreAction )
1494 {
1495 isDropSuccessful = true;
1496 }
1497 else if ( data->hasFormat( u"application/x-qgsattributesformavailablewidgetsrelement"_s ) )
1498 {
1499 Q_ASSERT( action == Qt::CopyAction ); // External drop
1500 QByteArray itemData = data->data( u"application/x-qgsattributesformavailablewidgetsrelement"_s );
1501 QDataStream stream( &itemData, QIODevice::ReadOnly );
1502 QModelIndexList addedIndexes;
1503
1504 while ( !stream.atEnd() )
1505 {
1506 QString itemId;
1507 int itemTypeInt;
1508 QString itemName;
1509 stream >> itemId >> itemTypeInt >> itemName;
1510
1511 const auto itemType = static_cast< QgsAttributesFormData::AttributesFormItemType >( itemTypeInt );
1512 insertChild( parent, row + rows, itemId, itemType, itemName );
1513
1514 isDropSuccessful = true;
1515 addedIndexes << index( row + rows, 0, parent );
1516
1517 rows++;
1518 }
1519
1520 if ( !addedIndexes.isEmpty() )
1521 emit externalItemsDropped( addedIndexes );
1522 }
1523 else if ( data->hasFormat( u"application/x-qgsattributesformlayoutelement"_s ) )
1524 {
1525 Q_ASSERT( action == Qt::MoveAction ); // Internal move
1526
1527 const QList< QPersistentModelIndex > draggedIndexes = mDraggedLayoutIndexes;
1528 mDraggedLayoutIndexes.clear();
1529
1530 if ( draggedIndexes.isEmpty() )
1531 return false;
1532
1533 for ( const QPersistentModelIndex &source : draggedIndexes )
1534 {
1535 if ( !source.isValid() )
1536 return false;
1537 }
1538
1539 // Defer the actual relocation until after the drag's modal event loop has
1540 // exited. Mutating the model while the drag is still in
1541 // progress corrupts the proxy's incremental source-to-proxy mapping
1542 const QPersistentModelIndex persistentParent( parent );
1543 QMetaObject::invokeMethod( this, [this, draggedIndexes, persistentParent, row]() { performInternalMove( draggedIndexes, persistentParent, row ); }, Qt::QueuedConnection );
1544
1545 isDropSuccessful = true;
1546 }
1547
1548 return isDropSuccessful;
1549}
1550
1551void QgsAttributesFormLayoutModel::performInternalMove( const QList< QPersistentModelIndex > &draggedIndexes, const QModelIndex &parent, int row )
1552{
1553 for ( const QPersistentModelIndex &source : draggedIndexes )
1554 {
1555 if ( !source.isValid() )
1556 return;
1557 }
1558
1559 QgsAttributesFormItem *destParentItem = itemForIndex( parent );
1560
1561 // Capture the dragged items as raw pointers: they survive the move (only the
1562 // owning unique_ptr is relocated), so they stay valid across the mutations
1563 // below and across the model reset.
1564 QList< QgsAttributesFormItem * > draggedItems;
1565 draggedItems.reserve( draggedIndexes.size() );
1566 for ( const QPersistentModelIndex &source : draggedIndexes )
1567 draggedItems << itemForIndex( source );
1568
1569 // Returns true if ancestor is item itself or one of its ancestors.
1570 const auto isSelfOrAncestorOf = []( const QgsAttributesFormItem *ancestor, QgsAttributesFormItem *item ) {
1571 for ( QgsAttributesFormItem *walk = item; walk; walk = walk->parent() )
1572 {
1573 if ( walk == ancestor )
1574 return true;
1575 }
1576 return false;
1577 };
1578
1579 // We signal the move as a full model reset rather than via fine-grained
1580 // beginInsertRows() and friends.
1581 // This is safe here because dropMimeData() defers this call out of the drag's modal
1582 // loop, so no drag indexes are invalidated. The view restores the
1583 // expanded state and selection afterwards (see internalItemsDropped()).
1584 beginResetModel();
1585
1586 int rows = 0;
1587 QList< QgsAttributesFormItem * > movedItems;
1588 for ( QgsAttributesFormItem *item : std::as_const( draggedItems ) )
1589 {
1590 QgsAttributesFormItem *sourceParentItem = item->parent();
1591 if ( !sourceParentItem )
1592 continue;
1593
1594 const int sourceRow = item->row();
1595 const int destRow = row + rows;
1596
1597 // Moving an item onto its current position is a no-op.
1598 if ( sourceParentItem == destParentItem && ( destRow == sourceRow || destRow == sourceRow + 1 ) )
1599 {
1600 movedItems << item;
1601 rows++;
1602 continue;
1603 }
1604
1605 // A container cannot be moved into itself or one of its own descendants.
1606 if ( isSelfOrAncestorOf( item, destParentItem ) )
1607 continue;
1608
1609 std::unique_ptr< QgsAttributesFormItem > movedItem = sourceParentItem->takeChild( sourceRow );
1610
1611 int insertPosition = destRow;
1612 if ( sourceParentItem == destParentItem && sourceRow < destRow )
1613 insertPosition = destRow - 1; // account for the just-removed source row
1614 insertPosition = std::clamp( insertPosition, 0, destParentItem->childCount() );
1615
1616 destParentItem->insertChild( insertPosition, std::move( movedItem ) );
1617 movedItems << item;
1618
1619 // Removing a source row placed above the drop point shifts all rows below
1620 if ( sourceParentItem == destParentItem && sourceRow < row )
1621 row--;
1622
1623 rows++;
1624 }
1625
1626 endResetModel();
1627
1628 if ( !movedItems.isEmpty() )
1629 {
1630 QModelIndexList indexes;
1631 indexes.reserve( movedItems.size() );
1632 for ( QgsAttributesFormItem *item : std::as_const( movedItems ) )
1633 indexes << createIndex( item->row(), 0, item );
1634 emit internalItemsDropped( indexes );
1635 }
1636}
1637
1638void QgsAttributesFormLayoutModel::updateFieldConfigForFieldItemsRecursive( QgsAttributesFormItem *parent, const QString &fieldName, const QgsAttributesFormData::FieldConfig &config )
1639{
1640 for ( int i = 0; i < parent->childCount(); i++ )
1641 {
1642 QgsAttributesFormItem *child = parent->child( i );
1643 if ( child->name() == fieldName && child->type() == QgsAttributesFormData::Field )
1644 {
1645 child->setData( ItemFieldConfigRole, QVariant::fromValue( config ) );
1646 child->setIcon( QgsGui::instance()->editorWidgetRegistry()->icon( config.mEditorWidgetType ) );
1647 emit fieldConfigDataChanged( child ); // Item's field config has changed, let views know about it
1648 }
1649
1650 if ( child->childCount() > 0 )
1651 {
1652 updateFieldConfigForFieldItemsRecursive( child, fieldName, config );
1653 }
1654 }
1655}
1656
1658{
1659 updateFieldConfigForFieldItemsRecursive( mRootItem.get(), fieldName, config );
1660}
1661
1662void QgsAttributesFormLayoutModel::updateAliasForFieldItemsRecursive( QgsAttributesFormItem *parent, const QString &fieldName, const QString &fieldAlias )
1663{
1664 for ( int i = 0; i < parent->childCount(); i++ )
1665 {
1666 QgsAttributesFormItem *child = parent->child( i );
1667 if ( child->name() == fieldName && child->type() == QgsAttributesFormData::Field )
1668 {
1669 child->setData( ItemDisplayRole, fieldAlias );
1670 const QModelIndex index = createIndex( child->row(), 0, child );
1671 emit dataChanged( index, index ); // Item's alias has changed, let views know about it
1672 }
1673
1674 if ( child->childCount() > 0 )
1675 {
1676 updateAliasForFieldItemsRecursive( child, fieldName, fieldAlias );
1677 }
1678 }
1679}
1680
1681void QgsAttributesFormLayoutModel::updateAliasForFieldItems( const QString &fieldName, const QString &fieldAlias )
1682{
1683 updateAliasForFieldItemsRecursive( mRootItem.get(), fieldName, fieldAlias );
1684}
1685
1686QList< QgsAddAttributeFormContainerDialog::ContainerPair > QgsAttributesFormLayoutModel::recursiveListOfContainers( QgsAttributesFormItem *parent ) const
1687{
1688 QList< QgsAddAttributeFormContainerDialog::ContainerPair > containerList;
1689 for ( int i = 0; i < parent->childCount(); i++ )
1690 {
1691 QgsAttributesFormItem *child = parent->child( i );
1692 if ( child->type() == QgsAttributesFormData::Container )
1693 {
1694 containerList << QgsAddAttributeFormContainerDialog::ContainerPair( child->name(), createIndex( child->row(), 0, child ) );
1695 }
1696
1697 if ( child->childCount() > 0 )
1698 {
1699 containerList.append( recursiveListOfContainers( child ) );
1700 }
1701 }
1702
1703 return containerList;
1704}
1705
1707{
1708 QgsAttributeEditorElement *widgetDef = nullptr;
1709
1711 const int indexType = static_cast< QgsAttributesFormData::AttributesFormItemType >( index.data( QgsAttributesFormModel::ItemTypeRole ).toInt() );
1712 const QString indexName = index.data( QgsAttributesFormModel::ItemNameRole ).toString();
1713 const QString indexId = index.data( QgsAttributesFormModel::ItemIdRole ).toString();
1714
1715 switch ( indexType )
1716 {
1718 {
1719 const int fieldIndex = mLayer->fields().lookupField( indexName );
1720 widgetDef = new QgsAttributeEditorField( indexName, fieldIndex, parent );
1721 break;
1722 }
1723
1725 {
1726 const QgsAction action { mLayer->actions()->action( indexId ) };
1727 widgetDef = new QgsAttributeEditorAction( action, parent );
1728 break;
1729 }
1730
1732 {
1733 const QgsRelation relation = mProject->relationManager()->relation( indexId );
1734
1737 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
1738 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
1739 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
1740 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
1741 relDef->setLabel( relationEditorConfig.label );
1742 widgetDef = relDef;
1743 break;
1744 }
1745
1747 {
1748 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( indexName, parent, itemData.backgroundColor() );
1749 container->setColumnCount( itemData.columnCount() );
1750 // only top-level containers can be tabs
1752 bool isTopLevel = !index.parent().isValid();
1753 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
1754 {
1755 // a tab container found which isn't at the top level -- reset it to a group box instead
1757 }
1758 container->setType( type );
1759 container->setCollapsed( itemData.collapsed() );
1760 container->setCollapsedExpression( itemData.collapsedExpression() );
1761 container->setVisibilityExpression( itemData.visibilityExpression() );
1762 container->setBackgroundColor( itemData.backgroundColor() );
1763
1764 QModelIndex childIndex;
1765 for ( int t = 0; t < rowCount( index ); t++ )
1766 {
1767 childIndex = this->index( t, 0, index );
1768 QgsAttributeEditorElement *element { createAttributeEditorWidget( childIndex, container ) };
1769 if ( element )
1770 container->addChildElement( element );
1771 }
1772 widgetDef = container;
1773 break;
1774 }
1775
1777 {
1779 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
1780 widgetDef = element;
1781 break;
1782 }
1783
1785 {
1788 widgetDef = element;
1789 break;
1790 }
1791
1793 {
1795 element->setText( itemData.textElementEditorConfiguration().text );
1796 widgetDef = element;
1797 break;
1798 }
1799
1801 {
1804 widgetDef = element;
1805 break;
1806 }
1807
1809 default:
1810 break;
1811 }
1812
1813 if ( widgetDef )
1814 {
1815 widgetDef->setShowLabel( itemData.showLabel() );
1816 widgetDef->setLabelStyle( itemData.labelStyle() );
1817 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
1818 widgetDef->setVerticalStretch( itemData.verticalStretch() );
1819 }
1820
1821 return widgetDef;
1822}
1823
1824QList< QgsAddAttributeFormContainerDialog::ContainerPair > QgsAttributesFormLayoutModel::listOfContainers() const
1825{
1826 return recursiveListOfContainers( mRootItem.get() );
1827}
1828
1830{
1831 beginInsertRows( parent, rowCount( parent ), rowCount( parent ) );
1832
1833 QgsAttributesFormItem *parentItem = itemForIndex( parent );
1834
1835 auto containerItem = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::Container, name, QString(), parentItem );
1836
1838 itemData.setColumnCount( columnCount );
1840
1841 containerItem->setData( QgsAttributesFormModel::ItemDataRole, itemData );
1842 containerItem->setData( QgsAttributesFormModel::ItemIdRole, name ); // Make it searchable
1843 parentItem->addChild( std::move( containerItem ) );
1844
1845 endInsertRows();
1846}
1847
1848void QgsAttributesFormLayoutModel::setFieldItemDataFromLayer( QgsAttributesFormItem *item )
1849{
1850 const int fieldIndex = mLayer->fields().indexOf( item->name() );
1851 if ( fieldIndex == -1 )
1852 return;
1853
1854 item->setData( ItemDisplayRole, mLayer->fields().field( fieldIndex ).alias() );
1855
1856 const QgsAttributesFormData::FieldConfig config( mLayer, fieldIndex );
1857 item->setData( ItemFieldConfigRole, QVariant::fromValue( config ) );
1858 item->setIcon( QgsGui::instance()->editorWidgetRegistry()->icon( config.mEditorWidgetType ) );
1859}
1860
1861void QgsAttributesFormLayoutModel::insertChild( const QModelIndex &parent, int row, const QString &itemId, QgsAttributesFormData::AttributesFormItemType itemType, const QString &itemName )
1862{
1863 if ( row < 0 )
1864 return;
1865
1866 beginInsertRows( parent, row, row );
1867 auto item = std::make_unique< QgsAttributesFormItem >();
1868
1872
1873 // Set the same icon (and, for fields, the same data) the item has in the available widgets tree
1874 if ( itemType == QgsAttributesFormData::Field )
1875 {
1876 setFieldItemDataFromLayer( item.get() );
1877 }
1878 else
1879 {
1880 item->setIcon( iconForItemType( itemType ) );
1881 }
1882
1883 itemForIndex( parent )->insertChild( row, std::move( item ) );
1884 endInsertRows();
1885}
1886
1887
1889 : QSortFilterProxyModel( parent )
1890{}
1891
1893{
1894 mModel = model;
1895 QSortFilterProxyModel::setSourceModel( mModel );
1896}
1897
1902
1904{
1905 return mFilterText;
1906}
1907
1909{
1910 // Since we want to allow refreshing the filter when, e.g.,
1911 // users switch to aliases, then we allow this method to be
1912 // executed even if previous and new filters are equal
1913
1914 mFilterText = filterText.trimmed();
1915 invalidate();
1916}
1917
1918bool QgsAttributesFormProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
1919{
1920 if ( mFilterText.isEmpty() )
1921 return true;
1922
1923 QModelIndex sourceIndex = sourceModel()->index( sourceRow, 0, sourceParent );
1924 if ( !sourceIndex.isValid() )
1925 return false;
1926
1927 // If name or alias match, accept it before any other checks
1928 if ( sourceIndex.data( QgsAttributesFormModel::ItemNameRole ).toString().contains( mFilterText, Qt::CaseInsensitive )
1929 || sourceIndex.data( QgsAttributesFormModel::ItemDisplayRole ).toString().contains( mFilterText, Qt::CaseInsensitive ) )
1930 return true;
1931
1932 // Child is accepted if any of its parents is accepted
1933 QModelIndex parent = sourceIndex.parent();
1934 while ( parent.isValid() )
1935 {
1936 if ( parent.data( QgsAttributesFormModel::ItemNameRole ).toString().contains( mFilterText, Qt::CaseInsensitive )
1937 || parent.data( QgsAttributesFormModel::ItemDisplayRole ).toString().contains( mFilterText, Qt::CaseInsensitive ) )
1938 return true;
1939
1940 parent = parent.parent();
1941 }
1942
1943 return false;
1944}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:6120
@ Action
A layer action element.
Definition qgis.h:6107
@ Container
A container.
Definition qgis.h:6102
@ QmlElement
A QML element.
Definition qgis.h:6105
@ Relation
A relation.
Definition qgis.h:6104
@ HtmlElement
A HTML element.
Definition qgis.h:6106
@ TextElement
A text element.
Definition qgis.h:6108
@ SpacerElement
A spacer element.
Definition qgis.h:6109
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.
QDomElement toDomElement(QDomDocument &doc) const
Gets the XML Dom element to save this 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:46
int count
Definition qgsfields.h:50
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:109
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition qgsgui.cpp:93
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