QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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
48 : QWidget( parent )
49 , mLayer( layer )
50{
51 if ( !layer )
52 return;
53
54 setupUi( this );
55
56 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
57 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
58 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
59
60 // available widgets tree
61 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
63 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
64 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
65 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
66 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
67 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
69
70 // form layout tree
71 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
73 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
74 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
75 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
76 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
78
79 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
80 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
81 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
82 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
83 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
84 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
85 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
86 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
87
88 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this]
89 {
90 if ( !mBlockUpdates )
91 updatedFields();
92 } );
93}
94
104
106{
107 mAvailableWidgetsTree->clear();
108 mAvailableWidgetsTree->setSortingEnabled( false );
109 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
110 mAvailableWidgetsTree->setAcceptDrops( false );
111 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
112
113 //load Fields
114
115 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
116 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
117
118 const QgsFields fields = mLayer->fields();
119 for ( int i = 0; i < fields.size(); ++i )
120 {
121 const QgsField field = fields.at( i );
122 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
123 itemData.setShowLabel( true );
124
125 FieldConfig cfg( mLayer, i );
126
127 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
128
129 item->setData( 0, FieldConfigRole, cfg );
130 item->setData( 0, FieldNameRole, field.name() );
131
132 QString tooltip;
133 if ( !field.alias().isEmpty() )
134 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
135 else
136 tooltip = field.name();
137 item->setToolTip( 0, tooltip );
138 }
139 catitem->setExpanded( true );
140
141 //load Relations
142 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
143 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
144
145 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
146
147 for ( const QgsRelation &relation : relations )
148 {
149 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), relation.name() );
150 itemData.setShowLabel( true );
151 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
152 item->setData( 0, FieldNameRole, relation.id() );
153 }
154 catitem->setExpanded( true );
155
156 // Form actions
157 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
158 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
159
160 const QList<QgsAction> actions { mLayer->actions()->actions( ) };
161
162 for ( const auto &action : std::as_const( actions ) )
163 {
164 if ( action.isValid() && action.runable() &&
165 ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) ||
166 action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
167 {
168 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
169 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
170 itemData.setShowLabel( true );
171 mAvailableWidgetsTree->addItem( catitem, itemData );
172 }
173 }
174
175 // QML/HTML widget
176 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
177 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
178
179 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
180 itemData.setShowLabel( true );
181 mAvailableWidgetsTree->addItem( catitem, itemData );
182
183 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
184 itemDataHtml.setShowLabel( true );
185 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
186
187 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
188 itemDataText.setShowLabel( true );
189 mAvailableWidgetsTree->addItem( catitem, itemDataText );
190
191 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
192 itemDataSpacer.setShowLabel( false );
193 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
194
195 catitem ->setExpanded( true );
196}
197
199{
200 // tabs and groups info
201 mFormLayoutTree->clear();
202 mFormLayoutTree->setSortingEnabled( false );
203 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
204 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
205 mFormLayoutTree->setAcceptDrops( true );
206 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
207
208 const auto constTabs = mLayer->editFormConfig().tabs();
209 for ( QgsAttributeEditorElement *wdg : constTabs )
210 {
211 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
212 }
213}
214
215
217{
219 {
220 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
221 }
222 else
223 {
224 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
225 }
226 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
227 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
228
229 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
230}
231
238
240{
241 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
242
243 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
244
246 mEditFormLineEdit->setText( cfg.uiForm() );
247}
248
250{
252
253 mInitCodeSource = cfg.initCodeSource();
254 mInitFunction = cfg.initFunction();
255 mInitFilePath = cfg.initFilePath();
256 mInitCode = cfg.initCode();
257
258 if ( mInitCode.isEmpty() )
259 {
260 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
261 "QGIS forms can have a Python function that is called when the form is\n"
262 "opened.\n"
263 "\n"
264 "Use this function to add extra logic to your forms.\n"
265 "\n"
266 "Enter the name of the function in the \"Python Init function\"\n"
267 "field.\n"
268 "An example follows:\n"
269 "\"\"\"\n"
270 "from qgis.PyQt.QtWidgets import QWidget\n\n"
271 "def my_form_open(dialog, layer, feature):\n"
272 " geom = feature.geometry()\n"
273 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
274 }
275}
276
277void QgsAttributesFormProperties::loadAttributeTypeDialog()
278{
279 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
280 return;
281
282 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
283
284 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
285 const QString fieldName = item->data( 0, FieldNameRole ).toString();
286 const int index = mLayer->fields().indexOf( fieldName );
287
288 if ( index < 0 )
289 return;
290
291 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
292
293 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
294
295 mAttributeTypeDialog->setAlias( cfg.mAlias );
296 mAttributeTypeDialog->setDataDefinedProperties( cfg.mDataDefinedProperties );
297 mAttributeTypeDialog->setComment( cfg.mComment );
298 mAttributeTypeDialog->setFieldEditable( cfg.mEditable );
299 mAttributeTypeDialog->setLabelOnTop( cfg.mLabelOnTop );
300 mAttributeTypeDialog->setReuseLastValues( cfg.mReuseLastValues );
305 mAttributeTypeDialog->setSplitPolicy( cfg.mSplitPolicy );
306 mAttributeTypeDialog->setDuplicatePolicy( cfg.mDuplicatePolicy );
307
310 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
312 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
314 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
315 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
316
317 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
318 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
320 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
321 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
322
323 mAttributeTypeDialog->setEditorWidgetConfig( cfg.mEditorWidgetConfig );
324 mAttributeTypeDialog->setEditorWidgetType( cfg.mEditorWidgetType );
325
326 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
327 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
328
329 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
330}
331
332
333void QgsAttributesFormProperties::storeAttributeTypeDialog()
334{
336 return;
337
338 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
339 return;
340
341 FieldConfig cfg;
342
343 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
344 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
345 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
346 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
347 cfg.mAlias = mAttributeTypeDialog->alias();
348 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
349
350 QgsFieldConstraints constraints;
351 if ( mAttributeTypeDialog->notNull() )
352 {
354 }
355 else if ( mAttributeTypeDialog->notNullFromProvider() )
356 {
358 }
359
360 if ( mAttributeTypeDialog->unique() )
361 {
363 }
364 else if ( mAttributeTypeDialog->uniqueFromProvider() )
365 {
367 }
368
369 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
370 {
372 }
373
374 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
375
380 constraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, mAttributeTypeDialog->constraintExpressionEnforced() ?
382
383 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
384 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
385 cfg.mFieldConstraints = constraints;
386 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
387 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
388 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
389 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
390
391 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
392 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
393
394 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
395
396 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
397 {
398 QTreeWidgetItem *item = *itemIt;
399 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
400 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
401 }
402}
403
404void QgsAttributesFormProperties::storeAttributeWidgetEdit()
405{
407 return;
408
409 mAttributeWidgetEdit->updateItemData();
410}
411
412void QgsAttributesFormProperties::loadAttributeWidgetEdit()
413{
414 if ( mFormLayoutTree->selectedItems().count() != 1 )
415 return;
416
417 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
418 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
419 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
420 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
421}
422
423void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
424{
425 mInfoTextWidget = new QLabel( infoText );
426 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
427 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
428}
429
430void QgsAttributesFormProperties::storeAttributeContainerEdit()
431{
433 return;
434
435 mAttributeContainerEdit->updateItemData();
436}
437
438void QgsAttributesFormProperties::loadAttributeContainerEdit()
439{
440 if ( mFormLayoutTree->selectedItems().count() != 1 )
441 return;
442
443 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
444 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
445 mAttributeContainerEdit->registerExpressionContextGenerator( this );
446 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
447 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
448 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
449
450}
451
452QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
453{
454 auto setCommonProperties = [widgetDef]( DnDTreeItemData & itemData )
455 {
456 itemData.setShowLabel( widgetDef->showLabel() );
457 itemData.setLabelStyle( widgetDef->labelStyle() );
458 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
459 itemData.setVerticalStretch( widgetDef->verticalStretch() );
460 };
461
462 QTreeWidgetItem *newWidget = nullptr;
463 switch ( widgetDef->type() )
464 {
466 {
467 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
468 setCommonProperties( itemData );
469 newWidget = tree->addItem( parent, itemData );
470 break;
471 }
472
474 {
475 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
476 const QgsAction action { actionEditor->action( mLayer ) };
477 if ( action.isValid() )
478 {
479 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
480 setCommonProperties( itemData );
481 newWidget = tree->addItem( parent, itemData );
482 }
483 else
484 {
485 QgsDebugError( QStringLiteral( "Invalid form action" ) );
486 }
487 break;
488 }
489
491 {
492 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
493 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
494 setCommonProperties( itemData );
495
496 RelationEditorConfiguration relEdConfig;
497// relEdConfig.buttons = relationEditor->visibleButtons();
498 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
499 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
500 relEdConfig.nmRelationId = relationEditor->nmRelationId();
501 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
502 relEdConfig.label = relationEditor->label();
503 itemData.setRelationEditorConfiguration( relEdConfig );
504 newWidget = tree->addItem( parent, itemData );
505 break;
506 }
507
509 {
510 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
511
512 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
513 if ( !container )
514 break;
515
516 itemData.setColumnCount( container->columnCount() );
517 itemData.setContainerType( container->type() );
518 itemData.setBackgroundColor( container->backgroundColor() );
519 itemData.setVisibilityExpression( container->visibilityExpression() );
520 itemData.setCollapsedExpression( container->collapsedExpression() );
521 itemData.setCollapsed( container->collapsed() );
522
523 setCommonProperties( itemData );
524
525 newWidget = tree->addItem( parent, itemData );
526
527 const QList<QgsAttributeEditorElement *> children = container->children();
528 for ( QgsAttributeEditorElement *wdg : children )
529 {
530 loadAttributeEditorTreeItem( wdg, newWidget, tree );
531 }
532 break;
533 }
534
536 {
537 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
538 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
539 QmlElementEditorConfiguration qmlEdConfig;
540 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
541 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
542 setCommonProperties( itemData );
543 newWidget = tree->addItem( parent, itemData );
544 break;
545 }
546
548 {
549 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
550 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
551 HtmlElementEditorConfiguration htmlEdConfig;
552 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
553 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
554 setCommonProperties( itemData );
555 newWidget = tree->addItem( parent, itemData );
556 break;
557 }
558
560 {
561 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
562 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
563 TextElementEditorConfiguration textEdConfig;
564 textEdConfig.text = textElementEditor->text();
565 itemData.setTextElementEditorConfiguration( textEdConfig );
566 setCommonProperties( itemData );
567 newWidget = tree->addItem( parent, itemData );
568 break;
569 }
570
572 {
573 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
574 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
575 SpacerElementEditorConfiguration spacerEdConfig;
576 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
577 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
578 setCommonProperties( itemData );
579 itemData.setShowLabel( false );
580 newWidget = tree->addItem( parent, itemData );
581 break;
582 }
583
585 {
586 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
587 break;
588 }
589 }
590
591 if ( newWidget )
592 newWidget->setExpanded( true );
593
594 return newWidget;
595}
596
597
598void QgsAttributesFormProperties::onAttributeSelectionChanged()
599{
600 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
601 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
602 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
603}
604
605void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
606{
607 // when the selection changes in the DnD layout, sync the main tree
608 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
609 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
610 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
611}
612
613void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
614{
615 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
616
618 storeAttributeWidgetEdit();
619
620 clearAttributeTypeFrame();
621
622 if ( emitter->selectedItems().count() != 1 )
623 {
624 receiver->clearSelection();
625 }
626 else
627 {
628 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
629 switch ( itemData.type() )
630 {
632 {
633 receiver->selectFirstMatchingItem( itemData );
635 {
636 loadAttributeWidgetEdit();
637 }
638 else
639 {
640 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
641 }
642 break;
643 }
645 {
646 receiver->selectFirstMatchingItem( itemData );
648 loadAttributeWidgetEdit();
649 loadAttributeTypeDialog();
650 break;
651 }
653 {
654 receiver->clearSelection();
655 loadAttributeContainerEdit();
656 break;
657 }
659 {
660 receiver->selectFirstMatchingItem( itemData );
661 const QgsAction action {mLayer->actions()->action( itemData.name() )};
662 loadInfoWidget( action.html() );
663 break;
664 }
669 {
671 {
672 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
673 }
674 else
675 {
676 loadInfoWidget( tr( "This configuration is available with double-click" ) );
677 }
678 receiver->clearSelection();
679 break;
680 }
682 {
683 receiver->clearSelection();
684 break;
685 }
686 }
687 }
688}
689
690void QgsAttributesFormProperties::clearAttributeTypeFrame()
691{
693 {
694 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
695 mAttributeWidgetEdit->deleteLater();
696 mAttributeWidgetEdit = nullptr;
697 }
699 {
700 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
701 mAttributeTypeDialog->deleteLater();
702 mAttributeTypeDialog = nullptr;
703 }
705 {
706 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
707 mAttributeContainerEdit->deleteLater();
708 mAttributeContainerEdit = nullptr;
709 }
710 if ( mInfoTextWidget )
711 {
712 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
713 mInfoTextWidget->deleteLater();
714 mInfoTextWidget = nullptr;
715 }
716}
717
718void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
719{
720 Q_UNUSED( checked )
721 const auto selectedItemList { mFormLayoutTree->selectedItems() };
722 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
723 for ( int i = 0; i < rootItem->childCount(); ++i )
724 {
725 rootItem->child( i )->setSelected( ! selectedItemList.contains( rootItem->child( i ) ) );
726 }
727}
728
729void QgsAttributesFormProperties::addContainer()
730{
731 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
732
733 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
734 {
735 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
736 if ( itemData.type() == DnDTreeItemData::Container )
737 {
738 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
739 }
740 }
741 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
742 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
743
744 if ( !dialog.exec() )
745 return;
746
747 const QString name = dialog.name();
748 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
749 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(),
750 name,
751 dialog.columnCount(),
752 dialog.containerType() );
753}
754
755void QgsAttributesFormProperties::removeTabOrGroupButton()
756{
757 // deleting an item may delete any number of nested child items -- so we delete
758 // them one at a time and then see if there's any selection left
759 while ( true )
760 {
761 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
762 if ( items.empty() )
763 break;
764
765 delete items.at( 0 );
766 }
767
768}
769
771{
772 QgsAttributeEditorElement *widgetDef = nullptr;
773
774 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
775
776 switch ( itemData.type() )
777 {
778 //indexed here?
780 {
781 const int idx = mLayer->fields().lookupField( itemData.name() );
782 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
783 break;
784 }
785
787 {
788 const QgsAction action { mLayer->actions()->action( itemData.name() )};
789 widgetDef = new QgsAttributeEditorAction( action, parent );
790 break;
791 }
792
794 {
795 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
796 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
798 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
799 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
800 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
801 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
802 relDef->setLabel( relationEditorConfig.label );
803 widgetDef = relDef;
804 break;
805 }
806
808 {
809 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
810 container->setColumnCount( itemData.columnCount() );
811 // only top-level containers can be tabs
813 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
814 {
815 // a top container found which isn't at the top level -- reset it to a group box instead
817 }
818 container->setType( type );
819 container->setCollapsed( itemData.collapsed() );
820 container->setCollapsedExpression( itemData.collapsedExpression() );
821 container->setVisibilityExpression( itemData.visibilityExpression() );
822 container->setBackgroundColor( itemData.backgroundColor( ) );
823
824 for ( int t = 0; t < item->childCount(); t++ )
825 {
826 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
827 if ( element )
828 container->addChildElement( element );
829 }
830
831 widgetDef = container;
832 break;
833 }
834
836 {
837 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
838 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
839 widgetDef = element;
840 break;
841 }
842
844 {
845 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
847 widgetDef = element;
848 break;
849 }
850
852 {
853 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
854 element->setText( itemData.textElementEditorConfiguration().text );
855 widgetDef = element;
856 break;
857 }
858
860 {
861 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
863 widgetDef = element;
864 break;
865 }
866
868 break;
869
870 }
871
872 if ( widgetDef )
873 {
874 widgetDef->setShowLabel( itemData.showLabel() );
875 widgetDef->setLabelStyle( itemData.labelStyle() );
876 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
877 widgetDef->setVerticalStretch( itemData.verticalStretch() );
878 }
879
880 return widgetDef;
881}
882
883void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
884{
885 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
886 switch ( layout )
887 {
889 mFormLayoutWidget->setVisible( false );
890 mUiFileFrame->setVisible( false );
891 mAddTabOrGroupButton->setVisible( false );
892 mRemoveTabOrGroupButton->setVisible( false );
893 mInvertSelectionButton->setVisible( false );
894 break;
895
897 mFormLayoutWidget->setVisible( true );
898 mUiFileFrame->setVisible( false );
899 mAddTabOrGroupButton->setVisible( true );
900 mRemoveTabOrGroupButton->setVisible( true );
901 mInvertSelectionButton->setVisible( true );
902 break;
903
905 // ui file
906 mFormLayoutWidget->setVisible( false );
907 mUiFileFrame->setVisible( true );
908 mAddTabOrGroupButton->setVisible( false );
909 mRemoveTabOrGroupButton->setVisible( false );
910 mInvertSelectionButton->setVisible( false );
911 break;
912 }
913}
914
915void QgsAttributesFormProperties::mTbInitCode_clicked()
916{
917 QgsAttributesFormInitCode attributesFormInitCode;
918
919 attributesFormInitCode.setCodeSource( mInitCodeSource );
920 attributesFormInitCode.setInitCode( mInitCode );
921 attributesFormInitCode.setInitFilePath( mInitFilePath );
922 attributesFormInitCode.setInitFunction( mInitFunction );
923
924 if ( !attributesFormInitCode.exec() )
925 return;
926
927 mInitCodeSource = attributesFormInitCode.codeSource();
928 mInitCode = attributesFormInitCode.initCode();
929 mInitFilePath = attributesFormInitCode.initFilePath();
930 mInitFunction = attributesFormInitCode.initFunction();
931
932}
933
934void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
935{
936 QgsSettings myQSettings;
937 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
938 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
939
940 if ( uifilename.isNull() )
941 return;
942
943 const QFileInfo fi( uifilename );
944 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
945 mEditFormLineEdit->setText( uifilename );
946}
947
949{
950 storeAttributeWidgetEdit();
951 storeAttributeContainerEdit();
952 storeAttributeTypeDialog();
953}
954
956{
957 mBlockUpdates++;
958 storeAttributeWidgetEdit();
959 storeAttributeContainerEdit();
960 storeAttributeTypeDialog();
961
962 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
963
964 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
965
966 for ( int i = 0; i < fieldContainer->childCount(); i++ )
967 {
968 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
969 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
970
971 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
972 const int idx = mLayer->fields().indexOf( fieldName );
973
974 //continue in case field does not exist anymore
975 if ( idx < 0 )
976 continue;
977
978 editFormConfig.setReadOnly( idx, !cfg.mEditable );
979 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
980 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
981
982 if ( cfg.mDataDefinedProperties.count() > 0 )
983 {
984 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
985 }
986
988
989 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
990 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
992 {
994 }
995 else
996 {
998 }
1000 {
1002 }
1003 else
1004 {
1006 }
1008 {
1010 }
1011 else
1012 {
1014 }
1015
1016 mLayer->setFieldAlias( idx, cfg.mAlias );
1019 }
1020
1021 // tabs and groups
1022 editFormConfig.clearTabs();
1023 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1024 {
1025 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1026 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1027 if ( editorElement )
1028 editFormConfig.addTab( editorElement );
1029 }
1030
1031 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1032
1033 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value< Qgis::AttributeFormLayout >() );
1034
1035 editFormConfig.setInitCodeSource( mInitCodeSource );
1036 editFormConfig.setInitFunction( mInitFunction );
1037 editFormConfig.setInitFilePath( mInitFilePath );
1038 editFormConfig.setInitCode( mInitCode );
1039
1040 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value< Qgis::AttributeFormSuppression >() );
1041
1042 // write the legacy config of relation widgets to support settings read by the API
1043 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1044
1045 for ( int i = 0; i < relationContainer->childCount(); i++ )
1046 {
1047 QTreeWidgetItem *relationItem = relationContainer->child( i );
1048 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1049
1050 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1051 {
1052 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1053 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1054
1055 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1056 {
1057 QVariantMap cfg;
1058
1059 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1060 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1061
1062 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1063 break;
1064 }
1065 }
1066 }
1067
1068 mLayer->setEditFormConfig( editFormConfig );
1069 mBlockUpdates--;
1070}
1071
1072
1073/*
1074 * FieldConfig implementation
1075 */
1077{
1078 mAlias = layer->fields().at( idx ).alias();
1080 mComment = layer->fields().at( idx ).comment();
1081 mEditable = !layer->editFormConfig().readOnly( idx );
1082 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1084 mFieldConstraints = layer->fields().at( idx ).constraints();
1085 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1086 mEditorWidgetType = setup.type();
1087 mEditorWidgetConfig = setup.config();
1088 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1089 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1090}
1091
1092QgsAttributesFormProperties::FieldConfig::operator QVariant()
1093{
1094 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1095}
1096
1097/*
1098 * RelationEditorConfiguration implementation
1099 */
1100
1101QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1102{
1103 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1104}
1105
1106/*
1107 * DnDTree implementation
1108 */
1109
1110QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1111{
1112 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1113 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1114 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1116 itemData.setColumnCount( columnCount );
1117 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1118 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1119 parent->addChild( newItem );
1120 newItem->setExpanded( true );
1121 return newItem;
1122}
1123
1125 : QTreeWidget( parent )
1126 , mLayer( layer )
1127{
1128 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1129}
1130
1131QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon )
1132{
1133 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1134
1135 switch ( data.type() )
1136 {
1144 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1145 break;
1146
1149 {
1150 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1151 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1152 }
1153 break;
1154 }
1155
1156 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) );
1157 newItem->setText( 0, data.displayName() );
1158 newItem->setIcon( 0, icon );
1159
1160 if ( index < 0 )
1161 parent->addChild( newItem );
1162 else
1163 parent->insertChild( index, newItem );
1164
1165 return newItem;
1166}
1167
1173void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1174{
1175 const QMimeData *data = event->mimeData();
1176
1177 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1178 {
1180
1181 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1182 QDataStream stream( &itemData, QIODevice::ReadOnly );
1183 stream >> itemElement;
1184
1185 // Inner drag and drop actions are always MoveAction
1186 if ( event->source() == this )
1187 {
1188 event->setDropAction( Qt::MoveAction );
1189 }
1190 }
1191 else
1192 {
1193 event->ignore();
1194 }
1195
1196 QTreeWidget::dragMoveEvent( event );
1197}
1198
1199
1200bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1201{
1202 bool bDropSuccessful = false;
1203
1204 if ( action == Qt::IgnoreAction )
1205 {
1206 bDropSuccessful = true;
1207 }
1208 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1209 {
1210 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1211 QDataStream stream( &itemData, QIODevice::ReadOnly );
1213
1214 while ( !stream.atEnd() )
1215 {
1216 stream >> itemElement;
1217
1218 QTreeWidgetItem *newItem;
1219
1220 if ( parent )
1221 {
1222 newItem = addItem( parent, itemElement, index++ );
1223 bDropSuccessful = true;
1224 }
1225 else
1226 {
1227 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1228 bDropSuccessful = true;
1229 }
1230
1232 {
1233 onItemDoubleClicked( newItem, 0 );
1234 }
1235
1237 {
1238 onItemDoubleClicked( newItem, 0 );
1239 }
1240
1242 {
1243 onItemDoubleClicked( newItem, 0 );
1244 }
1245
1247 {
1248 onItemDoubleClicked( newItem, 0 );
1249 }
1250
1251 clearSelection();
1252 newItem->setSelected( true );
1253 }
1254 }
1255
1256 return bDropSuccessful;
1257}
1258
1259void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1260{
1261 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1262 return;
1263
1264 if ( event->source() == this )
1265 {
1266 event->setDropAction( Qt::MoveAction );
1267 }
1268
1269 QTreeWidget::dropEvent( event );
1270}
1271
1273{
1274 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1275}
1276
1277#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1278QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1279#else
1280QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1281#endif
1282{
1283 if ( items.count() <= 0 )
1284 return nullptr;
1285
1286 const QStringList types = mimeTypes();
1287
1288 if ( types.isEmpty() )
1289 return nullptr;
1290
1291 QMimeData *data = new QMimeData();
1292 const QString format = types.at( 0 );
1293 QByteArray encoded;
1294 QDataStream stream( &encoded, QIODevice::WriteOnly );
1295
1296 const auto constItems = items;
1297 for ( const QTreeWidgetItem *item : constItems )
1298 {
1299 if ( item )
1300 {
1301 // Relevant information is always in the DnDTreeRole of the first column
1303 stream << itemData;
1304 }
1305 }
1306
1307 data->setData( format, encoded );
1308
1309 return data;
1310}
1311
1312void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1313{
1314 Q_UNUSED( column )
1315
1317
1318 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1319
1320 QFormLayout *baseLayout = new QFormLayout();
1321 baseData->setLayout( baseLayout );
1322 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1323 showLabelCheckbox->setChecked( itemData.showLabel() );
1324 baseLayout->addRow( showLabelCheckbox );
1325 QWidget *baseWidget = new QWidget();
1326 baseWidget->setLayout( baseLayout );
1327
1328 switch ( itemData.type() )
1329 {
1335 break;
1336
1338 {
1339 if ( mType == QgsAttributesDnDTree::Type::Drag )
1340 return;
1341
1342 QDialog dlg;
1343 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1344
1345 QVBoxLayout *mainLayout = new QVBoxLayout();
1346 QHBoxLayout *qmlLayout = new QHBoxLayout();
1347 QVBoxLayout *layout = new QVBoxLayout();
1348 mainLayout->addLayout( qmlLayout );
1349 qmlLayout->addLayout( layout );
1350 dlg.setLayout( mainLayout );
1351 layout->addWidget( baseWidget );
1352
1353 QLineEdit *title = new QLineEdit( itemData.name() );
1354
1355 //qmlCode
1356 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1357 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1358
1359 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1360 QgsFeature previewFeature;
1361 mLayer->getFeatures().nextFeature( previewFeature );
1362
1363 //update preview on text change
1364 connect( qmlCode, &QsciScintilla::textChanged, this, [ = ]
1365 {
1366 qmlWrapper->setQmlCode( qmlCode->text() );
1367 qmlWrapper->reinitWidget();
1368 qmlWrapper->setFeature( previewFeature );
1369 } );
1370
1371 //templates
1372 QComboBox *qmlObjectTemplate = new QComboBox();
1373 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1374 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1375 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1376 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1377 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [ = ]( int index )
1378 {
1379 qmlCode->clear();
1380 switch ( index )
1381 {
1382 case 0:
1383 {
1384 qmlCode->setText( QString() );
1385 break;
1386 }
1387 case 1:
1388 {
1389 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1390 "\n"
1391 "Rectangle {\n"
1392 " width: 100\n"
1393 " height: 100\n"
1394 " color: \"steelblue\"\n"
1395 " Text{ text: \"A rectangle\" }\n"
1396 "}\n" ) );
1397 break;
1398 }
1399 case 2:
1400 {
1401 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1402 "import QtCharts 2.0\n"
1403 "\n"
1404 "ChartView {\n"
1405 " width: 400\n"
1406 " height: 400\n"
1407 "\n"
1408 " PieSeries {\n"
1409 " id: pieSeries\n"
1410 " PieSlice { label: \"First slice\"; value: 25 }\n"
1411 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1412 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1413 " }\n"
1414 "}\n" ) );
1415 break;
1416 }
1417 case 3:
1418 {
1419 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1420 "import QtCharts 2.0\n"
1421 "\n"
1422 "ChartView {\n"
1423 " title: \"Bar series\"\n"
1424 " width: 600\n"
1425 " height:400\n"
1426 " legend.alignment: Qt.AlignBottom\n"
1427 " antialiasing: true\n"
1428 " ValueAxis{\n"
1429 " id: valueAxisY\n"
1430 " min: 0\n"
1431 " max: 15\n"
1432 " }\n"
1433 "\n"
1434 " BarSeries {\n"
1435 " id: mySeries\n"
1436 " axisY: valueAxisY\n"
1437 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1438 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1439 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1440 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1441 " }\n"
1442 "}\n" ) );
1443 break;
1444 }
1445 default:
1446 break;
1447 }
1448 } );
1449
1450 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1451 expressionWidget->setButtonVisible( false );
1452 expressionWidget->registerExpressionContextGenerator( this );
1453 expressionWidget->setLayer( mLayer );
1454 QToolButton *addFieldButton = new QToolButton();
1455 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1456
1457 QToolButton *editExpressionButton = new QToolButton();
1458 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1459 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1460
1461 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1462 {
1463 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1464 if ( !expression.isEmpty() )
1465 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1466 } );
1467
1468 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1469 {
1470 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1471 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1473 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1474
1475 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1476 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1477 {
1478 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1479 if ( !expression.isEmpty() )
1480 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1481 }
1482 } );
1483
1484 layout->addWidget( new QLabel( tr( "Title" ) ) );
1485 layout->addWidget( title );
1486 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1487 qmlCodeBox->setLayout( new QVBoxLayout );
1488 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1489 QWidget *expressionWidgetBox = new QWidget();
1490 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1491 expressionWidgetBox->setLayout( new QHBoxLayout );
1492 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1493 expressionWidgetBox->layout()->addWidget( expressionWidget );
1494 expressionWidgetBox->layout()->addWidget( addFieldButton );
1495 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1496 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1497 layout->addWidget( qmlCodeBox );
1498 layout->addWidget( qmlCode );
1499 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1500 qmlPreviewBox->setLayout( new QGridLayout );
1501 qmlPreviewBox->setMinimumWidth( 400 );
1502 qmlPreviewBox->layout()->addWidget( qmlWrapper->widget() );
1503 //emit to load preview for the first time
1504 emit qmlCode->textChanged();
1505 qmlLayout->addWidget( qmlPreviewBox );
1506
1507 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1508
1509 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1510 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1511
1512 mainLayout->addWidget( buttonBox );
1513
1514 if ( dlg.exec() )
1515 {
1517 qmlEdCfg.qmlCode = qmlCode->text();
1518 itemData.setName( title->text() );
1519 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1520 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1521
1522 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1523 item->setText( 0, title->text() );
1524 }
1525 }
1526 break;
1527
1529 {
1530 if ( mType == QgsAttributesDnDTree::Type::Drag )
1531 return;
1532 QDialog dlg;
1533 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1534
1535 QVBoxLayout *mainLayout = new QVBoxLayout();
1536 QHBoxLayout *htmlLayout = new QHBoxLayout();
1537 QVBoxLayout *layout = new QVBoxLayout();
1538 mainLayout->addLayout( htmlLayout );
1539 htmlLayout->addLayout( layout );
1540 dlg.setLayout( mainLayout );
1541 layout->addWidget( baseWidget );
1542
1543 QLineEdit *title = new QLineEdit( itemData.name() );
1544
1545 //htmlCode
1546 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML( );
1547 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1548 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1549
1550 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1551 QgsFeature previewFeature;
1552 mLayer->getFeatures().nextFeature( previewFeature );
1553
1554 //update preview on text change
1555 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [ = ]
1556 {
1557 htmlWrapper->setHtmlCode( htmlCode->text( ) );
1558 htmlWrapper->reinitWidget();
1559 htmlWrapper->setFeature( previewFeature );
1560 } );
1561
1562 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1563 expressionWidget->setButtonVisible( false );
1564 expressionWidget->registerExpressionContextGenerator( this );
1565 expressionWidget->setLayer( mLayer );
1566 QToolButton *addFieldButton = new QToolButton();
1567 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1568
1569 QToolButton *editExpressionButton = new QToolButton();
1570 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1571 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1572
1573 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1574 {
1575 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1576 if ( !expression.isEmpty() )
1577 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1578 } );
1579
1580 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1581 {
1582 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1583 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1585 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1586
1587 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1588 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1589 {
1590 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1591 if ( !expression.isEmpty() )
1592 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1593 }
1594 } );
1595
1596 layout->addWidget( new QLabel( tr( "Title" ) ) );
1597 layout->addWidget( title );
1598 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1599 layout->addWidget( expressionWidgetBox );
1600 expressionWidgetBox->setLayout( new QHBoxLayout );
1601 expressionWidgetBox->layout()->addWidget( expressionWidget );
1602 expressionWidgetBox->layout()->addWidget( addFieldButton );
1603 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1604 layout->addWidget( htmlCode );
1605 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1606 htmlPreviewBox->setLayout( new QGridLayout );
1607 htmlPreviewBox->setMinimumWidth( 400 );
1608 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1609 //emit to load preview for the first time
1610 emit htmlCode->textChanged();
1611 htmlLayout->addWidget( htmlPreviewBox );
1612
1613 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1614
1615 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1616 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1617
1618 mainLayout->addWidget( buttonBox );
1619
1620 if ( dlg.exec() )
1621 {
1623 htmlEdCfg.htmlCode = htmlCode->text();
1624 itemData.setName( title->text() );
1625 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1626 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1627
1628 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1629 item->setText( 0, title->text() );
1630 }
1631 break;
1632 }
1633
1635 {
1636 if ( mType == QgsAttributesDnDTree::Type::Drag )
1637 return;
1638 QDialog dlg;
1639 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1640
1641 QVBoxLayout *mainLayout = new QVBoxLayout();
1642 QHBoxLayout *textLayout = new QHBoxLayout();
1643 QVBoxLayout *layout = new QVBoxLayout();
1644 mainLayout->addLayout( textLayout );
1645 textLayout->addLayout( layout );
1646 dlg.setLayout( mainLayout );
1647 layout->addWidget( baseWidget );
1648
1649 QLineEdit *title = new QLineEdit( itemData.name() );
1650
1651 QgsCodeEditorHTML *text = new QgsCodeEditorHTML( );
1652 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1653 text->setText( itemData.textElementEditorConfiguration().text );
1654
1655 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1656 QgsFeature previewFeature;
1657 mLayer->getFeatures().nextFeature( previewFeature );
1658
1659 //update preview on text change
1660 connect( text, &QgsCodeEditorExpression::textChanged, this, [ = ]
1661 {
1662 textWrapper->setText( text->text( ) );
1663 textWrapper->reinitWidget();
1664 textWrapper->setFeature( previewFeature );
1665 } );
1666
1667 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1668 expressionWidget->setButtonVisible( false );
1669 expressionWidget->registerExpressionContextGenerator( this );
1670 expressionWidget->setLayer( mLayer );
1671 QToolButton *addFieldButton = new QToolButton();
1672 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1673
1674 QToolButton *editExpressionButton = new QToolButton();
1675 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1676 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1677
1678 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1679 {
1680 QString expression = expressionWidget->expression().trimmed();
1681 if ( !expression.isEmpty() )
1682 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1683 } );
1684 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1685 {
1686 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1687
1689 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1690
1691 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1692 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1693 {
1694 QString expression = exprDlg.expressionText().trimmed();
1695 if ( !expression.isEmpty() )
1696 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1697 }
1698 } );
1699
1700 layout->addWidget( new QLabel( tr( "Title" ) ) );
1701 layout->addWidget( title );
1702 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1703 layout->addWidget( expressionWidgetBox );
1704 expressionWidgetBox->setLayout( new QHBoxLayout );
1705 expressionWidgetBox->layout()->addWidget( expressionWidget );
1706 expressionWidgetBox->layout()->addWidget( addFieldButton );
1707 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1708 layout->addWidget( text );
1709 QScrollArea *textPreviewBox = new QgsScrollArea();
1710 textPreviewBox->setLayout( new QGridLayout );
1711 textPreviewBox->setMinimumWidth( 400 );
1712 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1713 //emit to load preview for the first time
1714 emit text->textChanged();
1715 textLayout->addWidget( textPreviewBox );
1716
1717 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1718
1719 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1720 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1721
1722 mainLayout->addWidget( buttonBox );
1723
1724 if ( dlg.exec() )
1725 {
1727 textEdCfg.text = text->text();
1728 itemData.setName( title->text() );
1729 itemData.setTextElementEditorConfiguration( textEdCfg );
1730 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1731
1732 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1733 item->setText( 0, title->text() );
1734 }
1735 break;
1736 }
1737
1739 {
1740 if ( mType == QgsAttributesDnDTree::Type::Drag )
1741 return;
1742 QDialog dlg;
1743 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1744
1745 QVBoxLayout *mainLayout = new QVBoxLayout();
1746 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1747 QLineEdit *title = new QLineEdit( itemData.name() );
1748 mainLayout->addWidget( title );
1749
1750 QHBoxLayout *cbLayout = new QHBoxLayout( );
1751 mainLayout->addLayout( cbLayout );
1752 dlg.setLayout( mainLayout );
1753 QCheckBox *cb = new QCheckBox { &dlg };
1754 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1755 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1756 cbLayout->addWidget( cb );
1757
1758
1759 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1760
1761 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1762 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1763
1764 mainLayout->addWidget( buttonBox );
1765
1766 if ( dlg.exec() )
1767 {
1769 spacerEdCfg.drawLine = cb->isChecked();
1770 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1771 itemData.setShowLabel( false );
1772 itemData.setName( title->text() );
1773 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1774 item->setText( 0, title->text() );
1775 }
1776
1777 break;
1778 }
1779 }
1780}
1781
1783{
1784 QgsExpressionContext expContext;
1787
1788 if ( mLayer )
1789 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1790
1792 return expContext;
1793}
1794
1796{
1797 return mType;
1798}
1799
1801{
1802 mType = value;
1803}
1804
1806{
1807 QTreeWidgetItemIterator it( this );
1808 while ( *it )
1809 {
1811 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1812 {
1813 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1814 {
1815 // the selection is already good
1816 }
1817 else
1818 {
1819 clearSelection();
1820 ( *it )->setSelected( true );
1821 }
1822 return;
1823 }
1824 ++it;
1825 }
1826 clearSelection();
1827}
1828
1829
1830/*
1831 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1832 */
1833
1834QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1835{
1836 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1837 return stream;
1838}
1839
1840QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1841{
1842 QString name;
1843 QString displayName;
1844 quint32 type;
1845
1846 stream >> type >> name >> displayName;
1847
1849 data.setName( name );
1850 data.setDisplayName( displayName );
1851
1852 return stream;
1853}
1854
1859
1864
1869
1871{
1872 mLabelStyle = labelStyle;
1873}
1874
1876{
1877 return mShowLabel;
1878}
1879
1881{
1882 mShowLabel = showLabel;
1883}
1884
1889
1891{
1892 mVisibilityExpression = visibilityExpression;
1893}
1894
1899
1901{
1902 mCollapsedExpression = collapsedExpression;
1903}
1904
1909
1911{
1912 mRelationEditorConfiguration = relationEditorConfiguration;
1913}
1914
1919
1921{
1922 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1923}
1924
1925
1930
1932{
1933 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1934}
1935
1940
1942{
1943 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
1944}
1945
1947{
1948 return mBackgroundColor;
1949}
1950
1952{
1953 mBackgroundColor = backgroundColor;
1954}
1955
1960
1962{
1963 mTextElementEditorConfiguration = textElementEditorConfiguration;
1964}
1965
1966void QgsAttributesFormProperties::updatedFields()
1967{
1968 // Store configuration to insure changes made are kept after refreshing the list
1969 QMap<QString, FieldConfig> fieldConfigs;
1970 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1971 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1972 {
1973 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1974 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1975 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1976 fieldConfigs[fieldName] = cfg;
1977 }
1978
1980
1981 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1982 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1983 {
1984 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1985 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1986 if ( fieldConfigs.contains( fieldName ) )
1987 {
1988 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
1989 }
1990 }
1991}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5081
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5111
@ 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:5096
@ 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.
@ 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 setRelationEditorConfiguration(RelationEditorConfiguration relationEditorConfiguration)
void setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
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 setQmlElementEditorConfiguration(QmlElementEditorConfiguration qmlElementEditorConfiguration)
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.
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...
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
void setHtmlElementEditorConfiguration(HtmlElementEditorConfiguration htmlElementEditorConfiguration)
bool collapsed() const
For group box containers returns if this group box is collapsed.
RelationEditorConfiguration relationEditorConfiguration() const
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 insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
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.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Holder for the widget type and its configuration for a field.
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.
@ 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.
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().
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
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
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:94
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
An expression with an additional enabled flag.
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
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
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.
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.