20#include "qgsattributetypedialog.h"
41#include <QPlainTextEdit>
48#include "moc_qgsattributesformview.cpp"
50using namespace Qt::StringLiterals;
60 if ( selectionModel()->selectedRows( 0 ).count() == 0 )
63 return mModel->mapToSource( selectionModel()->selectedRows( 0 ).at( 0 ) );
83 QModelIndex index =
mModel->mapFromSource( model->firstRecursiveMatchingModelIndex( itemType, itemId ) );
85 if ( index.isValid() )
89 const QModelIndexList selectedIndexes = selectionModel()->selectedRows( 0 );
90 if ( !( selectedIndexes.count() == 1 && selectedIndexes.at( 0 ) == index ) )
92 selectionModel()->setCurrentIndex( index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
97 selectionModel()->clearSelection();
103 mModel->setFilterText( text );
124 viewport()->repaint();
127 viewport()->repaint();
139 const QList<QgsAttributesFormItem *> keys =
mIndicators.keys();
151 return mModel->sourceAttributesFormModel();
162 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
166 QTreeView::setModel(
mModel );
178 connect(
this, &QTreeView::doubleClicked,
this, &QgsAttributesFormLayoutView::onItemDoubleClicked );
183 mModel = qobject_cast<QgsAttributesFormProxyModel *>( model );
187 QTreeView::setModel(
mModel );
195void QgsAttributesFormLayoutView::handleExternalDroppedItem( QModelIndex &index )
197 selectionModel()->setCurrentIndex(
mModel->mapFromSource( index ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
206 onItemDoubleClicked(
mModel->mapFromSource( index ) );
210void QgsAttributesFormLayoutView::handleInternalDroppedItem( QModelIndex &index )
212 selectionModel()->clearCurrentIndex();
216 expandRecursively(
mModel->mapFromSource( index ) );
222 const QMimeData *data =
event->mimeData();
224 if ( data->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s )
225 || data->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) )
228 if ( event->source() ==
this )
230 event->setDropAction( Qt::MoveAction );
238 QTreeView::dragEnterEvent( event );
247 const QMimeData *data =
event->mimeData();
249 if ( data->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s )
250 || data->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) )
253 if ( event->source() ==
this )
255 event->setDropAction( Qt::MoveAction );
263 QTreeView::dragMoveEvent( event );
268 if ( !( event->mimeData()->hasFormat( u
"application/x-qgsattributesformavailablewidgetsrelement"_s )
269 || event->mimeData()->hasFormat( u
"application/x-qgsattributesformlayoutelement"_s ) ) )
272 if ( event->source() ==
this )
274 event->setDropAction( Qt::MoveAction );
277 QTreeView::dropEvent( event );
280void QgsAttributesFormLayoutView::onItemDoubleClicked(
const QModelIndex &index )
282 QModelIndex sourceIndex =
mModel->mapToSource( index );
287 QGroupBox *baseData =
new QGroupBox( tr(
"Base configuration" ) );
289 QFormLayout *baseLayout =
new QFormLayout();
290 baseData->setLayout( baseLayout );
291 QCheckBox *showLabelCheckbox =
new QCheckBox( u
"Show label"_s );
292 showLabelCheckbox->setChecked( itemData.
showLabel() );
293 baseLayout->addRow( showLabelCheckbox );
294 QWidget *baseWidget =
new QWidget();
295 baseWidget->setLayout( baseLayout );
309 dlg.setObjectName(
"QML Form Configuration Widget" );
311 dlg.setWindowTitle( tr(
"Configure QML Widget" ) );
313 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
314 QSplitter *qmlSplitter =
new QSplitter();
315 QWidget *qmlConfigWiget =
new QWidget();
316 QVBoxLayout *layout =
new QVBoxLayout( qmlConfigWiget );
317 layout->setContentsMargins( 0, 0, 0, 0 );
318 mainLayout->addWidget( qmlSplitter );
319 qmlSplitter->addWidget( qmlConfigWiget );
320 layout->addWidget( baseWidget );
322 QLineEdit *title =
new QLineEdit( itemName );
341 QComboBox *qmlObjectTemplate =
new QComboBox();
342 qmlObjectTemplate->addItem( tr(
"Free Text…" ) );
343 qmlObjectTemplate->addItem( tr(
"Rectangle" ) );
344 qmlObjectTemplate->addItem( tr(
"Pie Chart" ) );
345 qmlObjectTemplate->addItem( tr(
"Bar Chart" ) );
346 connect( qmlObjectTemplate, qOverload<int>( &QComboBox::activated ), qmlCode, [qmlCode](
int index ) {
357 qmlCode->
setText( QStringLiteral(
"import QtQuick 2.0\n"
362 " color: \"steelblue\"\n"
363 " Text{ text: \"A rectangle\" }\n"
369 qmlCode->
setText( QStringLiteral(
"import QtQuick 2.0\n"
370 "import QtCharts 2.0\n"
378 " PieSlice { label: \"First slice\"; value: 25 }\n"
379 " PieSlice { label: \"Second slice\"; value: 45 }\n"
380 " PieSlice { label: \"Third slice\"; value: 30 }\n"
387 qmlCode->
setText( QStringLiteral(
"import QtQuick 2.0\n"
388 "import QtCharts 2.0\n"
391 " title: \"Bar series\"\n"
394 " legend.alignment: Qt.AlignBottom\n"
395 " antialiasing: true\n"
404 " axisY: valueAxisY\n"
405 " axisX: BarCategoryAxis { categories: [\"2007\", \"2008\", \"2009\", \"2010\", \"2011\", \"2012\" ] }\n"
406 " BarSet { label: \"Bob\"; values: [2, 2, 3, 4, 5, 6] }\n"
407 " BarSet { label: \"Susan\"; values: [5, 1, 2, 4, 1, 7] }\n"
408 " BarSet { label: \"James\"; values: [3, 5, 8, 13, 5, 8] }\n"
418 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
419 expressionWidget->setButtonVisible(
false );
420 expressionWidget->registerExpressionContextGenerator(
this );
421 expressionWidget->setLayer(
mLayer );
422 QToolButton *addFieldButton =
new QToolButton();
425 QToolButton *editExpressionButton =
new QToolButton();
427 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
429 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, qmlCode] {
430 QString expression = expressionWidget->expression().trimmed().replace(
'"',
"\\\""_L1 );
431 if ( !expression.isEmpty() )
432 qmlCode->
insertText( u
"expression.evaluate(\"%1\")"_s.arg( expression ) );
435 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, qmlCode] {
437 expression.replace(
"\\\""_L1,
"\""_L1 );
439 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
441 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
442 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
444 QString expression = exprDlg.expressionText().trimmed().replace(
'"',
"\\\""_L1 );
445 if ( !expression.isEmpty() )
446 qmlCode->
insertText( u
"expression.evaluate(\"%1\")"_s.arg( expression ) );
450 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
451 layout->addWidget( title );
452 QGroupBox *qmlCodeBox =
new QGroupBox( tr(
"QML Code" ) );
453 qmlCodeBox->setLayout(
new QVBoxLayout );
454 qmlCodeBox->layout()->addWidget( qmlObjectTemplate );
455 QWidget *expressionWidgetBox =
new QWidget();
456 qmlCodeBox->layout()->addWidget( expressionWidgetBox );
457 expressionWidgetBox->setLayout(
new QHBoxLayout );
458 expressionWidgetBox->layout()->setContentsMargins( 0, 0, 0, 0 );
459 expressionWidgetBox->layout()->addWidget( expressionWidget );
460 expressionWidgetBox->layout()->addWidget( addFieldButton );
461 expressionWidgetBox->layout()->addWidget( editExpressionButton );
462 expressionWidgetBox->layout()->addWidget( editExpressionButton );
463 layout->addWidget( qmlCodeBox );
464 layout->addWidget( qmlCode );
465 QScrollArea *qmlPreviewBox =
new QgsScrollArea();
466 qmlPreviewBox->setMinimumWidth( 200 );
467 qmlPreviewBox->setWidget( qmlWrapper->
widget() );
470 qmlSplitter->addWidget( qmlPreviewBox );
471 qmlSplitter->setChildrenCollapsible(
false );
472 qmlSplitter->setHandleWidth( 6 );
473 qmlSplitter->setSizes( QList<int>() << 1 << 1 );
475 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
477 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
478 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
479 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
480 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s );
483 mainLayout->addWidget( buttonBox );
487 QgsAttributesFormData::QmlElementEditorConfiguration qmlEdCfg;
488 qmlEdCfg.
qmlCode = qmlCode->text();
490 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
501 dlg.setObjectName(
"HTML Form Configuration Widget" );
503 dlg.setWindowTitle( tr(
"Configure HTML Widget" ) );
505 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
506 QSplitter *htmlSplitter =
new QSplitter();
507 QWidget *htmlConfigWiget =
new QWidget();
508 QVBoxLayout *layout =
new QVBoxLayout( htmlConfigWiget );
509 layout->setContentsMargins( 0, 0, 0, 0 );
510 mainLayout->addWidget( htmlSplitter );
511 htmlSplitter->addWidget( htmlConfigWiget );
512 htmlSplitter->setChildrenCollapsible(
false );
513 htmlSplitter->setHandleWidth( 6 );
514 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
515 layout->addWidget( baseWidget );
517 QLineEdit *title =
new QLineEdit( itemName );
520 QgsCodeEditorHTML *htmlCode =
new QgsCodeEditorHTML();
521 htmlCode->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
524 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper(
mLayer,
nullptr,
this );
525 QgsFeature previewFeature;
526 mLayer->getFeatures().nextFeature( previewFeature );
529 connect( htmlCode, &QgsCodeEditorHTML::textChanged,
this, [htmlWrapper, htmlCode, previewFeature] {
535 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
536 expressionWidget->setButtonVisible(
false );
537 expressionWidget->registerExpressionContextGenerator(
this );
538 expressionWidget->setLayer(
mLayer );
539 QToolButton *addFieldButton =
new QToolButton();
542 QToolButton *editExpressionButton =
new QToolButton();
544 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
546 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, htmlCode] {
547 QString expression = expressionWidget->expression().trimmed().replace(
'"',
"\\\""_L1 );
548 if ( !expression.isEmpty() )
549 htmlCode->
insertText( u
"<script>document.write(expression.evaluate(\"%1\"));</script>"_s.arg( expression ) );
552 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, htmlCode] {
554 expression.replace(
"\\\""_L1,
"\""_L1 );
556 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
558 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
559 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
561 QString expression = exprDlg.expressionText().trimmed().replace(
'"',
"\\\""_L1 );
562 if ( !expression.isEmpty() )
563 htmlCode->
insertText( u
"<script>document.write(expression.evaluate(\"%1\"));</script>"_s.arg( expression ) );
567 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
568 layout->addWidget( title );
569 QGroupBox *expressionWidgetBox =
new QGroupBox( tr(
"HTML Code" ) );
570 layout->addWidget( expressionWidgetBox );
571 expressionWidgetBox->setLayout(
new QHBoxLayout );
572 expressionWidgetBox->layout()->addWidget( expressionWidget );
573 expressionWidgetBox->layout()->addWidget( addFieldButton );
574 expressionWidgetBox->layout()->addWidget( editExpressionButton );
575 layout->addWidget( htmlCode );
576 QScrollArea *htmlPreviewBox =
new QgsScrollArea();
577 htmlPreviewBox->setLayout(
new QGridLayout );
578 htmlPreviewBox->setMinimumWidth( 200 );
579 htmlPreviewBox->layout()->addWidget( htmlWrapper->
widget() );
581 emit htmlCode->textChanged();
582 htmlSplitter->addWidget( htmlPreviewBox );
583 htmlSplitter->setChildrenCollapsible(
false );
584 htmlSplitter->setHandleWidth( 6 );
585 htmlSplitter->setSizes( QList<int>() << 1 << 1 );
587 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
589 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
590 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
591 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
592 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s );
595 mainLayout->addWidget( buttonBox );
599 QgsAttributesFormData::HtmlElementEditorConfiguration htmlEdCfg;
600 htmlEdCfg.
htmlCode = htmlCode->text();
602 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
613 dlg.setObjectName(
"Text Form Configuration Widget" );
615 dlg.setWindowTitle( tr(
"Configure Text Widget" ) );
617 QVBoxLayout *mainLayout =
new QVBoxLayout( &dlg );
618 QSplitter *textSplitter =
new QSplitter();
619 QWidget *textConfigWiget =
new QWidget();
620 QVBoxLayout *layout =
new QVBoxLayout( textConfigWiget );
621 layout->setContentsMargins( 0, 0, 0, 0 );
622 mainLayout->addWidget( textSplitter );
623 textSplitter->addWidget( textConfigWiget );
624 layout->addWidget( baseWidget );
626 QLineEdit *title =
new QLineEdit( itemName );
628 QgsCodeEditorHTML *text =
new QgsCodeEditorHTML();
629 text->setSizePolicy( QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding );
632 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper(
mLayer,
nullptr,
this );
633 QgsFeature previewFeature;
634 ( void )
mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( previewFeature );
637 connect( text, &QgsCodeEditorExpression::textChanged,
this, [textWrapper, previewFeature, text] {
638 textWrapper->
setText( text->text() );
643 QgsFieldExpressionWidget *expressionWidget =
new QgsFieldExpressionWidget;
644 expressionWidget->setButtonVisible(
false );
645 expressionWidget->registerExpressionContextGenerator(
this );
646 expressionWidget->setLayer(
mLayer );
647 QToolButton *addFieldButton =
new QToolButton();
650 QToolButton *editExpressionButton =
new QToolButton();
652 editExpressionButton->setToolTip( tr(
"Insert/Edit Expression" ) );
654 connect( addFieldButton, &QAbstractButton::clicked,
this, [expressionWidget, text] {
655 QString expression = expressionWidget->expression().trimmed();
656 if ( !expression.isEmpty() )
657 text->
insertText( u
"[%%1%]"_s.arg( expression ) );
659 connect( editExpressionButton, &QAbstractButton::clicked,
this, [
this, text] {
663 QgsExpressionBuilderDialog exprDlg(
mLayer, expression,
this, u
"generic"_s, context );
665 exprDlg.setWindowTitle( tr(
"Insert Expression" ) );
666 if ( exprDlg.exec() == QDialog::Accepted && !exprDlg.expressionText().trimmed().isEmpty() )
668 QString expression = exprDlg.expressionText().trimmed();
669 if ( !expression.isEmpty() )
670 text->
insertText( u
"[%%1%]"_s.arg( expression ) );
674 layout->addWidget(
new QLabel( tr(
"Title" ) ) );
675 layout->addWidget( title );
676 QGroupBox *expressionWidgetBox =
new QGroupBox( tr(
"Text" ) );
677 layout->addWidget( expressionWidgetBox );
678 expressionWidgetBox->setLayout(
new QHBoxLayout );
679 expressionWidgetBox->layout()->addWidget( expressionWidget );
680 expressionWidgetBox->layout()->addWidget( addFieldButton );
681 expressionWidgetBox->layout()->addWidget( editExpressionButton );
682 layout->addWidget( text );
683 QScrollArea *textPreviewBox =
new QgsScrollArea();
684 textPreviewBox->setLayout(
new QGridLayout );
685 textPreviewBox->setMinimumWidth( 200 );
686 textPreviewBox->layout()->addWidget( textWrapper->
widget() );
688 emit text->textChanged();
689 textSplitter->addWidget( textPreviewBox );
690 textSplitter->setChildrenCollapsible(
false );
691 textSplitter->setHandleWidth( 6 );
692 textSplitter->setSizes( QList<int>() << 1 << 1 );
694 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
696 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
697 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
698 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
699 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s );
702 mainLayout->addWidget( buttonBox );
706 QgsAttributesFormData::TextElementEditorConfiguration textEdCfg;
707 textEdCfg.
text = text->text();
709 itemData.
setShowLabel( showLabelCheckbox->isChecked() );
720 dlg.setObjectName(
"Spacer Form Configuration Widget" );
722 dlg.setWindowTitle( tr(
"Configure Spacer Widget" ) );
724 QVBoxLayout *mainLayout =
new QVBoxLayout();
725 mainLayout->addWidget(
new QLabel( tr(
"Title" ) ) );
726 QLineEdit *title =
new QLineEdit( itemName );
727 mainLayout->addWidget( title );
729 QHBoxLayout *cbLayout =
new QHBoxLayout();
730 mainLayout->addLayout( cbLayout );
731 dlg.setLayout( mainLayout );
732 QCheckBox *cb =
new QCheckBox { &dlg };
734 cbLayout->addWidget(
new QLabel( tr(
"Draw horizontal line" ), &dlg ) );
735 cbLayout->addWidget( cb );
737 QDialogButtonBox *buttonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help );
739 connect( buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
740 connect( buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
741 connect( buttonBox, &QDialogButtonBox::helpRequested, &dlg, [] {
742 QgsHelp::openHelp( u
"working_with_vector/vector_properties.html#other-widgets"_s );
745 mainLayout->addWidget( buttonBox );
749 QgsAttributesFormData::SpacerElementEditorConfiguration spacerEdCfg;
750 spacerEdCfg.
drawLine = cb->isChecked();
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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:
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 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.
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...
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...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
static QgsProject * instance()
Returns the QgsProject singleton instance.
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 dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.