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