QGIS API Documentation 3.41.0-Master (d2aaa9c6e02)
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 "moc_qgsattributesformproperties.cpp"
22#include "qgsattributetypedialog.h"
23#include "qgsattributeformcontaineredit.h"
24#include "qgsattributewidgetedit.h"
26#include "qgsqmlwidgetwrapper.h"
28#include "qgsapplication.h"
29#include "qgscodeeditor.h"
30#include "qgscodeeditorhtml.h"
40#include "qgsgui.h"
43#include "qgsfieldcombobox.h"
44#include "qgsexpressionfinder.h"
46#include "qgshelp.h"
47#include "qgsxmlutils.h"
48
50 : QWidget( parent )
51 , mLayer( layer )
52{
53 if ( !layer )
54 return;
55
56 setupUi( this );
57
58 mEditorLayoutComboBox->addItem( tr( "Autogenerate" ), QVariant::fromValue( Qgis::AttributeFormLayout::AutoGenerated ) );
59 mEditorLayoutComboBox->addItem( tr( "Drag and Drop Designer" ), QVariant::fromValue( Qgis::AttributeFormLayout::DragAndDrop ) );
60 mEditorLayoutComboBox->addItem( tr( "Provide ui-file" ), QVariant::fromValue( Qgis::AttributeFormLayout::UiFile ) );
61
62 // available widgets tree
63 QGridLayout *availableWidgetsWidgetLayout = new QGridLayout;
65 availableWidgetsWidgetLayout->addWidget( mAvailableWidgetsTree );
66 availableWidgetsWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
67 mAvailableWidgetsWidget->setLayout( availableWidgetsWidgetLayout );
68 mAvailableWidgetsTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
69 mAvailableWidgetsTree->setHeaderLabels( QStringList() << tr( "Available Widgets" ) );
71 mAvailableWidgetsTree->setContextMenuPolicy( Qt::CustomContextMenu );
72
73 // form layout tree
74 QGridLayout *formLayoutWidgetLayout = new QGridLayout;
76 mFormLayoutWidget->setLayout( formLayoutWidgetLayout );
77 formLayoutWidgetLayout->addWidget( mFormLayoutTree );
78 formLayoutWidgetLayout->setContentsMargins( 0, 0, 0, 0 );
79 mFormLayoutTree->setHeaderLabels( QStringList() << tr( "Form Layout" ) );
81
82 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
83 connect( mAvailableWidgetsTree, &QWidget::customContextMenuRequested, this, &QgsAttributesFormProperties::onContextMenuRequested );
84 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
85 connect( mAddTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::addContainer );
86 connect( mRemoveTabOrGroupButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::removeTabOrGroupButton );
87 connect( mInvertSelectionButton, &QAbstractButton::clicked, this, &QgsAttributesFormProperties::onInvertSelectionButtonClicked );
88 connect( mEditorLayoutComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged );
89 connect( pbnSelectEditForm, &QToolButton::clicked, this, &QgsAttributesFormProperties::pbnSelectEditForm_clicked );
90 connect( mTbInitCode, &QPushButton::clicked, this, &QgsAttributesFormProperties::mTbInitCode_clicked );
91
92 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
93 if ( !mBlockUpdates )
94 updatedFields();
95 } );
96
97 // Context menu and children actions
98 mAvailableWidgetsTreeContextMenu = new QMenu( this );
99 mActionCopyWidgetConfiguration = new QAction( tr( "Copy widget configuration" ), this );
100 mActionPasteWidgetConfiguration = new QAction( tr( "Paste widget configuration" ), this );
101
102 connect( mActionCopyWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::copyWidgetConfiguration );
103 connect( mActionPasteWidgetConfiguration, &QAction::triggered, this, &QgsAttributesFormProperties::pasteWidgetConfiguration );
104
105 mAvailableWidgetsTreeContextMenu->addAction( mActionCopyWidgetConfiguration );
106 mAvailableWidgetsTreeContextMenu->addAction( mActionPasteWidgetConfiguration );
107
108 mMessageBar = new QgsMessageBar();
109 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
110 gridLayout->addWidget( mMessageBar, 0, 0 );
111}
112
122
124{
125 mAvailableWidgetsTree->clear();
126 mAvailableWidgetsTree->setSortingEnabled( false );
127 mAvailableWidgetsTree->setSelectionBehavior( QAbstractItemView::SelectRows );
128 mAvailableWidgetsTree->setAcceptDrops( false );
129 mAvailableWidgetsTree->setDragDropMode( QAbstractItemView::DragOnly );
130
131 //load Fields
132
133 DnDTreeItemData catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Fields" ), QStringLiteral( "Fields" ) );
134 QTreeWidgetItem *catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
135
136 const QgsFields fields = mLayer->fields();
137 for ( int i = 0; i < fields.size(); ++i )
138 {
139 const QgsField field = fields.at( i );
140 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, field.name(), field.name() );
141 itemData.setShowLabel( true );
142
143 FieldConfig cfg( mLayer, i );
144
145 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData, -1, fields.iconForField( i, true ) );
146
147 item->setData( 0, FieldConfigRole, cfg );
148 item->setData( 0, FieldNameRole, field.name() );
149
150 QString tooltip;
151 if ( !field.alias().isEmpty() )
152 tooltip = tr( "%1 (%2)" ).arg( field.name(), field.alias() );
153 else
154 tooltip = field.name();
155 item->setToolTip( 0, tooltip );
156 }
157 catitem->setExpanded( true );
158
159 //load Relations
160 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Relations" ), tr( "Relations" ) );
161 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
162
163 const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
164
165 for ( const QgsRelation &relation : relations )
166 {
167 QString name;
168 const QgsPolymorphicRelation polymorphicRelation = relation.polymorphicRelation();
169 if ( polymorphicRelation.isValid() )
170 {
171 name = QStringLiteral( "%1 (%2)" ).arg( relation.name(), polymorphicRelation.name() );
172 }
173 else
174 {
175 name = relation.name();
176 }
177 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relation.id(), name );
178 itemData.setShowLabel( true );
179 QTreeWidgetItem *item = mAvailableWidgetsTree->addItem( catitem, itemData );
180 item->setData( 0, FieldNameRole, relation.id() );
181 }
182 catitem->setExpanded( true );
183
184 // Form actions
185 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Actions" ), tr( "Actions" ) );
186 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
187
188 const QList<QgsAction> actions { mLayer->actions()->actions() };
189
190 for ( const auto &action : std::as_const( actions ) )
191 {
192 if ( action.isValid() && action.runable() && ( action.actionScopes().contains( QStringLiteral( "Feature" ) ) || action.actionScopes().contains( QStringLiteral( "Layer" ) ) ) )
193 {
194 const QString actionTitle { action.shortTitle().isEmpty() ? action.name() : action.shortTitle() };
195 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), actionTitle );
196 itemData.setShowLabel( true );
197 mAvailableWidgetsTree->addItem( catitem, itemData );
198 }
199 }
200
201 // QML/HTML widget
202 catItemData = DnDTreeItemData( DnDTreeItemData::WidgetType, QStringLiteral( "Other" ), tr( "Other Widgets" ) );
203 catitem = mAvailableWidgetsTree->addItem( mAvailableWidgetsTree->invisibleRootItem(), catItemData );
204
205 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, QStringLiteral( "QML Widget" ), tr( "QML Widget" ) );
206 itemData.setShowLabel( true );
207 mAvailableWidgetsTree->addItem( catitem, itemData );
208
209 auto itemDataHtml { DnDTreeItemData( DnDTreeItemData::HtmlWidget, QStringLiteral( "HTML Widget" ), tr( "HTML Widget" ) ) };
210 itemDataHtml.setShowLabel( true );
211 mAvailableWidgetsTree->addItem( catitem, itemDataHtml );
212
213 auto itemDataText { DnDTreeItemData( DnDTreeItemData::TextWidget, QStringLiteral( "Text Widget" ), tr( "Text Widget" ) ) };
214 itemDataText.setShowLabel( true );
215 mAvailableWidgetsTree->addItem( catitem, itemDataText );
216
217 auto itemDataSpacer { DnDTreeItemData( DnDTreeItemData::SpacerWidget, QStringLiteral( "Spacer Widget" ), tr( "Spacer Widget" ) ) };
218 itemDataSpacer.setShowLabel( false );
219 mAvailableWidgetsTree->addItem( catitem, itemDataSpacer );
220
221 catitem->setExpanded( true );
222}
223
225{
226 // tabs and groups info
227 mFormLayoutTree->clear();
228 mFormLayoutTree->setSortingEnabled( false );
229 mFormLayoutTree->setSelectionBehavior( QAbstractItemView::SelectRows );
230 mFormLayoutTree->setSelectionMode( QAbstractItemView::SelectionMode::ExtendedSelection );
231 mFormLayoutTree->setAcceptDrops( true );
232 mFormLayoutTree->setDragDropMode( QAbstractItemView::DragDrop );
233
234 const auto constTabs = mLayer->editFormConfig().tabs();
235 for ( QgsAttributeEditorElement *wdg : constTabs )
236 {
237 loadAttributeEditorTreeItem( wdg, mFormLayoutTree->invisibleRootItem(), mFormLayoutTree );
238 }
239}
240
241
243{
245 {
246 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
247 }
248 else
249 {
250 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature (global settings)" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Default ) );
251 }
252 mFormSuppressCmbBx->addItem( tr( "Hide Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::On ) );
253 mFormSuppressCmbBx->addItem( tr( "Show Form on Add Feature" ), QVariant::fromValue( Qgis::AttributeFormSuppression::Off ) );
254
255 mFormSuppressCmbBx->setCurrentIndex( mFormSuppressCmbBx->findData( QVariant::fromValue( mLayer->editFormConfig().suppress() ) ) );
256}
257
264
266{
267 mEditorLayoutComboBox->setCurrentIndex( mEditorLayoutComboBox->findData( QVariant::fromValue( mLayer->editFormConfig().layout() ) ) );
268
269 mEditorLayoutComboBox_currentIndexChanged( mEditorLayoutComboBox->currentIndex() );
270
272 mEditFormLineEdit->setText( cfg.uiForm() );
273}
274
276{
278
279 mInitCodeSource = cfg.initCodeSource();
280 mInitFunction = cfg.initFunction();
281 mInitFilePath = cfg.initFilePath();
282 mInitCode = cfg.initCode();
283
284 if ( mInitCode.isEmpty() )
285 {
286 mInitCode.append( tr( "# -*- coding: utf-8 -*-\n\"\"\"\n"
287 "QGIS forms can have a Python function that is called when the form is\n"
288 "opened.\n"
289 "\n"
290 "Use this function to add extra logic to your forms.\n"
291 "\n"
292 "Enter the name of the function in the \"Python Init function\"\n"
293 "field.\n"
294 "An example follows:\n"
295 "\"\"\"\n"
296 "from qgis.PyQt.QtWidgets import QWidget\n\n"
297 "def my_form_open(dialog, layer, feature):\n"
298 " geom = feature.geometry()\n"
299 " control = dialog.findChild(QWidget, \"MyLineEdit\")\n" ) );
300 }
301}
302
303void QgsAttributesFormProperties::loadAttributeTypeDialog()
304{
305 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
306 return;
307
308 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
309
310 const FieldConfig cfg = item->data( 0, FieldConfigRole ).value<FieldConfig>();
311 const QString fieldName = item->data( 0, FieldNameRole ).toString();
312 const int index = mLayer->fields().indexOf( fieldName );
313
314 if ( index < 0 )
315 return;
316
317 mAttributeTypeDialog = new QgsAttributeTypeDialog( mLayer, index, mAttributeTypeFrame );
318
319 loadAttributeTypeDialogFromConfiguration( cfg );
320
321 mAttributeTypeDialog->setDefaultValueExpression( mLayer->defaultValueDefinition( index ).expression() );
322 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( mLayer->defaultValueDefinition( index ).applyOnUpdate() );
323
324 mAttributeTypeDialog->layout()->setContentsMargins( 0, 0, 0, 0 );
325 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
326
327 mAttributeTypeFrame->layout()->addWidget( mAttributeTypeDialog );
328}
329
330void QgsAttributesFormProperties::loadAttributeTypeDialogFromConfiguration( const FieldConfig config )
331{
332 const QgsFieldConstraints constraints = config.mFieldConstraints;
333
334 mAttributeTypeDialog->setAlias( config.mAlias );
335 mAttributeTypeDialog->setDataDefinedProperties( config.mDataDefinedProperties );
336 mAttributeTypeDialog->setComment( config.mComment );
337 mAttributeTypeDialog->setFieldEditable( config.mEditable );
338 mAttributeTypeDialog->setLabelOnTop( config.mLabelOnTop );
339 mAttributeTypeDialog->setReuseLastValues( config.mReuseLastValues );
344 mAttributeTypeDialog->setSplitPolicy( config.mSplitPolicy );
345 mAttributeTypeDialog->setDuplicatePolicy( config.mDuplicatePolicy );
346
349 providerConstraints |= QgsFieldConstraints::ConstraintNotNull;
351 providerConstraints |= QgsFieldConstraints::ConstraintUnique;
353 providerConstraints |= QgsFieldConstraints::ConstraintExpression;
354 mAttributeTypeDialog->setProviderConstraints( providerConstraints );
355
356 mAttributeTypeDialog->setConstraintExpression( constraints.constraintExpression() );
357 mAttributeTypeDialog->setConstraintExpressionDescription( constraints.constraintDescription() );
359
360 // Make sure the widget is refreshed, even if
361 // the new widget type matches the current one
362 mAttributeTypeDialog->setEditorWidgetConfig( config.mEditorWidgetConfig );
363 mAttributeTypeDialog->setEditorWidgetType( config.mEditorWidgetType, true );
364}
365
366void QgsAttributesFormProperties::storeAttributeTypeDialog()
367{
369 return;
370
371 if ( mAttributeTypeDialog->fieldIdx() < 0 || mAttributeTypeDialog->fieldIdx() >= mLayer->fields().count() )
372 return;
373
374 FieldConfig cfg;
375
376 cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
377 cfg.mEditable = mAttributeTypeDialog->fieldEditable();
378 cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
379 cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
380 cfg.mAlias = mAttributeTypeDialog->alias();
381 cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();
382
383 QgsFieldConstraints constraints;
384 if ( mAttributeTypeDialog->notNull() )
385 {
387 }
388 else if ( mAttributeTypeDialog->notNullFromProvider() )
389 {
391 }
392
393 if ( mAttributeTypeDialog->unique() )
394 {
396 }
397 else if ( mAttributeTypeDialog->uniqueFromProvider() )
398 {
400 }
401
402 if ( !mAttributeTypeDialog->constraintExpression().isEmpty() )
403 {
405 }
406
407 constraints.setConstraintExpression( mAttributeTypeDialog->constraintExpression(), mAttributeTypeDialog->constraintExpressionDescription() );
408
412
413 // The call to mLayer->setDefaultValueDefinition will possibly emit updatedFields
414 // which will set mAttributeTypeDialog to nullptr so we need to store any value before calling it
415 cfg.mFieldConstraints = constraints;
416 cfg.mEditorWidgetType = mAttributeTypeDialog->editorWidgetType();
417 cfg.mEditorWidgetConfig = mAttributeTypeDialog->editorWidgetConfig();
418 cfg.mSplitPolicy = mAttributeTypeDialog->splitPolicy();
419 cfg.mDuplicatePolicy = mAttributeTypeDialog->duplicatePolicy();
420
421 const int fieldIndex = mAttributeTypeDialog->fieldIdx();
422 mLayer->setDefaultValueDefinition( fieldIndex, QgsDefaultValue( mAttributeTypeDialog->defaultValueExpression(), mAttributeTypeDialog->applyDefaultValueOnUpdate() ) );
423
424 const QString fieldName = mLayer->fields().at( fieldIndex ).name();
425
426 for ( auto itemIt = QTreeWidgetItemIterator( mAvailableWidgetsTree ); *itemIt; ++itemIt )
427 {
428 QTreeWidgetItem *item = *itemIt;
429 if ( item->data( 0, FieldNameRole ).toString() == fieldName )
430 item->setData( 0, FieldConfigRole, QVariant::fromValue<FieldConfig>( cfg ) );
431 }
432}
433
434void QgsAttributesFormProperties::storeAttributeWidgetEdit()
435{
437 return;
438
439 mAttributeWidgetEdit->updateItemData();
440}
441
442void QgsAttributesFormProperties::loadAttributeWidgetEdit()
443{
444 if ( mFormLayoutTree->selectedItems().count() != 1 )
445 return;
446
447 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
448 mAttributeWidgetEdit = new QgsAttributeWidgetEdit( currentItem, this );
449 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
450 mAttributeTypeFrame->layout()->addWidget( mAttributeWidgetEdit );
451}
452
453void QgsAttributesFormProperties::loadInfoWidget( const QString &infoText )
454{
455 mInfoTextWidget = new QLabel( infoText );
456 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
457 mAttributeTypeFrame->layout()->addWidget( mInfoTextWidget );
458}
459
460void QgsAttributesFormProperties::storeAttributeContainerEdit()
461{
463 return;
464
465 mAttributeContainerEdit->updateItemData();
466}
467
468void QgsAttributesFormProperties::loadAttributeContainerEdit()
469{
470 if ( mFormLayoutTree->selectedItems().count() != 1 )
471 return;
472
473 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().at( 0 );
474 mAttributeContainerEdit = new QgsAttributeFormContainerEdit( currentItem, mLayer, this );
475 mAttributeContainerEdit->registerExpressionContextGenerator( this );
476 mAttributeContainerEdit->layout()->setContentsMargins( 0, 0, 0, 0 );
477 mAttributeTypeFrame->layout()->setContentsMargins( 0, 0, 0, 0 );
478 mAttributeTypeFrame->layout()->addWidget( mAttributeContainerEdit );
479}
480
481QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAttributeEditorElement *const widgetDef, QTreeWidgetItem *parent, QgsAttributesDnDTree *tree )
482{
483 auto setCommonProperties = [widgetDef]( DnDTreeItemData &itemData ) {
484 itemData.setShowLabel( widgetDef->showLabel() );
485 itemData.setLabelStyle( widgetDef->labelStyle() );
486 itemData.setHorizontalStretch( widgetDef->horizontalStretch() );
487 itemData.setVerticalStretch( widgetDef->verticalStretch() );
488 };
489
490 QTreeWidgetItem *newWidget = nullptr;
491 switch ( widgetDef->type() )
492 {
494 {
495 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Field, widgetDef->name(), widgetDef->name() );
496 setCommonProperties( itemData );
497 newWidget = tree->addItem( parent, itemData );
498 break;
499 }
500
502 {
503 const QgsAttributeEditorAction *actionEditor = static_cast<const QgsAttributeEditorAction *>( widgetDef );
504 const QgsAction action { actionEditor->action( mLayer ) };
505 if ( action.isValid() )
506 {
507 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Action, action.id().toString(), action.shortTitle().isEmpty() ? action.name() : action.shortTitle() );
508 setCommonProperties( itemData );
509 newWidget = tree->addItem( parent, itemData );
510 }
511 else
512 {
513 QgsDebugError( QStringLiteral( "Invalid form action" ) );
514 }
515 break;
516 }
517
519 {
520 const QgsAttributeEditorRelation *relationEditor = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
521 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::Relation, relationEditor->relation().id(), relationEditor->relation().name() );
522 setCommonProperties( itemData );
523
524 RelationEditorConfiguration relEdConfig;
525 // relEdConfig.buttons = relationEditor->visibleButtons();
526 relEdConfig.mRelationWidgetType = relationEditor->relationWidgetTypeId();
527 relEdConfig.mRelationWidgetConfig = relationEditor->relationEditorConfiguration();
528 relEdConfig.nmRelationId = relationEditor->nmRelationId();
529 relEdConfig.forceSuppressFormPopup = relationEditor->forceSuppressFormPopup();
530 relEdConfig.label = relationEditor->label();
531 itemData.setRelationEditorConfiguration( relEdConfig );
532 newWidget = tree->addItem( parent, itemData );
533 break;
534 }
535
537 {
538 DnDTreeItemData itemData( DnDTreeItemData::Container, widgetDef->name(), widgetDef->name() );
539
540 const QgsAttributeEditorContainer *container = static_cast<const QgsAttributeEditorContainer *>( widgetDef );
541 if ( !container )
542 break;
543
544 itemData.setColumnCount( container->columnCount() );
545 itemData.setContainerType( container->type() );
546 itemData.setBackgroundColor( container->backgroundColor() );
547 itemData.setVisibilityExpression( container->visibilityExpression() );
548 itemData.setCollapsedExpression( container->collapsedExpression() );
549 itemData.setCollapsed( container->collapsed() );
550
551 setCommonProperties( itemData );
552
553 newWidget = tree->addItem( parent, itemData );
554
555 const QList<QgsAttributeEditorElement *> children = container->children();
556 for ( QgsAttributeEditorElement *wdg : children )
557 {
558 loadAttributeEditorTreeItem( wdg, newWidget, tree );
559 }
560 break;
561 }
562
564 {
565 const QgsAttributeEditorQmlElement *qmlElementEditor = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
566 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::QmlWidget, widgetDef->name(), widgetDef->name() );
567 QmlElementEditorConfiguration qmlEdConfig;
568 qmlEdConfig.qmlCode = qmlElementEditor->qmlCode();
569 itemData.setQmlElementEditorConfiguration( qmlEdConfig );
570 setCommonProperties( itemData );
571 newWidget = tree->addItem( parent, itemData );
572 break;
573 }
574
576 {
577 const QgsAttributeEditorHtmlElement *htmlElementEditor = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
578 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::HtmlWidget, widgetDef->name(), widgetDef->name() );
579 HtmlElementEditorConfiguration htmlEdConfig;
580 htmlEdConfig.htmlCode = htmlElementEditor->htmlCode();
581 itemData.setHtmlElementEditorConfiguration( htmlEdConfig );
582 setCommonProperties( itemData );
583 newWidget = tree->addItem( parent, itemData );
584 break;
585 }
586
588 {
589 const QgsAttributeEditorTextElement *textElementEditor = static_cast<const QgsAttributeEditorTextElement *>( widgetDef );
590 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::TextWidget, widgetDef->name(), widgetDef->name() );
591 TextElementEditorConfiguration textEdConfig;
592 textEdConfig.text = textElementEditor->text();
593 itemData.setTextElementEditorConfiguration( textEdConfig );
594 setCommonProperties( itemData );
595 newWidget = tree->addItem( parent, itemData );
596 break;
597 }
598
600 {
601 const QgsAttributeEditorSpacerElement *spacerElementEditor = static_cast<const QgsAttributeEditorSpacerElement *>( widgetDef );
602 DnDTreeItemData itemData = DnDTreeItemData( DnDTreeItemData::SpacerWidget, widgetDef->name(), widgetDef->name() );
603 SpacerElementEditorConfiguration spacerEdConfig;
604 spacerEdConfig.drawLine = spacerElementEditor->drawLine();
605 itemData.setSpacerElementEditorConfiguration( spacerEdConfig );
606 setCommonProperties( itemData );
607 itemData.setShowLabel( false );
608 newWidget = tree->addItem( parent, itemData );
609 break;
610 }
611
613 {
614 QgsDebugError( QStringLiteral( "Not loading invalid attribute editor type..." ) );
615 break;
616 }
617 }
618
619 if ( newWidget )
620 newWidget->setExpanded( true );
621
622 return newWidget;
623}
624
625
626void QgsAttributesFormProperties::onAttributeSelectionChanged()
627{
628 disconnect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
629 loadAttributeSpecificEditor( mAvailableWidgetsTree, mFormLayoutTree );
630 connect( mFormLayoutTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onFormLayoutSelectionChanged );
631}
632
633void QgsAttributesFormProperties::onFormLayoutSelectionChanged()
634{
635 // when the selection changes in the DnD layout, sync the main tree
636 disconnect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
637 loadAttributeSpecificEditor( mFormLayoutTree, mAvailableWidgetsTree );
638 connect( mAvailableWidgetsTree, &QTreeWidget::itemSelectionChanged, this, &QgsAttributesFormProperties::onAttributeSelectionChanged );
639}
640
641void QgsAttributesFormProperties::loadAttributeSpecificEditor( QgsAttributesDnDTree *emitter, QgsAttributesDnDTree *receiver )
642{
643 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
644
646 storeAttributeWidgetEdit();
647
648 clearAttributeTypeFrame();
649
650 if ( emitter->selectedItems().count() != 1 )
651 {
652 receiver->clearSelection();
653 }
654 else
655 {
656 const DnDTreeItemData itemData = emitter->selectedItems().at( 0 )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
657 switch ( itemData.type() )
658 {
660 {
661 receiver->selectFirstMatchingItem( itemData );
663 {
664 loadAttributeWidgetEdit();
665 }
666 else
667 {
668 loadInfoWidget( tr( "This configuration is available in the Drag and Drop Designer" ) );
669 }
670 break;
671 }
673 {
674 receiver->selectFirstMatchingItem( itemData );
676 loadAttributeWidgetEdit();
677 loadAttributeTypeDialog();
678 break;
679 }
681 {
682 receiver->clearSelection();
683 loadAttributeContainerEdit();
684 break;
685 }
687 {
688 receiver->selectFirstMatchingItem( itemData );
689 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
690 loadInfoWidget( action.html() );
691 break;
692 }
697 {
699 {
700 loadInfoWidget( tr( "This configuration is available with double-click in the Drag and Drop Designer" ) );
701 }
702 else
703 {
704 loadInfoWidget( tr( "This configuration is available with double-click" ) );
705 }
706 receiver->clearSelection();
707 break;
708 }
710 {
711 receiver->clearSelection();
712 break;
713 }
714 }
715 }
716}
717
718void QgsAttributesFormProperties::clearAttributeTypeFrame()
719{
721 {
722 mAttributeTypeFrame->layout()->removeWidget( mAttributeWidgetEdit );
723 mAttributeWidgetEdit->deleteLater();
724 mAttributeWidgetEdit = nullptr;
725 }
727 {
728 mAttributeTypeFrame->layout()->removeWidget( mAttributeTypeDialog );
729 mAttributeTypeDialog->deleteLater();
730 mAttributeTypeDialog = nullptr;
731 }
733 {
734 mAttributeTypeFrame->layout()->removeWidget( mAttributeContainerEdit );
735 mAttributeContainerEdit->deleteLater();
736 mAttributeContainerEdit = nullptr;
737 }
738 if ( mInfoTextWidget )
739 {
740 mAttributeTypeFrame->layout()->removeWidget( mInfoTextWidget );
741 mInfoTextWidget->deleteLater();
742 mInfoTextWidget = nullptr;
743 }
744}
745
746void QgsAttributesFormProperties::onInvertSelectionButtonClicked( bool checked )
747{
748 Q_UNUSED( checked )
749 const auto selectedItemList { mFormLayoutTree->selectedItems() };
750 const auto rootItem { mFormLayoutTree->invisibleRootItem() };
751 for ( int i = 0; i < rootItem->childCount(); ++i )
752 {
753 rootItem->child( i )->setSelected( !selectedItemList.contains( rootItem->child( i ) ) );
754 }
755}
756
757void QgsAttributesFormProperties::addContainer()
758{
759 QList<QgsAddAttributeFormContainerDialog::ContainerPair> existingContainerList;
760
761 for ( QTreeWidgetItemIterator it( mFormLayoutTree ); *it; ++it )
762 {
763 const DnDTreeItemData itemData = ( *it )->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
764 if ( itemData.type() == DnDTreeItemData::Container )
765 {
766 existingContainerList.append( QgsAddAttributeFormContainerDialog::ContainerPair( itemData.name(), *it ) );
767 }
768 }
769 QTreeWidgetItem *currentItem = mFormLayoutTree->selectedItems().value( 0 );
770 QgsAddAttributeFormContainerDialog dialog( mLayer, existingContainerList, currentItem, this );
771
772 if ( !dialog.exec() )
773 return;
774
775 const QString name = dialog.name();
776 QTreeWidgetItem *parentContainerItem = dialog.parentContainerItem();
777 mFormLayoutTree->addContainer( parentContainerItem ? parentContainerItem : mFormLayoutTree->invisibleRootItem(), name, dialog.columnCount(), dialog.containerType() );
778}
779
780void QgsAttributesFormProperties::removeTabOrGroupButton()
781{
782 // deleting an item may delete any number of nested child items -- so we delete
783 // them one at a time and then see if there's any selection left
784 while ( true )
785 {
786 const QList<QTreeWidgetItem *> items = mFormLayoutTree->selectedItems();
787 if ( items.empty() )
788 break;
789
790 delete items.at( 0 );
791 }
792}
793
795{
796 QgsAttributeEditorElement *widgetDef = nullptr;
797
798 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
799
800 switch ( itemData.type() )
801 {
802 //indexed here?
804 {
805 const int idx = mLayer->fields().lookupField( itemData.name() );
806 widgetDef = new QgsAttributeEditorField( itemData.name(), idx, parent );
807 break;
808 }
809
811 {
812 const QgsAction action { mLayer->actions()->action( itemData.name() ) };
813 widgetDef = new QgsAttributeEditorAction( action, parent );
814 break;
815 }
816
818 {
819 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( itemData.name() );
820
821 QgsAttributeEditorRelation *relDef = new QgsAttributeEditorRelation( relation, parent );
823 relDef->setRelationWidgetTypeId( relationEditorConfig.mRelationWidgetType );
824 relDef->setRelationEditorConfiguration( relationEditorConfig.mRelationWidgetConfig );
825 relDef->setNmRelationId( relationEditorConfig.nmRelationId );
826 relDef->setForceSuppressFormPopup( relationEditorConfig.forceSuppressFormPopup );
827 relDef->setLabel( relationEditorConfig.label );
828 widgetDef = relDef;
829 break;
830 }
831
833 {
834 QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() );
835 container->setColumnCount( itemData.columnCount() );
836 // only top-level containers can be tabs
838 if ( type == Qgis::AttributeEditorContainerType::Tab && !isTopLevel )
839 {
840 // a top container found which isn't at the top level -- reset it to a group box instead
842 }
843 container->setType( type );
844 container->setCollapsed( itemData.collapsed() );
845 container->setCollapsedExpression( itemData.collapsedExpression() );
846 container->setVisibilityExpression( itemData.visibilityExpression() );
847 container->setBackgroundColor( itemData.backgroundColor() );
848
849 for ( int t = 0; t < item->childCount(); t++ )
850 {
851 QgsAttributeEditorElement *element { createAttributeEditorWidget( item->child( t ), container, false ) };
852 if ( element )
853 container->addChildElement( element );
854 }
855
856 widgetDef = container;
857 break;
858 }
859
861 {
862 QgsAttributeEditorQmlElement *element = new QgsAttributeEditorQmlElement( item->text( 0 ), parent );
863 element->setQmlCode( itemData.qmlElementEditorConfiguration().qmlCode );
864 widgetDef = element;
865 break;
866 }
867
869 {
870 QgsAttributeEditorHtmlElement *element = new QgsAttributeEditorHtmlElement( item->text( 0 ), parent );
872 widgetDef = element;
873 break;
874 }
875
877 {
878 QgsAttributeEditorTextElement *element = new QgsAttributeEditorTextElement( item->text( 0 ), parent );
879 element->setText( itemData.textElementEditorConfiguration().text );
880 widgetDef = element;
881 break;
882 }
883
885 {
886 QgsAttributeEditorSpacerElement *element = new QgsAttributeEditorSpacerElement( item->text( 0 ), parent );
888 widgetDef = element;
889 break;
890 }
891
893 break;
894 }
895
896 if ( widgetDef )
897 {
898 widgetDef->setShowLabel( itemData.showLabel() );
899 widgetDef->setLabelStyle( itemData.labelStyle() );
900 widgetDef->setHorizontalStretch( itemData.horizontalStretch() );
901 widgetDef->setVerticalStretch( itemData.verticalStretch() );
902 }
903
904 return widgetDef;
905}
906
907void QgsAttributesFormProperties::mEditorLayoutComboBox_currentIndexChanged( int )
908{
909 const Qgis::AttributeFormLayout layout = mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>();
910 switch ( layout )
911 {
913 mFormLayoutWidget->setVisible( false );
914 mUiFileFrame->setVisible( false );
915 mAddTabOrGroupButton->setVisible( false );
916 mRemoveTabOrGroupButton->setVisible( false );
917 mInvertSelectionButton->setVisible( false );
918 break;
919
921 mFormLayoutWidget->setVisible( true );
922 mUiFileFrame->setVisible( false );
923 mAddTabOrGroupButton->setVisible( true );
924 mRemoveTabOrGroupButton->setVisible( true );
925 mInvertSelectionButton->setVisible( true );
926 break;
927
929 // ui file
930 mFormLayoutWidget->setVisible( false );
931 mUiFileFrame->setVisible( true );
932 mAddTabOrGroupButton->setVisible( false );
933 mRemoveTabOrGroupButton->setVisible( false );
934 mInvertSelectionButton->setVisible( false );
935 break;
936 }
937}
938
939void QgsAttributesFormProperties::mTbInitCode_clicked()
940{
941 QgsAttributesFormInitCode attributesFormInitCode;
942
943 attributesFormInitCode.setCodeSource( mInitCodeSource );
944 attributesFormInitCode.setInitCode( mInitCode );
945 attributesFormInitCode.setInitFilePath( mInitFilePath );
946 attributesFormInitCode.setInitFunction( mInitFunction );
947
948 if ( !attributesFormInitCode.exec() )
949 return;
950
951 mInitCodeSource = attributesFormInitCode.codeSource();
952 mInitCode = attributesFormInitCode.initCode();
953 mInitFilePath = attributesFormInitCode.initFilePath();
954 mInitFunction = attributesFormInitCode.initFunction();
955}
956
957void QgsAttributesFormProperties::pbnSelectEditForm_clicked()
958{
959 QgsSettings myQSettings;
960 const QString lastUsedDir = myQSettings.value( QStringLiteral( "style/lastUIDir" ), QDir::homePath() ).toString();
961 const QString uifilename = QFileDialog::getOpenFileName( this, tr( "Select edit form" ), lastUsedDir, tr( "UI file" ) + " (*.ui)" );
962
963 if ( uifilename.isNull() )
964 return;
965
966 const QFileInfo fi( uifilename );
967 myQSettings.setValue( QStringLiteral( "style/lastUIDir" ), fi.path() );
968 mEditFormLineEdit->setText( uifilename );
969}
970
972{
973 storeAttributeWidgetEdit();
974 storeAttributeContainerEdit();
975 storeAttributeTypeDialog();
976}
977
979{
980 mBlockUpdates++;
981 storeAttributeWidgetEdit();
982 storeAttributeContainerEdit();
983 storeAttributeTypeDialog();
984
985 QgsEditFormConfig editFormConfig = mLayer->editFormConfig();
986
987 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
988
989 for ( int i = 0; i < fieldContainer->childCount(); i++ )
990 {
991 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
992 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
993
994 const QString fieldName { fieldItem->data( 0, FieldNameRole ).toString() };
995 const int idx = mLayer->fields().indexOf( fieldName );
996
997 //continue in case field does not exist anymore
998 if ( idx < 0 )
999 continue;
1000
1001 editFormConfig.setReadOnly( idx, !cfg.mEditable );
1002 editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
1003 editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );
1004
1005 if ( cfg.mDataDefinedProperties.count() > 0 )
1006 {
1007 editFormConfig.setDataDefinedFieldProperties( fieldName, cfg.mDataDefinedProperties );
1008 }
1009
1011
1012 const QgsFieldConstraints constraints = cfg.mFieldConstraints;
1013 mLayer->setConstraintExpression( idx, constraints.constraintExpression(), constraints.constraintDescription() );
1015 {
1017 }
1018 else
1019 {
1021 }
1023 {
1025 }
1026 else
1027 {
1029 }
1031 {
1033 }
1034 else
1035 {
1037 }
1038
1039 mLayer->setFieldAlias( idx, cfg.mAlias );
1042 }
1043
1044 // tabs and groups
1045 editFormConfig.clearTabs();
1046 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1047 {
1048 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1049 QgsAttributeEditorElement *editorElement { createAttributeEditorWidget( tabItem, nullptr, true ) };
1050 if ( editorElement )
1051 editFormConfig.addTab( editorElement );
1052 }
1053
1054 editFormConfig.setUiForm( mEditFormLineEdit->text() );
1055
1056 editFormConfig.setLayout( mEditorLayoutComboBox->currentData().value<Qgis::AttributeFormLayout>() );
1057
1058 editFormConfig.setInitCodeSource( mInitCodeSource );
1059 editFormConfig.setInitFunction( mInitFunction );
1060 editFormConfig.setInitFilePath( mInitFilePath );
1061 editFormConfig.setInitCode( mInitCode );
1062
1063 editFormConfig.setSuppress( mFormSuppressCmbBx->currentData().value<Qgis::AttributeFormSuppression>() );
1064
1065 // write the legacy config of relation widgets to support settings read by the API
1066 QTreeWidgetItem *relationContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 1 );
1067
1068 for ( int i = 0; i < relationContainer->childCount(); i++ )
1069 {
1070 QTreeWidgetItem *relationItem = relationContainer->child( i );
1071 const DnDTreeItemData itemData = relationItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1072
1073 for ( int t = 0; t < mFormLayoutTree->invisibleRootItem()->childCount(); t++ )
1074 {
1075 QTreeWidgetItem *tabItem = mFormLayoutTree->invisibleRootItem()->child( t );
1076 const DnDTreeItemData tabItemData = tabItem->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
1077
1078 if ( tabItemData.type() == itemData.type() && tabItemData.name() == itemData.name() )
1079 {
1080 QVariantMap cfg;
1081
1082 cfg[QStringLiteral( "nm-rel" )] = tabItemData.relationEditorConfiguration().nmRelationId;
1083 cfg[QStringLiteral( "force-suppress-popup" )] = tabItemData.relationEditorConfiguration().forceSuppressFormPopup;
1084
1085 editFormConfig.setWidgetConfig( tabItemData.name(), cfg );
1086 break;
1087 }
1088 }
1089 }
1090
1091 mLayer->setEditFormConfig( editFormConfig );
1092 mBlockUpdates--;
1093}
1094
1095
1096/*
1097 * FieldConfig implementation
1098 */
1100{
1101 mAlias = layer->fields().at( idx ).alias();
1103 mComment = layer->fields().at( idx ).comment();
1104 mEditable = !layer->editFormConfig().readOnly( idx );
1105 mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
1107 mFieldConstraints = layer->fields().at( idx ).constraints();
1108 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
1109 mEditorWidgetType = setup.type();
1110 mEditorWidgetConfig = setup.config();
1111 mSplitPolicy = layer->fields().at( idx ).splitPolicy();
1112 mDuplicatePolicy = layer->fields().at( idx ).duplicatePolicy();
1113}
1114
1115QgsAttributesFormProperties::FieldConfig::operator QVariant()
1116{
1117 return QVariant::fromValue<QgsAttributesFormProperties::FieldConfig>( *this );
1118}
1119
1120/*
1121 * RelationEditorConfiguration implementation
1122 */
1123
1124QgsAttributesFormProperties::RelationEditorConfiguration::operator QVariant()
1125{
1126 return QVariant::fromValue<QgsAttributesFormProperties::RelationEditorConfiguration>( *this );
1127}
1128
1129/*
1130 * DnDTree implementation
1131 */
1132
1133QTreeWidgetItem *QgsAttributesDnDTree::addContainer( QTreeWidgetItem *parent, const QString &title, int columnCount, Qgis::AttributeEditorContainerType type )
1134{
1135 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << title );
1136 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1137 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1139 itemData.setColumnCount( columnCount );
1140 itemData.setContainerType( !parent ? Qgis::AttributeEditorContainerType::Tab : type );
1141 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1142 parent->addChild( newItem );
1143 newItem->setExpanded( true );
1144 return newItem;
1145}
1146
1148 : QTreeWidget( parent )
1149 , mLayer( layer )
1150{
1151 connect( this, &QTreeWidget::itemDoubleClicked, this, &QgsAttributesDnDTree::onItemDoubleClicked );
1152}
1153
1154QTreeWidgetItem *QgsAttributesDnDTree::addItem( QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index, const QIcon &icon )
1155{
1156 QTreeWidgetItem *newItem = new QTreeWidgetItem( QStringList() << data.name() );
1157
1158 switch ( data.type() )
1159 {
1167 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1168 break;
1169
1172 {
1173 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
1174 newItem->setBackground( 0, QBrush( Qt::lightGray ) );
1175 }
1176 break;
1177 }
1178
1179 newItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, QVariant::fromValue( data ) );
1180 newItem->setText( 0, data.displayName() );
1181 newItem->setIcon( 0, icon );
1182
1184 {
1185 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( data.name() );
1186 if ( !relation.isValid() || relation.referencedLayer() != mLayer )
1187 {
1188 newItem->setText( 0, tr( "Invalid relation" ) );
1189 newItem->setForeground( 0, QColor( 255, 0, 0 ) );
1190 }
1191 }
1192
1193 if ( index < 0 )
1194 parent->addChild( newItem );
1195 else
1196 parent->insertChild( index, newItem );
1197
1198 return newItem;
1199}
1200
1206void QgsAttributesDnDTree::dragMoveEvent( QDragMoveEvent *event )
1207{
1208 const QMimeData *data = event->mimeData();
1209
1210 if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1211 {
1213
1214 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1215 QDataStream stream( &itemData, QIODevice::ReadOnly );
1216 stream >> itemElement;
1217
1218 // Inner drag and drop actions are always MoveAction
1219 if ( event->source() == this )
1220 {
1221 event->setDropAction( Qt::MoveAction );
1222 }
1223 }
1224 else
1225 {
1226 event->ignore();
1227 }
1228
1229 QTreeWidget::dragMoveEvent( event );
1230}
1231
1232
1233bool QgsAttributesDnDTree::dropMimeData( QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action )
1234{
1235 bool bDropSuccessful = false;
1236
1237 if ( action == Qt::IgnoreAction )
1238 {
1239 bDropSuccessful = true;
1240 }
1241 else if ( data->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1242 {
1243 QByteArray itemData = data->data( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) );
1244 QDataStream stream( &itemData, QIODevice::ReadOnly );
1246
1247 while ( !stream.atEnd() )
1248 {
1249 stream >> itemElement;
1250
1251 QTreeWidgetItem *newItem;
1252
1253 if ( parent )
1254 {
1255 newItem = addItem( parent, itemElement, index++ );
1256 bDropSuccessful = true;
1257 }
1258 else
1259 {
1260 newItem = addItem( invisibleRootItem(), itemElement, index++ );
1261 bDropSuccessful = true;
1262 }
1263
1265 {
1266 onItemDoubleClicked( newItem, 0 );
1267 }
1268
1270 {
1271 onItemDoubleClicked( newItem, 0 );
1272 }
1273
1275 {
1276 onItemDoubleClicked( newItem, 0 );
1277 }
1278
1280 {
1281 onItemDoubleClicked( newItem, 0 );
1282 }
1283
1284 clearSelection();
1285 newItem->setSelected( true );
1286 }
1287 }
1288
1289 return bDropSuccessful;
1290}
1291
1292void QgsAttributesDnDTree::dropEvent( QDropEvent *event )
1293{
1294 if ( !event->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelement" ) ) )
1295 return;
1296
1297 if ( event->source() == this )
1298 {
1299 event->setDropAction( Qt::MoveAction );
1300 }
1301
1302 QTreeWidget::dropEvent( event );
1303}
1304
1306{
1307 return QStringList() << QStringLiteral( "application/x-qgsattributetabledesignerelement" );
1308}
1309
1310#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
1311QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> items ) const
1312#else
1313QMimeData *QgsAttributesDnDTree::mimeData( const QList<QTreeWidgetItem *> &items ) const
1314#endif
1315{
1316 if ( items.count() <= 0 )
1317 return nullptr;
1318
1319 const QStringList types = mimeTypes();
1320
1321 if ( types.isEmpty() )
1322 return nullptr;
1323
1324 QMimeData *data = new QMimeData();
1325 const QString format = types.at( 0 );
1326 QByteArray encoded;
1327 QDataStream stream( &encoded, QIODevice::WriteOnly );
1328
1329 const auto constItems = items;
1330 for ( const QTreeWidgetItem *item : constItems )
1331 {
1332 if ( item )
1333 {
1334 // Relevant information is always in the DnDTreeRole of the first column
1336 stream << itemData;
1337 }
1338 }
1339
1340 data->setData( format, encoded );
1341
1342 return data;
1343}
1344
1345void QgsAttributesDnDTree::onItemDoubleClicked( QTreeWidgetItem *item, int column )
1346{
1347 Q_UNUSED( column )
1348
1350
1351 QGroupBox *baseData = new QGroupBox( tr( "Base configuration" ) );
1352
1353 QFormLayout *baseLayout = new QFormLayout();
1354 baseData->setLayout( baseLayout );
1355 QCheckBox *showLabelCheckbox = new QCheckBox( QStringLiteral( "Show label" ) );
1356 showLabelCheckbox->setChecked( itemData.showLabel() );
1357 baseLayout->addRow( showLabelCheckbox );
1358 QWidget *baseWidget = new QWidget();
1359 baseWidget->setLayout( baseLayout );
1360
1361 switch ( itemData.type() )
1362 {
1368 break;
1369
1371 {
1372 if ( mType == QgsAttributesDnDTree::Type::Drag )
1373 return;
1374
1375 QDialog dlg;
1376 dlg.setObjectName( "QML Form Configuration Widget" );
1378 dlg.setWindowTitle( tr( "Configure QML Widget" ) );
1379
1380 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1381 QSplitter *qmlSplitter = new QSplitter();
1382 QWidget *qmlConfigWiget = new QWidget();
1383 QVBoxLayout *layout = new QVBoxLayout( qmlConfigWiget );
1384 layout->setContentsMargins( 0, 0, 0, 0 );
1385 mainLayout->addWidget( qmlSplitter );
1386 qmlSplitter->addWidget( qmlConfigWiget );
1387 layout->addWidget( baseWidget );
1388
1389 QLineEdit *title = new QLineEdit( itemData.name() );
1390
1391 //qmlCode
1392 QgsCodeEditor *qmlCode = new QgsCodeEditor( this );
1393 qmlCode->setEditingTimeoutInterval( 250 );
1394 qmlCode->setText( itemData.qmlElementEditorConfiguration().qmlCode );
1395
1396 QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
1397 QgsFeature previewFeature;
1398 mLayer->getFeatures().nextFeature( previewFeature );
1399
1400 //update preview on text change
1401 connect( qmlCode, &QgsCodeEditor::editingTimeout, this, [=] {
1402 qmlWrapper->setQmlCode( qmlCode->text() );
1403 qmlWrapper->reinitWidget();
1404 qmlWrapper->setFeature( previewFeature );
1405 } );
1406
1407 //templates
1408 QComboBox *qmlObjectTemplate = new QComboBox();
1409 qmlObjectTemplate->addItem( tr( "Free Text…" ) );
1410 qmlObjectTemplate->addItem( tr( "Rectangle" ) );
1411 qmlObjectTemplate->addItem( tr( "Pie Chart" ) );
1412 qmlObjectTemplate->addItem( tr( "Bar Chart" ) );
1413 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [=]( int index ) {
1414 qmlCode->clear();
1415 switch ( index )
1416 {
1417 case 0:
1418 {
1419 qmlCode->setText( QString() );
1420 break;
1421 }
1422 case 1:
1423 {
1424 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1425 "\n"
1426 "Rectangle {\n"
1427 " width: 100\n"
1428 " height: 100\n"
1429 " color: \"steelblue\"\n"
1430 " Text{ text: \"A rectangle\" }\n"
1431 "}\n" ) );
1432 break;
1433 }
1434 case 2:
1435 {
1436 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1437 "import QtCharts 2.0\n"
1438 "\n"
1439 "ChartView {\n"
1440 " width: 400\n"
1441 " height: 400\n"
1442 "\n"
1443 " PieSeries {\n"
1444 " id: pieSeries\n"
1445 " PieSlice { label: \"First slice\"; value: 25 }\n"
1446 " PieSlice { label: \"Second slice\"; value: 45 }\n"
1447 " PieSlice { label: \"Third slice\"; value: 30 }\n"
1448 " }\n"
1449 "}\n" ) );
1450 break;
1451 }
1452 case 3:
1453 {
1454 qmlCode->setText( QStringLiteral( "import QtQuick 2.0\n"
1455 "import QtCharts 2.0\n"
1456 "\n"
1457 "ChartView {\n"
1458 " title: \"Bar series\"\n"
1459 " width: 600\n"
1460 " height:400\n"
1461 " legend.alignment: Qt.AlignBottom\n"
1462 " antialiasing: true\n"
1463 " ValueAxis{\n"
1464 " id: valueAxisY\n"
1465 " min: 0\n"
1466 " max: 15\n"
1467 " }\n"
1468 "\n"
1469 " BarSeries {\n"
1470 " id: mySeries\n"
1471 " axisY: valueAxisY\n"
1472 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
1473 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
1474 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
1475 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
1476 " }\n"
1477 "}\n" ) );
1478 break;
1479 }
1480 default:
1481 break;
1482 }
1483 } );
1484
1485 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1486 expressionWidget->setButtonVisible( false );
1487 expressionWidget->registerExpressionContextGenerator( this );
1488 expressionWidget->setLayer( mLayer );
1489 QToolButton *addFieldButton = new QToolButton();
1490 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1491
1492 QToolButton *editExpressionButton = new QToolButton();
1493 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1494 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1495
1496 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1497 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1498 if ( !expression.isEmpty() )
1499 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1500 } );
1501
1502 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1503 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( qmlCode, QStringLiteral( "expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)" ) );
1504 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1506 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1507
1508 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1509 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1510 {
1511 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1512 if ( !expression.isEmpty() )
1513 qmlCode->insertText( QStringLiteral( "expression.evaluate(\"%1\")" ).arg( expression ) );
1514 }
1515 } );
1516
1517 layout->addWidget( new QLabel( tr( "Title" ) ) );
1518 layout->addWidget( title );
1519 QGroupBox *qmlCodeBox = new QGroupBox( tr( "QML Code" ) );
1520 qmlCodeBox->setLayout( new QVBoxLayout );
1521 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
1522 QWidget *expressionWidgetBox = new QWidget();
1523 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
1524 expressionWidgetBox->setLayout( new QHBoxLayout );
1525 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
1526 expressionWidgetBox->layout()->addWidget( expressionWidget );
1527 expressionWidgetBox->layout()->addWidget( addFieldButton );
1528 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1529 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1530 layout->addWidget( qmlCodeBox );
1531 layout->addWidget( qmlCode );
1532 QScrollArea *qmlPreviewBox = new QgsScrollArea();
1533 qmlPreviewBox->setMinimumWidth( 200 );
1534 qmlPreviewBox->setWidget( qmlWrapper->widget() );
1535 //emit to load preview for the first time
1536 emit qmlCode->editingTimeout();
1537 qmlSplitter->addWidget( qmlPreviewBox );
1538 qmlSplitter->setChildrenCollapsible( false );
1539 qmlSplitter->setHandleWidth( 6 );
1540 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
1541
1542 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1543
1544 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1545 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1546 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1547 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1548 } );
1549
1550 mainLayout->addWidget( buttonBox );
1551
1552 if ( dlg.exec() )
1553 {
1555 qmlEdCfg.qmlCode = qmlCode->text();
1556 itemData.setName( title->text() );
1557 itemData.setQmlElementEditorConfiguration( qmlEdCfg );
1558 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1559
1560 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1561 item->setText( 0, title->text() );
1562 }
1563 }
1564 break;
1565
1567 {
1568 if ( mType == QgsAttributesDnDTree::Type::Drag )
1569 return;
1570 QDialog dlg;
1571 dlg.setObjectName( "HTML Form Configuration Widget" );
1573 dlg.setWindowTitle( tr( "Configure HTML Widget" ) );
1574
1575 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1576 QSplitter *htmlSplitter = new QSplitter();
1577 QWidget *htmlConfigWiget = new QWidget();
1578 QVBoxLayout *layout = new QVBoxLayout( htmlConfigWiget );
1579 layout->setContentsMargins( 0, 0, 0, 0 );
1580 mainLayout->addWidget( htmlSplitter );
1581 htmlSplitter->addWidget( htmlConfigWiget );
1582 htmlSplitter->setChildrenCollapsible( false );
1583 htmlSplitter->setHandleWidth( 6 );
1584 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1585 layout->addWidget( baseWidget );
1586
1587 QLineEdit *title = new QLineEdit( itemData.name() );
1588
1589 //htmlCode
1590 QgsCodeEditorHTML *htmlCode = new QgsCodeEditorHTML();
1591 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1592 htmlCode->setText( itemData.htmlElementEditorConfiguration().htmlCode );
1593
1594 QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
1595 QgsFeature previewFeature;
1596 mLayer->getFeatures().nextFeature( previewFeature );
1597
1598 //update preview on text change
1599 connect( htmlCode, &QgsCodeEditorHTML::textChanged, this, [=] {
1600 htmlWrapper->setHtmlCode( htmlCode->text() );
1601 htmlWrapper->reinitWidget();
1602 htmlWrapper->setFeature( previewFeature );
1603 } );
1604
1605 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1606 expressionWidget->setButtonVisible( false );
1607 expressionWidget->registerExpressionContextGenerator( this );
1608 expressionWidget->setLayer( mLayer );
1609 QToolButton *addFieldButton = new QToolButton();
1610 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1611
1612 QToolButton *editExpressionButton = new QToolButton();
1613 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1614 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1615
1616 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1617 QString expression = expressionWidget->expression().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1618 if ( !expression.isEmpty() )
1619 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1620 } );
1621
1622 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1623 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( htmlCode, QStringLiteral( "<script>\\s*document\\.write\\(\\s*expression\\.evaluate\\(\\s*\"(.*?)\\s*\"\\s*\\)\\s*\\)\\s*;?\\s*</script>" ) );
1624 expression.replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) );
1626 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1627
1628 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1629 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1630 {
1631 QString expression = exprDlg.expressionText().trimmed().replace( '"', QLatin1String( "\\\"" ) );
1632 if ( !expression.isEmpty() )
1633 htmlCode->insertText( QStringLiteral( "<script>document.write(expression.evaluate(\"%1\"));</script>" ).arg( expression ) );
1634 }
1635 } );
1636
1637 layout->addWidget( new QLabel( tr( "Title" ) ) );
1638 layout->addWidget( title );
1639 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "HTML Code" ) );
1640 layout->addWidget( expressionWidgetBox );
1641 expressionWidgetBox->setLayout( new QHBoxLayout );
1642 expressionWidgetBox->layout()->addWidget( expressionWidget );
1643 expressionWidgetBox->layout()->addWidget( addFieldButton );
1644 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1645 layout->addWidget( htmlCode );
1646 QScrollArea *htmlPreviewBox = new QgsScrollArea();
1647 htmlPreviewBox->setLayout( new QGridLayout );
1648 htmlPreviewBox->setMinimumWidth( 200 );
1649 htmlPreviewBox->layout()->addWidget( htmlWrapper->widget() );
1650 //emit to load preview for the first time
1651 emit htmlCode->textChanged();
1652 htmlSplitter->addWidget( htmlPreviewBox );
1653 htmlSplitter->setChildrenCollapsible( false );
1654 htmlSplitter->setHandleWidth( 6 );
1655 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
1656
1657 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1658
1659 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1660 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1661 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1662 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1663 } );
1664
1665 mainLayout->addWidget( buttonBox );
1666
1667 if ( dlg.exec() )
1668 {
1670 htmlEdCfg.htmlCode = htmlCode->text();
1671 itemData.setName( title->text() );
1672 itemData.setHtmlElementEditorConfiguration( htmlEdCfg );
1673 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1674
1675 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1676 item->setText( 0, title->text() );
1677 }
1678 break;
1679 }
1680
1682 {
1683 if ( mType == QgsAttributesDnDTree::Type::Drag )
1684 return;
1685 QDialog dlg;
1686 dlg.setObjectName( "Text Form Configuration Widget" );
1688 dlg.setWindowTitle( tr( "Configure Text Widget" ) );
1689
1690 QVBoxLayout *mainLayout = new QVBoxLayout( &dlg );
1691 QSplitter *textSplitter = new QSplitter();
1692 QWidget *textConfigWiget = new QWidget();
1693 QVBoxLayout *layout = new QVBoxLayout( textConfigWiget );
1694 layout->setContentsMargins( 0, 0, 0, 0 );
1695 mainLayout->addWidget( textSplitter );
1696 textSplitter->addWidget( textConfigWiget );
1697 layout->addWidget( baseWidget );
1698
1699 QLineEdit *title = new QLineEdit( itemData.name() );
1700
1702 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
1703 text->setText( itemData.textElementEditorConfiguration().text );
1704
1705 QgsTextWidgetWrapper *textWrapper = new QgsTextWidgetWrapper( mLayer, nullptr, this );
1706 QgsFeature previewFeature;
1707 mLayer->getFeatures().nextFeature( previewFeature );
1708
1709 //update preview on text change
1710 connect( text, &QgsCodeEditorExpression::textChanged, this, [=] {
1711 textWrapper->setText( text->text() );
1712 textWrapper->reinitWidget();
1713 textWrapper->setFeature( previewFeature );
1714 } );
1715
1716 QgsFieldExpressionWidget *expressionWidget = new QgsFieldExpressionWidget;
1717 expressionWidget->setButtonVisible( false );
1718 expressionWidget->registerExpressionContextGenerator( this );
1719 expressionWidget->setLayer( mLayer );
1720 QToolButton *addFieldButton = new QToolButton();
1721 addFieldButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
1722
1723 QToolButton *editExpressionButton = new QToolButton();
1724 editExpressionButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpression.svg" ) ) );
1725 editExpressionButton->setToolTip( tr( "Insert/Edit Expression" ) );
1726
1727 connect( addFieldButton, &QAbstractButton::clicked, this, [=] {
1728 QString expression = expressionWidget->expression().trimmed();
1729 if ( !expression.isEmpty() )
1730 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1731 } );
1732 connect( editExpressionButton, &QAbstractButton::clicked, this, [=] {
1733 QString expression = QgsExpressionFinder::findAndSelectActiveExpression( text );
1734
1736 QgsExpressionBuilderDialog exprDlg( mLayer, expression, this, QStringLiteral( "generic" ), context );
1737
1738 exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1739 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
1740 {
1741 QString expression = exprDlg.expressionText().trimmed();
1742 if ( !expression.isEmpty() )
1743 text->insertText( QStringLiteral( "[%%1%]" ).arg( expression ) );
1744 }
1745 } );
1746
1747 layout->addWidget( new QLabel( tr( "Title" ) ) );
1748 layout->addWidget( title );
1749 QGroupBox *expressionWidgetBox = new QGroupBox( tr( "Text" ) );
1750 layout->addWidget( expressionWidgetBox );
1751 expressionWidgetBox->setLayout( new QHBoxLayout );
1752 expressionWidgetBox->layout()->addWidget( expressionWidget );
1753 expressionWidgetBox->layout()->addWidget( addFieldButton );
1754 expressionWidgetBox->layout()->addWidget( editExpressionButton );
1755 layout->addWidget( text );
1756 QScrollArea *textPreviewBox = new QgsScrollArea();
1757 textPreviewBox->setLayout( new QGridLayout );
1758 textPreviewBox->setMinimumWidth( 200 );
1759 textPreviewBox->layout()->addWidget( textWrapper->widget() );
1760 //emit to load preview for the first time
1761 emit text->textChanged();
1762 textSplitter->addWidget( textPreviewBox );
1763 textSplitter->setChildrenCollapsible( false );
1764 textSplitter->setHandleWidth( 6 );
1765 textSplitter->setSizes( QList<int>() << 1 << 1 );
1766
1767 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1768
1769 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1770 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1771 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1772 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1773 } );
1774
1775 mainLayout->addWidget( buttonBox );
1776
1777 if ( dlg.exec() )
1778 {
1780 textEdCfg.text = text->text();
1781 itemData.setName( title->text() );
1782 itemData.setTextElementEditorConfiguration( textEdCfg );
1783 itemData.setShowLabel( showLabelCheckbox->isChecked() );
1784
1785 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1786 item->setText( 0, title->text() );
1787 }
1788 break;
1789 }
1790
1792 {
1793 if ( mType == QgsAttributesDnDTree::Type::Drag )
1794 return;
1795 QDialog dlg;
1796 dlg.setObjectName( "Spacer Form Configuration Widget" );
1798 dlg.setWindowTitle( tr( "Configure Spacer Widget" ) );
1799
1800 QVBoxLayout *mainLayout = new QVBoxLayout();
1801 mainLayout->addWidget( new QLabel( tr( "Title" ) ) );
1802 QLineEdit *title = new QLineEdit( itemData.name() );
1803 mainLayout->addWidget( title );
1804
1805 QHBoxLayout *cbLayout = new QHBoxLayout();
1806 mainLayout->addLayout( cbLayout );
1807 dlg.setLayout( mainLayout );
1808 QCheckBox *cb = new QCheckBox { &dlg };
1809 cb->setChecked( itemData.spacerElementEditorConfiguration().drawLine );
1810 cbLayout->addWidget( new QLabel( tr( "Draw horizontal line" ), &dlg ) );
1811 cbLayout->addWidget( cb );
1812
1813 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
1814
1815 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
1816 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
1817 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [=] {
1818 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#other-widgets" ) );
1819 } );
1820
1821 mainLayout->addWidget( buttonBox );
1822
1823 if ( dlg.exec() )
1824 {
1826 spacerEdCfg.drawLine = cb->isChecked();
1827 itemData.setSpacerElementEditorConfiguration( spacerEdCfg );
1828 itemData.setShowLabel( false );
1829 itemData.setName( title->text() );
1830 item->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData );
1831 item->setText( 0, title->text() );
1832 }
1833
1834 break;
1835 }
1836 }
1837}
1838
1840{
1841 QgsExpressionContext expContext;
1844
1845 if ( mLayer )
1846 expContext << QgsExpressionContextUtils::layerScope( mLayer );
1847
1849 return expContext;
1850}
1851
1853{
1854 return mType;
1855}
1856
1858{
1859 mType = value;
1860}
1861
1863{
1864 QTreeWidgetItemIterator it( this );
1865 while ( *it )
1866 {
1868 if ( data.type() == rowData.type() && data.name() == rowData.name() )
1869 {
1870 if ( selectedItems().count() == 1 && ( *it )->isSelected() == true )
1871 {
1872 // the selection is already good
1873 }
1874 else
1875 {
1876 clearSelection();
1877 ( *it )->setSelected( true );
1878 }
1879 return;
1880 }
1881 ++it;
1882 }
1883 clearSelection();
1884}
1885
1886
1887/*
1888 * Serialization helpers for DesigerTreeItemData so we can stuff this easily into QMimeData
1889 */
1890
1891QDataStream &operator<<( QDataStream &stream, const QgsAttributesFormProperties::DnDTreeItemData &data )
1892{
1893 stream << static_cast<quint32>( data.type() ) << data.name() << data.displayName();
1894 return stream;
1895}
1896
1897QDataStream &operator>>( QDataStream &stream, QgsAttributesFormProperties::DnDTreeItemData &data )
1898{
1899 QString name;
1900 QString displayName;
1901 quint32 type;
1902
1903 stream >> type >> name >> displayName;
1904
1906 data.setName( name );
1907 data.setDisplayName( displayName );
1908
1909 return stream;
1910}
1911
1916
1921
1926
1928{
1929 mLabelStyle = labelStyle;
1930}
1931
1933{
1934 return mShowLabel;
1935}
1936
1938{
1939 mShowLabel = showLabel;
1940}
1941
1946
1948{
1949 mVisibilityExpression = visibilityExpression;
1950}
1951
1956
1958{
1959 mCollapsedExpression = collapsedExpression;
1960}
1961
1966
1968{
1969 mRelationEditorConfiguration = relationEditorConfiguration;
1970}
1971
1976
1978{
1979 mQmlElementEditorConfiguration = qmlElementEditorConfiguration;
1980}
1981
1982
1987
1989{
1990 mHtmlElementEditorConfiguration = htmlElementEditorConfiguration;
1991}
1992
1997
1999{
2000 mSpacerElementEditorConfiguration = spacerElementEditorConfiguration;
2001}
2002
2004{
2005 return mBackgroundColor;
2006}
2007
2009{
2010 mBackgroundColor = backgroundColor;
2011}
2012
2017
2019{
2020 mTextElementEditorConfiguration = textElementEditorConfiguration;
2021}
2022
2023void QgsAttributesFormProperties::updatedFields()
2024{
2025 // Store configuration to insure changes made are kept after refreshing the list
2026 QMap<QString, FieldConfig> fieldConfigs;
2027 QTreeWidgetItem *fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2028 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2029 {
2030 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2031 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2032 const FieldConfig cfg = fieldItem->data( 0, FieldConfigRole ).value<FieldConfig>();
2033 fieldConfigs[fieldName] = cfg;
2034 }
2035
2037
2038 fieldContainer = mAvailableWidgetsTree->invisibleRootItem()->child( 0 );
2039 for ( int i = 0; i < fieldContainer->childCount(); i++ )
2040 {
2041 QTreeWidgetItem *fieldItem = fieldContainer->child( i );
2042 const QString fieldName = fieldItem->data( 0, FieldNameRole ).toString();
2043 if ( fieldConfigs.contains( fieldName ) )
2044 {
2045 fieldItem->setData( 0, FieldConfigRole, fieldConfigs[fieldName] );
2046 }
2047 }
2048}
2049
2050void QgsAttributesFormProperties::onContextMenuRequested( QPoint point )
2051{
2052 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2053 return;
2054
2055 QPoint globalPos = mAvailableWidgetsTree->viewport()->mapToGlobal( point );
2056
2057 QTreeWidgetItem *item = mAvailableWidgetsTree->itemAt( point );
2058 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2059 if ( itemData.type() == DnDTreeItemData::Field )
2060 {
2061 const QClipboard *clipboard = QApplication::clipboard();
2062 const bool pasteEnabled = clipboard->mimeData()->hasFormat( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) );
2063 mActionPasteWidgetConfiguration->setEnabled( pasteEnabled );
2064 mAvailableWidgetsTreeContextMenu->popup( globalPos );
2065 }
2066}
2067
2068void QgsAttributesFormProperties::copyWidgetConfiguration()
2069{
2070 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2071 return;
2072
2073 const QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2074 const DnDTreeItemData itemData = item->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2075 if ( itemData.type() != DnDTreeItemData::Field )
2076 return;
2077
2078 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2079 const int index = mLayer->fields().indexOf( fieldName );
2080
2081 if ( index < 0 )
2082 return;
2083
2084 const QgsField field = mLayer->fields().field( index );
2085
2086 // We won't copy field aliases nor comments
2087 QDomDocument doc;
2088 QDomElement documentElement = doc.createElement( QStringLiteral( "FormWidgetClipboard" ) );
2089 documentElement.setAttribute( QStringLiteral( "name" ), field.name() );
2090
2091 // Editor widget setup
2092 QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup();
2093
2094 QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) );
2095 documentElement.appendChild( editWidgetElement );
2096 editWidgetElement.setAttribute( QStringLiteral( "type" ), widgetSetup.type() );
2097 QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) );
2098
2099 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) );
2100 editWidgetElement.appendChild( editWidgetConfigElement );
2101
2102 // Split policy
2103 QDomElement splitPolicyElement = doc.createElement( QStringLiteral( "splitPolicy" ) );
2104 splitPolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.splitPolicy() ) );
2105 documentElement.appendChild( splitPolicyElement );
2106
2107 // Duplicate policy
2108 QDomElement duplicatePolicyElement = doc.createElement( QStringLiteral( "duplicatePolicy" ) );
2109 duplicatePolicyElement.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
2110 documentElement.appendChild( duplicatePolicyElement );
2111
2112 // Default expressions
2113 QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) );
2114 defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() );
2115 defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
2116 documentElement.appendChild( defaultElem );
2117
2118 // Constraints
2119 QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) );
2120 constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() );
2121 constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) );
2122 constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) );
2123 constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) );
2124 documentElement.appendChild( constraintElem );
2125
2126 // Constraint expressions
2127 QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraintExpression" ) );
2128 constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() );
2129 constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() );
2130 documentElement.appendChild( constraintExpressionElem );
2131
2132 // Widget general settings
2133 QDomElement widgetGeneralSettingsElem = doc.createElement( QStringLiteral( "widgetGeneralSettings" ) );
2134 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "editable" ), !mLayer->editFormConfig().readOnly( index ) );
2135 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "reuse_last_values" ), mLayer->editFormConfig().labelOnTop( index ) );
2136 widgetGeneralSettingsElem.setAttribute( QStringLiteral( "label_on_top" ), mLayer->editFormConfig().reuseLastValue( index ) );
2137 documentElement.appendChild( widgetGeneralSettingsElem );
2138
2139 // Widget display section
2141 {
2142 // Go for the corresponding form layout item and extract its display settings
2143 if ( mFormLayoutTree->selectedItems().count() != 1 )
2144 return;
2145
2146 const QTreeWidgetItem *itemLayout = mFormLayoutTree->selectedItems().at( 0 );
2147 const DnDTreeItemData itemDataLayout = itemLayout->data( 0, DnDTreeRole ).value<DnDTreeItemData>();
2148
2149 QDomElement displayElement = doc.createElement( QStringLiteral( "widgetDisplay" ) );
2150 displayElement.setAttribute( QStringLiteral( "showLabel" ), itemDataLayout.showLabel() );
2151 displayElement.setAttribute( QStringLiteral( "horizontalStretch" ), itemDataLayout.horizontalStretch() );
2152 displayElement.setAttribute( QStringLiteral( "verticalStretch" ), itemDataLayout.verticalStretch() );
2153 displayElement.appendChild( itemDataLayout.labelStyle().writeXml( doc ) );
2154 documentElement.appendChild( displayElement );
2155 }
2156
2157 doc.appendChild( documentElement );
2158
2159 QMimeData *mimeData = new QMimeData;
2160 mimeData->setData( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ), doc.toByteArray() );
2161 QClipboard *clipboard = QApplication::clipboard();
2162 clipboard->setMimeData( mimeData );
2163}
2164
2165void QgsAttributesFormProperties::pasteWidgetConfiguration()
2166{
2167 if ( mAvailableWidgetsTree->selectedItems().count() != 1 )
2168 return;
2169
2170 QTreeWidgetItem *item = mAvailableWidgetsTree->selectedItems().at( 0 );
2171
2172 const QString fieldName = item->data( 0, FieldNameRole ).toString();
2173 const int fieldIndex = mLayer->fields().indexOf( fieldName );
2174
2175 if ( fieldIndex < 0 )
2176 return;
2177
2178 // Get base config from target item and ovewrite settings when possible
2179 FieldConfig config = item->data( 0, FieldConfigRole ).value<FieldConfig>();
2180
2181 QDomDocument doc;
2182 QClipboard *clipboard = QApplication::clipboard();
2183 if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "application/x-qgsattributetabledesignerelementclipboard" ) ) ) )
2184 {
2185 QDomElement docElem = doc.documentElement();
2186 if ( docElem.tagName() != QLatin1String( "FormWidgetClipboard" ) )
2187 return;
2188
2189 // When pasting, the target item has already been selected and
2190 // has triggered attribute type dialog loading. Therefore, we'll
2191 // only overwrite GUI settings instead of destroying and recreating
2192 // the whole dialog.
2193
2194 // Editor widget configuration
2195 const QDomElement fieldWidgetElement = docElem.firstChildElement( QStringLiteral( "editWidget" ) );
2196 if ( !fieldWidgetElement.isNull() )
2197 {
2198 const QString widgetType = fieldWidgetElement.attribute( QStringLiteral( "type" ) );
2199
2200 // Only paste if source editor widget type is supported by target field
2201 const QgsEditorWidgetFactory *factory = QgsGui::editorWidgetRegistry()->factory( widgetType );
2202 if ( factory->supportsField( mLayer, fieldIndex ) )
2203 {
2204 const QDomElement configElement = fieldWidgetElement.firstChildElement( QStringLiteral( "config" ) );
2205 if ( !configElement.isNull() )
2206 {
2207 const QDomElement optionsElem = configElement.childNodes().at( 0 ).toElement();
2208 QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
2209 QgsReadWriteContext context;
2210 if ( widgetType == QLatin1String( "ValueRelation" ) )
2211 {
2212 optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() );
2213 }
2214
2215 config.mEditorWidgetType = widgetType;
2216 config.mEditorWidgetConfig = optionsMap;
2217 }
2218 }
2219 else
2220 {
2221 mMessageBar->pushMessage( QString(), tr( "Unable to paste widget configuration. The target field (%1) does not support the %2 widget type." ).arg( fieldName, widgetType ), Qgis::MessageLevel::Warning );
2222 }
2223 }
2224
2225 // Split policy
2226 const QDomElement splitPolicyElement = docElem.firstChildElement( QStringLiteral( "splitPolicy" ) );
2227 if ( !splitPolicyElement.isNull() )
2228 {
2229 const Qgis::FieldDomainSplitPolicy policy = qgsEnumKeyToValue( splitPolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainSplitPolicy::Duplicate );
2230 config.mSplitPolicy = policy;
2231 }
2232
2233 // Duplicate policy
2234 const QDomElement duplicatePolicyElement = docElem.firstChildElement( QStringLiteral( "duplicatePolicy" ) );
2235 if ( !duplicatePolicyElement.isNull() )
2236 {
2237 const Qgis::FieldDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElement.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDuplicatePolicy::Duplicate );
2238 config.mDuplicatePolicy = policy;
2239 }
2240
2241 // Default expressions
2242 const QDomElement defaultElement = docElem.firstChildElement( QStringLiteral( "default" ) );
2243 if ( !defaultElement.isNull() )
2244 {
2245 mAttributeTypeDialog->setDefaultValueExpression( defaultElement.attribute( QStringLiteral( "expression" ) ) );
2246 mAttributeTypeDialog->setApplyDefaultValueOnUpdate( defaultElement.attribute( QStringLiteral( "applyOnUpdate" ) ).toInt() );
2247 }
2248
2249 // Constraints
2250 // take target field constraints as a basis
2251 QgsFieldConstraints fieldConstraints = config.mFieldConstraints;
2252 const QDomElement constraintElement = docElem.firstChildElement( QStringLiteral( "constraint" ) );
2253 if ( !constraintElement.isNull() )
2254 {
2255 const int intConstraints = constraintElement.attribute( QStringLiteral( "constraints" ), QStringLiteral( "0" ) ).toInt();
2256 QgsFieldConstraints::Constraints constraints = static_cast< QgsFieldConstraints::Constraints >( intConstraints );
2257
2258 // always keep provider constraints intact
2260 {
2261 if ( constraints & QgsFieldConstraints::ConstraintNotNull )
2263 else
2265 }
2267 {
2268 if ( constraints & QgsFieldConstraints::ConstraintUnique )
2270 else
2272 }
2274 {
2277 else
2279 }
2280
2281 const int uniqueStrength = constraintElement.attribute( QStringLiteral( "unique_strength" ), QStringLiteral( "1" ) ).toInt();
2282 const int notNullStrength = constraintElement.attribute( QStringLiteral( "notnull_strength" ), QStringLiteral( "1" ) ).toInt();
2283 const int expStrength = constraintElement.attribute( QStringLiteral( "exp_strength" ), QStringLiteral( "1" ) ).toInt();
2284
2288 }
2289
2290 // Constraint expressions
2291 // always keep provider constraints intact
2293 {
2294 const QDomElement constraintExpressionElement = docElem.firstChildElement( QStringLiteral( "constraintExpression" ) );
2295 if ( !constraintExpressionElement.isNull() )
2296 {
2297 QString expression = constraintExpressionElement.attribute( QStringLiteral( "exp" ), QString() );
2298 QString description = constraintExpressionElement.attribute( QStringLiteral( "desc" ), QString() );
2299 fieldConstraints.setConstraintExpression( expression, description );
2300 }
2301 }
2302 config.mFieldConstraints = fieldConstraints;
2303
2304 const QDomElement widgetGeneralSettingsElement = docElem.firstChildElement( QStringLiteral( "widgetGeneralSettings" ) );
2305 if ( !widgetGeneralSettingsElement.isNull() )
2306 {
2307 const int editable = widgetGeneralSettingsElement.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
2308 const int reuse = widgetGeneralSettingsElement.attribute( QStringLiteral( "reuse_last_values" ), QStringLiteral( "0" ) ).toInt();
2309 const int labelOnTop = widgetGeneralSettingsElement.attribute( QStringLiteral( "label_on_top" ), QStringLiteral( "0" ) ).toInt();
2310
2311 config.mEditable = editable;
2312 config.mReuseLastValues = reuse;
2313 config.mLabelOnTop = labelOnTop;
2314 }
2315
2316 loadAttributeTypeDialogFromConfiguration( config );
2317
2318 // Widget display section
2320 {
2321 const QDomElement displayElement = docElem.firstChildElement( QStringLiteral( "widgetDisplay" ) );
2322 if ( !displayElement.isNull() )
2323 {
2324 const int showLabel = displayElement.attribute( QStringLiteral( "showLabel" ), QStringLiteral( "0" ) ).toInt();
2325 const int horizontalStretch = displayElement.attribute( QStringLiteral( "horizontalStretch" ), QStringLiteral( "0" ) ).toInt();
2326 const int verticalStretch = displayElement.attribute( QStringLiteral( "verticalStretch" ), QStringLiteral( "0" ) ).toInt();
2328 style.readXml( displayElement );
2329
2330 // Update current GUI controls
2331 mAttributeWidgetEdit->setShowLabel( showLabel );
2332 mAttributeWidgetEdit->setHorizontalStretch( horizontalStretch );
2333 mAttributeWidgetEdit->setVerticalStretch( verticalStretch );
2334 mAttributeWidgetEdit->setLabelStyle( style );
2335 }
2336 }
2337 }
2338}
AttributeEditorContainerType
Attribute editor container types.
Definition qgis.h:5144
AttributeFormSuppression
Available form types for layout of the attribute form editor.
Definition qgis.h:5174
@ 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:5159
@ 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.
@ Warning
Warning message.
Definition qgis.h:156
FieldDomainSplitPolicy
Split policy for field domains.
Definition qgis.h:3691
@ Duplicate
Duplicate original value.
FieldDuplicatePolicy
Duplicate policy for fields.
Definition qgis.h:3723
@ Duplicate
Duplicate original value.
@ 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)
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)
QTreeWidgetItem * addItem(QTreeWidgetItem *parent, const QgsAttributesFormProperties::DnDTreeItemData &data, int index=-1, const QIcon &icon=QIcon())
Adds a new item to a parent.
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 setLabelStyle(const QgsAttributeEditorElement::LabelStyle &labelStyle)
Sets the label style to labelStyle.
QmlElementEditorConfiguration qmlElementEditorConfiguration() const
Returns the QML editor configuration.
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 setRelationEditorConfiguration(const RelationEditorConfiguration &relationEditorConfiguration)
Sets the relation editor configuration.
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.
void setQmlElementEditorConfiguration(const QmlElementEditorConfiguration &qmlElementEditorConfiguration)
Sets the QML editor configuration.
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...
void setHtmlElementEditorConfiguration(const HtmlElementEditorConfiguration &htmlElementEditorConfiguration)
Sets the HTML editor configuration.
HtmlElementEditorConfiguration htmlElementEditorConfiguration() const
Returns the HTML editor configuration.
void setSpacerElementEditorConfiguration(SpacerElementEditorConfiguration spacerElementEditorConfiguration)
Sets the the spacer element configuration to spacerElementEditorConfiguration.
bool collapsed() const
For group box containers returns if this group box is collapsed.
RelationEditorConfiguration relationEditorConfiguration() const
Returns the relation editor configuration.
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 setText(const QString &text) override
void setEditingTimeoutInterval(int timeout)
Sets the timeout (in milliseconds) threshold for the editingTimeout() signal to be emitted after an e...
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
void editingTimeout()
Emitted when either:
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.
Every attribute editor widget needs a factory, which inherits this class.
bool supportsField(const QgsVectorLayer *vl, int fieldIdx) const
Check if this editor widget type supports a certain field.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
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.
ConstraintStrength
Strength of constraints.
@ 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.
@ ConstraintOriginLayer
Constraint was set by layer.
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().
void removeConstraint(Constraint constraint)
Removes a constraint from the field.
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:755
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:765
QgsDefaultValue defaultValueDefinition
Definition qgsfield.h:64
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:740
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:95
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:210
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
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
QString id
Definition qgsmaplayer.h:79
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
An expression with an additional enabled flag.
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
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
The class is used as a container of context for various read/write operations on other objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
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
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
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.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6301
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6282
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.
void readXml(const QDomNode &node)
Reads configuration from node.