QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
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
306 QgsFieldConstraints::Constraints providerConstraints = QgsFieldConstraints::Constraints();
308 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
310 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
312 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
313 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
314
315 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
316 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
318 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
319 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
320
321 mAttributeTypeDialog->setEditorWidgetConfig( cfg.mEditorWidgetConfig );
322 mAttributeTypeDialog->setEditorWidgetType( cfg.mEditorWidgetType );
323
324 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
325 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
326
327 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
328}
329
330
331void QgsAttributesFormProperties::storeAttributeTypeDialog()
332{
334 return;
335
336 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
337 return;
338
339 FieldConfig cfg;
340
341 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
342 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
343 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
344 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
345 cfg.mAlias = mAttributeTypeDialog->alias();
346 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
347
348 QgsFieldConstraints constraints;
349 if ( mAttributeTypeDialog->notNull() )
350 {
352 }
353 else if ( mAttributeTypeDialog->notNullFromProvider() )
354 {
356 }
357
358 if ( mAttributeTypeDialog->unique() )
359 {
361 }
362 else if ( mAttributeTypeDialog->uniqueFromProvider() )
363 {
365 }
366
367 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
368 {
370 }
371
372 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
373
378 constraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, mAttributeTypeDialog->constraintExpressionEnforced() ?
380
381 cfg.mFieldConstraints = constraints;
382
383 mLayer->setDefaultValueDefinition( mAttributeTypeDialog->fieldIdx(), QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
384
385 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
386 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
387
388 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
389
390 const QString fieldName = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).name();
391
392 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
393 {
394 QTreeWidgetItem *item = *itemIt;
395 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
396 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
397 }
398}
399
400void QgsAttributesFormProperties::storeAttributeWidgetEdit()
401{
403 return;
404
405 mAttributeWidgetEdit->updateItemData();
406}
407
408void QgsAttributesFormProperties::loadAttributeWidgetEdit()
409{
410 if ( mFormLayoutTree->selectedItems().count() != 1 )
411 return;
412
413 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
414 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
415 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
416 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
417}
418
419void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
420{
421 mInfoTextWidget = new QLabel( infoText );
422 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
423 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
424}
425
426void QgsAttributesFormProperties::storeAttributeContainerEdit()
427{
429 return;
430
431 mAttributeContainerEdit->updateItemData();
432}
433
434void QgsAttributesFormProperties::loadAttributeContainerEdit()
435{
436 if ( mFormLayoutTree->selectedItems().count() != 1 )
437 return;
438
439 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
440 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
441 mAttributeContainerEdit->registerExpressionContextGenerator( this );
442 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
443 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
444 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
445
446}
447
448QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
449{
450 auto setCommonProperties = [widgetDef]( DnDTreeItemData & itemData )
451 {
452 itemData.setShowLabel( widgetDef->showLabel() );
453 itemData.setLabelStyle( widgetDef->labelStyle() );
454 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
455 itemData.setVerticalStretch( widgetDef->verticalStretch() );
456 };
457
458 QTreeWidgetItem *newWidget = nullptr;
459 switch ( widgetDef->type() )
460 {
462 {
463 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
464 setCommonProperties( itemData );
465 newWidget = tree->addItem( parent, itemData );
466 break;
467 }
468
470 {
471 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
472 const QgsAction action { actionEditor->action( mLayer ) };
473 if ( action.isValid() )
474 {
475 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
476 setCommonProperties( itemData );
477 newWidget = tree->addItem( parent, itemData );
478 }
479 else
480 {
481 QgsDebugError( QStringLiteral( "Invalid form action" ) );
482 }
483 break;
484 }
485
487 {
488 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
489 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
490 setCommonProperties( itemData );
491
492 RelationEditorConfiguration relEdConfig;
493// relEdConfig.buttons = relationEditor->visibleButtons();
494 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
495 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
496 relEdConfig.nmRelationId = relationEditor->nmRelationId();
497 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
498 relEdConfig.label = relationEditor->label();
499 itemData.setRelationEditorConfiguration( relEdConfig );
500 newWidget = tree->addItem( parent, itemData );
501 break;
502 }
503
505 {
506 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
507
508 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
509 if ( !container )
510 break;
511
512 itemData.setColumnCount( container->columnCount() );
513 itemData.setContainerType( container->type() );
514 itemData.setBackgroundColor( container->backgroundColor() );
515 itemData.setVisibilityExpression( container->visibilityExpression() );
516 itemData.setCollapsedExpression( container->collapsedExpression() );
517 itemData.setCollapsed( container->collapsed() );
518
519 setCommonProperties( itemData );
520
521 newWidget = tree->addItem( parent, itemData );
522
523 const QList<QgsAttributeEditorElement *> children = container->children();
524 for ( QgsAttributeEditorElement *wdg : children )
525 {
526 loadAttributeEditorTreeItem( wdg, newWidget, tree );
527 }
528 break;
529 }
530
532 {
533 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
534 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
535 QmlElementEditorConfiguration qmlEdConfig;
536 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
537 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
538 setCommonProperties( itemData );
539 newWidget = tree->addItem( parent, itemData );
540 break;
541 }
542
544 {
545 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
546 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
547 HtmlElementEditorConfiguration htmlEdConfig;
548 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
549 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
550 setCommonProperties( itemData );
551 newWidget = tree->addItem( parent, itemData );
552 break;
553 }
554
556 {
557 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
558 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
559 TextElementEditorConfiguration textEdConfig;
560 textEdConfig.text = textElementEditor->text();
561 itemData.setTextElementEditorConfiguration( textEdConfig );
562 setCommonProperties( itemData );
563 newWidget = tree->addItem( parent, itemData );
564 break;
565 }
566
568 {
569 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
570 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
571 SpacerElementEditorConfiguration spacerEdConfig;
572 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
573 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
574 setCommonProperties( itemData );
575 itemData.setShowLabel( false );
576 newWidget = tree->addItem( parent, itemData );
577 break;
578 }
579
581 {
582 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
583 break;
584 }
585 }
586
587 if ( newWidget )
588 newWidget->setExpanded( true );
589
590 return newWidget;
591}
592
593
594void QgsAttributesFormProperties::onAttributeSelectionChanged()
595{
596 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
597 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
598 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
599}
600
601void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
602{
603 // when the selection changes in the DnD layout, sync the main tree
604 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
605 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
606 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
607}
608
609void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
610{
611 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
612
614 storeAttributeWidgetEdit();
615 storeAttributeTypeDialog();
616 storeAttributeContainerEdit();
617
618 clearAttributeTypeFrame();
619
620 if ( emitter->selectedItems().count() != 1 )
621 {
622 receiver->clearSelection();
623 }
624 else
625 {
626 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
627 switch ( itemData.type() )
628 {
630 {
631 receiver->selectFirstMatchingItem( itemData );
633 {
634 loadAttributeWidgetEdit();
635 }
636 else
637 {
638 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
639 }
640 break;
641 }
643 {
644 receiver->selectFirstMatchingItem( itemData );
646 loadAttributeWidgetEdit();
647 loadAttributeTypeDialog();
648 break;
649 }
651 {
652 receiver->clearSelection();
653 loadAttributeContainerEdit();
654 break;
655 }
657 {
658 receiver->selectFirstMatchingItem( itemData );
659 const QgsAction action {mLayer->actions()->action( itemData.name() )};
660 loadInfoWidget( action.html() );
661 break;
662 }
667 {
669 {
670 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
671 }
672 else
673 {
674 loadInfoWidget( tr( "This configuration is available with double-click" ) );
675 }
676 receiver->clearSelection();
677 break;
678 }
680 {
681 receiver->clearSelection();
682 break;
683 }
684 }
685 }
686}
687
688void QgsAttributesFormProperties::clearAttributeTypeFrame()
689{
691 {
692 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
693 mAttributeWidgetEdit->deleteLater();
694 mAttributeWidgetEdit = nullptr;
695 }
697 {
698 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
699 mAttributeTypeDialog->deleteLater();
700 mAttributeTypeDialog = nullptr;
701 }
703 {
704 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
705 mAttributeContainerEdit->deleteLater();
706 mAttributeContainerEdit = nullptr;
707 }
708 if ( mInfoTextWidget )
709 {
710 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
711 mInfoTextWidget->deleteLater();
712 mInfoTextWidget = nullptr;
713 }
714}
715
716void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
717{
718 Q_UNUSED( checked )
719 const auto selectedItemList { mFormLayoutTree->selectedItems() };
720 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
721 for ( int i = 0; i < rootItem->childCount(); ++i )
722 {
723 rootItem->child( i )->setSelected( ! selectedItemList.contains( rootItem->child( i ) ) );
724 }
725}
726
727void QgsAttributesFormProperties::addContainer()
728{
729 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
730
731 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
732 {
733 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
734 if ( itemData.type() == DnDTreeItemData::Container )
735 {
736 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
737 }
738 }
739 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
740 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
741
742 if ( !dialog.exec() )
743 return;
744
745 const QString name = dialog.name();
746 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
747 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(),
748 name,
749 dialog.columnCount(),
750 dialog.containerType() );
751}
752
753void QgsAttributesFormProperties::removeTabOrGroupButton()
754{
755 // deleting an item may delete any number of nested child items -- so we delete
756 // them one at a time and then see if there's any selection left
757 while ( true )
758 {
759 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
760 if ( items.empty() )
761 break;
762
763 delete items.at( 0 );
764 }
765
766}
767
769{
770 QgsAttributeEditorElement *widgetDef = nullptr;
771
772 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
773
774 switch ( itemData.type() )
775 {
776 //indexed here?
778 {
779 const int idx = mLayer->fields().lookupField( itemData.name() );
780 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
781 break;
782 }
783
785 {
786 const QgsAction action { mLayer->actions()->action( itemData.name() )};
787 widgetDef = new QgsAttributeEditorAction( action, parent );
788 break;
789 }
790
792 {
793 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
794 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
796 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
797 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
798 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
799 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
800 relDef->setLabel( relationEditorConfig.label );
801 widgetDef = relDef;
802 break;
803 }
804
806 {
807 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
808 container->setColumnCount( itemData.columnCount() );
809 // only top-level containers can be tabs
811 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
812 {
813 // a top container found which isn't at the top level -- reset it to a group box instead
815 }
816 container->setType( type );
817 container->setCollapsed( itemData.collapsed() );
818 container->setCollapsedExpression( itemData.collapsedExpression() );
819 container->setVisibilityExpression( itemData.visibilityExpression() );
820 container->setBackgroundColor( itemData.backgroundColor( ) );
821
822 for ( int t = 0; t < item->childCount(); t++ )
823 {
824 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
825 if ( element )
826 container->addChildElement( element );
827 }
828
829 widgetDef = container;
830 break;
831 }
832
834 {
835 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
836 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
837 widgetDef = element;
838 break;
839 }
840
842 {
843 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
845 widgetDef = element;
846 break;
847 }
848
850 {
851 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
852 element->setText( itemData.textElementEditorConfiguration().text );
853 widgetDef = element;
854 break;
855 }
856
858 {
859 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
861 widgetDef = element;
862 break;
863 }
864
866 break;
867
868 }
869
870 if ( widgetDef )
871 {
872 widgetDef->setShowLabel( itemData.showLabel() );
873 widgetDef->setLabelStyle( itemData.labelStyle() );
874 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
875 widgetDef->setVerticalStretch( itemData.verticalStretch() );
876 }
877
878 return widgetDef;
879}
880
881void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
882{
883 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
884 switch ( layout )
885 {
887 mFormLayoutWidget->setVisible( false );
888 mUiFileFrame->setVisible( false );
889 mAddTabOrGroupButton->setVisible( false );
890 mRemoveTabOrGroupButton->setVisible( false );
891 mInvertSelectionButton->setVisible( false );
892 break;
893
895 mFormLayoutWidget->setVisible( true );
896 mUiFileFrame->setVisible( false );
897 mAddTabOrGroupButton->setVisible( true );
898 mRemoveTabOrGroupButton->setVisible( true );
899 mInvertSelectionButton->setVisible( true );
900 break;
901
903 // ui file
904 mFormLayoutWidget->setVisible( false );
905 mUiFileFrame->setVisible( true );
906 mAddTabOrGroupButton->setVisible( false );
907 mRemoveTabOrGroupButton->setVisible( false );
908 mInvertSelectionButton->setVisible( false );
909 break;
910 }
911}
912
913void QgsAttributesFormProperties::mTbInitCode_clicked()
914{
915 QgsAttributesFormInitCode attributesFormInitCode;
916
917 attributesFormInitCode.setCodeSource( mInitCodeSource );
918 attributesFormInitCode.setInitCode( mInitCode );
919 attributesFormInitCode.setInitFilePath( mInitFilePath );
920 attributesFormInitCode.setInitFunction( mInitFunction );
921
922 if ( !attributesFormInitCode.exec() )
923 return;
924
925 mInitCodeSource = attributesFormInitCode.codeSource();
926 mInitCode = attributesFormInitCode.initCode();
927 mInitFilePath = attributesFormInitCode.initFilePath();
928 mInitFunction = attributesFormInitCode.initFunction();
929
930}
931
932void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
933{
934 QgsSettings myQSettings;
935 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
936 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
937
938 if ( uifilename.isNull() )
939 return;
940
941 const QFileInfo fi( uifilename );
942 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
943 mEditFormLineEdit->setText( uifilename );
944}
945
947{
948 storeAttributeWidgetEdit();
949 storeAttributeContainerEdit();
950 storeAttributeTypeDialog();
951}
952
954{
955 mBlockUpdates++;
956 storeAttributeWidgetEdit();
957 storeAttributeContainerEdit();
958 storeAttributeTypeDialog();
959
960 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
961
962 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
963
964 for ( int i = 0; i < fieldContainer->childCount(); i++ )
965 {
966 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
967 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
968
969 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
970 const int idx = mLayer->fields().indexOf( fieldName );
971
972 //continue in case field does not exist anymore
973 if ( idx < 0 )
974 continue;
975
976 editFormConfig.setReadOnly( idx, !cfg.mEditable );
977 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
978 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
979
980 if ( cfg.mDataDefinedProperties.count() > 0 )
981 {
982 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
983 }
984
986
987 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
988 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
990 {
992 }
993 else
994 {
996 }
998 {
1000 }
1001 else
1002 {
1004 }
1006 {
1008 }
1009 else
1010 {
1012 }
1013
1014 mLayer->setFieldAlias( idx, cfg.mAlias );
1016 }
1017
1018 // tabs and groups
1019 editFormConfig.clearTabs();
1020 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1021 {
1022 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1023 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1024 if ( editorElement )
1025 editFormConfig.addTab( editorElement );
1026 }
1027
1028 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1029
1030 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value< Qgis::AttributeFormLayout >() );
1031
1032 editFormConfig.setInitCodeSource( mInitCodeSource );
1033 editFormConfig.setInitFunction( mInitFunction );
1034 editFormConfig.setInitFilePath( mInitFilePath );
1035 editFormConfig.setInitCode( mInitCode );
1036
1037 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value< Qgis::AttributeFormSuppression >() );
1038
1039 // write the legacy config of relation widgets to support settings read by the API
1040 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1041
1042 for ( int i = 0; i < relationContainer->childCount(); i++ )
1043 {
1044 QTreeWidgetItem *relationItem = relationContainer->child( i );
1045 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1046
1047 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1048 {
1049 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1050 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1051
1052 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1053 {
1054 QVariantMap cfg;
1055
1056 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1057 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1058
1059 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1060 break;
1061 }
1062 }
1063 }
1064
1065 mLayer->setEditFormConfig( editFormConfig );
1066 mBlockUpdates--;
1067}
1068
1069
1070/*
1071 * FieldConfig implementation
1072 */
1074{
1075 mAlias = layer->fields().at( idx ).alias();
1077 mComment = layer->fields().at( idx ).comment();
1078 mEditable = !layer->editFormConfig().readOnly( idx );
1080 && layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
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}
1089
1090QgsAttributesFormProperties::FieldConfig::operator QVariant()
1091{
1092 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1093}
1094
1095/*
1096 * RelationEditorConfiguration implementation
1097 */
1098
1099QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1100{
1101 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1102}
1103
1104/*
1105 * DnDTree implementation
1106 */
1107
1108QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1109{
1110 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1111 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1112 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1114 itemData.setColumnCount( columnCount );
1115 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1116 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1117 parent->addChild( newItem );
1118 newItem->setExpanded( true );
1119 return newItem;
1120}
1121
1123 : QTreeWidget( parent )
1124 , mLayer( layer )
1125{
1126 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1127}
1128
1129QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, QgsAttributesFormProperties::DnDTreeItemData data, int index, const QIcon &icon )
1130{
1131 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1132
1133 switch ( data.type() )
1134 {
1142 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1143 break;
1144
1147 {
1148 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1149 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1150 }
1151 break;
1152 }
1153
1154 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, data );
1155 newItem->setText( 0, data.displayName() );
1156 newItem->setIcon( 0, icon );
1157
1158 if ( index < 0 )
1159 parent->addChild( newItem );
1160 else
1161 parent->insertChild( index, newItem );
1162
1163 return newItem;
1164}
1165
1171void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1172{
1173 const QMimeData *data = event->mimeData();
1174
1175 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1176 {
1178
1179 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1180 QDataStream stream( &itemData, QIODevice::ReadOnly );
1181 stream >> itemElement;
1182
1183 // Inner drag and drop actions are always MoveAction
1184 if ( event->source() == this )
1185 {
1186 event->setDropAction( Qt::MoveAction );
1187 }
1188 }
1189 else
1190 {
1191 event->ignore();
1192 }
1193
1194 QTreeWidget::dragMoveEvent( event );
1195}
1196
1197
1198bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1199{
1200 bool bDropSuccessful = false;
1201
1202 if ( action == Qt::IgnoreAction )
1203 {
1204 bDropSuccessful = true;
1205 }
1206 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1207 {
1208 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1209 QDataStream stream( &itemData, QIODevice::ReadOnly );
1211
1212 while ( !stream.atEnd() )
1213 {
1214 stream >> itemElement;
1215
1216 QTreeWidgetItem *newItem;
1217
1218 if ( parent )
1219 {
1220 newItem = addItem( parent, itemElement, index++ );
1221 bDropSuccessful = true;
1222 }
1223 else
1224 {
1225 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1226 bDropSuccessful = true;
1227 }
1228
1230 {
1231 onItemDoubleClicked( newItem, 0 );
1232 }
1233
1235 {
1236 onItemDoubleClicked( newItem, 0 );
1237 }
1238
1240 {
1241 onItemDoubleClicked( newItem, 0 );
1242 }
1243
1245 {
1246 onItemDoubleClicked( newItem, 0 );
1247 }
1248
1249 clearSelection();
1250 newItem->setSelected( true );
1251 }
1252 }
1253
1254 return bDropSuccessful;
1255}
1256
1257void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1258{
1259 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1260 return;
1261
1262 if ( event->source() == this )
1263 {
1264 event->setDropAction( Qt::MoveAction );
1265 }
1266
1267 QTreeWidget::dropEvent( event );
1268}
1269
1271{
1272 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1273}
1274
1275#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1276QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1277#else
1278QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1279#endif
1280{
1281 if ( items.count() <= 0 )
1282 return nullptr;
1283
1284 const QStringList types = mimeTypes();
1285
1286 if ( types.isEmpty() )
1287 return nullptr;
1288
1289 QMimeData *data = new QMimeData();
1290 const QString format = types.at( 0 );
1291 QByteArray encoded;
1292 QDataStream stream( &encoded, QIODevice::WriteOnly );
1293
1294 const auto constItems = items;
1295 for ( const QTreeWidgetItem *item : constItems )
1296 {
1297 if ( item )
1298 {
1299 // Relevant information is always in the DnDTreeRole of the first column
1301 stream << itemData;
1302 }
1303 }
1304
1305 data->setData( format, encoded );
1306
1307 return data;
1308}
1309
1310void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1311{
1312 Q_UNUSED( column )
1313
1315
1316 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1317
1318 QFormLayout *baseLayout = new QFormLayout();
1319 baseData->setLayout( baseLayout );
1320 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1321 showLabelCheckbox->setChecked( itemData.showLabel() );
1322 baseLayout->addRow( showLabelCheckbox );
1323 QWidget *baseWidget = new QWidget();
1324 baseWidget->setLayout( baseLayout );
1325
1326 switch ( itemData.type() )
1327 {
1333 break;
1334
1336 {
1337 if ( mType == QgsAttributesDnDTree::Type::Drag )
1338 return;
1339
1340 QDialog dlg;
1341 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1342
1343 QVBoxLayout *mainLayout = new QVBoxLayout();
1344 QHBoxLayout *qmlLayout = new QHBoxLayout();
1345 QVBoxLayout *layout = new QVBoxLayout();
1346 mainLayout->addLayout( qmlLayout );
1347 qmlLayout->addLayout( layout );
1348 dlg.setLayout( mainLayout );
1349 layout->addWidget( baseWidget );
1350
1351 QLineEdit *title = new QLineEdit( itemData.name() );
1352
1353 //qmlCode
1354 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1355 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1356
1357 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1358 QgsFeature previewFeature;
1359 mLayer->getFeatures().nextFeature( previewFeature );
1360
1361 //update preview on text change
1362 connect( qmlCode, &QsciScintilla::textChanged, this, [ = ]
1363 {
1364 qmlWrapper->setQmlCode( qmlCode->text() );
1365 qmlWrapper->reinitWidget();
1366 qmlWrapper->setFeature( previewFeature );
1367 } );
1368
1369 //templates
1370 QComboBox *qmlObjectTemplate = new QComboBox();
1371 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1372 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1373 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1374 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1375 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [ = ]( int index )
1376 {
1377 qmlCode->clear();
1378 switch ( index )
1379 {
1380 case 0:
1381 {
1382 qmlCode->setText( QString() );
1383 break;
1384 }
1385 case 1:
1386 {
1387 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1388 "\n"
1389 "Rectangle {\n"
1390 " width: 100\n"
1391 " height: 100\n"
1392 " color: \"steelblue\"\n"
1393 " Text{ text: \"A rectangle\" }\n"
1394 "}\n" ) );
1395 break;
1396 }
1397 case 2:
1398 {
1399 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1400 "import QtCharts 2.0\n"
1401 "\n"
1402 "ChartView {\n"
1403 " width: 400\n"
1404 " height: 400\n"
1405 "\n"
1406 " PieSeries {\n"
1407 " id: pieSeries\n"
1408 " PieSlice { label: \"First slice\"; value: 25 }\n"
1409 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1410 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1411 " }\n"
1412 "}\n" ) );
1413 break;
1414 }
1415 case 3:
1416 {
1417 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1418 "import QtCharts 2.0\n"
1419 "\n"
1420 "ChartView {\n"
1421 " title: \"Bar series\"\n"
1422 " width: 600\n"
1423 " height:400\n"
1424 " legend.alignment: Qt.AlignBottom\n"
1425 " antialiasing: true\n"
1426 " ValueAxis{\n"
1427 " id: valueAxisY\n"
1428 " min: 0\n"
1429 " max: 15\n"
1430 " }\n"
1431 "\n"
1432 " BarSeries {\n"
1433 " id: mySeries\n"
1434 " axisY: valueAxisY\n"
1435 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1436 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1437 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1438 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1439 " }\n"
1440 "}\n" ) );
1441 break;
1442 }
1443 default:
1444 break;
1445 }
1446 } );
1447
1448 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1449 expressionWidget->setButtonVisible( false );
1450 expressionWidget->registerExpressionContextGenerator( this );
1451 expressionWidget->setLayer( mLayer );
1452 QToolButton *addFieldButton = new QToolButton();
1453 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1454
1455 QToolButton *editExpressionButton = new QToolButton();
1456 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1457 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1458
1459 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1460 {
1461 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1462 if ( !expression.isEmpty() )
1463 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1464 } );
1465
1466 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1467 {
1468 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1469 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1471 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1472
1473 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1474 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1475 {
1476 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1477 if ( !expression.isEmpty() )
1478 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1479 }
1480 } );
1481
1482 layout->addWidget( new QLabel( tr( "Title" ) ) );
1483 layout->addWidget( title );
1484 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1485 qmlCodeBox->setLayout( new QVBoxLayout );
1486 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1487 QWidget *expressionWidgetBox = new QWidget();
1488 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1489 expressionWidgetBox->setLayout( new QHBoxLayout );
1490 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1491 expressionWidgetBox->layout()->addWidget( expressionWidget );
1492 expressionWidgetBox->layout()->addWidget( addFieldButton );
1493 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1494 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1495 layout->addWidget( qmlCodeBox );
1496 layout->addWidget( qmlCode );
1497 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1498 qmlPreviewBox->setLayout( new QGridLayout );
1499 qmlPreviewBox->setMinimumWidth( 400 );
1500 qmlPreviewBox->layout()->addWidget( qmlWrapper->widget() );
1501 //emit to load preview for the first time
1502 emit qmlCode->textChanged();
1503 qmlLayout->addWidget( qmlPreviewBox );
1504
1505 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1506
1507 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1508 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1509
1510 mainLayout->addWidget( buttonBox );
1511
1512 if ( dlg.exec() )
1513 {
1515 qmlEdCfg.qmlCode = qmlCode->text();
1516 itemData.setName( title->text() );
1517 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1518 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1519
1520 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1521 item->setText( 0, title->text() );
1522 }
1523 }
1524 break;
1525
1527 {
1528 if ( mType == QgsAttributesDnDTree::Type::Drag )
1529 return;
1530 QDialog dlg;
1531 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1532
1533 QVBoxLayout *mainLayout = new QVBoxLayout();
1534 QHBoxLayout *htmlLayout = new QHBoxLayout();
1535 QVBoxLayout *layout = new QVBoxLayout();
1536 mainLayout->addLayout( htmlLayout );
1537 htmlLayout->addLayout( layout );
1538 dlg.setLayout( mainLayout );
1539 layout->addWidget( baseWidget );
1540
1541 QLineEdit *title = new QLineEdit( itemData.name() );
1542
1543 //htmlCode
1544 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML( );
1545 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1546 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1547
1548 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1549 QgsFeature previewFeature;
1550 mLayer->getFeatures().nextFeature( previewFeature );
1551
1552 //update preview on text change
1553 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [ = ]
1554 {
1555 htmlWrapper->setHtmlCode( htmlCode->text( ) );
1556 htmlWrapper->reinitWidget();
1557 htmlWrapper->setFeature( previewFeature );
1558 } );
1559
1560 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1561 expressionWidget->setButtonVisible( false );
1562 expressionWidget->registerExpressionContextGenerator( this );
1563 expressionWidget->setLayer( mLayer );
1564 QToolButton *addFieldButton = new QToolButton();
1565 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1566
1567 QToolButton *editExpressionButton = new QToolButton();
1568 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1569 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1570
1571 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1572 {
1573 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1574 if ( !expression.isEmpty() )
1575 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1576 } );
1577
1578 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1579 {
1580 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1581 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1583 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1584
1585 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1586 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1587 {
1588 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1589 if ( !expression.isEmpty() )
1590 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1591 }
1592 } );
1593
1594 layout->addWidget( new QLabel( tr( "Title" ) ) );
1595 layout->addWidget( title );
1596 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1597 layout->addWidget( expressionWidgetBox );
1598 expressionWidgetBox->setLayout( new QHBoxLayout );
1599 expressionWidgetBox->layout()->addWidget( expressionWidget );
1600 expressionWidgetBox->layout()->addWidget( addFieldButton );
1601 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1602 layout->addWidget( htmlCode );
1603 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1604 htmlPreviewBox->setLayout( new QGridLayout );
1605 htmlPreviewBox->setMinimumWidth( 400 );
1606 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1607 //emit to load preview for the first time
1608 emit htmlCode->textChanged();
1609 htmlLayout->addWidget( htmlPreviewBox );
1610
1611 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1612
1613 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1614 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1615
1616 mainLayout->addWidget( buttonBox );
1617
1618 if ( dlg.exec() )
1619 {
1621 htmlEdCfg.htmlCode = htmlCode->text();
1622 itemData.setName( title->text() );
1623 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1624 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1625
1626 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1627 item->setText( 0, title->text() );
1628 }
1629 break;
1630 }
1631
1633 {
1634 if ( mType == QgsAttributesDnDTree::Type::Drag )
1635 return;
1636 QDialog dlg;
1637 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1638
1639 QVBoxLayout *mainLayout = new QVBoxLayout();
1640 QHBoxLayout *textLayout = new QHBoxLayout();
1641 QVBoxLayout *layout = new QVBoxLayout();
1642 mainLayout->addLayout( textLayout );
1643 textLayout->addLayout( layout );
1644 dlg.setLayout( mainLayout );
1645 layout->addWidget( baseWidget );
1646
1647 QLineEdit *title = new QLineEdit( itemData.name() );
1648
1649 QgsCodeEditorHTML *text = new QgsCodeEditorHTML( );
1650 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1651 text->setText( itemData.textElementEditorConfiguration().text );
1652
1653 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1654 QgsFeature previewFeature;
1655 mLayer->getFeatures().nextFeature( previewFeature );
1656
1657 //update preview on text change
1658 connect( text, &QgsCodeEditorExpression::textChanged, this, [ = ]
1659 {
1660 textWrapper->setText( text->text( ) );
1661 textWrapper->reinitWidget();
1662 textWrapper->setFeature( previewFeature );
1663 } );
1664
1665 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1666 expressionWidget->setButtonVisible( false );
1667 expressionWidget->registerExpressionContextGenerator( this );
1668 expressionWidget->setLayer( mLayer );
1669 QToolButton *addFieldButton = new QToolButton();
1670 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1671
1672 QToolButton *editExpressionButton = new QToolButton();
1673 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1674 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1675
1676 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1677 {
1678 QString expression = expressionWidget->expression().trimmed();
1679 if ( !expression.isEmpty() )
1680 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1681 } );
1682 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1683 {
1684 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1685
1687 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1688
1689 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1690 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1691 {
1692 QString expression = exprDlg.expressionText().trimmed();
1693 if ( !expression.isEmpty() )
1694 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1695 }
1696 } );
1697
1698 layout->addWidget( new QLabel( tr( "Title" ) ) );
1699 layout->addWidget( title );
1700 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1701 layout->addWidget( expressionWidgetBox );
1702 expressionWidgetBox->setLayout( new QHBoxLayout );
1703 expressionWidgetBox->layout()->addWidget( expressionWidget );
1704 expressionWidgetBox->layout()->addWidget( addFieldButton );
1705 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1706 layout->addWidget( text );
1707 QScrollArea *textPreviewBox = new QgsScrollArea();
1708 textPreviewBox->setLayout( new QGridLayout );
1709 textPreviewBox->setMinimumWidth( 400 );
1710 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1711 //emit to load preview for the first time
1712 emit text->textChanged();
1713 textLayout->addWidget( textPreviewBox );
1714
1715 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1716
1717 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1718 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1719
1720 mainLayout->addWidget( buttonBox );
1721
1722 if ( dlg.exec() )
1723 {
1725 textEdCfg.text = text->text();
1726 itemData.setName( title->text() );
1727 itemData.setTextElementEditorConfiguration( textEdCfg );
1728 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1729
1730 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1731 item->setText( 0, title->text() );
1732 }
1733 break;
1734 }
1735
1737 {
1738 if ( mType == QgsAttributesDnDTree::Type::Drag )
1739 return;
1740 QDialog dlg;
1741 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1742
1743 QVBoxLayout *mainLayout = new QVBoxLayout();
1744 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1745 QLineEdit *title = new QLineEdit( itemData.name() );
1746 mainLayout->addWidget( title );
1747
1748 QHBoxLayout *cbLayout = new QHBoxLayout( );
1749 mainLayout->addLayout( cbLayout );
1750 dlg.setLayout( mainLayout );
1751 QCheckBox *cb = new QCheckBox { &dlg };
1752 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1753 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1754 cbLayout->addWidget( cb );
1755
1756
1757 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1758
1759 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1760 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1761
1762 mainLayout->addWidget( buttonBox );
1763
1764 if ( dlg.exec() )
1765 {
1767 spacerEdCfg.drawLine = cb->isChecked();
1768 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1769 itemData.setShowLabel( false );
1770 itemData.setName( title->text() );
1771 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1772 item->setText( 0, title->text() );
1773 }
1774
1775 break;
1776 }
1777 }
1778}
1779
1781{
1782 QgsExpressionContext expContext;
1785
1786 if ( mLayer )
1787 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1788
1790 return expContext;
1791}
1792
1794{
1795 return mType;
1796}
1797
1799{
1800 mType = value;
1801}
1802
1804{
1805 QTreeWidgetItemIterator it( this );
1806 while ( *it )
1807 {
1809 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1810 {
1811 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1812 {
1813 // the selection is already good
1814 }
1815 else
1816 {
1817 clearSelection();
1818 ( *it )->setSelected( true );
1819 }
1820 return;
1821 }
1822 ++it;
1823 }
1824 clearSelection();
1825}
1826
1827
1828/*
1829 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1830 */
1831
1832QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1833{
1834 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1835 return stream;
1836}
1837
1838QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1839{
1840 QString name;
1841 QString displayName;
1842 quint32 type;
1843
1844 stream >> type >> name >> displayName;
1845
1847 data.setName( name );
1848 data.setDisplayName( displayName );
1849
1850 return stream;
1851}
1852
1857
1862
1867
1869{
1870 mLabelStyle = labelStyle;
1871}
1872
1874{
1875 return mShowLabel;
1876}
1877
1879{
1880 mShowLabel = showLabel;
1881}
1882
1887
1889{
1890 mVisibilityExpression = visibilityExpression;
1891}
1892
1897
1899{
1900 mCollapsedExpression = collapsedExpression;
1901}
1902
1907
1909{
1910 mRelationEditorConfiguration = relationEditorConfiguration;
1911}
1912
1917
1919{
1920 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1921}
1922
1923
1928
1930{
1931 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1932}
1933
1938
1940{
1941 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
1942}
1943
1945{
1946 return mBackgroundColor;
1947}
1948
1950{
1951 mBackgroundColor = backgroundColor;
1952}
1953
1958
1960{
1961 mTextElementEditorConfiguration = textElementEditorConfiguration;
1962}
1963
1964void QgsAttributesFormProperties::updatedFields()
1965{
1966 // Store configuration to insure changes made are kept after refreshing the list
1967 QMap<QString, FieldConfig> fieldConfigs;
1968 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1969 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1970 {
1971 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1972 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1973 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1974 fieldConfigs[fieldName] = cfg;
1975 }
1976
1978
1979 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1980 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1981 {
1982 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1983 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1984 if ( fieldConfigs.contains( fieldName ) )
1985 {
1986 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
1987 }
1988 }
1989}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:4341
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:4371
@ 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:4356
@ 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:56
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.
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:729
QString alias
Definition qgsfield.h:63
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
Container of fields for a vector layer.
Definition qgsfields.h:45
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
@ OriginExpression
Field is calculated from an expression.
Definition qgsfields.h:54
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition qgsfields.h:52
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
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).
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.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.
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.