1 /***************************************************************************
2     qgsrelationreferenceconfigdlg.cpp
3      --------------------------------------
4     Date                 : 21.4.2013
5     Copyright            : (C) 2013 Matthias Kuhn
6     Email                : matthias 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 "qgsrelationreferenceconfigdlg.h"
17 
18 #include "qgseditorwidgetfactory.h"
19 #include "qgsfields.h"
20 #include "qgsproject.h"
21 #include "qgsrelationmanager.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsexpressionbuilderdialog.h"
24 #include "qgsexpressioncontextutils.h"
25 
QgsRelationReferenceConfigDlg(QgsVectorLayer * vl,int fieldIdx,QWidget * parent)26 QgsRelationReferenceConfigDlg::QgsRelationReferenceConfigDlg( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
27   : QgsEditorConfigWidget( vl, fieldIdx, parent )
28 
29 {
30   setupUi( this );
31   connect( mAddFilterButton, &QToolButton::clicked, this, &QgsRelationReferenceConfigDlg::mAddFilterButton_clicked );
32   connect( mRemoveFilterButton, &QToolButton::clicked, this, &QgsRelationReferenceConfigDlg::mRemoveFilterButton_clicked );
33 
34   mExpressionWidget->registerExpressionContextGenerator( vl );
35 
36   connect( mComboRelation, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRelationReferenceConfigDlg::relationChanged );
37 
38   const auto constReferencingRelations = vl->referencingRelations( fieldIdx );
39   for ( const QgsRelation &relation : constReferencingRelations )
40   {
41     if ( relation.name().isEmpty() )
42       mComboRelation->addItem( QStringLiteral( "%1 (%2)" ).arg( relation.id(), relation.referencedLayerId() ), relation.id() );
43     else
44       mComboRelation->addItem( QStringLiteral( "%1 (%2)" ).arg( relation.name(), relation.referencedLayerId() ), relation.id() );
45     if ( auto *lReferencedLayer = relation.referencedLayer() )
46     {
47       mExpressionWidget->setField( lReferencedLayer->displayExpression() );
48     }
49   }
50 
51   connect( mCbxAllowNull, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
52   connect( mCbxOrderByValue, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
53   connect( mCbxShowForm, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
54   connect( mCbxShowOpenFormButton, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
55   connect( mCbxMapIdentification, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
56   connect( mCbxReadOnly, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
57   connect( mComboRelation, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsEditorConfigWidget::changed );
58   connect( mCbxAllowAddFeatures, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
59   connect( mFilterGroupBox, &QGroupBox::toggled, this, &QgsEditorConfigWidget::changed );
60   connect( mFilterFieldsList, &QListWidget::itemChanged, this, &QgsEditorConfigWidget::changed );
61   connect( mCbxChainFilters, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
62   connect( mExpressionWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString & )>( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsEditorConfigWidget::changed );
63   connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationReferenceConfigDlg::mEditExpression_clicked );
64   connect( mFilterExpression, &QTextEdit::textChanged, this, &QgsEditorConfigWidget::changed );
65 }
66 
mEditExpression_clicked()67 void QgsRelationReferenceConfigDlg::mEditExpression_clicked()
68 {
69   QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer() );
70   if ( !vl )
71     return;
72 
73   QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vl ) );
74   context << QgsExpressionContextUtils::formScope( );
75   context << QgsExpressionContextUtils::parentFormScope( );
76 
77   context.setHighlightedFunctions( QStringList() << QStringLiteral( "current_value" ) << QStringLiteral( "current_parent_value" ) );
78   context.setHighlightedVariables( QStringList() << QStringLiteral( "current_geometry" )
79                                    << QStringLiteral( "current_feature" )
80                                    << QStringLiteral( "form_mode" )
81                                    << QStringLiteral( "current_parent_geometry" )
82                                    << QStringLiteral( "current_parent_feature" ) );
83 
84   QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, QStringLiteral( "generic" ), context );
85   dlg.setWindowTitle( tr( "Edit Filter Expression" ) );
86 
87   if ( dlg.exec() == QDialog::Accepted )
88   {
89     mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
90   }
91 }
92 
setConfig(const QVariantMap & config)93 void QgsRelationReferenceConfigDlg::setConfig( const QVariantMap &config )
94 {
95   mCbxAllowNull->setChecked( config.value( QStringLiteral( "AllowNULL" ), false ).toBool() );
96   mCbxOrderByValue->setChecked( config.value( QStringLiteral( "OrderByValue" ), false ).toBool() );
97   mCbxShowForm->setChecked( config.value( QStringLiteral( "ShowForm" ), false ).toBool() );
98   mCbxShowOpenFormButton->setChecked( config.value( QStringLiteral( "ShowOpenFormButton" ), true ).toBool() );
99 
100   if ( config.contains( QStringLiteral( "Relation" ) ) )
101   {
102     mComboRelation->setCurrentIndex( mComboRelation->findData( config.value( QStringLiteral( "Relation" ) ).toString() ) );
103     relationChanged( mComboRelation->currentIndex() );
104   }
105 
106   mCbxMapIdentification->setChecked( config.value( QStringLiteral( "MapIdentification" ), false ).toBool() );
107   mCbxAllowAddFeatures->setChecked( config.value( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );
108   mCbxReadOnly->setChecked( config.value( QStringLiteral( "ReadOnly" ), false ).toBool() );
109   mFilterExpression->setPlainText( config.value( QStringLiteral( "FilterExpression" ) ).toString() );
110 
111   if ( config.contains( QStringLiteral( "FilterFields" ) ) )
112   {
113     mFilterGroupBox->setChecked( true );
114     const auto constToStringList = config.value( "FilterFields" ).toStringList();
115     for ( const QString &fld : constToStringList )
116     {
117       addFilterField( fld );
118     }
119 
120     mCbxChainFilters->setChecked( config.value( QStringLiteral( "ChainFilters" ) ).toBool() );
121   }
122 }
123 
relationChanged(int idx)124 void QgsRelationReferenceConfigDlg::relationChanged( int idx )
125 {
126   QString relName = mComboRelation->itemData( idx ).toString();
127   QgsRelation rel = QgsProject::instance()->relationManager()->relation( relName );
128 
129   mReferencedLayer = rel.referencedLayer();
130   mExpressionWidget->setLayer( mReferencedLayer ); // set even if 0
131   if ( mReferencedLayer )
132   {
133     mExpressionWidget->setField( mReferencedLayer->displayExpression() );
134     mCbxMapIdentification->setEnabled( mReferencedLayer->isSpatial() );
135   }
136 
137   loadFields();
138 }
139 
mAddFilterButton_clicked()140 void QgsRelationReferenceConfigDlg::mAddFilterButton_clicked()
141 {
142   const auto constSelectedItems = mAvailableFieldsList->selectedItems();
143   for ( QListWidgetItem *item : constSelectedItems )
144   {
145     addFilterField( item );
146   }
147 }
148 
mRemoveFilterButton_clicked()149 void QgsRelationReferenceConfigDlg::mRemoveFilterButton_clicked()
150 {
151   const auto constSelectedItems = mFilterFieldsList->selectedItems();
152   for ( QListWidgetItem *item : constSelectedItems )
153   {
154     mFilterFieldsList->takeItem( indexFromListWidgetItem( item ) );
155     mAvailableFieldsList->addItem( item );
156   }
157 }
158 
config()159 QVariantMap QgsRelationReferenceConfigDlg::config()
160 {
161   QVariantMap myConfig;
162   myConfig.insert( QStringLiteral( "AllowNULL" ), mCbxAllowNull->isChecked() );
163   myConfig.insert( QStringLiteral( "OrderByValue" ), mCbxOrderByValue->isChecked() );
164   myConfig.insert( QStringLiteral( "ShowForm" ), mCbxShowForm->isChecked() );
165   myConfig.insert( QStringLiteral( "ShowOpenFormButton" ), mCbxShowOpenFormButton->isChecked() );
166   myConfig.insert( QStringLiteral( "MapIdentification" ), mCbxMapIdentification->isEnabled() && mCbxMapIdentification->isChecked() );
167   myConfig.insert( QStringLiteral( "ReadOnly" ), mCbxReadOnly->isChecked() );
168   myConfig.insert( QStringLiteral( "Relation" ), mComboRelation->currentData() );
169   myConfig.insert( QStringLiteral( "AllowAddFeatures" ), mCbxAllowAddFeatures->isChecked() );
170 
171   if ( mFilterGroupBox->isChecked() )
172   {
173     QStringList filterFields;
174     filterFields.reserve( mFilterFieldsList->count() );
175     for ( int i = 0; i < mFilterFieldsList->count(); i++ )
176     {
177       filterFields << mFilterFieldsList->item( i )->data( Qt::UserRole ).toString();
178     }
179     myConfig.insert( QStringLiteral( "FilterFields" ), filterFields );
180 
181     myConfig.insert( QStringLiteral( "ChainFilters" ), mCbxChainFilters->isChecked() );
182     myConfig.insert( QStringLiteral( "FilterExpression" ), mFilterExpression->toPlainText() );
183   }
184 
185   if ( mReferencedLayer )
186   {
187     // Store referenced layer data source and provider
188     myConfig.insert( QStringLiteral( "ReferencedLayerDataSource" ), mReferencedLayer->publicSource() );
189     myConfig.insert( QStringLiteral( "ReferencedLayerProviderKey" ), mReferencedLayer->providerType() );
190     myConfig.insert( QStringLiteral( "ReferencedLayerId" ), mReferencedLayer->id() );
191     myConfig.insert( QStringLiteral( "ReferencedLayerName" ), mReferencedLayer->name() );
192     mReferencedLayer->setDisplayExpression( mExpressionWidget->currentField() );
193   }
194 
195   return myConfig;
196 }
197 
loadFields()198 void QgsRelationReferenceConfigDlg::loadFields()
199 {
200   mAvailableFieldsList->clear();
201   mFilterFieldsList->clear();
202 
203   if ( mReferencedLayer )
204   {
205     QgsVectorLayer *l = mReferencedLayer;
206     const QgsFields &flds = l->fields();
207     for ( int i = 0; i < flds.count(); i++ )
208     {
209       mAvailableFieldsList->addItem( flds.at( i ).displayName() );
210       mAvailableFieldsList->item( mAvailableFieldsList->count() - 1 )->setData( Qt::UserRole, flds.at( i ).name() );
211     }
212   }
213 }
214 
addFilterField(const QString & field)215 void QgsRelationReferenceConfigDlg::addFilterField( const QString &field )
216 {
217   for ( int i = 0; i < mAvailableFieldsList->count(); i++ )
218   {
219     if ( mAvailableFieldsList->item( i )->data( Qt::UserRole ).toString() == field )
220     {
221       addFilterField( mAvailableFieldsList->item( i ) );
222       break;
223     }
224   }
225 }
226 
addFilterField(QListWidgetItem * item)227 void QgsRelationReferenceConfigDlg::addFilterField( QListWidgetItem *item )
228 {
229   mAvailableFieldsList->takeItem( indexFromListWidgetItem( item ) );
230   mFilterFieldsList->addItem( item );
231 }
232 
indexFromListWidgetItem(QListWidgetItem * item)233 int QgsRelationReferenceConfigDlg::indexFromListWidgetItem( QListWidgetItem *item )
234 {
235   QListWidget *lw = item->listWidget();
236 
237   for ( int i = 0; i < lw->count(); i++ )
238   {
239     if ( lw->item( i ) == item )
240       return i;
241   }
242 
243   return -1;
244 }
245