QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditor.cpp
3  --------------------------------------
4  Date : 17.5.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias dot kuhn at gmx 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 
17 
18 #include "qgsapplication.h"
19 #include "qgsdistancearea.h"
20 #include "qgsvectordataprovider.h"
21 #include "qgsexpression.h"
22 #include "qgsfeature.h"
23 #include "qgsfeatureselectiondlg.h"
25 #include "qgsrelation.h"
26 #include "qgsvectorlayertools.h"
27 
28 #include <QHBoxLayout>
29 #include <QLabel>
30 
32  : QgsCollapsibleGroupBox( parent )
33  , mViewMode( QgsDualView::AttributeEditor )
34  , mEditorContext( QgsAttributeEditorContext() )
35  , mRelation( QgsRelation() )
36  , mFeature( QgsFeature() )
37  , mInitialized( false )
38 {
39  QVBoxLayout* topLayout = new QVBoxLayout( this );
40  topLayout->setContentsMargins( 0, 9, 0, 0 );
41  setLayout( topLayout );
42 
43  // buttons
44  QHBoxLayout* buttonLayout = new QHBoxLayout();
45  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
46  // toogle editing
47  mToggleEditingButton = new QToolButton( this );
48  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( "/mActionToggleEditing.svg" ) );
49  mToggleEditingButton->setText( tr( "Toggle editing" ) );
50  mToggleEditingButton->setEnabled( false );
51  mToggleEditingButton->setCheckable( true );
52  buttonLayout->addWidget( mToggleEditingButton );
53  // save Edits
54  mSaveEditsButton = new QToolButton( this );
55  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( "/mActionSaveEdits.svg" ) );
56  mSaveEditsButton->setText( tr( "Save layer edits" ) );
57  mSaveEditsButton->setEnabled( true );
58  buttonLayout->addWidget( mSaveEditsButton );
59  // add feature
60  mAddFeatureButton = new QToolButton( this );
61  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionAdd.svg" ) );
62  mAddFeatureButton->setText( tr( "Add feature" ) );
63  buttonLayout->addWidget( mAddFeatureButton );
64  // delete feature
65  mDeleteFeatureButton = new QToolButton( this );
66  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.svg" ) );
67  mDeleteFeatureButton->setText( tr( "Delete feature" ) );
68  buttonLayout->addWidget( mDeleteFeatureButton );
69  // link feature
70  mLinkFeatureButton = new QToolButton( this );
71  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionLink.svg" ) );
72  mLinkFeatureButton->setText( tr( "Link feature" ) );
73  buttonLayout->addWidget( mLinkFeatureButton );
74  // unlink feature
75  mUnlinkFeatureButton = new QToolButton( this );
76  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( "/mActionUnlink.svg" ) );
77  mUnlinkFeatureButton->setText( tr( "Unlink feature" ) );
78  buttonLayout->addWidget( mUnlinkFeatureButton );
79  // spacer
80  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
81  // form view
82  mFormViewButton = new QToolButton( this );
83  mFormViewButton->setText( tr( "Form view" ) );
84  mFormViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) );
85  mFormViewButton->setCheckable( true );
86  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
87  buttonLayout->addWidget( mFormViewButton );
88  // table view
89  mTableViewButton = new QToolButton( this );
90  mTableViewButton->setText( tr( "Table view" ) );
91  mTableViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) );
92  mTableViewButton->setCheckable( true );
93  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
94  buttonLayout->addWidget( mTableViewButton );
95  // button group
96  mViewModeButtonGroup = new QButtonGroup( this );
97  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
98  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
99 
100  // add buttons layout
101  topLayout->addLayout( buttonLayout );
102 
103  // Set initial state for add/remove etc. buttons
104  referencingLayerEditingToggled();
105 
106  mRelationLayout = new QGridLayout();
107  mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
108  topLayout->addLayout( mRelationLayout );
109 
110  mDualView = new QgsDualView( this );
111  mDualView->setView( mViewMode );
112  mFeatureSelectionMgr = new QgsGenericFeatureSelectionManager( mDualView );
113  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
114 
115  mRelationLayout->addWidget( mDualView );
116 
117  connect( this, SIGNAL( collapsedStateChanged( bool ) ), this, SLOT( onCollapsedStateChanged( bool ) ) );
118  connect( mViewModeButtonGroup, SIGNAL( buttonClicked( int ) ), this, SLOT( setViewMode( int ) ) );
119  connect( mToggleEditingButton, SIGNAL( clicked( bool ) ), this, SLOT( toggleEditing( bool ) ) );
120  connect( mSaveEditsButton, SIGNAL( clicked() ), this, SLOT( saveEdits() ) );
121  connect( mAddFeatureButton, SIGNAL( clicked() ), this, SLOT( addFeature() ) );
122  connect( mDeleteFeatureButton, SIGNAL( clicked() ), this, SLOT( deleteFeature() ) );
123  connect( mLinkFeatureButton, SIGNAL( clicked() ), this, SLOT( linkFeature() ) );
124  connect( mUnlinkFeatureButton, SIGNAL( clicked() ), this, SLOT( unlinkFeature() ) );
125 }
126 
128 {
129  if ( mRelation.isValid() )
130  {
131  disconnect( mRelation.referencingLayer(), SIGNAL( editingStarted() ), this, SLOT( referencingLayerEditingToggled() ) );
132  disconnect( mRelation.referencingLayer(), SIGNAL( editingStopped() ), this, SLOT( referencingLayerEditingToggled() ) );
133  }
134 
135  mRelation = relation;
136  mFeature = feature;
137 
138  connect( mRelation.referencingLayer(), SIGNAL( editingStarted() ), this, SLOT( referencingLayerEditingToggled() ) );
139  connect( mRelation.referencingLayer(), SIGNAL( editingStopped() ), this, SLOT( referencingLayerEditingToggled() ) );
140 
141  setTitle( relation.name() );
142 
143  QgsVectorLayer* lyr = relation.referencingLayer();
144 
145  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
146  if ( canChangeAttributes && !lyr->isReadOnly() )
147  {
148  mToggleEditingButton->setEnabled( true );
149  referencingLayerEditingToggled();
150  }
151  else
152  {
153  mToggleEditingButton->setEnabled( false );
154  }
155 
156  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
157  // If it is already initialized, it has been set visible before and the currently shown feature is changing
158  // and the widget needs updating
159 
160  if ( mInitialized )
161  {
162  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
163 
164  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
165  }
166 }
167 
169 {
170  mEditorContext = context;
171 }
172 
174 {
175  mDualView->setView( mode );
176  mViewMode = mode;
177 }
178 
179 void QgsRelationEditorWidget::referencingLayerEditingToggled()
180 {
181  bool editable = false;
182  if ( mRelation.isValid() )
183  {
184  editable = mRelation.referencingLayer()->isEditable();
185  }
186 
187  mAddFeatureButton->setEnabled( editable );
188  mLinkFeatureButton->setEnabled( editable );
189  mDeleteFeatureButton->setEnabled( editable );
190  mUnlinkFeatureButton->setEnabled( editable );
191  mToggleEditingButton->setChecked( editable );
192  mSaveEditsButton->setEnabled( editable );
193 }
194 
195 void QgsRelationEditorWidget::addFeature()
196 {
197  QgsAttributeMap keyAttrs;
198 
199  QgsFields fields = mRelation.referencingLayer()->pendingFields();
200 
201  Q_FOREACH ( QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
202  {
203  keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
204  }
205 
206  mEditorContext.vectorLayerTools()->addFeature( mDualView->masterModel()->layer(), keyAttrs );
207 }
208 
209 void QgsRelationEditorWidget::linkFeature()
210 {
211  QgsFeatureSelectionDlg selectionDlg( mRelation.referencingLayer(), this );
212 
213  if ( selectionDlg.exec() )
214  {
215  QMap<int, QVariant> keys;
216  Q_FOREACH ( const QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
217  {
218  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
219  QVariant val = mFeature.attribute( fieldPair.referencedField() );
220  keys.insert( idx, val );
221  }
222 
223  Q_FOREACH ( QgsFeatureId fid, selectionDlg.selectedFeatures() )
224  {
225  QMapIterator<int, QVariant> it( keys );
226  while ( it.hasNext() )
227  {
228  it.next();
229  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
230  }
231  }
232  }
233 }
234 
235 void QgsRelationEditorWidget::deleteFeature()
236 {
237  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
238  {
239  mRelation.referencingLayer()->deleteFeature( fid );
240  }
241 }
242 
243 void QgsRelationEditorWidget::unlinkFeature()
244 {
245  QMap<int, QgsField> keyFields;
246  Q_FOREACH ( const QgsRelation::FieldPair fieldPair, mRelation.fieldPairs() )
247  {
248  int idx = mRelation.referencingLayer()->fieldNameIndex( fieldPair.referencingField() );
249  if ( idx < 0 )
250  {
251  QgsDebugMsg( QString( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
252  return;
253  }
254  QgsField fld = mRelation.referencingLayer()->pendingFields().at( idx );
255  keyFields.insert( idx, fld );
256  }
257 
258  Q_FOREACH ( QgsFeatureId fid, mFeatureSelectionMgr->selectedFeaturesIds() )
259  {
260  QMapIterator<int, QgsField> it( keyFields );
261  while ( it.hasNext() )
262  {
263  it.next();
264  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
265  }
266  }
267 }
268 
269 void QgsRelationEditorWidget::toggleEditing( bool state )
270 {
271  if ( state )
272  {
273  mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
274  }
275  else
276  {
277  mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
278  }
279 }
280 
281 void QgsRelationEditorWidget::saveEdits()
282 {
283  mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
284 }
285 
286 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
287 {
288  if ( !mInitialized && !collapsed && mRelation.isValid() )
289  {
290  mInitialized = true;
291 
292  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
293 
294  mDualView->init( mRelation.referencingLayer(), 0, myRequest, mEditorContext );
295  }
296 }