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