1 /***************************************************************************
2                               qgsjoindialog.cpp
3                               --------------------
4   begin                : July 10, 2010
5   copyright            : (C) 2010 by Marco Hugentobler
6   email                : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsjoindialog.h"
19 #include "qgsmaplayer.h"
20 #include "qgsproject.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsvectorlayerjoininfo.h"
24 #include "qgsmaplayercombobox.h"
25 #include "qgsfieldcombobox.h"
26 #include "qgshelp.h"
27 
28 #include <QStandardItemModel>
29 #include <QPushButton>
30 
QgsJoinDialog(QgsVectorLayer * layer,QList<QgsMapLayer * > alreadyJoinedLayers,QWidget * parent,Qt::WindowFlags f)31 QgsJoinDialog::QgsJoinDialog( QgsVectorLayer *layer, QList<QgsMapLayer *> alreadyJoinedLayers, QWidget *parent, Qt::WindowFlags f )
32   : QDialog( parent, f )
33   , mLayer( layer )
34 {
35   setupUi( this );
36   connect( buttonBox, &QDialogButtonBox::helpRequested, this,  [ = ]
37   {
38     QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#joins-properties" ) );
39   } );
40 
41   if ( !mLayer )
42   {
43     return;
44   }
45   // adds self layer to the joined layer (cannot join to itself)
46   alreadyJoinedLayers.append( layer );
47 
48   mTargetFieldComboBox->setLayer( mLayer );
49 
50   mDynamicFormCheckBox->setToolTip( tr( "This option allows values of the joined fields to be automatically reloaded when the \"Target Field\" is changed" ) );
51 
52   mEditableJoinLayer->setToolTip( tr( "This option allows values of the joined layers to be editable if they're themselves editable" ) );
53   mUpsertOnEditCheckBox->setToolTip( tr( "Automatically adds a matching row to the joined table, but if one already exists then update that matching row instead" ) );
54   mDeleteCascadeCheckBox->setToolTip( tr( "Automatically delete the corresponding feature of the linked layer if one exists" ) );
55 
56   mJoinLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer );
57   mJoinLayerComboBox->setExceptedLayerList( alreadyJoinedLayers );
58   connect( mJoinLayerComboBox, &QgsMapLayerComboBox::layerChanged, mJoinFieldComboBox, &QgsFieldComboBox::setLayer );
59   connect( mJoinLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsJoinDialog::joinedLayerChanged );
60 
61   mCacheInMemoryCheckBox->setChecked( true );
62   mCacheEnabled = mCacheInMemoryCheckBox->isChecked();
63 
64   QgsMapLayer *joinLayer = mJoinLayerComboBox->currentLayer();
65   if ( joinLayer && joinLayer->isValid() )
66   {
67     mJoinFieldComboBox->setLayer( joinLayer );
68     joinedLayerChanged( joinLayer );
69   }
70 
71   connect( mJoinLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsJoinDialog::checkDefinitionValid );
72   connect( mJoinFieldComboBox, &QgsFieldComboBox::fieldChanged, this, &QgsJoinDialog::checkDefinitionValid );
73   connect( mTargetFieldComboBox, &QgsFieldComboBox::fieldChanged, this, &QgsJoinDialog::checkDefinitionValid );
74   connect( mEditableJoinLayer, &QGroupBox::toggled, this, &QgsJoinDialog::editableJoinLayerChanged );
75 
76   checkDefinitionValid();
77 }
78 
setJoinInfo(const QgsVectorLayerJoinInfo & joinInfo)79 void QgsJoinDialog::setJoinInfo( const QgsVectorLayerJoinInfo &joinInfo )
80 {
81   mJoinLayerComboBox->setLayer( joinInfo.joinLayer() );
82   mJoinFieldComboBox->setField( joinInfo.joinFieldName() );
83   mTargetFieldComboBox->setField( joinInfo.targetFieldName() );
84 
85   mCacheEnabled = joinInfo.isUsingMemoryCache();
86   mCacheInMemoryCheckBox->setChecked( joinInfo.isUsingMemoryCache() );
87 
88   mDynamicFormCheckBox->setChecked( joinInfo.isDynamicFormEnabled() );
89   mEditableJoinLayer->setChecked( joinInfo.isEditable() );
90   mUpsertOnEditCheckBox->setChecked( joinInfo.hasUpsertOnEdit() );
91   mDeleteCascadeCheckBox->setChecked( joinInfo.hasCascadedDelete() );
92 
93   if ( joinInfo.prefix().isNull() )
94   {
95     mUseCustomPrefix->setChecked( false );
96   }
97   else
98   {
99     mUseCustomPrefix->setChecked( true );
100     mCustomPrefix->setText( joinInfo.prefix() );
101   }
102 
103   QStringList *lst = joinInfo.joinFieldNamesSubset();
104   mUseJoinFieldsSubset->setChecked( lst && !lst->isEmpty() );
105   QAbstractItemModel *model = mJoinFieldsSubsetView->model();
106   if ( model )
107   {
108     for ( int i = 0; i < model->rowCount(); ++i )
109     {
110       const QModelIndex index = model->index( i, 0 );
111       if ( lst && lst->contains( model->data( index, Qt::DisplayRole ).toString() ) )
112       {
113         model->setData( index, Qt::Checked, Qt::CheckStateRole );
114       }
115       else
116       {
117         model->setData( index, Qt::Unchecked, Qt::CheckStateRole );
118       }
119     }
120   }
121 
122   editableJoinLayerChanged();
123 }
124 
joinInfo() const125 QgsVectorLayerJoinInfo QgsJoinDialog::joinInfo() const
126 {
127   QgsVectorLayerJoinInfo info;
128   info.setJoinLayer( qobject_cast<QgsVectorLayer *>( mJoinLayerComboBox->currentLayer() ) );
129   info.setJoinFieldName( mJoinFieldComboBox->currentField() );
130   info.setTargetFieldName( mTargetFieldComboBox->currentField() );
131   info.setUsingMemoryCache( mCacheInMemoryCheckBox->isChecked() );
132   info.setDynamicFormEnabled( mDynamicFormCheckBox->isChecked() );
133 
134   info.setEditable( mEditableJoinLayer->isChecked() );
135   if ( info.isEditable() )
136   {
137     info.setUpsertOnEdit( mUpsertOnEditCheckBox->isChecked() );
138     info.setCascadedDelete( mDeleteCascadeCheckBox->isChecked() );
139   }
140 
141   if ( mUseCustomPrefix->isChecked() )
142     info.setPrefix( mCustomPrefix->text() );
143   else
144     info.setPrefix( QString() );
145 
146   if ( mUseJoinFieldsSubset->isChecked() )
147   {
148     QStringList lst;
149     QAbstractItemModel *model = mJoinFieldsSubsetView->model();
150     if ( model )
151     {
152       for ( int i = 0; i < model->rowCount(); ++i )
153       {
154         const QModelIndex index = model->index( i, 0 );
155         if ( model->data( index, Qt::CheckStateRole ).toInt() == Qt::Checked )
156           lst << model->data( index ).toString();
157       }
158     }
159     info.setJoinFieldNamesSubset( new QStringList( lst ) );
160   }
161 
162   return info;
163 }
164 
createAttributeIndex() const165 bool QgsJoinDialog::createAttributeIndex() const
166 {
167   return mCreateIndexCheckBox->isChecked();
168 }
169 
joinedLayerChanged(QgsMapLayer * layer)170 void QgsJoinDialog::joinedLayerChanged( QgsMapLayer *layer )
171 {
172   mJoinFieldComboBox->clear();
173 
174   QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( layer );
175   if ( !vLayer )
176   {
177     return;
178   }
179 
180   mUseJoinFieldsSubset->setChecked( false );
181   QStandardItemModel *subsetModel = new QStandardItemModel( this );
182   const QgsFields layerFields = vLayer->fields();
183   for ( const QgsField &field : layerFields )
184   {
185     QStandardItem *subsetItem = new QStandardItem( field.name() );
186     subsetItem->setCheckable( true );
187     //subsetItem->setFlags( subsetItem->flags() | Qt::ItemIsUserCheckable );
188     subsetModel->appendRow( subsetItem );
189   }
190   mJoinFieldsSubsetView->setModel( subsetModel );
191 
192   QgsVectorDataProvider *dp = vLayer->dataProvider();
193   const bool canCreateAttrIndex = dp && ( dp->capabilities() & QgsVectorDataProvider::CreateAttributeIndex );
194   if ( canCreateAttrIndex )
195   {
196     mCreateIndexCheckBox->setEnabled( true );
197   }
198   else
199   {
200     mCreateIndexCheckBox->setEnabled( false );
201     mCreateIndexCheckBox->setChecked( false );
202   }
203 
204   if ( !mUseCustomPrefix->isChecked() )
205   {
206     mCustomPrefix->setText( layer->name() + '_' );
207   }
208 }
209 
checkDefinitionValid()210 void QgsJoinDialog::checkDefinitionValid()
211 {
212   buttonBox->button( QDialogButtonBox::Ok )->setEnabled( mJoinLayerComboBox->currentIndex() != -1
213       && mJoinFieldComboBox->currentIndex() != -1
214       && mTargetFieldComboBox->currentIndex() != -1 );
215 }
216 
editableJoinLayerChanged()217 void QgsJoinDialog::editableJoinLayerChanged()
218 {
219   if ( mEditableJoinLayer->isChecked() )
220   {
221     mCacheInMemoryCheckBox->setEnabled( false );
222     mCacheInMemoryCheckBox->setToolTip( tr( "Caching can not be enabled if editable join layer is enabled" ) );
223     mCacheEnabled = mCacheInMemoryCheckBox->isChecked();
224     mCacheInMemoryCheckBox->setChecked( false );
225   }
226   else
227   {
228     mCacheInMemoryCheckBox->setEnabled( true );
229     mCacheInMemoryCheckBox->setToolTip( QString() );
230     mCacheInMemoryCheckBox->setChecked( mCacheEnabled );
231   }
232 }
233