QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
qgsattributesformproperties.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsattributesformproperties.cpp
3 ---------------------
4 begin : August 2017
5 copyright : (C) 2017 by David Signer
6 email : david at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsactionmanager.h"
17#include "qgsaddtaborgroup.h"
21#include "qgsattributetypedialog.h"
22#include "qgsattributeformcontaineredit.h"
23#include "qgsattributewidgetedit.h"
25#include "qgsqmlwidgetwrapper.h"
27#include "qgsapplication.h"
28#include "qgscodeeditor.h"
29#include "qgscodeeditorhtml.h"
39#include "qgsgui.h"
42#include "qgsfieldcombobox.h"
43#include "qgsexpressionfinder.h"
45
47 : QWidget( parent )
48 , mLayer( layer )
49{
50 if ( !layer )
51 return;
52
53 setupUi( this );
54
55 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
56 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
57 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
58
59 // available widgets tree
60 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
62 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
63 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
64 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
65 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
66 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
68
69 // form layout tree
70 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
72 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
73 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
74 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
75 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
77
78 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
79 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
80 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
81 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
82 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
83 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
84 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
85 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
86
87 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this]
88 {
89 if ( !mBlockUpdates )
90 updatedFields();
91 } );
92}
93
103
105{
106 mAvailableWidgetsTree->clear();
107 mAvailableWidgetsTree->setSortingEnabled( false );
108 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
109 mAvailableWidgetsTree->setAcceptDrops( false );
110 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
111
112 //load Fields
113
114 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
115 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
116
117 const QgsFields fields = mLayer->fields();
118 for ( int i = 0; i < fields.size(); ++i )
119 {
120 const QgsField field = fields.at( i );
121 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
122 itemData.setShowLabel( true );
123
124 FieldConfig cfg( mLayer, i );
125
126 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
127
128 item->setData( 0, FieldConfigRole, cfg );
129 item->setData( 0, FieldNameRole, field.name() );
130
131 QString tooltip;
132 if ( !field.alias().isEmpty() )
133 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
134 else
135 tooltip = field.name();
136 item->setToolTip( 0, tooltip );
137 }
138 catitem->setExpanded( true );
139
140 //load Relations
141 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
142 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
143
144 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
145
146 for ( const QgsRelation &relation : relations )
147 {
148 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), relation.name() );
149 itemData.setShowLabel( true );
150 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
151 item->setData( 0, FieldNameRole, relation.id() );
152 }
153 catitem->setExpanded( true );
154
155 // Form actions
156 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
157 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
158
159 const QList<QgsAction> actions { mLayer->actions()->actions( ) };
160
161 for ( const auto &action : std::as_const( actions ) )
162 {
163 if ( action.isValid() && action.runable() &&
164 ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) ||
165 action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
166 {
167 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
168 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
169 itemData.setShowLabel( true );
170 mAvailableWidgetsTree->addItem( catitem, itemData );
171 }
172 }
173
174 // QML/HTML widget
175 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
176 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
177
178 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
179 itemData.setShowLabel( true );
180 mAvailableWidgetsTree->addItem( catitem, itemData );
181
182 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
183 itemDataHtml.setShowLabel( true );
184 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
185
186 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
187 itemDataText.setShowLabel( true );
188 mAvailableWidgetsTree->addItem( catitem, itemDataText );
189
190 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
191 itemDataSpacer.setShowLabel( false );
192 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
193
194 catitem ->setExpanded( true );
195}
196
198{
199 // tabs and groups info
200 mFormLayoutTree->clear();
201 mFormLayoutTree->setSortingEnabled( false );
202 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
203 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
204 mFormLayoutTree->setAcceptDrops( true );
205 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
206
207 const auto constTabs = mLayer->editFormConfig().tabs();
208 for ( QgsAttributeEditorElement *wdg : constTabs )
209 {
210 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
211 }
212}
213
214
216{
218 {
219 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
220 }
221 else
222 {
223 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
224 }
225 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
226 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
227
228 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
229}
230
237
239{
240 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
241
242 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
243
245 mEditFormLineEdit->setText( cfg.uiForm() );
246}
247
249{
251
252 mInitCodeSource = cfg.initCodeSource();
253 mInitFunction = cfg.initFunction();
254 mInitFilePath = cfg.initFilePath();
255 mInitCode = cfg.initCode();
256
257 if ( mInitCode.isEmpty() )
258 {
259 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
260 "QGIS forms can have a Python function that is called when the form is\n"
261 "opened.\n"
262 "\n"
263 "Use this function to add extra logic to your forms.\n"
264 "\n"
265 "Enter the name of the function in the \"Python Init function\"\n"
266 "field.\n"
267 "An example follows:\n"
268 "\"\"\"\n"
269 "from qgis.PyQt.QtWidgets import QWidget\n\n"
270 "def my_form_open(dialog, layer, feature):\n"
271 " geom = feature.geometry()\n"
272 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
273 }
274}
275
276void QgsAttributesFormProperties::loadAttributeTypeDialog()
277{
278 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
279 return;
280
281 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
282
283 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
284 const QString fieldName = item->data( 0, FieldNameRole ).toString();
285 const int index = mLayer->fields().indexOf( fieldName );
286
287 if ( index < 0 )
288 return;
289
290 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
291
292 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
293
294 mAttributeTypeDialog->setAlias( cfg.mAlias );
295 mAttributeTypeDialog->setDataDefinedProperties( cfg.mDataDefinedProperties );
296 mAttributeTypeDialog->setComment( cfg.mComment );
297 mAttributeTypeDialog->setFieldEditable( cfg.mEditable );
298 mAttributeTypeDialog->setLabelOnTop( cfg.mLabelOnTop );
299 mAttributeTypeDialog->setReuseLastValues( cfg.mReuseLastValues );
304 mAttributeTypeDialog->setSplitPolicy( cfg.mSplitPolicy );
305 mAttributeTypeDialog->setDuplicatePolicy( cfg.mDuplicatePolicy );
306
309 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
311 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
313 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
314 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
315
316 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
317 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
319 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
320 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
321
322 mAttributeTypeDialog->setEditorWidgetConfig( cfg.mEditorWidgetConfig );
323 mAttributeTypeDialog->setEditorWidgetType( cfg.mEditorWidgetType );
324
325 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
326 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
327
328 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
329}
330
331
332void QgsAttributesFormProperties::storeAttributeTypeDialog()
333{
335 return;
336
337 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
338 return;
339
340 FieldConfig cfg;
341
342 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
343 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
344 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
345 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
346 cfg.mAlias = mAttributeTypeDialog->alias();
347 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
348
349 QgsFieldConstraints constraints;
350 if ( mAttributeTypeDialog->notNull() )
351 {
353 }
354 else if ( mAttributeTypeDialog->notNullFromProvider() )
355 {
357 }
358
359 if ( mAttributeTypeDialog->unique() )
360 {
362 }
363 else if ( mAttributeTypeDialog->uniqueFromProvider() )
364 {
366 }
367
368 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
369 {
371 }
372
373 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
374
379 constraints.setConstraintStrength( QgsFieldConstraints::ConstraintExpression, mAttributeTypeDialog->constraintExpressionEnforced() ?
381
382 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
383 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
384 cfg.mFieldConstraints = constraints;
385 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
386 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
387 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
388 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
389
390 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
391 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
392
393 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
394
395 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
396 {
397 QTreeWidgetItem *item = *itemIt;
398 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
399 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
400 }
401}
402
403void QgsAttributesFormProperties::storeAttributeWidgetEdit()
404{
406 return;
407
408 mAttributeWidgetEdit->updateItemData();
409}
410
411void QgsAttributesFormProperties::loadAttributeWidgetEdit()
412{
413 if ( mFormLayoutTree->selectedItems().count() != 1 )
414 return;
415
416 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
417 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
418 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
419 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
420}
421
422void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
423{
424 mInfoTextWidget = new QLabel( infoText );
425 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
426 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
427}
428
429void QgsAttributesFormProperties::storeAttributeContainerEdit()
430{
432 return;
433
434 mAttributeContainerEdit->updateItemData();
435}
436
437void QgsAttributesFormProperties::loadAttributeContainerEdit()
438{
439 if ( mFormLayoutTree->selectedItems().count() != 1 )
440 return;
441
442 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
443 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
444 mAttributeContainerEdit->registerExpressionContextGenerator( this );
445 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
446 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
447 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
448
449}
450
451QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
452{
453 auto setCommonProperties = [widgetDef]( DnDTreeItemData & itemData )
454 {
455 itemData.setShowLabel( widgetDef->showLabel() );
456 itemData.setLabelStyle( widgetDef->labelStyle() );
457 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
458 itemData.setVerticalStretch( widgetDef->verticalStretch() );
459 };
460
461 QTreeWidgetItem *newWidget = nullptr;
462 switch ( widgetDef->type() )
463 {
465 {
466 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
467 setCommonProperties( itemData );
468 newWidget = tree->addItem( parent, itemData );
469 break;
470 }
471
473 {
474 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
475 const QgsAction action { actionEditor->action( mLayer ) };
476 if ( action.isValid() )
477 {
478 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
479 setCommonProperties( itemData );
480 newWidget = tree->addItem( parent, itemData );
481 }
482 else
483 {
484 QgsDebugError( QStringLiteral( "Invalid form action" ) );
485 }
486 break;
487 }
488
490 {
491 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
492 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
493 setCommonProperties( itemData );
494
495 RelationEditorConfiguration relEdConfig;
496// relEdConfig.buttons = relationEditor->visibleButtons();
497 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
498 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
499 relEdConfig.nmRelationId = relationEditor->nmRelationId();
500 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
501 relEdConfig.label = relationEditor->label();
502 itemData.setRelationEditorConfiguration( relEdConfig );
503 newWidget = tree->addItem( parent, itemData );
504 break;
505 }
506
508 {
509 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
510
511 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
512 if ( !container )
513 break;
514
515 itemData.setColumnCount( container->columnCount() );
516 itemData.setContainerType( container->type() );
517 itemData.setBackgroundColor( container->backgroundColor() );
518 itemData.setVisibilityExpression( container->visibilityExpression() );
519 itemData.setCollapsedExpression( container->collapsedExpression() );
520 itemData.setCollapsed( container->collapsed() );
521
522 setCommonProperties( itemData );
523
524 newWidget = tree->addItem( parent, itemData );
525
526 const QList<QgsAttributeEditorElement *> children = container->children();
527 for ( QgsAttributeEditorElement *wdg : children )
528 {
529 loadAttributeEditorTreeItem( wdg, newWidget, tree );
530 }
531 break;
532 }
533
535 {
536 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
537 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
538 QmlElementEditorConfiguration qmlEdConfig;
539 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
540 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
541 setCommonProperties( itemData );
542 newWidget = tree->addItem( parent, itemData );
543 break;
544 }
545
547 {
548 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
549 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
550 HtmlElementEditorConfiguration htmlEdConfig;
551 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
552 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
553 setCommonProperties( itemData );
554 newWidget = tree->addItem( parent, itemData );
555 break;
556 }
557
559 {
560 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
561 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
562 TextElementEditorConfiguration textEdConfig;
563 textEdConfig.text = textElementEditor->text();
564 itemData.setTextElementEditorConfiguration( textEdConfig );
565 setCommonProperties( itemData );
566 newWidget = tree->addItem( parent, itemData );
567 break;
568 }
569
571 {
572 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
573 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
574 SpacerElementEditorConfiguration spacerEdConfig;
575 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
576 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
577 setCommonProperties( itemData );
578 itemData.setShowLabel( false );
579 newWidget = tree->addItem( parent, itemData );
580 break;
581 }
582
584 {
585 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
586 break;
587 }
588 }
589
590 if ( newWidget )
591 newWidget->setExpanded( true );
592
593 return newWidget;
594}
595
596
597void QgsAttributesFormProperties::onAttributeSelectionChanged()
598{
599 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
600 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
601 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
602}
603
604void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
605{
606 // when the selection changes in the DnD layout, sync the main tree
607 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
608 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
609 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
610}
611
612void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
613{
614 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
615
617 storeAttributeWidgetEdit();
618
619 // Do not store while initializing!
620 if ( !mAvailableWidgetsTree->selectedItems().empty() )
621 {
622 storeAttributeTypeDialog();
623 storeAttributeContainerEdit();
624 }
625
626 clearAttributeTypeFrame();
627
628 if ( emitter->selectedItems().count() != 1 )
629 {
630 receiver->clearSelection();
631 }
632 else
633 {
634 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
635 switch ( itemData.type() )
636 {
638 {
639 receiver->selectFirstMatchingItem( itemData );
641 {
642 loadAttributeWidgetEdit();
643 }
644 else
645 {
646 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
647 }
648 break;
649 }
651 {
652 receiver->selectFirstMatchingItem( itemData );
654 loadAttributeWidgetEdit();
655 loadAttributeTypeDialog();
656 break;
657 }
659 {
660 receiver->clearSelection();
661 loadAttributeContainerEdit();
662 break;
663 }
665 {
666 receiver->selectFirstMatchingItem( itemData );
667 const QgsAction action {mLayer->actions()->action( itemData.name() )};
668 loadInfoWidget( action.html() );
669 break;
670 }
675 {
677 {
678 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
679 }
680 else
681 {
682 loadInfoWidget( tr( "This configuration is available with double-click" ) );
683 }
684 receiver->clearSelection();
685 break;
686 }
688 {
689 receiver->clearSelection();
690 break;
691 }
692 }
693 }
694}
695
696void QgsAttributesFormProperties::clearAttributeTypeFrame()
697{
699 {
700 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
701 mAttributeWidgetEdit->deleteLater();
702 mAttributeWidgetEdit = nullptr;
703 }
705 {
706 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
707 mAttributeTypeDialog->deleteLater();
708 mAttributeTypeDialog = nullptr;
709 }
711 {
712 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
713 mAttributeContainerEdit->deleteLater();
714 mAttributeContainerEdit = nullptr;
715 }
716 if ( mInfoTextWidget )
717 {
718 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
719 mInfoTextWidget->deleteLater();
720 mInfoTextWidget = nullptr;
721 }
722}
723
724void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
725{
726 Q_UNUSED( checked )
727 const auto selectedItemList { mFormLayoutTree->selectedItems() };
728 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
729 for ( int i = 0; i < rootItem->childCount(); ++i )
730 {
731 rootItem->child( i )->setSelected( ! selectedItemList.contains( rootItem->child( i ) ) );
732 }
733}
734
735void QgsAttributesFormProperties::addContainer()
736{
737 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
738
739 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
740 {
741 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
742 if ( itemData.type() == DnDTreeItemData::Container )
743 {
744 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
745 }
746 }
747 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
748 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
749
750 if ( !dialog.exec() )
751 return;
752
753 const QString name = dialog.name();
754 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
755 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(),
756 name,
757 dialog.columnCount(),
758 dialog.containerType() );
759}
760
761void QgsAttributesFormProperties::removeTabOrGroupButton()
762{
763 // deleting an item may delete any number of nested child items -- so we delete
764 // them one at a time and then see if there's any selection left
765 while ( true )
766 {
767 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
768 if ( items.empty() )
769 break;
770
771 delete items.at( 0 );
772 }
773
774}
775
777{
778 QgsAttributeEditorElement *widgetDef = nullptr;
779
780 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
781
782 switch ( itemData.type() )
783 {
784 //indexed here?
786 {
787 const int idx = mLayer->fields().lookupField( itemData.name() );
788 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
789 break;
790 }
791
793 {
794 const QgsAction action { mLayer->actions()->action( itemData.name() )};
795 widgetDef = new QgsAttributeEditorAction( action, parent );
796 break;
797 }
798
800 {
801 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
802 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
804 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
805 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
806 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
807 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
808 relDef->setLabel( relationEditorConfig.label );
809 widgetDef = relDef;
810 break;
811 }
812
814 {
815 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
816 container->setColumnCount( itemData.columnCount() );
817 // only top-level containers can be tabs
819 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
820 {
821 // a top container found which isn't at the top level -- reset it to a group box instead
823 }
824 container->setType( type );
825 container->setCollapsed( itemData.collapsed() );
826 container->setCollapsedExpression( itemData.collapsedExpression() );
827 container->setVisibilityExpression( itemData.visibilityExpression() );
828 container->setBackgroundColor( itemData.backgroundColor( ) );
829
830 for ( int t = 0; t < item->childCount(); t++ )
831 {
832 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
833 if ( element )
834 container->addChildElement( element );
835 }
836
837 widgetDef = container;
838 break;
839 }
840
842 {
843 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
844 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
845 widgetDef = element;
846 break;
847 }
848
850 {
851 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
853 widgetDef = element;
854 break;
855 }
856
858 {
859 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
860 element->setText( itemData.textElementEditorConfiguration().text );
861 widgetDef = element;
862 break;
863 }
864
866 {
867 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
869 widgetDef = element;
870 break;
871 }
872
874 break;
875
876 }
877
878 if ( widgetDef )
879 {
880 widgetDef->setShowLabel( itemData.showLabel() );
881 widgetDef->setLabelStyle( itemData.labelStyle() );
882 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
883 widgetDef->setVerticalStretch( itemData.verticalStretch() );
884 }
885
886 return widgetDef;
887}
888
889void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
890{
891 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
892 switch ( layout )
893 {
895 mFormLayoutWidget->setVisible( false );
896 mUiFileFrame->setVisible( false );
897 mAddTabOrGroupButton->setVisible( false );
898 mRemoveTabOrGroupButton->setVisible( false );
899 mInvertSelectionButton->setVisible( false );
900 break;
901
903 mFormLayoutWidget->setVisible( true );
904 mUiFileFrame->setVisible( false );
905 mAddTabOrGroupButton->setVisible( true );
906 mRemoveTabOrGroupButton->setVisible( true );
907 mInvertSelectionButton->setVisible( true );
908 break;
909
911 // ui file
912 mFormLayoutWidget->setVisible( false );
913 mUiFileFrame->setVisible( true );
914 mAddTabOrGroupButton->setVisible( false );
915 mRemoveTabOrGroupButton->setVisible( false );
916 mInvertSelectionButton->setVisible( false );
917 break;
918 }
919}
920
921void QgsAttributesFormProperties::mTbInitCode_clicked()
922{
923 QgsAttributesFormInitCode attributesFormInitCode;
924
925 attributesFormInitCode.setCodeSource( mInitCodeSource );
926 attributesFormInitCode.setInitCode( mInitCode );
927 attributesFormInitCode.setInitFilePath( mInitFilePath );
928 attributesFormInitCode.setInitFunction( mInitFunction );
929
930 if ( !attributesFormInitCode.exec() )
931 return;
932
933 mInitCodeSource = attributesFormInitCode.codeSource();
934 mInitCode = attributesFormInitCode.initCode();
935 mInitFilePath = attributesFormInitCode.initFilePath();
936 mInitFunction = attributesFormInitCode.initFunction();
937
938}
939
940void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
941{
942 QgsSettings myQSettings;
943 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
944 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
945
946 if ( uifilename.isNull() )
947 return;
948
949 const QFileInfo fi( uifilename );
950 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
951 mEditFormLineEdit->setText( uifilename );
952}
953
955{
956 storeAttributeWidgetEdit();
957 storeAttributeContainerEdit();
958 storeAttributeTypeDialog();
959}
960
962{
963 mBlockUpdates++;
964 storeAttributeWidgetEdit();
965 storeAttributeContainerEdit();
966 storeAttributeTypeDialog();
967
968 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
969
970 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
971
972 for ( int i = 0; i < fieldContainer->childCount(); i++ )
973 {
974 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
975 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
976
977 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
978 const int idx = mLayer->fields().indexOf( fieldName );
979
980 //continue in case field does not exist anymore
981 if ( idx < 0 )
982 continue;
983
984 editFormConfig.setReadOnly( idx, !cfg.mEditable );
985 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
986 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
987
988 if ( cfg.mDataDefinedProperties.count() > 0 )
989 {
990 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
991 }
992
994
995 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
996 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
998 {
1000 }
1001 else
1002 {
1004 }
1006 {
1008 }
1009 else
1010 {
1012 }
1014 {
1016 }
1017 else
1018 {
1020 }
1021
1022 mLayer->setFieldAlias( idx, cfg.mAlias );
1025 }
1026
1027 // tabs and groups
1028 editFormConfig.clearTabs();
1029 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1030 {
1031 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1032 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1033 if ( editorElement )
1034 editFormConfig.addTab( editorElement );
1035 }
1036
1037 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1038
1039 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value< Qgis::AttributeFormLayout >() );
1040
1041 editFormConfig.setInitCodeSource( mInitCodeSource );
1042 editFormConfig.setInitFunction( mInitFunction );
1043 editFormConfig.setInitFilePath( mInitFilePath );
1044 editFormConfig.setInitCode( mInitCode );
1045
1046 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value< Qgis::AttributeFormSuppression >() );
1047
1048 // write the legacy config of relation widgets to support settings read by the API
1049 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1050
1051 for ( int i = 0; i < relationContainer->childCount(); i++ )
1052 {
1053 QTreeWidgetItem *relationItem = relationContainer->child( i );
1054 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1055
1056 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1057 {
1058 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1059 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1060
1061 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1062 {
1063 QVariantMap cfg;
1064
1065 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1066 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1067
1068 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1069 break;
1070 }
1071 }
1072 }
1073
1074 mLayer->setEditFormConfig( editFormConfig );
1075 mBlockUpdates--;
1076}
1077
1078
1079/*
1080 * FieldConfig implementation
1081 */
1083{
1084 mAlias = layer->fields().at( idx ).alias();
1086 mComment = layer->fields().at( idx ).comment();
1087 mEditable = !layer->editFormConfig().readOnly( idx );
1088 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1090 mFieldConstraints = layer->fields().at( idx ).constraints();
1091 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1092 mEditorWidgetType = setup.type();
1093 mEditorWidgetConfig = setup.config();
1094 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1095 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1096}
1097
1098QgsAttributesFormProperties::FieldConfig::operator QVariant()
1099{
1100 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1101}
1102
1103/*
1104 * RelationEditorConfiguration implementation
1105 */
1106
1107QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1108{
1109 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1110}
1111
1112/*
1113 * DnDTree implementation
1114 */
1115
1116QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1117{
1118 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1119 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1120 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1122 itemData.setColumnCount( columnCount );
1123 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1124 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1125 parent->addChild( newItem );
1126 newItem->setExpanded( true );
1127 return newItem;
1128}
1129
1131 : QTreeWidget( parent )
1132 , mLayer( layer )
1133{
1134 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1135}
1136
1137QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, QgsAttributesFormProperties::DnDTreeItemData data, int index, const QIcon &icon )
1138{
1139 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1140
1141 switch ( data.type() )
1142 {
1150 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1151 break;
1152
1155 {
1156 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1157 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1158 }
1159 break;
1160 }
1161
1162 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, data );
1163 newItem->setText( 0, data.displayName() );
1164 newItem->setIcon( 0, icon );
1165
1166 if ( index < 0 )
1167 parent->addChild( newItem );
1168 else
1169 parent->insertChild( index, newItem );
1170
1171 return newItem;
1172}
1173
1179void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1180{
1181 const QMimeData *data = event->mimeData();
1182
1183 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1184 {
1186
1187 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1188 QDataStream stream( &itemData, QIODevice::ReadOnly );
1189 stream >> itemElement;
1190
1191 // Inner drag and drop actions are always MoveAction
1192 if ( event->source() == this )
1193 {
1194 event->setDropAction( Qt::MoveAction );
1195 }
1196 }
1197 else
1198 {
1199 event->ignore();
1200 }
1201
1202 QTreeWidget::dragMoveEvent( event );
1203}
1204
1205
1206bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1207{
1208 bool bDropSuccessful = false;
1209
1210 if ( action == Qt::IgnoreAction )
1211 {
1212 bDropSuccessful = true;
1213 }
1214 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1215 {
1216 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1217 QDataStream stream( &itemData, QIODevice::ReadOnly );
1219
1220 while ( !stream.atEnd() )
1221 {
1222 stream >> itemElement;
1223
1224 QTreeWidgetItem *newItem;
1225
1226 if ( parent )
1227 {
1228 newItem = addItem( parent, itemElement, index++ );
1229 bDropSuccessful = true;
1230 }
1231 else
1232 {
1233 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1234 bDropSuccessful = true;
1235 }
1236
1238 {
1239 onItemDoubleClicked( newItem, 0 );
1240 }
1241
1243 {
1244 onItemDoubleClicked( newItem, 0 );
1245 }
1246
1248 {
1249 onItemDoubleClicked( newItem, 0 );
1250 }
1251
1253 {
1254 onItemDoubleClicked( newItem, 0 );
1255 }
1256
1257 clearSelection();
1258 newItem->setSelected( true );
1259 }
1260 }
1261
1262 return bDropSuccessful;
1263}
1264
1265void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1266{
1267 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1268 return;
1269
1270 if ( event->source() == this )
1271 {
1272 event->setDropAction( Qt::MoveAction );
1273 }
1274
1275 QTreeWidget::dropEvent( event );
1276}
1277
1279{
1280 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1281}
1282
1283#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1284QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1285#else
1286QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1287#endif
1288{
1289 if ( items.count() <= 0 )
1290 return nullptr;
1291
1292 const QStringList types = mimeTypes();
1293
1294 if ( types.isEmpty() )
1295 return nullptr;
1296
1297 QMimeData *data = new QMimeData();
1298 const QString format = types.at( 0 );
1299 QByteArray encoded;
1300 QDataStream stream( &encoded, QIODevice::WriteOnly );
1301
1302 const auto constItems = items;
1303 for ( const QTreeWidgetItem *item : constItems )
1304 {
1305 if ( item )
1306 {
1307 // Relevant information is always in the DnDTreeRole of the first column
1309 stream << itemData;
1310 }
1311 }
1312
1313 data->setData( format, encoded );
1314
1315 return data;
1316}
1317
1318void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1319{
1320 Q_UNUSED( column )
1321
1323
1324 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1325
1326 QFormLayout *baseLayout = new QFormLayout();
1327 baseData->setLayout( baseLayout );
1328 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1329 showLabelCheckbox->setChecked( itemData.showLabel() );
1330 baseLayout->addRow( showLabelCheckbox );
1331 QWidget *baseWidget = new QWidget();
1332 baseWidget->setLayout( baseLayout );
1333
1334 switch ( itemData.type() )
1335 {
1341 break;
1342
1344 {
1345 if ( mType == QgsAttributesDnDTree::Type::Drag )
1346 return;
1347
1348 QDialog dlg;
1349 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1350
1351 QVBoxLayout *mainLayout = new QVBoxLayout();
1352 QHBoxLayout *qmlLayout = new QHBoxLayout();
1353 QVBoxLayout *layout = new QVBoxLayout();
1354 mainLayout->addLayout( qmlLayout );
1355 qmlLayout->addLayout( layout );
1356 dlg.setLayout( mainLayout );
1357 layout->addWidget( baseWidget );
1358
1359 QLineEdit *title = new QLineEdit( itemData.name() );
1360
1361 //qmlCode
1362 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1363 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1364
1365 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1366 QgsFeature previewFeature;
1367 mLayer->getFeatures().nextFeature( previewFeature );
1368
1369 //update preview on text change
1370 connect( qmlCode, &QsciScintilla::textChanged, this, [ = ]
1371 {
1372 qmlWrapper->setQmlCode( qmlCode->text() );
1373 qmlWrapper->reinitWidget();
1374 qmlWrapper->setFeature( previewFeature );
1375 } );
1376
1377 //templates
1378 QComboBox *qmlObjectTemplate = new QComboBox();
1379 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1380 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1381 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1382 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1383 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [ = ]( int index )
1384 {
1385 qmlCode->clear();
1386 switch ( index )
1387 {
1388 case 0:
1389 {
1390 qmlCode->setText( QString() );
1391 break;
1392 }
1393 case 1:
1394 {
1395 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1396 "\n"
1397 "Rectangle {\n"
1398 " width: 100\n"
1399 " height: 100\n"
1400 " color: \"steelblue\"\n"
1401 " Text{ text: \"A rectangle\" }\n"
1402 "}\n" ) );
1403 break;
1404 }
1405 case 2:
1406 {
1407 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1408 "import QtCharts 2.0\n"
1409 "\n"
1410 "ChartView {\n"
1411 " width: 400\n"
1412 " height: 400\n"
1413 "\n"
1414 " PieSeries {\n"
1415 " id: pieSeries\n"
1416 " PieSlice { label: \"First slice\"; value: 25 }\n"
1417 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1418 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1419 " }\n"
1420 "}\n" ) );
1421 break;
1422 }
1423 case 3:
1424 {
1425 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1426 "import QtCharts 2.0\n"
1427 "\n"
1428 "ChartView {\n"
1429 " title: \"Bar series\"\n"
1430 " width: 600\n"
1431 " height:400\n"
1432 " legend.alignment: Qt.AlignBottom\n"
1433 " antialiasing: true\n"
1434 " ValueAxis{\n"
1435 " id: valueAxisY\n"
1436 " min: 0\n"
1437 " max: 15\n"
1438 " }\n"
1439 "\n"
1440 " BarSeries {\n"
1441 " id: mySeries\n"
1442 " axisY: valueAxisY\n"
1443 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1444 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1445 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1446 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1447 " }\n"
1448 "}\n" ) );
1449 break;
1450 }
1451 default:
1452 break;
1453 }
1454 } );
1455
1456 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1457 expressionWidget->setButtonVisible( false );
1458 expressionWidget->registerExpressionContextGenerator( this );
1459 expressionWidget->setLayer( mLayer );
1460 QToolButton *addFieldButton = new QToolButton();
1461 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1462
1463 QToolButton *editExpressionButton = new QToolButton();
1464 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1465 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1466
1467 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1468 {
1469 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1470 if ( !expression.isEmpty() )
1471 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1472 } );
1473
1474 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1475 {
1476 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1477 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1479 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1480
1481 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1482 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1483 {
1484 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1485 if ( !expression.isEmpty() )
1486 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1487 }
1488 } );
1489
1490 layout->addWidget( new QLabel( tr( "Title" ) ) );
1491 layout->addWidget( title );
1492 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1493 qmlCodeBox->setLayout( new QVBoxLayout );
1494 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1495 QWidget *expressionWidgetBox = new QWidget();
1496 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1497 expressionWidgetBox->setLayout( new QHBoxLayout );
1498 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1499 expressionWidgetBox->layout()->addWidget( expressionWidget );
1500 expressionWidgetBox->layout()->addWidget( addFieldButton );
1501 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1502 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1503 layout->addWidget( qmlCodeBox );
1504 layout->addWidget( qmlCode );
1505 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1506 qmlPreviewBox->setLayout( new QGridLayout );
1507 qmlPreviewBox->setMinimumWidth( 400 );
1508 qmlPreviewBox->layout()->addWidget( qmlWrapper->widget() );
1509 //emit to load preview for the first time
1510 emit qmlCode->textChanged();
1511 qmlLayout->addWidget( qmlPreviewBox );
1512
1513 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1514
1515 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1516 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1517
1518 mainLayout->addWidget( buttonBox );
1519
1520 if ( dlg.exec() )
1521 {
1523 qmlEdCfg.qmlCode = qmlCode->text();
1524 itemData.setName( title->text() );
1525 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1526 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1527
1528 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1529 item->setText( 0, title->text() );
1530 }
1531 }
1532 break;
1533
1535 {
1536 if ( mType == QgsAttributesDnDTree::Type::Drag )
1537 return;
1538 QDialog dlg;
1539 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1540
1541 QVBoxLayout *mainLayout = new QVBoxLayout();
1542 QHBoxLayout *htmlLayout = new QHBoxLayout();
1543 QVBoxLayout *layout = new QVBoxLayout();
1544 mainLayout->addLayout( htmlLayout );
1545 htmlLayout->addLayout( layout );
1546 dlg.setLayout( mainLayout );
1547 layout->addWidget( baseWidget );
1548
1549 QLineEdit *title = new QLineEdit( itemData.name() );
1550
1551 //htmlCode
1552 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML( );
1553 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1554 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1555
1556 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1557 QgsFeature previewFeature;
1558 mLayer->getFeatures().nextFeature( previewFeature );
1559
1560 //update preview on text change
1561 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [ = ]
1562 {
1563 htmlWrapper->setHtmlCode( htmlCode->text( ) );
1564 htmlWrapper->reinitWidget();
1565 htmlWrapper->setFeature( previewFeature );
1566 } );
1567
1568 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1569 expressionWidget->setButtonVisible( false );
1570 expressionWidget->registerExpressionContextGenerator( this );
1571 expressionWidget->setLayer( mLayer );
1572 QToolButton *addFieldButton = new QToolButton();
1573 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1574
1575 QToolButton *editExpressionButton = new QToolButton();
1576 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1577 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1578
1579 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1580 {
1581 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1582 if ( !expression.isEmpty() )
1583 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1584 } );
1585
1586 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1587 {
1588 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1589 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1591 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1592
1593 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1594 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1595 {
1596 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1597 if ( !expression.isEmpty() )
1598 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1599 }
1600 } );
1601
1602 layout->addWidget( new QLabel( tr( "Title" ) ) );
1603 layout->addWidget( title );
1604 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1605 layout->addWidget( expressionWidgetBox );
1606 expressionWidgetBox->setLayout( new QHBoxLayout );
1607 expressionWidgetBox->layout()->addWidget( expressionWidget );
1608 expressionWidgetBox->layout()->addWidget( addFieldButton );
1609 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1610 layout->addWidget( htmlCode );
1611 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1612 htmlPreviewBox->setLayout( new QGridLayout );
1613 htmlPreviewBox->setMinimumWidth( 400 );
1614 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1615 //emit to load preview for the first time
1616 emit htmlCode->textChanged();
1617 htmlLayout->addWidget( htmlPreviewBox );
1618
1619 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1620
1621 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1622 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1623
1624 mainLayout->addWidget( buttonBox );
1625
1626 if ( dlg.exec() )
1627 {
1629 htmlEdCfg.htmlCode = htmlCode->text();
1630 itemData.setName( title->text() );
1631 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1632 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1633
1634 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1635 item->setText( 0, title->text() );
1636 }
1637 break;
1638 }
1639
1641 {
1642 if ( mType == QgsAttributesDnDTree::Type::Drag )
1643 return;
1644 QDialog dlg;
1645 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1646
1647 QVBoxLayout *mainLayout = new QVBoxLayout();
1648 QHBoxLayout *textLayout = new QHBoxLayout();
1649 QVBoxLayout *layout = new QVBoxLayout();
1650 mainLayout->addLayout( textLayout );
1651 textLayout->addLayout( layout );
1652 dlg.setLayout( mainLayout );
1653 layout->addWidget( baseWidget );
1654
1655 QLineEdit *title = new QLineEdit( itemData.name() );
1656
1657 QgsCodeEditorHTML *text = new QgsCodeEditorHTML( );
1658 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1659 text->setText( itemData.textElementEditorConfiguration().text );
1660
1661 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1662 QgsFeature previewFeature;
1663 mLayer->getFeatures().nextFeature( previewFeature );
1664
1665 //update preview on text change
1666 connect( text, &QgsCodeEditorExpression::textChanged, this, [ = ]
1667 {
1668 textWrapper->setText( text->text( ) );
1669 textWrapper->reinitWidget();
1670 textWrapper->setFeature( previewFeature );
1671 } );
1672
1673 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1674 expressionWidget->setButtonVisible( false );
1675 expressionWidget->registerExpressionContextGenerator( this );
1676 expressionWidget->setLayer( mLayer );
1677 QToolButton *addFieldButton = new QToolButton();
1678 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1679
1680 QToolButton *editExpressionButton = new QToolButton();
1681 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1682 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1683
1684 connect( addFieldButton, &QAbstractButton::clicked, this, [ = ]
1685 {
1686 QString expression = expressionWidget->expression().trimmed();
1687 if ( !expression.isEmpty() )
1688 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1689 } );
1690 connect( editExpressionButton, &QAbstractButton::clicked, this, [ = ]
1691 {
1692 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1693
1695 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1696
1697 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1698 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1699 {
1700 QString expression = exprDlg.expressionText().trimmed();
1701 if ( !expression.isEmpty() )
1702 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1703 }
1704 } );
1705
1706 layout->addWidget( new QLabel( tr( "Title" ) ) );
1707 layout->addWidget( title );
1708 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1709 layout->addWidget( expressionWidgetBox );
1710 expressionWidgetBox->setLayout( new QHBoxLayout );
1711 expressionWidgetBox->layout()->addWidget( expressionWidget );
1712 expressionWidgetBox->layout()->addWidget( addFieldButton );
1713 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1714 layout->addWidget( text );
1715 QScrollArea *textPreviewBox = new QgsScrollArea();
1716 textPreviewBox->setLayout( new QGridLayout );
1717 textPreviewBox->setMinimumWidth( 400 );
1718 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1719 //emit to load preview for the first time
1720 emit text->textChanged();
1721 textLayout->addWidget( textPreviewBox );
1722
1723 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1724
1725 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1726 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1727
1728 mainLayout->addWidget( buttonBox );
1729
1730 if ( dlg.exec() )
1731 {
1733 textEdCfg.text = text->text();
1734 itemData.setName( title->text() );
1735 itemData.setTextElementEditorConfiguration( textEdCfg );
1736 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1737
1738 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1739 item->setText( 0, title->text() );
1740 }
1741 break;
1742 }
1743
1745 {
1746 if ( mType == QgsAttributesDnDTree::Type::Drag )
1747 return;
1748 QDialog dlg;
1749 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1750
1751 QVBoxLayout *mainLayout = new QVBoxLayout();
1752 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1753 QLineEdit *title = new QLineEdit( itemData.name() );
1754 mainLayout->addWidget( title );
1755
1756 QHBoxLayout *cbLayout = new QHBoxLayout( );
1757 mainLayout->addLayout( cbLayout );
1758 dlg.setLayout( mainLayout );
1759 QCheckBox *cb = new QCheckBox { &dlg };
1760 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1761 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1762 cbLayout->addWidget( cb );
1763
1764
1765 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1766
1767 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1768 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1769
1770 mainLayout->addWidget( buttonBox );
1771
1772 if ( dlg.exec() )
1773 {
1775 spacerEdCfg.drawLine = cb->isChecked();
1776 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1777 itemData.setShowLabel( false );
1778 itemData.setName( title->text() );
1779 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1780 item->setText( 0, title->text() );
1781 }
1782
1783 break;
1784 }
1785 }
1786}
1787
1789{
1790 QgsExpressionContext expContext;
1793
1794 if ( mLayer )
1795 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1796
1798 return expContext;
1799}
1800
1802{
1803 return mType;
1804}
1805
1807{
1808 mType = value;
1809}
1810
1812{
1813 QTreeWidgetItemIterator it( this );
1814 while ( *it )
1815 {
1817 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1818 {
1819 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1820 {
1821 // the selection is already good
1822 }
1823 else
1824 {
1825 clearSelection();
1826 ( *it )->setSelected( true );
1827 }
1828 return;
1829 }
1830 ++it;
1831 }
1832 clearSelection();
1833}
1834
1835
1836/*
1837 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1838 */
1839
1840QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1841{
1842 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1843 return stream;
1844}
1845
1846QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1847{
1848 QString name;
1849 QString displayName;
1850 quint32 type;
1851
1852 stream >> type >> name >> displayName;
1853
1855 data.setName( name );
1856 data.setDisplayName( displayName );
1857
1858 return stream;
1859}
1860
1865
1870
1875
1877{
1878 mLabelStyle = labelStyle;
1879}
1880
1882{
1883 return mShowLabel;
1884}
1885
1887{
1888 mShowLabel = showLabel;
1889}
1890
1895
1897{
1898 mVisibilityExpression = visibilityExpression;
1899}
1900
1905
1907{
1908 mCollapsedExpression = collapsedExpression;
1909}
1910
1915
1917{
1918 mRelationEditorConfiguration = relationEditorConfiguration;
1919}
1920
1925
1927{
1928 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1929}
1930
1931
1936
1938{
1939 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1940}
1941
1946
1948{
1949 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
1950}
1951
1953{
1954 return mBackgroundColor;
1955}
1956
1958{
1959 mBackgroundColor = backgroundColor;
1960}
1961
1966
1968{
1969 mTextElementEditorConfiguration = textElementEditorConfiguration;
1970}
1971
1972void QgsAttributesFormProperties::updatedFields()
1973{
1974 // Store configuration to insure changes made are kept after refreshing the list
1975 QMap<QString, FieldConfig> fieldConfigs;
1976 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1977 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1978 {
1979 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1980 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1981 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
1982 fieldConfigs[fieldName] = cfg;
1983 }
1984
1986
1987 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
1988 for ( int i = 0; i < fieldContainer->childCount(); i++ )
1989 {
1990 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
1991 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
1992 if ( fieldConfigs.contains( fieldName ) )
1993 {
1994 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
1995 }
1996 }
1997}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5016
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5046
@ 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:5031
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ AutoGenerated
Autogenerate a simple tabular layout for the form.
@ UiFile
Load a .ui file for the layout. Needs to be configured.
@ Action
A layer action element.
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element.
@ SpacerElement
A spacer element.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Returns a list of actions that are available in the given action scope.
QgsAction action(QUuid id) const
Gets an action by its id.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:37
Dialog to add a tab or group of attributes.
QPair< QString, QTreeWidgetItem * > ContainerPair
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This element will load a layer action onto the form.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
virtual void addChildElement(QgsAttributeEditorElement *element)
Add a child element to this container.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
void setColumnCount(int columnCount)
Set the number of columns in this group.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
The visibility expression is used in the attribute form to show or hide this container based on an ex...
QgsOptionalExpression collapsedExpression() const
The collapsed expression is used in the attribute form to set the collapsed status of the group box c...
bool collapsed() const
For group box containers returns true if this group box is collapsed.
Qgis::AttributeEditorContainerType type() const
Returns the container type.
void setType(Qgis::AttributeEditorContainerType type)
Sets the container type.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
The collapsed expression is used in the attribute form to set the collapsed status of the group box o...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
Returns the background color of the container.
void setCollapsed(bool collapsed)
For group box containers sets if this group box is collapsed.
int columnCount() const
Gets the number of columns in this group.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color to backgroundColor.
This is an abstract base class for any elements of a drag and drop form.
void setHorizontalStretch(int stretch)
Sets the horizontal stretch factor for the element.
LabelStyle labelStyle() const
Returns the label style.
void setLabelStyle(const LabelStyle &labelStyle)
Sets the labelStyle.
Qgis::AttributeEditorType type() const
The type of this element.
int verticalStretch() const
Returns the vertical stretch factor for the element.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString name() const
Returns the name of this element.
void setVerticalStretch(int stretch)
Sets the vertical stretch factor for the element.
void setShowLabel(bool showLabel)
Controls if this element should be labeled with a title (field, relation or groupname).
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
This element will load a field's widget onto the form.
An attribute editor widget that will represent arbitrary HTML code.
QString htmlCode() const
The Html code that will be represented within this widget.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code that will be represented within this widget to htmlCode.
An attribute editor widget that will represent arbitrary QML code.
QString qmlCode() const
The QML code that will be represented within this widget.
void setQmlCode(const QString &qmlCode)
Sets the QML code that will be represented within this widget to qmlCode.
This element will load a relation editor onto the form.
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
void setRelationWidgetTypeId(const QString &relationWidgetTypeId)
Sets the relation widget type.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
QVariantMap relationEditorConfiguration() const
Returns the relation editor widget configuration.
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
QString relationWidgetTypeId() const
Returns the current relation widget type id.
void setRelationEditorConfiguration(const QVariantMap &config)
Sets the relation editor configuration.
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
QString label() const
Determines the label of this element.
An attribute editor widget that will represent a spacer.
void setDrawLine(bool drawLine)
Sets a flag to define if the spacer element will contain an horizontal line.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
An attribute editor widget that will represent arbitrary text code.
void setText(const QString &text)
Sets the text that will be represented within this widget to text.
QString text() const
The Text that will be represented within this widget.
This class overrides mime type handling to be able to work with the drag and drop attribute editor.
void setType(QgsAttributesDnDTree::Type value)
QTreeWidgetItem * addItem(QTreeWidgetItem *parent, QgsAttributesFormProperties::DnDTreeItemData data, int index=-1, const QIcon &icon=QIcon())
Adds a new item to a parent.
void dropEvent(QDropEvent *event) override
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QTreeWidgetItem * addContainer(QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type)
Adds a new container to parent.
QStringList mimeTypes() const override
QgsAttributesDnDTree(QgsVectorLayer *layer, QWidget *parent=nullptr)
QMimeData * mimeData(const QList< QTreeWidgetItem * > &items) const override
bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) override
void dragMoveEvent(QDragMoveEvent *event) override
Is called when mouse is moved over attributes tree before a drop event.
void selectFirstMatchingItem(const QgsAttributesFormProperties::DnDTreeItemData &data)
A dialog for configuring the Python init code handling for attribute forms.
QString initFilePath() const
Returns the file path for the file containing the init code.
void setCodeSource(Qgis::AttributeFormPythonInitCodeSource source)
Sets the Python init code source.
void setInitFilePath(const QString &initFilePath)
Sets the file path for the file containing the init code.
void setInitFunction(const QString &initFunction)
Sets the name of the init function.
QString initFunction() const
Returns the name of the init function.
void setInitCode(const QString &initCode)
Sets the init code contents.
Qgis::AttributeFormPythonInitCodeSource codeSource() const
Returns the Python init code source.
QString initCode() const
Returns the init code contents.
void setRelationEditorConfiguration(RelationEditorConfiguration relationEditorConfiguration)
void setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
int verticalStretch() const
Returns the vertical stretch factor for the element.
void setTextElementEditorConfiguration(const TextElementEditorConfiguration &textElementEditorConfiguration)
Sets the editor configuration for text element to textElementEditorConfiguration.
SpacerElementEditorConfiguration spacerElementEditorConfiguration() const
Returns the spacer element configuration.
void setQmlElementEditorConfiguration(QmlElementEditorConfiguration qmlElementEditorConfiguration)
Qgis::AttributeEditorContainerType containerType() const
Returns the container type.
const QgsAttributeEditorElement::LabelStyle labelStyle() const
Returns the label style.
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
Sets the optional visibilityExpression that dynamically controls the visibility status of a container...
QgsOptionalExpression collapsedExpression() const
Returns the optional expression that dynamically controls the collapsed status of a group box contain...
@ WidgetType
In the widget tree, the type of widget.
int horizontalStretch() const
Returns the horizontal stretch factor for the element.
void setContainerType(Qgis::AttributeEditorContainerType type)
Sets the container type.
TextElementEditorConfiguration textElementEditorConfiguration() const
Returns the editor configuration for text element.
void setCollapsedExpression(const QgsOptionalExpression &collapsedExpression)
Sets the optional collapsedExpression that dynamically controls the collapsed status of a group box c...
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
void setHtmlElementEditorConfiguration(HtmlElementEditorConfiguration htmlElementEditorConfiguration)
bool collapsed() const
For group box containers returns if this group box is collapsed.
RelationEditorConfiguration relationEditorConfiguration() const
QgsAttributeEditorElement * createAttributeEditorWidget(QTreeWidgetItem *item, QgsAttributeEditorElement *parent, bool isTopLevel=false)
Creates a new attribute editor element based on the definition stored in item.
QgsAttributeFormContainerEdit * mAttributeContainerEdit
QgsAttributesFormProperties(QgsVectorLayer *layer, QWidget *parent=nullptr)
QgsAttributeTypeDialog * mAttributeTypeDialog
void store()
Stores currently opened widget configuration.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsAttributeWidgetEdit * mAttributeWidgetEdit
void apply()
Applies the attribute from properties to the vector layer.
A HTML editor based on QScintilla2.
A text editor based on QScintilla2.
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
The QgsDefaultValue class provides a container for managing client side default values for fields.
Contains configuration settings for an editor form.
Qgis::AttributeFormPythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
bool reuseLastValue(int index) const
If this returns true, the widget at the given index will remember the previously entered value from t...
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
QString initCode() const
Gets Python code for edit form initialization.
void setInitFunction(const QString &function)
Set Python function for edit form initialization.
void addTab(QgsAttributeEditorElement *data)
Adds a new element to the invisible root container in the layout.
QString initFilePath() const
Gets Python external file path for edit form initialization.
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
void setSuppress(Qgis::AttributeFormSuppression s)
Sets type of feature form pop-up suppression after feature creation (overrides app setting)
bool setWidgetConfig(const QString &widgetName, const QVariantMap &config)
Set the editor widget config for a widget which is not for a simple field.
void setLayout(Qgis::AttributeFormLayout editorLayout)
Sets the active layout style for the attribute editor for this layer.
void clearTabs()
Clears all the tabs for the attribute editor form with EditorLayout::TabLayout.
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
Qgis::AttributeFormSuppression suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting)
QString uiForm() const
Returns the path or URL to the .ui form.
void setLabelOnTop(int idx, bool onTop)
If this is set to true, the widget at the given index will receive its label on the previous line whi...
void setInitCodeSource(Qgis::AttributeFormPythonInitCodeSource initCodeSource)
Sets if Python code shall be used for edit form initialization and its origin.
void setDataDefinedFieldProperties(const QString &fieldName, const QgsPropertyCollection &properties)
Set data defined properties for fieldName to properties.
QString initFunction() const
Gets Python function for edit form initialization.
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
void setReuseLastValue(int index, bool reuse)
Sets whether the widget at the given index will remember the previously entered value from this QGIS ...
void setUiForm(const QString &ui)
Set path to the .ui form.
void setInitFilePath(const QString &filePath)
Set Python external file path for edit form initialization.
void setInitCode(const QString &code)
Set Python code for edit form initialization.
Qgis::AttributeFormLayout layout() const
Gets the active layout style for the attribute editor for this layer.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
A generic dialog for building expression strings.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QString findAndSelectActiveExpression(QgsCodeEditor *editor, const QString &pattern=QString())
Find the expression under the cursor in the given editor and select it.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Stores information about constraints which may be present on a field.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
void setConstraintStrength(Constraint constraint, ConstraintStrength strength)
Sets the strength of a constraint.
void setConstraintExpression(const QString &expression, const QString &description=QString())
Set the constraint expression for the field.
@ ConstraintOriginProvider
Constraint was set at data provider.
ConstraintStrength constraintStrength(Constraint constraint) const
Returns the strength of a field constraint, or ConstraintStrengthNotSet if the constraint is not pres...
ConstraintOrigin constraintOrigin(Constraint constraint) const
Returns the origin of a field constraint, or ConstraintOriginNotSet if the constraint is not present ...
QString constraintExpression() const
Returns the constraint expression for the field, if set.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
@ ConstraintExpression
Field has an expression constraint set. See constraintExpression().
QString constraintDescription() const
Returns the descriptive name for the constraint expression.
void setConstraint(Constraint constraint, ConstraintOrigin origin=ConstraintOriginLayer)
Sets a constraint on the field.
QFlags< Constraint > Constraints
The QgsFieldExpressionWidget class creates a widget to choose fields and edit expressions It contains...
void setButtonVisible(bool visible)
Set the visibility of the button.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the field's split policy, which indicates how field values should be handled during a split o...
Definition qgsfield.cpp:754
QString alias
Definition qgsfield.h:63
Qgis::FieldDuplicatePolicy duplicatePolicy() const
Returns the field's duplicate policy, which indicates how field values should be handled during a dup...
Definition qgsfield.cpp:764
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:93
Wraps a QQuickWidget to display HTML code.
void reinitWidget()
Clears the content and makes new initialization.
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
void setFeature(const QgsFeature &feature) override
An expression with an additional enabled flag.
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
int count() const
Returns the number of properties contained within the collection.
Wraps a QQuickWidget to display QML code.
void setFeature(const QgsFeature &feature) override
void reinitWidget()
Clears the content and makes new initialization.
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QString name
Definition qgsrelation.h:50
QString id
Definition qgsrelation.h:47
A QScrollArea subclass with improved scrolling behavior.
static const QgsSettingsEntryBool * settingsDigitizingDisableEnterAttributeValuesDialog
Settings entry digitizing disable enter attribute values dialog.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Wraps a label widget to display text.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
void setFeature(const QgsFeature &feature) override
Represents a vector layer which manages a vector based data sets.
QgsDefaultValue defaultValueDefinition(int index) const
Returns the definition of the expression used when calculating the default value for a field.
void setFieldConstraint(int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthHard)
Sets a constraint for a specified field index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void removeFieldConstraint(int index, QgsFieldConstraints::Constraint constraint)
Removes a constraint for a specified field index.
void setDefaultValueDefinition(int index, const QgsDefaultValue &definition)
Sets the definition of the expression to use when calculating the default value for a field.
void setEditFormConfig(const QgsEditFormConfig &editFormConfig)
Sets the editFormConfig (configuration) of the form used to represent this vector layer.
void setEditorWidgetSetup(int index, const QgsEditorWidgetSetup &setup)
Sets the editor widget setup for the field at the specified index.
void setConstraintExpression(int index, const QString &expression, const QString &description=QString())
Sets the constraint expression for the specified field index.
void setFieldDuplicatePolicy(int index, Qgis::FieldDuplicatePolicy policy)
Sets a duplicate policy for the field with the specified index.
QgsActionManager * actions()
Returns all layer actions defined on this layer.
void setFieldAlias(int index, const QString &aliasString)
Sets an alias (a display name) for attributes to display in dialogs.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig
void setFieldSplitPolicy(int index, Qgis::FieldDomainSplitPolicy policy)
Sets a split policy for the field with the specified index.
QWidget * widget()
Access the widget managed by this wrapper.
QDataStream & operator>>(QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data)
QDataStream & operator<<(QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data)
#define QgsDebugError(str)
Definition qgslogger.h:38
The TabStyle struct defines color and font overrides for form fields, tabs and groups labels.