QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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 storeAttributeTypeDialog();
619 storeAttributeContainerEdit();
620
621 clearAttributeTypeFrame();
622
623 if ( emitter->selectedItems().count() != 1 )
624 {
625 receiver->clearSelection();
626 }
627 else
628 {
629 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
630 switch ( itemData.type() )
631 {
633 {
634 receiver->selectFirstMatchingItem( itemData );
636 {
637 loadAttributeWidgetEdit();
638 }
639 else
640 {
641 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
642 }
643 break;
644 }
646 {
647 receiver->selectFirstMatchingItem( itemData );
649 loadAttributeWidgetEdit();
650 loadAttributeTypeDialog();
651 break;
652 }
654 {
655 receiver->clearSelection();
656 loadAttributeContainerEdit();
657 break;
658 }
660 {
661 receiver->selectFirstMatchingItem( itemData );
662 const QgsAction action {mLayer->actions()->action( itemData.name() )};
663 loadInfoWidget( action.html() );
664 break;
665 }
670 {
672 {
673 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
674 }
675 else
676 {
677 loadInfoWidget( tr( "This configuration is available with double-click" ) );
678 }
679 receiver->clearSelection();
680 break;
681 }
683 {
684 receiver->clearSelection();
685 break;
686 }
687 }
688 }
689}
690
691void QgsAttributesFormProperties::clearAttributeTypeFrame()
692{
694 {
695 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
696 mAttributeWidgetEdit->deleteLater();
697 mAttributeWidgetEdit = nullptr;
698 }
700 {
701 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
702 mAttributeTypeDialog->deleteLater();
703 mAttributeTypeDialog = nullptr;
704 }
706 {
707 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
708 mAttributeContainerEdit->deleteLater();
709 mAttributeContainerEdit = nullptr;
710 }
711 if ( mInfoTextWidget )
712 {
713 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
714 mInfoTextWidget->deleteLater();
715 mInfoTextWidget = nullptr;
716 }
717}
718
719void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
720{
721 Q_UNUSED( checked )
722 const auto selectedItemList { mFormLayoutTree->selectedItems() };
723 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
724 for ( int i = 0; i < rootItem->childCount(); ++i )
725 {
726 rootItem->child( i )->setSelected( ! selectedItemList.contains( rootItem->child( i ) ) );
727 }
728}
729
730void QgsAttributesFormProperties::addContainer()
731{
732 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
733
734 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
735 {
736 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
737 if ( itemData.type() == DnDTreeItemData::Container )
738 {
739 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
740 }
741 }
742 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
743 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
744
745 if ( !dialog.exec() )
746 return;
747
748 const QString name = dialog.name();
749 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
750 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(),
751 name,
752 dialog.columnCount(),
753 dialog.containerType() );
754}
755
756void QgsAttributesFormProperties::removeTabOrGroupButton()
757{
758 // deleting an item may delete any number of nested child items -- so we delete
759 // them one at a time and then see if there's any selection left
760 while ( true )
761 {
762 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
763 if ( items.empty() )
764 break;
765
766 delete items.at( 0 );
767 }
768
769}
770
772{
773 QgsAttributeEditorElement *widgetDef = nullptr;
774
775 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
776
777 switch ( itemData.type() )
778 {
779 //indexed here?
781 {
782 const int idx = mLayer->fields().lookupField( itemData.name() );
783 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
784 break;
785 }
786
788 {
789 const QgsAction action { mLayer->actions()->action( itemData.name() )};
790 widgetDef = new QgsAttributeEditorAction( action, parent );
791 break;
792 }
793
795 {
796 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
797 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
799 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
800 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
801 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
802 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
803 relDef->setLabel( relationEditorConfig.label );
804 widgetDef = relDef;
805 break;
806 }
807
809 {
810 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
811 container->setColumnCount( itemData.columnCount() );
812 // only top-level containers can be tabs
814 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
815 {
816 // a top container found which isn't at the top level -- reset it to a group box instead
818 }
819 container->setType( type );
820 container->setCollapsed( itemData.collapsed() );
821 container->setCollapsedExpression( itemData.collapsedExpression() );
822 container->setVisibilityExpression( itemData.visibilityExpression() );
823 container->setBackgroundColor( itemData.backgroundColor( ) );
824
825 for ( int t = 0; t < item->childCount(); t++ )
826 {
827 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
828 if ( element )
829 container->addChildElement( element );
830 }
831
832 widgetDef = container;
833 break;
834 }
835
837 {
838 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
839 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
840 widgetDef = element;
841 break;
842 }
843
845 {
846 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
848 widgetDef = element;
849 break;
850 }
851
853 {
854 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
855 element->setText( itemData.textElementEditorConfiguration().text );
856 widgetDef = element;
857 break;
858 }
859
861 {
862 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
864 widgetDef = element;
865 break;
866 }
867
869 break;
870
871 }
872
873 if ( widgetDef )
874 {
875 widgetDef->setShowLabel( itemData.showLabel() );
876 widgetDef->setLabelStyle( itemData.labelStyle() );
877 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
878 widgetDef->setVerticalStretch( itemData.verticalStretch() );
879 }
880
881 return widgetDef;
882}
883
884void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
885{
886 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
887 switch ( layout )
888 {
890 mFormLayoutWidget->setVisible( false );
891 mUiFileFrame->setVisible( false );
892 mAddTabOrGroupButton->setVisible( false );
893 mRemoveTabOrGroupButton->setVisible( false );
894 mInvertSelectionButton->setVisible( false );
895 break;
896
898 mFormLayoutWidget->setVisible( true );
899 mUiFileFrame->setVisible( false );
900 mAddTabOrGroupButton->setVisible( true );
901 mRemoveTabOrGroupButton->setVisible( true );
902 mInvertSelectionButton->setVisible( true );
903 break;
904
906 // ui file
907 mFormLayoutWidget->setVisible( false );
908 mUiFileFrame->setVisible( true );
909 mAddTabOrGroupButton->setVisible( false );
910 mRemoveTabOrGroupButton->setVisible( false );
911 mInvertSelectionButton->setVisible( false );
912 break;
913 }
914}
915
916void QgsAttributesFormProperties::mTbInitCode_clicked()
917{
918 QgsAttributesFormInitCode attributesFormInitCode;
919
920 attributesFormInitCode.setCodeSource( mInitCodeSource );
921 attributesFormInitCode.setInitCode( mInitCode );
922 attributesFormInitCode.setInitFilePath( mInitFilePath );
923 attributesFormInitCode.setInitFunction( mInitFunction );
924
925 if ( !attributesFormInitCode.exec() )
926 return;
927
928 mInitCodeSource = attributesFormInitCode.codeSource();
929 mInitCode = attributesFormInitCode.initCode();
930 mInitFilePath = attributesFormInitCode.initFilePath();
931 mInitFunction = attributesFormInitCode.initFunction();
932
933}
934
935void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
936{
937 QgsSettings myQSettings;
938 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
939 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
940
941 if ( uifilename.isNull() )
942 return;
943
944 const QFileInfo fi( uifilename );
945 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
946 mEditFormLineEdit->setText( uifilename );
947}
948
950{
951 storeAttributeWidgetEdit();
952 storeAttributeContainerEdit();
953 storeAttributeTypeDialog();
954}
955
957{
958 mBlockUpdates++;
959 storeAttributeWidgetEdit();
960 storeAttributeContainerEdit();
961 storeAttributeTypeDialog();
962
963 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
964
965 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
966
967 for ( int i = 0; i < fieldContainer->childCount(); i++ )
968 {
969 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
970 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
971
972 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
973 const int idx = mLayer->fields().indexOf( fieldName );
974
975 //continue in case field does not exist anymore
976 if ( idx < 0 )
977 continue;
978
979 editFormConfig.setReadOnly( idx, !cfg.mEditable );
980 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
981 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
982
983 if ( cfg.mDataDefinedProperties.count() > 0 )
984 {
985 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
986 }
987
989
990 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
991 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
993 {
995 }
996 else
997 {
999 }
1001 {
1003 }
1004 else
1005 {
1007 }
1009 {
1011 }
1012 else
1013 {
1015 }
1016
1017 mLayer->setFieldAlias( idx, cfg.mAlias );
1020 }
1021
1022 // tabs and groups
1023 editFormConfig.clearTabs();
1024 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1025 {
1026 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1027 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1028 if ( editorElement )
1029 editFormConfig.addTab( editorElement );
1030 }
1031
1032 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1033
1034 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value< Qgis::AttributeFormLayout >() );
1035
1036 editFormConfig.setInitCodeSource( mInitCodeSource );
1037 editFormConfig.setInitFunction( mInitFunction );
1038 editFormConfig.setInitFilePath( mInitFilePath );
1039 editFormConfig.setInitCode( mInitCode );
1040
1041 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value< Qgis::AttributeFormSuppression >() );
1042
1043 // write the legacy config of relation widgets to support settings read by the API
1044 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1045
1046 for ( int i = 0; i < relationContainer->childCount(); i++ )
1047 {
1048 QTreeWidgetItem *relationItem = relationContainer->child( i );
1049 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1050
1051 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1052 {
1053 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1054 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1055
1056 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1057 {
1058 QVariantMap cfg;
1059
1060 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1061 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1062
1063 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1064 break;
1065 }
1066 }
1067 }
1068
1069 mLayer->setEditFormConfig( editFormConfig );
1070 mBlockUpdates--;
1071}
1072
1073
1074/*
1075 * FieldConfig implementation
1076 */
1078{
1079 mAlias = layer->fields().at( idx ).alias();
1081 mComment = layer->fields().at( idx ).comment();
1082 mEditable = !layer->editFormConfig().readOnly( idx );
1083 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1085 mFieldConstraints = layer->fields().at( idx ).constraints();
1086 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1087 mEditorWidgetType = setup.type();
1088 mEditorWidgetConfig = setup.config();
1089 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1090 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1091}
1092
1093QgsAttributesFormProperties::FieldConfig::operator QVariant()
1094{
1095 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1096}
1097
1098/*
1099 * RelationEditorConfiguration implementation
1100 */
1101
1102QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1103{
1104 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1105}
1106
1107/*
1108 * DnDTree implementation
1109 */
1110
1111QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1112{
1113 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1114 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1115 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1117 itemData.setColumnCount( columnCount );
1118 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1119 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1120 parent->addChild( newItem );
1121 newItem->setExpanded( true );
1122 return newItem;
1123}
1124
1126 : QTreeWidget( parent )
1127 , mLayer( layer )
1128{
1129 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1130}
1131
1132QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, QgsAttributesFormProperties::DnDTreeItemData data, int index, const QIcon &icon )
1133{
1134 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1135
1136 switch ( data.type() )
1137 {
1145 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1146 break;
1147
1150 {
1151 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1152 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1153 }
1154 break;
1155 }
1156
1157 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, data );
1158 newItem->setText( 0, data.displayName() );
1159 newItem->setIcon( 0, icon );
1160
1161 if ( index < 0 )
1162 parent->addChild( newItem );
1163 else
1164 parent->insertChild( index, newItem );
1165
1166 return newItem;
1167}
1168
1174void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1175{
1176 const QMimeData *data = event->mimeData();
1177
1178 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1179 {
1181
1182 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1183 QDataStream stream( &itemData, QIODevice::ReadOnly );
1184 stream >> itemElement;
1185
1186 // Inner drag and drop actions are always MoveAction
1187 if ( event->source() == this )
1188 {
1189 event->setDropAction( Qt::MoveAction );
1190 }
1191 }
1192 else
1193 {
1194 event->ignore();
1195 }
1196
1197 QTreeWidget::dragMoveEvent( event );
1198}
1199
1200
1201bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1202{
1203 bool bDropSuccessful = false;
1204
1205 if ( action == Qt::IgnoreAction )
1206 {
1207 bDropSuccessful = true;
1208 }
1209 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1210 {
1211 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1212 QDataStream stream( &itemData, QIODevice::ReadOnly );
1214
1215 while ( !stream.atEnd() )
1216 {
1217 stream >> itemElement;
1218
1219 QTreeWidgetItem *newItem;
1220
1221 if ( parent )
1222 {
1223 newItem = addItem( parent, itemElement, index++ );
1224 bDropSuccessful = true;
1225 }
1226 else
1227 {
1228 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1229 bDropSuccessful = true;
1230 }
1231
1233 {
1234 onItemDoubleClicked( newItem, 0 );
1235 }
1236
1238 {
1239 onItemDoubleClicked( newItem, 0 );
1240 }
1241
1243 {
1244 onItemDoubleClicked( newItem, 0 );
1245 }
1246
1248 {
1249 onItemDoubleClicked( newItem, 0 );
1250 }
1251
1252 clearSelection();
1253 newItem->setSelected( true );
1254 }
1255 }
1256
1257 return bDropSuccessful;
1258}
1259
1260void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1261{
1262 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1263 return;
1264
1265 if ( event->source() == this )
1266 {
1267 event->setDropAction( Qt::MoveAction );
1268 }
1269
1270 QTreeWidget::dropEvent( event );
1271}
1272
1274{
1275 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1276}
1277
1278#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1279QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1280#else
1281QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1282#endif
1283{
1284 if ( items.count() <= 0 )
1285 return nullptr;
1286
1287 const QStringList types = mimeTypes();
1288
1289 if ( types.isEmpty() )
1290 return nullptr;
1291
1292 QMimeData *data = new QMimeData();
1293 const QString format = types.at( 0 );
1294 QByteArray encoded;
1295 QDataStream stream( &encoded, QIODevice::WriteOnly );
1296
1297 const auto constItems = items;
1298 for ( const QTreeWidgetItem *item : constItems )
1299 {
1300 if ( item )
1301 {
1302 // Relevant information is always in the DnDTreeRole of the first column
1304 stream << itemData;
1305 }
1306 }
1307
1308 data->setData( format, encoded );
1309
1310 return data;
1311}
1312
1313void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1314{
1315 Q_UNUSED( column )
1316
1318
1319 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1320
1321 QFormLayout *baseLayout = new QFormLayout();
1322 baseData->setLayout( baseLayout );
1323 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1324 showLabelCheckbox->setChecked( itemData.showLabel() );
1325 baseLayout->addRow( showLabelCheckbox );
1326 QWidget *baseWidget = new QWidget();
1327 baseWidget->setLayout( baseLayout );
1328
1329 switch ( itemData.type() )
1330 {
1336 break;
1337
1339 {
1340 if ( mType == QgsAttributesDnDTree::Type::Drag )
1341 return;
1342
1343 QDialog dlg;
1344 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1345
1346 QVBoxLayout *mainLayout = new QVBoxLayout();
1347 QHBoxLayout *qmlLayout = new QHBoxLayout();
1348 QVBoxLayout *layout = new QVBoxLayout();
1349 mainLayout->addLayout( qmlLayout );
1350 qmlLayout->addLayout( layout );
1351 dlg.setLayout( mainLayout );
1352 layout->addWidget( baseWidget );
1353
1354 QLineEdit *title = new QLineEdit( itemData.name() );
1355
1356 //qmlCode
1357 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1358 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1359
1360 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1361 QgsFeature previewFeature;
1362 mLayer->getFeatures().nextFeature( previewFeature );
1363
1364 //update preview on text change
1365 connect( qmlCode, &QsciScintilla::textChanged, this, [ = ]
1366 {
1367 qmlWrapper->setQmlCode( qmlCode->text() );
1368 qmlWrapper->reinitWidget();
1369 qmlWrapper->setFeature( previewFeature );
1370 } );
1371
1372 //templates
1373 QComboBox *qmlObjectTemplate = new QComboBox();
1374 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1375 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1376 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1377 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1378 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [ = ]( int index )
1379 {
1380 qmlCode->clear();
1381 switch ( index )
1382 {
1383 case 0:
1384 {
1385 qmlCode->setText( QString() );
1386 break;
1387 }
1388 case 1:
1389 {
1390 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1391 "\n"
1392 "Rectangle {\n"
1393 " width: 100\n"
1394 " height: 100\n"
1395 " color: \"steelblue\"\n"
1396 " Text{ text: \"A rectangle\" }\n"
1397 "}\n" ) );
1398 break;
1399 }
1400 case 2:
1401 {
1402 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1403 "import QtCharts 2.0\n"
1404 "\n"
1405 "ChartView {\n"
1406 " width: 400\n"
1407 " height: 400\n"
1408 "\n"
1409 " PieSeries {\n"
1410 " id: pieSeries\n"
1411 " PieSlice { label: \"First slice\"; value: 25 }\n"
1412 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1413 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1414 " }\n"
1415 "}\n" ) );
1416 break;
1417 }
1418 case 3:
1419 {
1420 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1421 "import QtCharts 2.0\n"
1422 "\n"
1423 "ChartView {\n"
1424 " title: \"Bar series\"\n"
1425 " width: 600\n"
1426 " height:400\n"
1427 " legend.alignment: Qt.AlignBottom\n"
1428 " antialiasing: true\n"
1429 " ValueAxis{\n"
1430 " id: valueAxisY\n"
1431 " min: 0\n"
1432 " max: 15\n"
1433 " }\n"
1434 "\n"
1435 " BarSeries {\n"
1436 " id: mySeries\n"
1437 " axisY: valueAxisY\n"
1438 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1439 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1440 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1441 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1442 " }\n"
1443 "}\n" ) );
1444 break;
1445 }
1446 default:
1447 break;
1448 }
1449 } );
1450
1451 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1452 expressionWidget->setButtonVisible( false );
1453 expressionWidget->registerExpressionContextGenerator( this );
1454 expressionWidget->setLayer( mLayer );
1455 QToolButton *addFieldButton = new QToolButton();
1456 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1457
1458 QToolButton *editExpressionButton = new QToolButton();
1459 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1460 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1461
1462 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1463 {
1464 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1465 if ( !expression.isEmpty() )
1466 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1467 } );
1468
1469 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1470 {
1471 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1472 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1474 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1475
1476 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1477 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1478 {
1479 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1480 if ( !expression.isEmpty() )
1481 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1482 }
1483 } );
1484
1485 layout->addWidget( new QLabel( tr( "Title" ) ) );
1486 layout->addWidget( title );
1487 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1488 qmlCodeBox->setLayout( new QVBoxLayout );
1489 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1490 QWidget *expressionWidgetBox = new QWidget();
1491 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1492 expressionWidgetBox->setLayout( new QHBoxLayout );
1493 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1494 expressionWidgetBox->layout()->addWidget( expressionWidget );
1495 expressionWidgetBox->layout()->addWidget( addFieldButton );
1496 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1497 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1498 layout->addWidget( qmlCodeBox );
1499 layout->addWidget( qmlCode );
1500 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1501 qmlPreviewBox->setLayout( new QGridLayout );
1502 qmlPreviewBox->setMinimumWidth( 400 );
1503 qmlPreviewBox->layout()->addWidget( qmlWrapper->widget() );
1504 //emit to load preview for the first time
1505 emit qmlCode->textChanged();
1506 qmlLayout->addWidget( qmlPreviewBox );
1507
1508 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1509
1510 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1511 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1512
1513 mainLayout->addWidget( buttonBox );
1514
1515 if ( dlg.exec() )
1516 {
1518 qmlEdCfg.qmlCode = qmlCode->text();
1519 itemData.setName( title->text() );
1520 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1521 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1522
1523 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1524 item->setText( 0, title->text() );
1525 }
1526 }
1527 break;
1528
1530 {
1531 if ( mType == QgsAttributesDnDTree::Type::Drag )
1532 return;
1533 QDialog dlg;
1534 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1535
1536 QVBoxLayout *mainLayout = new QVBoxLayout();
1537 QHBoxLayout *htmlLayout = new QHBoxLayout();
1538 QVBoxLayout *layout = new QVBoxLayout();
1539 mainLayout->addLayout( htmlLayout );
1540 htmlLayout->addLayout( layout );
1541 dlg.setLayout( mainLayout );
1542 layout->addWidget( baseWidget );
1543
1544 QLineEdit *title = new QLineEdit( itemData.name() );
1545
1546 //htmlCode
1547 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML( );
1548 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1549 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1550
1551 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1552 QgsFeature previewFeature;
1553 mLayer->getFeatures().nextFeature( previewFeature );
1554
1555 //update preview on text change
1556 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [ = ]
1557 {
1558 htmlWrapper->setHtmlCode( htmlCode->text( ) );
1559 htmlWrapper->reinitWidget();
1560 htmlWrapper->setFeature( previewFeature );
1561 } );
1562
1563 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1564 expressionWidget->setButtonVisible( false );
1565 expressionWidget->registerExpressionContextGenerator( this );
1566 expressionWidget->setLayer( mLayer );
1567 QToolButton *addFieldButton = new QToolButton();
1568 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1569
1570 QToolButton *editExpressionButton = new QToolButton();
1571 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1572 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1573
1574 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1575 {
1576 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1577 if ( !expression.isEmpty() )
1578 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1579 } );
1580
1581 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1582 {
1583 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1584 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1586 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1587
1588 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1589 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1590 {
1591 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1592 if ( !expression.isEmpty() )
1593 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1594 }
1595 } );
1596
1597 layout->addWidget( new QLabel( tr( "Title" ) ) );
1598 layout->addWidget( title );
1599 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1600 layout->addWidget( expressionWidgetBox );
1601 expressionWidgetBox->setLayout( new QHBoxLayout );
1602 expressionWidgetBox->layout()->addWidget( expressionWidget );
1603 expressionWidgetBox->layout()->addWidget( addFieldButton );
1604 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1605 layout->addWidget( htmlCode );
1606 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1607 htmlPreviewBox->setLayout( new QGridLayout );
1608 htmlPreviewBox->setMinimumWidth( 400 );
1609 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1610 //emit to load preview for the first time
1611 emit htmlCode->textChanged();
1612 htmlLayout->addWidget( htmlPreviewBox );
1613
1614 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1615
1616 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1617 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1618
1619 mainLayout->addWidget( buttonBox );
1620
1621 if ( dlg.exec() )
1622 {
1624 htmlEdCfg.htmlCode = htmlCode->text();
1625 itemData.setName( title->text() );
1626 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1627 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1628
1629 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1630 item->setText( 0, title->text() );
1631 }
1632 break;
1633 }
1634
1636 {
1637 if ( mType == QgsAttributesDnDTree::Type::Drag )
1638 return;
1639 QDialog dlg;
1640 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1641
1642 QVBoxLayout *mainLayout = new QVBoxLayout();
1643 QHBoxLayout *textLayout = new QHBoxLayout();
1644 QVBoxLayout *layout = new QVBoxLayout();
1645 mainLayout->addLayout( textLayout );
1646 textLayout->addLayout( layout );
1647 dlg.setLayout( mainLayout );
1648 layout->addWidget( baseWidget );
1649
1650 QLineEdit *title = new QLineEdit( itemData.name() );
1651
1652 QgsCodeEditorHTML *text = new QgsCodeEditorHTML( );
1653 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1654 text->setText( itemData.textElementEditorConfiguration().text );
1655
1656 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1657 QgsFeature previewFeature;
1658 mLayer->getFeatures().nextFeature( previewFeature );
1659
1660 //update preview on text change
1661 connect( text, &QgsCodeEditorExpression::textChanged, this, [ = ]
1662 {
1663 textWrapper->setText( text->text( ) );
1664 textWrapper->reinitWidget();
1665 textWrapper->setFeature( previewFeature );
1666 } );
1667
1668 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1669 expressionWidget->setButtonVisible( false );
1670 expressionWidget->registerExpressionContextGenerator( this );
1671 expressionWidget->setLayer( mLayer );
1672 QToolButton *addFieldButton = new QToolButton();
1673 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1674
1675 QToolButton *editExpressionButton = new QToolButton();
1676 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1677 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1678
1679 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1680 {
1681 QString expression = expressionWidget->expression().trimmed();
1682 if ( !expression.isEmpty() )
1683 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1684 } );
1685 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1686 {
1687 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1688
1690 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1691
1692 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1693 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1694 {
1695 QString expression = exprDlg.expressionText().trimmed();
1696 if ( !expression.isEmpty() )
1697 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1698 }
1699 } );
1700
1701 layout->addWidget( new QLabel( tr( "Title" ) ) );
1702 layout->addWidget( title );
1703 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1704 layout->addWidget( expressionWidgetBox );
1705 expressionWidgetBox->setLayout( new QHBoxLayout );
1706 expressionWidgetBox->layout()->addWidget( expressionWidget );
1707 expressionWidgetBox->layout()->addWidget( addFieldButton );
1708 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1709 layout->addWidget( text );
1710 QScrollArea *textPreviewBox = new QgsScrollArea();
1711 textPreviewBox->setLayout( new QGridLayout );
1712 textPreviewBox->setMinimumWidth( 400 );
1713 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1714 //emit to load preview for the first time
1715 emit text->textChanged();
1716 textLayout->addWidget( textPreviewBox );
1717
1718 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1719
1720 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1721 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1722
1723 mainLayout->addWidget( buttonBox );
1724
1725 if ( dlg.exec() )
1726 {
1728 textEdCfg.text = text->text();
1729 itemData.setName( title->text() );
1730 itemData.setTextElementEditorConfiguration( textEdCfg );
1731 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1732
1733 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1734 item->setText( 0, title->text() );
1735 }
1736 break;
1737 }
1738
1740 {
1741 if ( mType == QgsAttributesDnDTree::Type::Drag )
1742 return;
1743 QDialog dlg;
1744 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1745
1746 QVBoxLayout *mainLayout = new QVBoxLayout();
1747 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1748 QLineEdit *title = new QLineEdit( itemData.name() );
1749 mainLayout->addWidget( title );
1750
1751 QHBoxLayout *cbLayout = new QHBoxLayout( );
1752 mainLayout->addLayout( cbLayout );
1753 dlg.setLayout( mainLayout );
1754 QCheckBox *cb = new QCheckBox { &dlg };
1755 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1756 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1757 cbLayout->addWidget( cb );
1758
1759
1760 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1761
1762 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1763 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1764
1765 mainLayout->addWidget( buttonBox );
1766
1767 if ( dlg.exec() )
1768 {
1770 spacerEdCfg.drawLine = cb->isChecked();
1771 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1772 itemData.setShowLabel( false );
1773 itemData.setName( title->text() );
1774 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1775 item->setText( 0, title->text() );
1776 }
1777
1778 break;
1779 }
1780 }
1781}
1782
1784{
1785 QgsExpressionContext expContext;
1788
1789 if ( mLayer )
1790 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1791
1793 return expContext;
1794}
1795
1797{
1798 return mType;
1799}
1800
1802{
1803 mType = value;
1804}
1805
1807{
1808 QTreeWidgetItemIterator it( this );
1809 while ( *it )
1810 {
1812 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1813 {
1814 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1815 {
1816 // the selection is already good
1817 }
1818 else
1819 {
1820 clearSelection();
1821 ( *it )->setSelected( true );
1822 }
1823 return;
1824 }
1825 ++it;
1826 }
1827 clearSelection();
1828}
1829
1830
1831/*
1832 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1833 */
1834
1835QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1836{
1837 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1838 return stream;
1839}
1840
1841QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1842{
1843 QString name;
1844 QString displayName;
1845 quint32 type;
1846
1847 stream >> type >> name >> displayName;
1848
1850 data.setName( name );
1851 data.setDisplayName( displayName );
1852
1853 return stream;
1854}
1855
1860
1865
1870
1872{
1873 mLabelStyle = labelStyle;
1874}
1875
1877{
1878 return mShowLabel;
1879}
1880
1882{
1883 mShowLabel = showLabel;
1884}
1885
1890
1892{
1893 mVisibilityExpression = visibilityExpression;
1894}
1895
1900
1902{
1903 mCollapsedExpression = collapsedExpression;
1904}
1905
1910
1912{
1913 mRelationEditorConfiguration = relationEditorConfiguration;
1914}
1915
1920
1922{
1923 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1924}
1925
1926
1931
1933{
1934 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1935}
1936
1941
1943{
1944 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
1945}
1946
1948{
1949 return mBackgroundColor;
1950}
1951
1953{
1954 mBackgroundColor = backgroundColor;
1955}
1956
1961
1963{
1964 mTextElementEditorConfiguration = textElementEditorConfiguration;
1965}
1966
1967void QgsAttributesFormProperties::updatedFields()
1968{
1969 // Store configuration to insure changes made are kept after refreshing the list
1970 QMap<QString, FieldConfig> fieldConfigs;
1971 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1972 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1973 {
1974 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1975 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1976 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1977 fieldConfigs[fieldName] = cfg;
1978 }
1979
1981
1982 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1983 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1984 {
1985 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1986 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1987 if ( fieldConfigs.contains( fieldName ) )
1988 {
1989 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
1990 }
1991 }
1992}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:4642
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:4672
@ 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:4657
@ 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 (since QGIS 3.22)
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element (since QGIS 3.30)
@ SpacerElement
A spacer element (since QGIS 3.30)
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:89
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.
QString name
Definition qgsrelation.h:48
QString id
Definition qgsrelation.h:45
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)
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
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.