QGIS API Documentation 3.41.0-Master (f75d66fa9f9)
qgsattributesformproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformproperties.cpp
3 ---------------------
4 begin : August 2017
5 copyright : (C) 2017 by David Signer
6 email : david 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
16#include "qgsactionmanager.h"
17#include "qgsaddtaborgroup.h"
21#include "moc_qgsattributesformproperties.cpp"
22#include "qgsattributetypedialog.h"
23#include "qgsattributeformcontaineredit.h"
24#include "qgsattributewidgetedit.h"
26#include "qgsqmlwidgetwrapper.h"
28#include "qgsapplication.h"
29#include "qgscodeeditor.h"
30#include "qgscodeeditorhtml.h"
40#include "qgsgui.h"
43#include "qgsfieldcombobox.h"
44#include "qgsexpressionfinder.h"
46#include "qgshelp.h"
47#include "qgsxmlutils.h"
48
50 : QWidget( parent )
51 , mLayer( layer )
52{
53 if ( !layer )
54 return;
55
56 setupUi( this );
57
58 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
59 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
60 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
61
62 // available widgets tree
63 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
65 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
66 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
67 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
68 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
69 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
71 mAvailableWidgetsTree->setContextMenuPolicy( Qt::CustomContextMenu );
72
73 // form layout tree
74 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
76 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
77 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
78 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
79 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
81
82 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
83 connect( mAvailableWidgetsTree, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
84 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
85 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
86 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
87 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
88 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
89 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
90 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
91
92 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
93 if ( !mBlockUpdates )
94 updatedFields();
95 } );
96
97 // Context menu and children actions
98 mAvailableWidgetsTreeContextMenu = new QMenu( this );
99 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
100 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
101
102 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
103 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
104
105 mAvailableWidgetsTreeContextMenu->addAction( mActionCopyWidgetConfiguration );
106 mAvailableWidgetsTreeContextMenu->addAction( mActionPasteWidgetConfiguration );
107
108 mMessageBar = new QgsMessageBar();
109 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
110 gridLayout->addWidget( mMessageBar, 0, 0 );
111}
112
122
124{
125 mAvailableWidgetsTree->clear();
126 mAvailableWidgetsTree->setSortingEnabled( false );
127 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
128 mAvailableWidgetsTree->setAcceptDrops( false );
129 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
130
131 //load Fields
132
133 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
134 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
135
136 const QgsFields fields = mLayer->fields();
137 for ( int i = 0; i < fields.size(); ++i )
138 {
139 const QgsField field = fields.at( i );
140 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
141 itemData.setShowLabel( true );
142
143 FieldConfig cfg( mLayer, i );
144
145 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
146
147 item->setData( 0, FieldConfigRole, cfg );
148 item->setData( 0, FieldNameRole, field.name() );
149
150 QString tooltip;
151 if ( !field.alias().isEmpty() )
152 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
153 else
154 tooltip = field.name();
155 item->setToolTip( 0, tooltip );
156 }
157 catitem->setExpanded( true );
158
159 //load Relations
160 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
161 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
162
163 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
164
165 for ( const QgsRelation &relation : relations )
166 {
167 QString name;
168 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
169 if ( polymorphicRelation.isValid() )
170 {
171 name = QStringLiteral( "%1 (%2)" ).arg( relation.name(), polymorphicRelation.name() );
172 }
173 else
174 {
175 name = relation.name();
176 }
177 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), name );
178 itemData.setShowLabel( true );
179 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
180 item->setData( 0, FieldNameRole, relation.id() );
181 }
182 catitem->setExpanded( true );
183
184 // Form actions
185 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
186 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
187
188 const QList<QgsAction> actions { mLayer->actions()->actions() };
189
190 for ( const auto &action : std::as_const( actions ) )
191 {
192 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
193 {
194 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
195 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
196 itemData.setShowLabel( true );
197 mAvailableWidgetsTree->addItem( catitem, itemData );
198 }
199 }
200
201 // QML/HTML widget
202 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
203 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
204
205 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
206 itemData.setShowLabel( true );
207 mAvailableWidgetsTree->addItem( catitem, itemData );
208
209 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
210 itemDataHtml.setShowLabel( true );
211 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
212
213 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
214 itemDataText.setShowLabel( true );
215 mAvailableWidgetsTree->addItem( catitem, itemDataText );
216
217 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
218 itemDataSpacer.setShowLabel( false );
219 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
220
221 catitem->setExpanded( true );
222}
223
225{
226 // tabs and groups info
227 mFormLayoutTree->clear();
228 mFormLayoutTree->setSortingEnabled( false );
229 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
230 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
231 mFormLayoutTree->setAcceptDrops( true );
232 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
233
234 const auto constTabs = mLayer->editFormConfig().tabs();
235 for ( QgsAttributeEditorElement *wdg : constTabs )
236 {
237 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
238 }
239}
240
241
243{
245 {
246 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
247 }
248 else
249 {
250 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
251 }
252 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
253 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
254
255 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
256}
257
264
266{
267 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
268
269 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
270
272 mEditFormLineEdit->setText( cfg.uiForm() );
273}
274
276{
278
279 mInitCodeSource = cfg.initCodeSource();
280 mInitFunction = cfg.initFunction();
281 mInitFilePath = cfg.initFilePath();
282 mInitCode = cfg.initCode();
283
284 if ( mInitCode.isEmpty() )
285 {
286 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
287 "QGIS forms can have a Python function that is called when the form is\n"
288 "opened.\n"
289 "\n"
290 "Use this function to add extra logic to your forms.\n"
291 "\n"
292 "Enter the name of the function in the \"Python Init function\"\n"
293 "field.\n"
294 "An example follows:\n"
295 "\"\"\"\n"
296 "from qgis.PyQt.QtWidgets import QWidget\n\n"
297 "def my_form_open(dialog, layer, feature):\n"
298 " geom = feature.geometry()\n"
299 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
300 }
301}
302
303void QgsAttributesFormProperties::loadAttributeTypeDialog()
304{
305 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
306 return;
307
308 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
309
310 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
311 const QString fieldName = item->data( 0, FieldNameRole ).toString();
312 const int index = mLayer->fields().indexOf( fieldName );
313
314 if ( index < 0 )
315 return;
316
317 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
318
319 loadAttributeTypeDialogFromConfiguration( cfg );
320
321 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
322 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
323
324 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
325 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
326
327 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
328}
329
330void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const FieldConfig config )
331{
332 const QgsFieldConstraints constraints = config.mFieldConstraints;
333
334 mAttributeTypeDialog->setAlias( config.mAlias );
335 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
336 mAttributeTypeDialog->setComment( config.mComment );
337 mAttributeTypeDialog->setFieldEditable( config.mEditable );
338 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
339 mAttributeTypeDialog->setReuseLastValues( config.mReuseLastValues );
344 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
345 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
346
349 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
351 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
353 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
354 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
355
356 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
357 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
359
360 // Make sure the widget is refreshed, even if
361 // the new widget type matches the current one
362 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
363 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
364}
365
366void QgsAttributesFormProperties::storeAttributeTypeDialog()
367{
369 return;
370
371 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
372 return;
373
374 FieldConfig cfg;
375
376 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
377 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
378 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
379 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
380 cfg.mAlias = mAttributeTypeDialog->alias();
381 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
382
383 QgsFieldConstraints constraints;
384 if ( mAttributeTypeDialog->notNull() )
385 {
387 }
388 else if ( mAttributeTypeDialog->notNullFromProvider() )
389 {
391 }
392
393 if ( mAttributeTypeDialog->unique() )
394 {
396 }
397 else if ( mAttributeTypeDialog->uniqueFromProvider() )
398 {
400 }
401
402 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
403 {
405 }
406
407 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
408
412
413 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
414 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
415 cfg.mFieldConstraints = constraints;
416 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
417 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
418 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
419 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
420
421 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
422 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
423
424 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
425
426 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
427 {
428 QTreeWidgetItem *item = *itemIt;
429 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
430 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
431 }
432}
433
434void QgsAttributesFormProperties::storeAttributeWidgetEdit()
435{
437 return;
438
439 mAttributeWidgetEdit->updateItemData();
440}
441
442void QgsAttributesFormProperties::loadAttributeWidgetEdit()
443{
444 if ( mFormLayoutTree->selectedItems().count() != 1 )
445 return;
446
447 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
448 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
449 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
450 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
451}
452
453void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
454{
455 mInfoTextWidget = new QLabel( infoText );
456 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
457 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
458}
459
460void QgsAttributesFormProperties::storeAttributeContainerEdit()
461{
463 return;
464
465 mAttributeContainerEdit->updateItemData();
466}
467
468void QgsAttributesFormProperties::loadAttributeContainerEdit()
469{
470 if ( mFormLayoutTree->selectedItems().count() != 1 )
471 return;
472
473 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
474 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
475 mAttributeContainerEdit->registerExpressionContextGenerator( this );
476 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
477 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
478 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
479}
480
481QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
482{
483 auto setCommonProperties = [widgetDef]( DnDTreeItemData &itemData ) {
484 itemData.setShowLabel( widgetDef->showLabel() );
485 itemData.setLabelStyle( widgetDef->labelStyle() );
486 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
487 itemData.setVerticalStretch( widgetDef->verticalStretch() );
488 };
489
490 QTreeWidgetItem *newWidget = nullptr;
491 switch ( widgetDef->type() )
492 {
494 {
495 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
496 setCommonProperties( itemData );
497 newWidget = tree->addItem( parent, itemData );
498 break;
499 }
500
502 {
503 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
504 const QgsAction action { actionEditor->action( mLayer ) };
505 if ( action.isValid() )
506 {
507 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
508 setCommonProperties( itemData );
509 newWidget = tree->addItem( parent, itemData );
510 }
511 else
512 {
513 QgsDebugError( QStringLiteral( "Invalid form action" ) );
514 }
515 break;
516 }
517
519 {
520 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
521 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
522 setCommonProperties( itemData );
523
524 RelationEditorConfiguration relEdConfig;
525 // relEdConfig.buttons = relationEditor->visibleButtons();
526 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
527 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
528 relEdConfig.nmRelationId = relationEditor->nmRelationId();
529 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
530 relEdConfig.label = relationEditor->label();
531 itemData.setRelationEditorConfiguration( relEdConfig );
532 newWidget = tree->addItem( parent, itemData );
533 break;
534 }
535
537 {
538 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
539
540 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
541 if ( !container )
542 break;
543
544 itemData.setColumnCount( container->columnCount() );
545 itemData.setContainerType( container->type() );
546 itemData.setBackgroundColor( container->backgroundColor() );
547 itemData.setVisibilityExpression( container->visibilityExpression() );
548 itemData.setCollapsedExpression( container->collapsedExpression() );
549 itemData.setCollapsed( container->collapsed() );
550
551 setCommonProperties( itemData );
552
553 newWidget = tree->addItem( parent, itemData );
554
555 const QList<QgsAttributeEditorElement *> children = container->children();
556 for ( QgsAttributeEditorElement *wdg : children )
557 {
558 loadAttributeEditorTreeItem( wdg, newWidget, tree );
559 }
560 break;
561 }
562
564 {
565 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
566 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
567 QmlElementEditorConfiguration qmlEdConfig;
568 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
569 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
570 setCommonProperties( itemData );
571 newWidget = tree->addItem( parent, itemData );
572 break;
573 }
574
576 {
577 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
578 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
579 HtmlElementEditorConfiguration htmlEdConfig;
580 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
581 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
582 setCommonProperties( itemData );
583 newWidget = tree->addItem( parent, itemData );
584 break;
585 }
586
588 {
589 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
590 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
591 TextElementEditorConfiguration textEdConfig;
592 textEdConfig.text = textElementEditor->text();
593 itemData.setTextElementEditorConfiguration( textEdConfig );
594 setCommonProperties( itemData );
595 newWidget = tree->addItem( parent, itemData );
596 break;
597 }
598
600 {
601 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
602 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
603 SpacerElementEditorConfiguration spacerEdConfig;
604 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
605 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
606 setCommonProperties( itemData );
607 itemData.setShowLabel( false );
608 newWidget = tree->addItem( parent, itemData );
609 break;
610 }
611
613 {
614 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
615 break;
616 }
617 }
618
619 if ( newWidget )
620 newWidget->setExpanded( true );
621
622 return newWidget;
623}
624
625
626void QgsAttributesFormProperties::onAttributeSelectionChanged()
627{
628 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
629 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
630 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
631}
632
633void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
634{
635 // when the selection changes in the DnD layout, sync the main tree
636 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
637 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
638 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
639}
640
641void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
642{
643 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
644
646 {
647 storeAttributeWidgetEdit();
648 }
650 {
651 storeAttributeTypeDialog();
652 }
653
654 clearAttributeTypeFrame();
655
656 if ( emitter->selectedItems().count() != 1 )
657 {
658 receiver->clearSelection();
659 }
660 else
661 {
662 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
663 switch ( itemData.type() )
664 {
666 {
667 receiver->selectFirstMatchingItem( itemData );
669 {
670 loadAttributeWidgetEdit();
671 }
672 else
673 {
674 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
675 }
676 break;
677 }
679 {
680 receiver->selectFirstMatchingItem( itemData );
682 {
683 loadAttributeWidgetEdit();
684 }
685 loadAttributeTypeDialog();
686 break;
687 }
689 {
690 receiver->clearSelection();
691 loadAttributeContainerEdit();
692 break;
693 }
695 {
696 receiver->selectFirstMatchingItem( itemData );
697 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
698 loadInfoWidget( action.html() );
699 break;
700 }
705 {
707 {
708 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
709 }
710 else
711 {
712 loadInfoWidget( tr( "This configuration is available with double-click" ) );
713 }
714 receiver->clearSelection();
715 break;
716 }
718 {
719 receiver->clearSelection();
720 break;
721 }
722 }
723 }
724}
725
726void QgsAttributesFormProperties::clearAttributeTypeFrame()
727{
729 {
730 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
731 mAttributeWidgetEdit->deleteLater();
732 mAttributeWidgetEdit = nullptr;
733 }
735 {
736 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
737 mAttributeTypeDialog->deleteLater();
738 mAttributeTypeDialog = nullptr;
739 }
741 {
742 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
743 mAttributeContainerEdit->deleteLater();
744 mAttributeContainerEdit = nullptr;
745 }
746 if ( mInfoTextWidget )
747 {
748 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
749 mInfoTextWidget->deleteLater();
750 mInfoTextWidget = nullptr;
751 }
752}
753
754void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
755{
756 Q_UNUSED( checked )
757 const auto selectedItemList { mFormLayoutTree->selectedItems() };
758 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
759 for ( int i = 0; i < rootItem->childCount(); ++i )
760 {
761 rootItem->child( i )->setSelected( !selectedItemList.contains( rootItem->child( i ) ) );
762 }
763}
764
765void QgsAttributesFormProperties::addContainer()
766{
767 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
768
769 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
770 {
771 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
772 if ( itemData.type() == DnDTreeItemData::Container )
773 {
774 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
775 }
776 }
777 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
778 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
779
780 if ( !dialog.exec() )
781 return;
782
783 const QString name = dialog.name();
784 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
785 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(), name, dialog.columnCount(), dialog.containerType() );
786}
787
788void QgsAttributesFormProperties::removeTabOrGroupButton()
789{
790 // deleting an item may delete any number of nested child items -- so we delete
791 // them one at a time and then see if there's any selection left
792 while ( true )
793 {
794 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
795 if ( items.empty() )
796 break;
797
798 delete items.at( 0 );
799 }
800}
801
803{
804 QgsAttributeEditorElement *widgetDef = nullptr;
805
806 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
807
808 switch ( itemData.type() )
809 {
810 //indexed here?
812 {
813 const int idx = mLayer->fields().lookupField( itemData.name() );
814 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
815 break;
816 }
817
819 {
820 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
821 widgetDef = new QgsAttributeEditorAction( action, parent );
822 break;
823 }
824
826 {
827 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
828
829 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
831 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
832 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
833 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
834 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
835 relDef->setLabel( relationEditorConfig.label );
836 widgetDef = relDef;
837 break;
838 }
839
841 {
842 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
843 container->setColumnCount( itemData.columnCount() );
844 // only top-level containers can be tabs
846 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
847 {
848 // a top container found which isn't at the top level -- reset it to a group box instead
850 }
851 container->setType( type );
852 container->setCollapsed( itemData.collapsed() );
853 container->setCollapsedExpression( itemData.collapsedExpression() );
854 container->setVisibilityExpression( itemData.visibilityExpression() );
855 container->setBackgroundColor( itemData.backgroundColor() );
856
857 for ( int t = 0; t < item->childCount(); t++ )
858 {
859 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
860 if ( element )
861 container->addChildElement( element );
862 }
863
864 widgetDef = container;
865 break;
866 }
867
869 {
870 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
871 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
872 widgetDef = element;
873 break;
874 }
875
877 {
878 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
880 widgetDef = element;
881 break;
882 }
883
885 {
886 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
887 element->setText( itemData.textElementEditorConfiguration().text );
888 widgetDef = element;
889 break;
890 }
891
893 {
894 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
896 widgetDef = element;
897 break;
898 }
899
901 break;
902 }
903
904 if ( widgetDef )
905 {
906 widgetDef->setShowLabel( itemData.showLabel() );
907 widgetDef->setLabelStyle( itemData.labelStyle() );
908 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
909 widgetDef->setVerticalStretch( itemData.verticalStretch() );
910 }
911
912 return widgetDef;
913}
914
915void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
916{
917 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
918 switch ( layout )
919 {
921 mFormLayoutWidget->setVisible( false );
922 mUiFileFrame->setVisible( false );
923 mAddTabOrGroupButton->setVisible( false );
924 mRemoveTabOrGroupButton->setVisible( false );
925 mInvertSelectionButton->setVisible( false );
926 break;
927
929 mFormLayoutWidget->setVisible( true );
930 mUiFileFrame->setVisible( false );
931 mAddTabOrGroupButton->setVisible( true );
932 mRemoveTabOrGroupButton->setVisible( true );
933 mInvertSelectionButton->setVisible( true );
934 break;
935
937 // ui file
938 mFormLayoutWidget->setVisible( false );
939 mUiFileFrame->setVisible( true );
940 mAddTabOrGroupButton->setVisible( false );
941 mRemoveTabOrGroupButton->setVisible( false );
942 mInvertSelectionButton->setVisible( false );
943 break;
944 }
945}
946
947void QgsAttributesFormProperties::mTbInitCode_clicked()
948{
949 QgsAttributesFormInitCode attributesFormInitCode;
950
951 attributesFormInitCode.setCodeSource( mInitCodeSource );
952 attributesFormInitCode.setInitCode( mInitCode );
953 attributesFormInitCode.setInitFilePath( mInitFilePath );
954 attributesFormInitCode.setInitFunction( mInitFunction );
955
956 if ( !attributesFormInitCode.exec() )
957 return;
958
959 mInitCodeSource = attributesFormInitCode.codeSource();
960 mInitCode = attributesFormInitCode.initCode();
961 mInitFilePath = attributesFormInitCode.initFilePath();
962 mInitFunction = attributesFormInitCode.initFunction();
963}
964
965void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
966{
967 QgsSettings myQSettings;
968 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
969 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
970
971 if ( uifilename.isNull() )
972 return;
973
974 const QFileInfo fi( uifilename );
975 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
976 mEditFormLineEdit->setText( uifilename );
977}
978
980{
981 storeAttributeWidgetEdit();
982 storeAttributeContainerEdit();
983 storeAttributeTypeDialog();
984}
985
987{
988 mBlockUpdates++;
989 storeAttributeWidgetEdit();
990 storeAttributeContainerEdit();
991 storeAttributeTypeDialog();
992
993 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
994
995 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
996
997 for ( int i = 0; i < fieldContainer->childCount(); i++ )
998 {
999 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1000 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1001
1002 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
1003 const int idx = mLayer->fields().indexOf( fieldName );
1004
1005 //continue in case field does not exist anymore
1006 if ( idx < 0 )
1007 continue;
1008
1009 editFormConfig.setReadOnly( idx, !cfg.mEditable );
1010 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
1011 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
1012
1013 if ( cfg.mDataDefinedProperties.count() > 0 )
1014 {
1015 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
1016 }
1017
1019
1020 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
1021 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
1023 {
1025 }
1026 else
1027 {
1029 }
1031 {
1033 }
1034 else
1035 {
1037 }
1039 {
1041 }
1042 else
1043 {
1045 }
1046
1047 mLayer->setFieldAlias( idx, cfg.mAlias );
1050 }
1051
1052 // tabs and groups
1053 editFormConfig.clearTabs();
1054 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1055 {
1056 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1057 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1058 if ( editorElement )
1059 editFormConfig.addTab( editorElement );
1060 }
1061
1062 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1063
1064 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
1065
1066 editFormConfig.setInitCodeSource( mInitCodeSource );
1067 editFormConfig.setInitFunction( mInitFunction );
1068 editFormConfig.setInitFilePath( mInitFilePath );
1069 editFormConfig.setInitCode( mInitCode );
1070
1071 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
1072
1073 // write the legacy config of relation widgets to support settings read by the API
1074 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1075
1076 for ( int i = 0; i < relationContainer->childCount(); i++ )
1077 {
1078 QTreeWidgetItem *relationItem = relationContainer->child( i );
1079 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1080
1081 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1082 {
1083 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1084 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1085
1086 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1087 {
1088 QVariantMap cfg;
1089
1090 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1091 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1092
1093 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1094 break;
1095 }
1096 }
1097 }
1098
1099 mLayer->setEditFormConfig( editFormConfig );
1100 mBlockUpdates--;
1101}
1102
1103
1104/*
1105 * FieldConfig implementation
1106 */
1108{
1109 mAlias = layer->fields().at( idx ).alias();
1111 mComment = layer->fields().at( idx ).comment();
1112 mEditable = !layer->editFormConfig().readOnly( idx );
1113 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1115 mFieldConstraints = layer->fields().at( idx ).constraints();
1116 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1117 mEditorWidgetType = setup.type();
1118 mEditorWidgetConfig = setup.config();
1119 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1120 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1121}
1122
1123QgsAttributesFormProperties::FieldConfig::operator QVariant()
1124{
1125 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1126}
1127
1128/*
1129 * RelationEditorConfiguration implementation
1130 */
1131
1132QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1133{
1134 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1135}
1136
1137/*
1138 * DnDTree implementation
1139 */
1140
1141QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1142{
1143 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1144 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1145 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1147 itemData.setColumnCount( columnCount );
1148 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1149 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1150 parent->addChild( newItem );
1151 newItem->setExpanded( true );
1152 return newItem;
1153}
1154
1156 : QTreeWidget( parent )
1157 , mLayer( layer )
1158{
1159 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1160}
1161
1162QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon )
1163{
1164 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1165
1166 switch ( data.type() )
1167 {
1175 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1176 break;
1177
1180 {
1181 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1182 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1183 }
1184 break;
1185 }
1186
1187 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) );
1188 newItem->setText( 0, data.displayName() );
1189 newItem->setIcon( 0, icon );
1190
1192 {
1193 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( data.name() );
1194 if ( !relation.isValid() || relation.referencedLayer() != mLayer )
1195 {
1196 newItem->setText( 0, tr( "Invalid relation" ) );
1197 newItem->setForeground( 0, QColor( 255, 0, 0 ) );
1198 }
1199 }
1200
1201 if ( index < 0 )
1202 parent->addChild( newItem );
1203 else
1204 parent->insertChild( index, newItem );
1205
1206 return newItem;
1207}
1208
1214void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1215{
1216 const QMimeData *data = event->mimeData();
1217
1218 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1219 {
1221
1222 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1223 QDataStream stream( &itemData, QIODevice::ReadOnly );
1224 stream >> itemElement;
1225
1226 // Inner drag and drop actions are always MoveAction
1227 if ( event->source() == this )
1228 {
1229 event->setDropAction( Qt::MoveAction );
1230 }
1231 }
1232 else
1233 {
1234 event->ignore();
1235 }
1236
1237 QTreeWidget::dragMoveEvent( event );
1238}
1239
1240
1241bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1242{
1243 bool bDropSuccessful = false;
1244
1245 if ( action == Qt::IgnoreAction )
1246 {
1247 bDropSuccessful = true;
1248 }
1249 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1250 {
1251 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1252 QDataStream stream( &itemData, QIODevice::ReadOnly );
1254
1255 while ( !stream.atEnd() )
1256 {
1257 stream >> itemElement;
1258
1259 QTreeWidgetItem *newItem;
1260
1261 if ( parent )
1262 {
1263 newItem = addItem( parent, itemElement, index++ );
1264 bDropSuccessful = true;
1265 }
1266 else
1267 {
1268 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1269 bDropSuccessful = true;
1270 }
1271
1273 {
1274 onItemDoubleClicked( newItem, 0 );
1275 }
1276
1278 {
1279 onItemDoubleClicked( newItem, 0 );
1280 }
1281
1283 {
1284 onItemDoubleClicked( newItem, 0 );
1285 }
1286
1288 {
1289 onItemDoubleClicked( newItem, 0 );
1290 }
1291
1292 clearSelection();
1293 newItem->setSelected( true );
1294 }
1295 }
1296
1297 return bDropSuccessful;
1298}
1299
1300void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1301{
1302 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1303 return;
1304
1305 if ( event->source() == this )
1306 {
1307 event->setDropAction( Qt::MoveAction );
1308 }
1309
1310 QTreeWidget::dropEvent( event );
1311}
1312
1314{
1315 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1316}
1317
1318#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1319QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1320#else
1321QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1322#endif
1323{
1324 if ( items.count() <= 0 )
1325 return nullptr;
1326
1327 const QStringList types = mimeTypes();
1328
1329 if ( types.isEmpty() )
1330 return nullptr;
1331
1332 QMimeData *data = new QMimeData();
1333 const QString format = types.at( 0 );
1334 QByteArray encoded;
1335 QDataStream stream( &encoded, QIODevice::WriteOnly );
1336
1337 const auto constItems = items;
1338 for ( const QTreeWidgetItem *item : constItems )
1339 {
1340 if ( item )
1341 {
1342 // Relevant information is always in the DnDTreeRole of the first column
1344 stream << itemData;
1345 }
1346 }
1347
1348 data->setData( format, encoded );
1349
1350 return data;
1351}
1352
1353void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1354{
1355 Q_UNUSED( column )
1356
1358
1359 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1360
1361 QFormLayout *baseLayout = new QFormLayout();
1362 baseData->setLayout( baseLayout );
1363 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1364 showLabelCheckbox->setChecked( itemData.showLabel() );
1365 baseLayout->addRow( showLabelCheckbox );
1366 QWidget *baseWidget = new QWidget();
1367 baseWidget->setLayout( baseLayout );
1368
1369 switch ( itemData.type() )
1370 {
1376 break;
1377
1379 {
1380 if ( mType == QgsAttributesDnDTree::Type::Drag )
1381 return;
1382
1383 QDialog dlg;
1384 dlg.setObjectName( "QML Form Configuration Widget" );
1386 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1387
1388 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1389 QSplitter *qmlSplitter = new QSplitter();
1390 QWidget *qmlConfigWiget = new QWidget();
1391 QVBoxLayout *layout = new QVBoxLayout( qmlConfigWiget );
1392 layout->setContentsMargins( 0, 0, 0, 0 );
1393 mainLayout->addWidget( qmlSplitter );
1394 qmlSplitter->addWidget( qmlConfigWiget );
1395 layout->addWidget( baseWidget );
1396
1397 QLineEdit *title = new QLineEdit( itemData.name() );
1398
1399 //qmlCode
1400 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1401 qmlCode->setEditingTimeoutInterval( 250 );
1402 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1403
1404 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1405 QgsFeature previewFeature;
1406 mLayer->getFeatures().nextFeature( previewFeature );
1407
1408 //update preview on text change
1409 connect( qmlCode, &QgsCodeEditor::editingTimeout, this, [=] {
1410 qmlWrapper->setQmlCode( qmlCode->text() );
1411 qmlWrapper->reinitWidget();
1412 qmlWrapper->setFeature( previewFeature );
1413 } );
1414
1415 //templates
1416 QComboBox *qmlObjectTemplate = new QComboBox();
1417 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1418 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1419 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1420 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1421 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [=]( int index ) {
1422 qmlCode->clear();
1423 switch ( index )
1424 {
1425 case 0:
1426 {
1427 qmlCode->setText( QString() );
1428 break;
1429 }
1430 case 1:
1431 {
1432 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1433 "\n"
1434 "Rectangle {\n"
1435 " width: 100\n"
1436 " height: 100\n"
1437 " color: \"steelblue\"\n"
1438 " Text{ text: \"A rectangle\" }\n"
1439 "}\n" ) );
1440 break;
1441 }
1442 case 2:
1443 {
1444 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1445 "import QtCharts 2.0\n"
1446 "\n"
1447 "ChartView {\n"
1448 " width: 400\n"
1449 " height: 400\n"
1450 "\n"
1451 " PieSeries {\n"
1452 " id: pieSeries\n"
1453 " PieSlice { label: \"First slice\"; value: 25 }\n"
1454 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1455 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1456 " }\n"
1457 "}\n" ) );
1458 break;
1459 }
1460 case 3:
1461 {
1462 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1463 "import QtCharts 2.0\n"
1464 "\n"
1465 "ChartView {\n"
1466 " title: \"Bar series\"\n"
1467 " width: 600\n"
1468 " height:400\n"
1469 " legend.alignment: Qt.AlignBottom\n"
1470 " antialiasing: true\n"
1471 " ValueAxis{\n"
1472 " id: valueAxisY\n"
1473 " min: 0\n"
1474 " max: 15\n"
1475 " }\n"
1476 "\n"
1477 " BarSeries {\n"
1478 " id: mySeries\n"
1479 " axisY: valueAxisY\n"
1480 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1481 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1482 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1483 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1484 " }\n"
1485 "}\n" ) );
1486 break;
1487 }
1488 default:
1489 break;
1490 }
1491 } );
1492
1493 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1494 expressionWidget->setButtonVisible( false );
1495 expressionWidget->registerExpressionContextGenerator( this );
1496 expressionWidget->setLayer( mLayer );
1497 QToolButton *addFieldButton = new QToolButton();
1498 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1499
1500 QToolButton *editExpressionButton = new QToolButton();
1501 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1502 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1503
1504 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1505 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1506 if ( !expression.isEmpty() )
1507 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1508 } );
1509
1510 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1511 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1512 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1514 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1515
1516 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1517 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1518 {
1519 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1520 if ( !expression.isEmpty() )
1521 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1522 }
1523 } );
1524
1525 layout->addWidget( new QLabel( tr( "Title" ) ) );
1526 layout->addWidget( title );
1527 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1528 qmlCodeBox->setLayout( new QVBoxLayout );
1529 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1530 QWidget *expressionWidgetBox = new QWidget();
1531 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1532 expressionWidgetBox->setLayout( new QHBoxLayout );
1533 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1534 expressionWidgetBox->layout()->addWidget( expressionWidget );
1535 expressionWidgetBox->layout()->addWidget( addFieldButton );
1536 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1537 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1538 layout->addWidget( qmlCodeBox );
1539 layout->addWidget( qmlCode );
1540 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1541 qmlPreviewBox->setMinimumWidth( 200 );
1542 qmlPreviewBox->setWidget( qmlWrapper->widget() );
1543 //emit to load preview for the first time
1544 emit qmlCode->editingTimeout();
1545 qmlSplitter->addWidget( qmlPreviewBox );
1546 qmlSplitter->setChildrenCollapsible( false );
1547 qmlSplitter->setHandleWidth( 6 );
1548 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
1549
1550 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1551
1552 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1553 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1554 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1555 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1556 } );
1557
1558 mainLayout->addWidget( buttonBox );
1559
1560 if ( dlg.exec() )
1561 {
1563 qmlEdCfg.qmlCode = qmlCode->text();
1564 itemData.setName( title->text() );
1565 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1566 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1567
1568 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1569 item->setText( 0, title->text() );
1570 }
1571 }
1572 break;
1573
1575 {
1576 if ( mType == QgsAttributesDnDTree::Type::Drag )
1577 return;
1578 QDialog dlg;
1579 dlg.setObjectName( "HTML Form Configuration Widget" );
1581 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1582
1583 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1584 QSplitter *htmlSplitter = new QSplitter();
1585 QWidget *htmlConfigWiget = new QWidget();
1586 QVBoxLayout *layout = new QVBoxLayout( htmlConfigWiget );
1587 layout->setContentsMargins( 0, 0, 0, 0 );
1588 mainLayout->addWidget( htmlSplitter );
1589 htmlSplitter->addWidget( htmlConfigWiget );
1590 htmlSplitter->setChildrenCollapsible( false );
1591 htmlSplitter->setHandleWidth( 6 );
1592 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1593 layout->addWidget( baseWidget );
1594
1595 QLineEdit *title = new QLineEdit( itemData.name() );
1596
1597 //htmlCode
1598 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML();
1599 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1600 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1601
1602 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1603 QgsFeature previewFeature;
1604 mLayer->getFeatures().nextFeature( previewFeature );
1605
1606 //update preview on text change
1607 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [=] {
1608 htmlWrapper->setHtmlCode( htmlCode->text() );
1609 htmlWrapper->reinitWidget();
1610 htmlWrapper->setFeature( previewFeature );
1611 } );
1612
1613 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1614 expressionWidget->setButtonVisible( false );
1615 expressionWidget->registerExpressionContextGenerator( this );
1616 expressionWidget->setLayer( mLayer );
1617 QToolButton *addFieldButton = new QToolButton();
1618 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1619
1620 QToolButton *editExpressionButton = new QToolButton();
1621 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1622 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1623
1624 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1625 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1626 if ( !expression.isEmpty() )
1627 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1628 } );
1629
1630 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1631 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1632 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1634 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1635
1636 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1637 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1638 {
1639 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1640 if ( !expression.isEmpty() )
1641 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1642 }
1643 } );
1644
1645 layout->addWidget( new QLabel( tr( "Title" ) ) );
1646 layout->addWidget( title );
1647 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1648 layout->addWidget( expressionWidgetBox );
1649 expressionWidgetBox->setLayout( new QHBoxLayout );
1650 expressionWidgetBox->layout()->addWidget( expressionWidget );
1651 expressionWidgetBox->layout()->addWidget( addFieldButton );
1652 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1653 layout->addWidget( htmlCode );
1654 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1655 htmlPreviewBox->setLayout( new QGridLayout );
1656 htmlPreviewBox->setMinimumWidth( 200 );
1657 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1658 //emit to load preview for the first time
1659 emit htmlCode->textChanged();
1660 htmlSplitter->addWidget( htmlPreviewBox );
1661 htmlSplitter->setChildrenCollapsible( false );
1662 htmlSplitter->setHandleWidth( 6 );
1663 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1664
1665 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1666
1667 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1668 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1669 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1670 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1671 } );
1672
1673 mainLayout->addWidget( buttonBox );
1674
1675 if ( dlg.exec() )
1676 {
1678 htmlEdCfg.htmlCode = htmlCode->text();
1679 itemData.setName( title->text() );
1680 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1681 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1682
1683 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1684 item->setText( 0, title->text() );
1685 }
1686 break;
1687 }
1688
1690 {
1691 if ( mType == QgsAttributesDnDTree::Type::Drag )
1692 return;
1693 QDialog dlg;
1694 dlg.setObjectName( "Text Form Configuration Widget" );
1696 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1697
1698 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1699 QSplitter *textSplitter = new QSplitter();
1700 QWidget *textConfigWiget = new QWidget();
1701 QVBoxLayout *layout = new QVBoxLayout( textConfigWiget );
1702 layout->setContentsMargins( 0, 0, 0, 0 );
1703 mainLayout->addWidget( textSplitter );
1704 textSplitter->addWidget( textConfigWiget );
1705 layout->addWidget( baseWidget );
1706
1707 QLineEdit *title = new QLineEdit( itemData.name() );
1708
1710 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1711 text->setText( itemData.textElementEditorConfiguration().text );
1712
1713 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1714 QgsFeature previewFeature;
1715 mLayer->getFeatures().nextFeature( previewFeature );
1716
1717 //update preview on text change
1718 connect( text, &QgsCodeEditorExpression::textChanged, this, [=] {
1719 textWrapper->setText( text->text() );
1720 textWrapper->reinitWidget();
1721 textWrapper->setFeature( previewFeature );
1722 } );
1723
1724 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1725 expressionWidget->setButtonVisible( false );
1726 expressionWidget->registerExpressionContextGenerator( this );
1727 expressionWidget->setLayer( mLayer );
1728 QToolButton *addFieldButton = new QToolButton();
1729 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1730
1731 QToolButton *editExpressionButton = new QToolButton();
1732 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1733 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1734
1735 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1736 QString expression = expressionWidget->expression().trimmed();
1737 if ( !expression.isEmpty() )
1738 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1739 } );
1740 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1741 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1742
1744 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1745
1746 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1747 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1748 {
1749 QString expression = exprDlg.expressionText().trimmed();
1750 if ( !expression.isEmpty() )
1751 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1752 }
1753 } );
1754
1755 layout->addWidget( new QLabel( tr( "Title" ) ) );
1756 layout->addWidget( title );
1757 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1758 layout->addWidget( expressionWidgetBox );
1759 expressionWidgetBox->setLayout( new QHBoxLayout );
1760 expressionWidgetBox->layout()->addWidget( expressionWidget );
1761 expressionWidgetBox->layout()->addWidget( addFieldButton );
1762 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1763 layout->addWidget( text );
1764 QScrollArea *textPreviewBox = new QgsScrollArea();
1765 textPreviewBox->setLayout( new QGridLayout );
1766 textPreviewBox->setMinimumWidth( 200 );
1767 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1768 //emit to load preview for the first time
1769 emit text->textChanged();
1770 textSplitter->addWidget( textPreviewBox );
1771 textSplitter->setChildrenCollapsible( false );
1772 textSplitter->setHandleWidth( 6 );
1773 textSplitter->setSizes( QList<int>() << 1 << 1 );
1774
1775 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1776
1777 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1778 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1779 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1780 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1781 } );
1782
1783 mainLayout->addWidget( buttonBox );
1784
1785 if ( dlg.exec() )
1786 {
1788 textEdCfg.text = text->text();
1789 itemData.setName( title->text() );
1790 itemData.setTextElementEditorConfiguration( textEdCfg );
1791 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1792
1793 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1794 item->setText( 0, title->text() );
1795 }
1796 break;
1797 }
1798
1800 {
1801 if ( mType == QgsAttributesDnDTree::Type::Drag )
1802 return;
1803 QDialog dlg;
1804 dlg.setObjectName( "Spacer Form Configuration Widget" );
1806 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1807
1808 QVBoxLayout *mainLayout = new QVBoxLayout();
1809 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1810 QLineEdit *title = new QLineEdit( itemData.name() );
1811 mainLayout->addWidget( title );
1812
1813 QHBoxLayout *cbLayout = new QHBoxLayout();
1814 mainLayout->addLayout( cbLayout );
1815 dlg.setLayout( mainLayout );
1816 QCheckBox *cb = new QCheckBox { &dlg };
1817 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1818 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1819 cbLayout->addWidget( cb );
1820
1821 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1822
1823 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1824 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1825 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1826 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1827 } );
1828
1829 mainLayout->addWidget( buttonBox );
1830
1831 if ( dlg.exec() )
1832 {
1834 spacerEdCfg.drawLine = cb->isChecked();
1835 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1836 itemData.setShowLabel( false );
1837 itemData.setName( title->text() );
1838 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1839 item->setText( 0, title->text() );
1840 }
1841
1842 break;
1843 }
1844 }
1845}
1846
1848{
1849 QgsExpressionContext expContext;
1852
1853 if ( mLayer )
1854 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1855
1857 return expContext;
1858}
1859
1861{
1862 return mType;
1863}
1864
1866{
1867 mType = value;
1868}
1869
1871{
1872 QTreeWidgetItemIterator it( this );
1873 while ( *it )
1874 {
1876 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1877 {
1878 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1879 {
1880 // the selection is already good
1881 }
1882 else
1883 {
1884 clearSelection();
1885 ( *it )->setSelected( true );
1886 }
1887 return;
1888 }
1889 ++it;
1890 }
1891 clearSelection();
1892}
1893
1894
1895/*
1896 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1897 */
1898
1899QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1900{
1901 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1902 return stream;
1903}
1904
1905QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1906{
1907 QString name;
1908 QString displayName;
1909 quint32 type;
1910
1911 stream >> type >> name >> displayName;
1912
1914 data.setName( name );
1915 data.setDisplayName( displayName );
1916
1917 return stream;
1918}
1919
1924
1929
1934
1936{
1937 mLabelStyle = labelStyle;
1938}
1939
1941{
1942 return mShowLabel;
1943}
1944
1946{
1947 mShowLabel = showLabel;
1948}
1949
1954
1956{
1957 mVisibilityExpression = visibilityExpression;
1958}
1959
1964
1966{
1967 mCollapsedExpression = collapsedExpression;
1968}
1969
1974
1976{
1977 mRelationEditorConfiguration = relationEditorConfiguration;
1978}
1979
1984
1986{
1987 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1988}
1989
1990
1995
1997{
1998 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1999}
2000
2005
2007{
2008 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
2009}
2010
2012{
2013 return mBackgroundColor;
2014}
2015
2017{
2018 mBackgroundColor = backgroundColor;
2019}
2020
2025
2027{
2028 mTextElementEditorConfiguration = textElementEditorConfiguration;
2029}
2030
2031void QgsAttributesFormProperties::updatedFields()
2032{
2033 // Store configuration to insure changes made are kept after refreshing the list
2034 QMap<QString, FieldConfig> fieldConfigs;
2035 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2036 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2037 {
2038 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2039 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2040 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
2041 fieldConfigs[fieldName] = cfg;
2042 }
2043
2045
2046 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2047 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2048 {
2049 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2050 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2051 if ( fieldConfigs.contains( fieldName ) )
2052 {
2053 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
2054 }
2055 }
2056}
2057
2058void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
2059{
2060 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2061 return;
2062
2063 QPoint globalPos = mAvailableWidgetsTree->viewport()->mapToGlobal( point );
2064
2065 QTreeWidgetItem *item = mAvailableWidgetsTree->itemAt( point );
2066 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2067 if ( itemData.type() == DnDTreeItemData::Field )
2068 {
2069 const QClipboard *clipboard = QApplication::clipboard();
2070 const bool pasteEnabled = clipboard->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) );
2071 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
2072 mAvailableWidgetsTreeContextMenu->popup( globalPos );
2073 }
2074}
2075
2076void QgsAttributesFormProperties::copyWidgetConfiguration()
2077{
2078 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2079 return;
2080
2081 const QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2082 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2083 if ( itemData.type() != DnDTreeItemData::Field )
2084 return;
2085
2086 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2087 const int index = mLayer->fields().indexOf( fieldName );
2088
2089 if ( index < 0 )
2090 return;
2091
2092 const QgsField field = mLayer->fields().field( index );
2093
2094 // We won't copy field aliases nor comments
2095 QDomDocument doc;
2096 QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) );
2097 documentElement.setAttribute( QStringLiteral( "name" ), field.name() );
2098
2099 // Editor widget setup
2100 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
2101
2102 QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) );
2103 documentElement.appendChild( editWidgetElement );
2104 editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() );
2105 QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) );
2106
2107 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
2108 editWidgetElement.appendChild( editWidgetConfigElement );
2109
2110 // Split policy
2111 QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) );
2112 splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) );
2113 documentElement.appendChild( splitPolicyElement );
2114
2115 // Duplicate policy
2116 QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) );
2117 duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
2118 documentElement.appendChild( duplicatePolicyElement );
2119
2120 // Default expressions
2121 QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) );
2122 defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() );
2123 defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2124 documentElement.appendChild( defaultElem );
2125
2126 // Constraints
2127 QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) );
2128 constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() );
2129 constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
2130 constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
2131 constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
2132 documentElement.appendChild( constraintElem );
2133
2134 // Constraint expressions
2135 QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) );
2136 constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() );
2137 constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() );
2138 documentElement.appendChild( constraintExpressionElem );
2139
2140 // Widget general settings
2141 QDomElement widgetGeneralSettingsElem = doc.createElement( QStringLiteral( "widgetGeneralSettings" ) );
2142 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "editable" ), !mLayer->editFormConfig().readOnly( index ) );
2143 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "reuse_last_values" ), mLayer->editFormConfig().labelOnTop( index ) );
2144 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "label_on_top" ), mLayer->editFormConfig().reuseLastValue( index ) );
2145 documentElement.appendChild( widgetGeneralSettingsElem );
2146
2147 // Widget display section
2149 {
2150 // Go for the corresponding form layout item and extract its display settings
2151 if ( mFormLayoutTree->selectedItems().count() != 1 )
2152 return;
2153
2154 const QTreeWidgetItem *itemLayout = mFormLayoutTree->selectedItems().at( 0 );
2155 const DnDTreeItemData itemDataLayout = itemLayout->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2156
2157 QDomElement displayElement = doc.createElement( QStringLiteral( "widgetDisplay" ) );
2158 displayElement.setAttribute( QStringLiteral( "showLabel" ), itemDataLayout.showLabel() );
2159 displayElement.setAttribute( QStringLiteral( "horizontalStretch" ), itemDataLayout.horizontalStretch() );
2160 displayElement.setAttribute( QStringLiteral( "verticalStretch" ), itemDataLayout.verticalStretch() );
2161 displayElement.appendChild( itemDataLayout.labelStyle().writeXml( doc ) );
2162 documentElement.appendChild( displayElement );
2163 }
2164
2165 doc.appendChild( documentElement );
2166
2167 QMimeData *mimeData = new QMimeData;
2168 mimeData->setData( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ), doc.toByteArray() );
2169 QClipboard *clipboard = QApplication::clipboard();
2170 clipboard->setMimeData( mimeData );
2171}
2172
2173void QgsAttributesFormProperties::pasteWidgetConfiguration()
2174{
2175 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2176 return;
2177
2178 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2179
2180 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2181 const int fieldIndex = mLayer->fields().indexOf( fieldName );
2182
2183 if ( fieldIndex < 0 )
2184 return;
2185
2186 // Get base config from target item and ovewrite settings when possible
2187 FieldConfig config = item->data( 0, FieldConfigRole ).value<FieldConfig>();
2188
2189 QDomDocument doc;
2190 QClipboard *clipboard = QApplication::clipboard();
2191 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) ) ) )
2192 {
2193 QDomElement docElem = doc.documentElement();
2194 if ( docElem.tagName() != QLatin1String( "FormWidgetClipboard" ) )
2195 return;
2196
2197 // When pasting, the target item has already been selected and
2198 // has triggered attribute type dialog loading. Therefore, we'll
2199 // only overwrite GUI settings instead of destroying and recreating
2200 // the whole dialog.
2201
2202 // Editor widget configuration
2203 const QDomElement fieldWidgetElement = docElem.firstChildElement( QStringLiteral( "editWidget" ) );
2204 if ( !fieldWidgetElement.isNull() )
2205 {
2206 const QString widgetType = fieldWidgetElement.attribute( QStringLiteral( "type" ) );
2207
2208 // Only paste if source editor widget type is supported by target field
2209 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
2210 if ( factory->supportsField( mLayer, fieldIndex ) )
2211 {
2212 const QDomElement configElement = fieldWidgetElement.firstChildElement( QStringLiteral( "config" ) );
2213 if ( !configElement.isNull() )
2214 {
2215 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
2216 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
2217 QgsReadWriteContext context;
2218 if ( widgetType == QLatin1String( "ValueRelation" ) )
2219 {
2220 optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() );
2221 }
2222
2223 config.mEditorWidgetType = widgetType;
2224 config.mEditorWidgetConfig = optionsMap;
2225 }
2226 }
2227 else
2228 {
2229 mMessageBar->pushMessage( QString(), tr( "Unable to paste widget configuration. The target field (%1) does not support the %2 widget type." ).arg( fieldName, widgetType ), Qgis::MessageLevel::Warning );
2230 }
2231 }
2232
2233 // Split policy
2234 const QDomElement splitPolicyElement = docElem.firstChildElement( QStringLiteral( "splitPolicy" ) );
2235 if ( !splitPolicyElement.isNull() )
2236 {
2237 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainSplitPolicy::Duplicate );
2238 config.mSplitPolicy = policy;
2239 }
2240
2241 // Duplicate policy
2242 const QDomElement duplicatePolicyElement = docElem.firstChildElement( QStringLiteral( "duplicatePolicy" ) );
2243 if ( !duplicatePolicyElement.isNull() )
2244 {
2245 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDuplicatePolicy::Duplicate );
2246 config.mDuplicatePolicy = policy;
2247 }
2248
2249 // Default expressions
2250 const QDomElement defaultElement = docElem.firstChildElement( QStringLiteral( "default" ) );
2251 if ( !defaultElement.isNull() )
2252 {
2253 mAttributeTypeDialog->setDefaultValueExpression( defaultElement.attribute( QStringLiteral( "expression" ) ) );
2254 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( defaultElement.attribute( QStringLiteral( "applyOnUpdate" ) ).toInt() );
2255 }
2256
2257 // Constraints
2258 // take target field constraints as a basis
2259 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
2260 const QDomElement constraintElement = docElem.firstChildElement( QStringLiteral( "constraint" ) );
2261 if ( !constraintElement.isNull() )
2262 {
2263 const int intConstraints = constraintElement.attribute( QStringLiteral( "constraints" ), QStringLiteral( "0" ) ).toInt();
2264 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
2265
2266 // always keep provider constraints intact
2268 {
2269 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
2271 else
2273 }
2275 {
2276 if ( constraints & QgsFieldConstraints::ConstraintUnique )
2278 else
2280 }
2282 {
2285 else
2287 }
2288
2289 const int uniqueStrength = constraintElement.attribute( QStringLiteral( "unique_strength" ), QStringLiteral( "1" ) ).toInt();
2290 const int notNullStrength = constraintElement.attribute( QStringLiteral( "notnull_strength" ), QStringLiteral( "1" ) ).toInt();
2291 const int expStrength = constraintElement.attribute( QStringLiteral( "exp_strength" ), QStringLiteral( "1" ) ).toInt();
2292
2296 }
2297
2298 // Constraint expressions
2299 // always keep provider constraints intact
2301 {
2302 const QDomElement constraintExpressionElement = docElem.firstChildElement( QStringLiteral( "constraintExpression" ) );
2303 if ( !constraintExpressionElement.isNull() )
2304 {
2305 QString expression = constraintExpressionElement.attribute( QStringLiteral( "exp" ), QString() );
2306 QString description = constraintExpressionElement.attribute( QStringLiteral( "desc" ), QString() );
2307 fieldConstraints.setConstraintExpression( expression, description );
2308 }
2309 }
2310 config.mFieldConstraints = fieldConstraints;
2311
2312 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( QStringLiteral( "widgetGeneralSettings" ) );
2313 if ( !widgetGeneralSettingsElement.isNull() )
2314 {
2315 const int editable = widgetGeneralSettingsElement.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
2316 const int reuse = widgetGeneralSettingsElement.attribute( QStringLiteral( "reuse_last_values" ), QStringLiteral( "0" ) ).toInt();
2317 const int labelOnTop = widgetGeneralSettingsElement.attribute( QStringLiteral( "label_on_top" ), QStringLiteral( "0" ) ).toInt();
2318
2319 config.mEditable = editable;
2320 config.mReuseLastValues = reuse;
2321 config.mLabelOnTop = labelOnTop;
2322 }
2323
2324 loadAttributeTypeDialogFromConfiguration( config );
2325
2326 // Widget display section
2328 {
2329 const QDomElement displayElement = docElem.firstChildElement( QStringLiteral( "widgetDisplay" ) );
2330 if ( !displayElement.isNull() )
2331 {
2332 const int showLabel = displayElement.attribute( QStringLiteral( "showLabel" ), QStringLiteral( "0" ) ).toInt();
2333 const int horizontalStretch = displayElement.attribute( QStringLiteral( "horizontalStretch" ), QStringLiteral( "0" ) ).toInt();
2334 const int verticalStretch = displayElement.attribute( QStringLiteral( "verticalStretch" ), QStringLiteral( "0" ) ).toInt();
2336 style.readXml( displayElement );
2337
2338 // Update current GUI controls
2339 mAttributeWidgetEdit->setShowLabel( showLabel );
2340 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
2341 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
2342 mAttributeWidgetEdit->setLabelStyle( style );
2343 }
2344 }
2345 }
2346}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5144
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5174
@ On
Always suppress feature form.
@ Default
Use the application-wide setting.
@ Off
Never suppress feature form.
AttributeFormLayout
Available form types for layout of the attribute form editor.
Definition qgis.h:5159
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
@ UiFile
Load a .ui file for the layout. Needs to be configured.
@ Warning
Warning message.
Definition qgis.h:156
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:3691
@ Duplicate
Duplicate original value.
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:3723
@ Duplicate
Duplicate original value.
@ Action
A layer action element.
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element.
@ SpacerElement
A spacer element.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Returns a list of actions that are available in the given action scope.
QgsAction action(QUuid id) const
Gets an action by its id.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:37
Dialog to add a tab or group of attributes.
QPair< QString, QTreeWidgetItem * > 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.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
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.
This is 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.
This class overrides mime type handling to be able to work with the drag and drop attribute editor.
void setType(QgsAttributesDnDTree::Type value)
void dropEvent(QDropEvent *event) override
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QTreeWidgetItem * addContainer(QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type)
Adds a new container to parent.
QStringList mimeTypes() const override
QgsAttributesDnDTree(QgsVectorLayer *layer, QWidget *parent=nullptr)
QMimeData * mimeData(const QList< QTreeWidgetItem * > &items) const override
bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) override
void dragMoveEvent(QDragMoveEvent *event) override
Is called when mouse is moved over attributes tree before a drop event.
void selectFirstMatchingItem(const QgsAttributesFormProperties::DnDTreeItemData &data)
QTreeWidgetItem * addItem(QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index=-1, const QIcon &icon=QIcon())
Adds a new item to a parent.
A dialog for configuring the Python init code handling for attribute forms.
QString initFilePath() const
Returns the file path for the file containing the init code.
void setCodeSource(Qgis::AttributeFormPythonInitCodeSource source)
Sets the Python init code source.
void setInitFilePath(const QString &initFilePath)
Sets the file path for the file containing the init code.
void setInitFunction(const QString &initFunction)
Sets the name of the init function.
QString initFunction() const
Returns the name of the init function.
void setInitCode(const QString &initCode)
Sets the init code contents.
Qgis::AttributeFormPythonInitCodeSource codeSource() const
Returns the Python init code source.
QString initCode() const
Returns the init code contents.
void setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
Returns the QML editor configuration.
int verticalStretch() const
Returns the vertical stretch factor for the element.
void setTextElementEditorConfiguration(const TextElementEditorConfiguration &textElementEditorConfiguration)
Sets the editor configuration for text element to textElementEditorConfiguration.
SpacerElementEditorConfiguration spacerElementEditorConfiguration() const
Returns the spacer element configuration.
void setRelationEditorConfiguration(const RelationEditorConfiguration &relationEditorConfiguration)
Sets the relation editor configuration.
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
const QgsAttributeEditorElement::LabelStyle labelStyle() const
Returns the label style.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
Sets the optional visibilityExpression that dynamically controls the visibility status of a container...
QgsOptionalExpression collapsedExpression() const
Returns the optional expression that dynamically controls the collapsed status of a group box contain...
@ WidgetType
In the widget tree, the type of widget.
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
void setContainerType(Qgis::AttributeEditorContainerType type)
Sets the container type.
void setQmlElementEditorConfiguration(const QmlElementEditorConfiguration &qmlElementEditorConfiguration)
Sets the QML editor configuration.
TextElementEditorConfiguration textElementEditorConfiguration() const
Returns the editor configuration for text element.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
Sets the optional collapsedExpression that dynamically controls the collapsed status of a group box c...
void setHtmlElementEditorConfiguration(const HtmlElementEditorConfiguration &htmlElementEditorConfiguration)
Sets the HTML editor configuration.
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
Returns the HTML editor configuration.
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
bool collapsed() const
For group box containers returns if this group box is collapsed.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
QgsAttributeEditorElement * createAttributeEditorWidget(QTreeWidgetItem *item, QgsAttributeEditorElement *parent, bool isTopLevel=false)
Creates a new attribute editor element based on the definition stored in item.
QgsAttributeFormContainerEdit * mAttributeContainerEdit
QgsAttributesFormProperties(QgsVectorLayer *layer, QWidget *parent=nullptr)
QgsAttributeTypeDialog * mAttributeTypeDialog
void store()
Stores currently opened widget configuration.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsAttributeWidgetEdit * mAttributeWidgetEdit
void apply()
Applies the attribute from properties to the vector layer.
A HTML editor based on QScintilla2.
A text editor based on QScintilla2.
void setText(const QString &text) override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
void editingTimeout()
Emitted when either:
The QgsDefaultValue class provides a container for managing client side default values for fields.
Contains configuration settings for an editor form.
Qgis::AttributeFormPythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
bool reuseLastValue(int index) const
If this returns true, the widget at the given index will remember the previously entered value from t...
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 ...
QString initCode() const
Gets Python code for edit form initialization.
void setInitFunction(const QString &function)
Set Python function for edit form initialization.
void addTab(QgsAttributeEditorElement *data)
Adds a new element to the invisible root container in the layout.
QString initFilePath() const
Gets Python external file path for edit form initialization.
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
void setSuppress(Qgis::AttributeFormSuppression s)
Sets type of feature form pop-up suppression after feature creation (overrides app setting)
bool setWidgetConfig(const QString &widgetName, const QVariantMap &config)
Set the editor widget config for a widget which is not for a simple field.
void setLayout(Qgis::AttributeFormLayout editorLayout)
Sets the active layout style for the attribute editor for this layer.
void clearTabs()
Clears all the tabs for the attribute editor form with EditorLayout::TabLayout.
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::AttributeFormSuppression suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting)
QString uiForm() const
Returns the path or URL to the .ui form.
void setLabelOnTop(int idx, bool onTop)
If this is set to true, the widget at the given index will receive its label on the previous line whi...
void setInitCodeSource(Qgis::AttributeFormPythonInitCodeSource initCodeSource)
Sets if Python code shall be used for edit form initialization and its origin.
void setDataDefinedFieldProperties(const QString &fieldName, const QgsPropertyCollection &properties)
Set data defined properties for fieldName to properties.
QString initFunction() const
Gets Python function for edit form initialization.
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
void setReuseLastValue(int index, bool reuse)
Sets whether the widget at the given index will remember the previously entered value from this QGIS ...
void setUiForm(const QString &ui)
Set path to the .ui form.
void setInitFilePath(const QString &filePath)
Set Python external file path for edit form initialization.
void setInitCode(const QString &code)
Set Python code for edit form initialization.
Qgis::AttributeFormLayout layout() const
Gets the active layout style for the attribute editor for this layer.
Every attribute editor widget needs a factory, which inherits this class.
bool supportsField(const QgsVectorLayer *vl, int fieldIdx) const
Check if this editor widget type supports a certain field.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
A generic dialog for building expression strings.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Stores information about constraints which may be present on a field.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
void setConstraintStrength(Constraint constraint, ConstraintStrength strength)
Sets the strength of a constraint.
void setConstraintExpression(const QString &expression, const QString &description=QString())
Set the constraint expression for the field.
@ ConstraintOriginProvider
Constraint was set at data provider.
@ ConstraintOriginLayer
Constraint was set by layer.
ConstraintStrength constraintStrength(Constraint constraint) const
Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint is not pres...
ConstraintOrigin constraintOrigin(Constraint constraint) const
Returns the origin of a field constraint, or ConstraintOriginNotSet if the constraint is not present ...
QString constraintExpression() const
Returns the constraint expression for the field, if set.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
void removeConstraint(Constraint constraint)
Removes a constraint from the field.
QString constraintDescription() const
Returns the descriptive name for the constraint expression.
void setConstraint(Constraint constraint, ConstraintOrigin origin=ConstraintOriginLayer)
Sets a constraint on the field.
QFlags< Constraint > Constraints
The QgsFieldExpressionWidget class creates a widget to choose fields and edit expressions It contains...
void setButtonVisible(bool visible)
Set the visibility of the button.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
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:755
QString alias
Definition qgsfield.h:63
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:765
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:64
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:740
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).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
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:95
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:210
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
Wraps a QQuickWidget to display HTML code.
void reinitWidget()
Clears the content and makes new initialization.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
void setFeature(const QgsFeature &feature) override
QString id
Definition qgsmaplayer.h:79
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
An expression with an additional enabled flag.
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
int count() const
Returns the number of properties contained within the collection.
Wraps a QQuickWidget to display QML code.
void setFeature(const QgsFeature &feature) override
void reinitWidget()
Clears the content and makes new initialization.
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
The class is used as a container of context for various read/write operations on other objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
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:44
QString name
Definition qgsrelation.h:50
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QString id
Definition qgsrelation.h:47
A QScrollArea subclass with improved scrolling behavior.
static const QgsSettingsEntryBool * settingsDigitizingDisableEnterAttributeValuesDialog
Settings entry digitizing disable enter attribute values dialog.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Wraps a label widget to display text.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
void setFeature(const QgsFeature &feature) override
Represents a vector layer which manages a vector based data sets.
QgsDefaultValue defaultValueDefinition(int index) const
Returns the definition of the expression used when calculating the default value for a field.
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
void setDefaultValueDefinition(int index, const QgsDefaultValue &definition)
Sets the definition of the expression to use when calculating the default value for a field.
void setEditFormConfig(const QgsEditFormConfig &editFormConfig)
Sets the editFormConfig (configuration) of the form used to represent this vector layer.
void setEditorWidgetSetup(int index, const QgsEditorWidgetSetup &setup)
Sets the editor widget setup for the field at the specified index.
void setConstraintExpression(int index, const QString &expression, const QString &description=QString())
Sets the constraint expression for the specified field index.
void setFieldDuplicatePolicy(int index, Qgis::FieldDuplicatePolicy policy)
Sets a duplicate policy for the field with the specified index.
QgsActionManager * actions()
Returns all layer actions defined on this layer.
void setFieldAlias(int index, const QString &aliasString)
Sets an alias (a display name) for attributes to display in dialogs.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig
void setFieldSplitPolicy(int index, Qgis::FieldDomainSplitPolicy policy)
Sets a split policy for the field with the specified index.
QWidget * widget()
Access the widget managed by this wrapper.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6301
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6282
QDataStream & operator>>(QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data)
QDataStream & operator<<(QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data)
#define QgsDebugError(str)
Definition qgslogger.h:38
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.
void readXml(const QDomNode &node)
Reads configuration from node.