QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditorwidget.cpp
3  --------------------------------------
4  Date : 17.5.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 
17 
18 #include "qgsapplication.h"
19 #include "qgsdistancearea.h"
20 #include "qgsfeatureiterator.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsexpression.h"
23 #include "qgsfeature.h"
24 #include "qgsfeatureselectiondlg.h"
26 #include "qgsrelation.h"
27 #include "qgsvectorlayertools.h"
28 #include "qgsproject.h"
29 #include "qgstransactiongroup.h"
30 #include "qgslogger.h"
31 #include "qgsvectorlayerutils.h"
32 #include "qgsmapcanvas.h"
36 #include "qgsmessagebar.h"
37 #include "qgsmessagebaritem.h"
38 #include "qgscollapsiblegroupbox.h"
39 
40 #include <QHBoxLayout>
41 #include <QLabel>
42 #include <QMessageBox>
43 #include <QPushButton>
44 
47 QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
48  : QgsVectorLayerSelectionManager( layer, parent )
49  , mRequest( request )
50 {
51  if ( ! layer )
52  return;
53 
54  for ( auto fid : layer->selectedFeatureIds() )
55  if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
56  mSelectedFeatureIds << fid;
57 
58  connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
59 }
60 
61 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
62 
63 
64 {
65  return mSelectedFeatureIds;
66 }
67 
68 int QgsFilteredSelectionManager::selectedFeatureCount()
69 {
70  return mSelectedFeatureIds.count();
71 }
72 
73 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
74 {
75  QgsFeatureIds lselected = selected;
76  if ( clearAndSelect )
77  {
78  mSelectedFeatureIds.clear();
79  }
80  else
81  {
82  for ( auto fid : deselected )
83  mSelectedFeatureIds.remove( fid );
84  }
85 
86  for ( auto fid : selected )
87  if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
88  mSelectedFeatureIds << fid;
89  else
90  lselected.remove( fid );
91 
92  emit selectionChanged( lselected, deselected, clearAndSelect );
93 }
94 
96 
97 QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
98  : QgsAbstractRelationEditorWidget( config, parent )
99  , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
100 {
101  QVBoxLayout *rootLayout = new QVBoxLayout( this );
102  rootLayout->setContentsMargins( 0, 9, 0, 0 );
103 
104  // buttons
105  QHBoxLayout *buttonLayout = new QHBoxLayout();
106  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
107  // toggle editing
108  mToggleEditingButton = new QToolButton( this );
109  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
110  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
111  mToggleEditingButton->setText( tr( "Toggle Editing" ) );
112  mToggleEditingButton->setEnabled( false );
113  mToggleEditingButton->setCheckable( true );
114  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
115  buttonLayout->addWidget( mToggleEditingButton );
116  // save Edits
117  mSaveEditsButton = new QToolButton( this );
118  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
119  mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
120  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
121  mSaveEditsButton->setEnabled( true );
122  buttonLayout->addWidget( mSaveEditsButton );
123  // add feature with geometry
124  mAddFeatureGeometryButton = new QToolButton( this );
125  mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
126  buttonLayout->addWidget( mAddFeatureGeometryButton );
127  // add feature
128  mAddFeatureButton = new QToolButton( this );
129  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
130  mAddFeatureButton->setText( tr( "Add Child Feature" ) );
131  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
132  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
133  buttonLayout->addWidget( mAddFeatureButton );
134  // duplicate feature
135  mDuplicateFeatureButton = new QToolButton( this );
136  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
137  mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) );
138  mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature" ) );
139  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
140  buttonLayout->addWidget( mDuplicateFeatureButton );
141  // delete feature
142  mDeleteFeatureButton = new QToolButton( this );
143  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
144  mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) );
145  mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature" ) );
146  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
147  buttonLayout->addWidget( mDeleteFeatureButton );
148  // link feature
149  mLinkFeatureButton = new QToolButton( this );
150  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
151  mLinkFeatureButton->setText( tr( "Link Existing Features" ) );
152  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
153  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
154  buttonLayout->addWidget( mLinkFeatureButton );
155  // unlink feature
156  mUnlinkFeatureButton = new QToolButton( this );
157  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
158  mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) );
159  mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature" ) );
160  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
161  buttonLayout->addWidget( mUnlinkFeatureButton );
162  // zoom to linked feature
163  mZoomToFeatureButton = new QToolButton( this );
164  mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
165  mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) );
166  mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature" ) );
167  mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
168  buttonLayout->addWidget( mZoomToFeatureButton );
169  // spacer
170  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
171  // form view
172  mFormViewButton = new QToolButton( this );
173  mFormViewButton->setText( tr( "Form View" ) );
174  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
175  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
176  mFormViewButton->setCheckable( true );
177  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
178  buttonLayout->addWidget( mFormViewButton );
179  // table view
180  mTableViewButton = new QToolButton( this );
181  mTableViewButton->setText( tr( "Table View" ) );
182  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
183  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
184  mTableViewButton->setCheckable( true );
185  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
186  buttonLayout->addWidget( mTableViewButton );
187  // button group
188  mViewModeButtonGroup = new QButtonGroup( this );
189  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
190  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
191 
192  // add buttons layout
193  rootLayout->addLayout( buttonLayout );
194 
195  // add dual view
196  QGridLayout *relationLayout = new QGridLayout();
197  relationLayout->setContentsMargins( 0, 0, 0, 0 );
198  mDualView = new QgsDualView( this );
199  mDualView->setView( mViewMode );
200  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
201  relationLayout->addWidget( mDualView );
202  rootLayout->addLayout( relationLayout );
203 
204  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
205  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
206  connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
207  connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
208  connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } );
209  connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
210  connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
211  connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
212  connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
213  connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
214  connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
215 
216  // Set initial state for add/remove etc. buttons
217  updateButtons();
218 }
219 
220 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
221 {
224  mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx );
225  mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
226  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
227 
228  connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
229 
230  QIcon icon;
231  QString text;
232  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
233  {
234  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
235  text = tr( "Add Point child Feature" );
236  }
237  else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
238  {
239  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
240  text = tr( "Add Line child Feature" );
241  }
242  else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
243  {
244  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
245  text = tr( "Add Polygon Feature" );
246  }
247 
248  mAddFeatureGeometryButton->setIcon( icon );
249  mAddFeatureGeometryButton->setText( text );
250  mAddFeatureGeometryButton->setToolTip( text );
251 
252  updateButtons();
253 }
254 
256 {
257  mEditorContext = context;
258 
259  if ( context.mapCanvas() && context.cadDockWidget() )
260  {
261  mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
262  mMapToolDigitize->setButton( mAddFeatureGeometryButton );
263  }
264 
265  updateButtons();
266 }
267 
269 {
270  mDualView->setView( mode );
271  mViewMode = mode;
272 }
273 
274 void QgsRelationEditorWidget::updateButtons()
275 {
276  bool toggleEditingButtonEnabled = false;
277  bool editable = false;
278  bool linkable = false;
279  bool spatial = false;
280  bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
281 
282  if ( mRelation.isValid() )
283  {
284  toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
285  editable = mRelation.referencingLayer()->isEditable();
286  linkable = mRelation.referencingLayer()->isEditable();
287  spatial = mRelation.referencingLayer()->isSpatial();
288  }
289 
290  if ( mNmRelation.isValid() )
291  {
292  toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
293  editable = mNmRelation.referencedLayer()->isEditable();
294  spatial = mNmRelation.referencedLayer()->isSpatial();
295  }
296 
297  mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
298  mAddFeatureButton->setEnabled( editable );
299  mAddFeatureGeometryButton->setEnabled( editable );
300  mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
301  mLinkFeatureButton->setEnabled( linkable );
302  mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
303  mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
304  mZoomToFeatureButton->setEnabled( selectionNotEmpty );
305  mToggleEditingButton->setChecked( editable );
306  mSaveEditsButton->setEnabled( editable || linkable );
307 
308  mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
309 
310  mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
311  mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
312  mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
313  mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
314  mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
315  mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
316  mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
317  mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
318 }
319 
320 void QgsRelationEditorWidget::addFeatureGeometry()
321 {
322  QgsVectorLayer *layer = nullptr;
323  if ( mNmRelation.isValid() )
324  layer = mNmRelation.referencedLayer();
325  else
326  layer = mRelation.referencingLayer();
327 
328  mMapToolDigitize->setLayer( layer );
329 
330  // window is always on top, so we hide it to digitize without seeing it
331  window()->setVisible( false );
332  setMapTool( mMapToolDigitize );
333 
334  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
335  connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
336 
337  if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
338  {
339  QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );
340 
341  QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
342  QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
343  .arg( layer->name() );
344  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
345  lMainMessageBar->pushItem( mMessageBarItem );
346  }
347 
348 }
349 
350 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
351 {
353 
354  unsetMapTool();
355 }
356 
357 void QgsRelationEditorWidget::toggleEditing( bool state )
358 {
360 
361  updateButtons();
362 }
363 
365 {
366  if ( !mRelation.isValid() || !mFeature.isValid() )
367  return;
368 
369  if ( !isVisible() )
370  return;
371 
373 
374  if ( mNmRelation.isValid() )
375  {
377  QgsFeature fet;
378  QStringList filters;
379 
380  while ( it.nextFeature( fet ) )
381  {
383  filters << filter.prepend( '(' ).append( ')' );
384  }
385 
386  QgsFeatureRequest nmRequest;
387  nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
388 
389  initDualView( mNmRelation.referencedLayer(), nmRequest );
390  }
391  else if ( mRelation.referencingLayer() )
392  {
393  initDualView( mRelation.referencingLayer(), request );
394  }
395 }
396 
397 void QgsRelationEditorWidget::setVisibleButtons( const Buttons &buttons )
398 {
399  mButtonsVisibility = buttons;
400  updateButtons();
401 }
402 
403 QgsRelationEditorWidget::Buttons QgsRelationEditorWidget::visibleButtons() const
404 {
405  Buttons buttons;
406  if ( mLinkFeatureButton->isVisible() )
407  buttons |= Button::Link;
408  if ( mUnlinkFeatureButton->isVisible() )
409  buttons |= Button::Unlink;
410  if ( mSaveEditsButton->isVisible() )
411  buttons |= Button::SaveChildEdits;
412  if ( mAddFeatureButton->isVisible() )
413  buttons |= Button::AddChildFeature;
414  if ( mDuplicateFeatureButton->isVisible() )
415  buttons |= Button::DuplicateChildFeature;
416  if ( mDeleteFeatureButton->isVisible() )
417  buttons |= Button::DeleteChildFeature;
418  if ( mZoomToFeatureButton->isVisible() )
419  buttons |= Button::ZoomToChildFeature;
420  return buttons;
421 }
422 
423 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
424 {
425  mDualView->parentFormValueChanged( attribute, newValue );
426 }
427 
428 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
429 {
431  {
432  QAction *qAction = nullptr;
433 
434  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
435  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
436 
437  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
438  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
439  }
440 }
441 
442 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
443 {
444  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
445 
446  mapCanvas->setMapTool( mapTool );
447  mapCanvas->window()->raise();
448  mapCanvas->activateWindow();
449  mapCanvas->setFocus();
450  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
451 }
452 
453 void QgsRelationEditorWidget::unsetMapTool()
454 {
455  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
456 
457  // this will call mapToolDeactivated
458  mapCanvas->unsetMapTool( mMapToolDigitize );
459 
460  disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
461  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
462 }
463 
464 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
465 {
466  if ( e->key() == Qt::Key_Escape )
467  {
468  unsetMapTool();
469  }
470 }
471 
472 void QgsRelationEditorWidget::mapToolDeactivated()
473 {
474  window()->setVisible( true );
475  window()->raise();
476  window()->activateWindow();
477 
478  if ( mEditorContext.mainMessageBar() && mMessageBarItem )
479  {
480  mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
481  }
482  mMessageBarItem = nullptr;
483 }
484 
486 {
487  return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )}} );
488 }
489 
490 void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
491 {
492  mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
493  updateButtons();
494 }
495 
496 void QgsRelationEditorWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature )
497 {
498  Q_UNUSED( newRelation );
499  Q_UNUSED( newFeature );
500 
501  if ( ! mRelation.isValid() )
502  return;
503 
504  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
505  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
506 }
507 
509 {
510  if ( ! mRelation.isValid() )
511  {
512  updateButtons();
513  return;
514  }
515 
516  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
517  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
518 
519  updateButtons();
520 
522  initDualView( mRelation.referencingLayer(), myRequest );
523 }
524 
525 void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
526 {
527  Q_UNUSED( newRelation );
528  Q_UNUSED( newNmRelation );
529 
530  if ( mRelation.isValid() )
531  {
532  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
533  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
534  }
535 
536  if ( mNmRelation.isValid() )
537  {
538  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
539  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
540  }
541 }
542 
544 {
545  if ( !mRelation.isValid() )
546  return;
547 
548  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
549  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
550 
551  if ( mNmRelation.isValid() )
552  {
553  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
554  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
555  }
556 
557  updateButtons();
558 }
559 
561 {
562  return mFeatureSelectionMgr;
563 }
564 
566 {
567  unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
568 }
569 
571 {
572  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
573 }
574 
576 {
577  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
578 }
579 
581 {
582  QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
583  deleteFeatures( selectedFids );
584 }
585 
587 {
589  if ( !c )
590  return;
591 
592  c->zoomToFeatureIds(
594  mFeatureSelectionMgr->selectedFeatureIds()
595  );
596 }
597 
599 
600 
602  : QgsAbstractRelationEditorConfigWidget( relation, parent )
603 {
604  setupUi( this );
605 }
606 
608 {
609  QgsRelationEditorWidget::Buttons buttons;
610  buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
611  buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
612  buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
613  buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
614  buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
615  buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
616  buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
617 
618  return QVariantMap(
619  {
620  {"buttons", qgsFlagValueToKeys( buttons )},
621  } );
622 }
623 
624 void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
625 {
626  const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
627 
628  mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
629  mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
630  mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
631  mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
632  mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
633  mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
634  mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
635 }
636 
637 
639 
640 
641 #ifndef SIP_RUN
643 {
644 
645 }
646 
648 {
649  return QStringLiteral( "relation_editor" );
650 }
651 
653 {
654  return QStringLiteral( "Relation Editor" );
655 }
656 
657 QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
658 {
659  return new QgsRelationEditorWidget( config, parent );
660 }
661 
663 {
664  return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
665 }
666 #endif
void reset(T *p=nullptr)
Will reset the managed pointer to p.
This class should be subclassed for every configurable relation widget type.
Base class to build new relation widgets.
void toggleEditing(bool state)
Toggles editing state of the widget.
void deleteFeatures(const QgsFeatureIds &fids)
Deletes the features with fids.
void linkFeature()
Links a new feature to the relation.
void addFeature(const QgsGeometry &geometry=QgsGeometry())
Adds a new feature with given geometry.
void unlinkFeatures(const QgsFeatureIds &fids)
Unlinks the features with fids.
void deleteFeature(QgsFeatureId fid=QgsFeatureId())
Delete a feature with given fid.
QgsFeature feature() const
Returns the widget's current feature.
void saveEdits()
Saves the current modifications in the relation.
void unlinkFeature(QgsFeatureId fid=QgsFeatureId())
Unlinks a feature with given fid.
void duplicateFeatures(const QgsFeatureIds &fids)
Duplicates features.
This class is a menu that is populated automatically with the actions defined for a given layer.
Definition: qgsactionmenu.h:38
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class contains context information for attribute editor widgets.
QgsMapCanvas * mapCanvas() const
Returns the associated map canvas (e.g.
QgsMessageBar * mainMessageBar()
Returns the main message bar.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the associated CAD dock widget (e.g.
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent form.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:45
void showContextMenuExternally(QgsActionMenu *menu, QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:56
@ AttributeTable
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:61
@ AttributeEditor
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:68
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
void parentFormValueChanged(const QString &attribute, const QVariant &value)
Called in embedded forms when an attribute value in the parent form has changed.
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), bool loadFeatures=true)
Has to be called to initialize the dual view.
void setView(ViewMode view)
Change the current view mode.
QString expression() const
Returns the original, unmodified expression string.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsExpression * filterExpression() const
Returns the filter expression if set.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:191
Is an interface class to abstract feature selection handling.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void keyPressed(QKeyEvent *e)
Emit key press event.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
QString name
Definition: qgsmaplayer.h:73
This tool digitizes geometry of new point/line/polygon features on already existing vector layers Onc...
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
Abstract base class for all map tools.
Definition: qgsmaptool.h:66
void deactivated()
signal emitted once the map tool is deactivated
void setButton(QAbstractButton *button)
Use this to associate a button to this maptool.
Definition: qgsmaptool.cpp:140
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Creates a new configuration widget for the relation editor widget.
void setConfig(const QVariantMap &config)
Update the configuration widget to represent the given configuration.
QgsRelationEditorConfigWidget(const QgsRelation &relation, QWidget *parent)
Create a new configuration widget.
QVariantMap config()
Create a configuration from the current GUI state.
QString type() const override
Returns the machine readable identifier name of this widget type.
QString name() const override
Returns the human readable identifier name of this widget type.
QgsAbstractRelationEditorConfigWidget * configWidget(const QgsRelation &relation, QWidget *parent) const override
Override this in your implementation.
QgsAbstractRelationEditorWidget * create(const QVariantMap &config, QWidget *parent=nullptr) const override
Override this in your implementation.
The default relation widget in QGIS.
Q_DECL_DEPRECATED void duplicateFeature()
Duplicates a feature.
void zoomToSelectedFeatures()
Zooms to the selected features.
Button
Possible buttons shown in the relation editor.
void beforeSetRelationFeature(const QgsRelation &newRelation, const QgsFeature &newFeature) override
A hook called right before setRelationFeature() is executed.
void parentFormValueChanged(const QString &attribute, const QVariant &newValue) override
void setEditorContext(const QgsAttributeEditorContext &context) override
Sets the editor context.
QgsIFeatureSelectionManager * featureSelectionManager()
The feature selection manager is responsible for the selected features which are currently being edit...
virtual void updateUi() override
A hook called every time the state of the relation editor widget has changed via calling its set* met...
void unlinkSelectedFeatures()
Unlinks the selected features from the relation.
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
void setVisibleButtons(const Buttons &buttons)
Defines the buttons which are shown.
void duplicateSelectedFeatures()
Duplicates the selected features.
void afterSetRelationFeature() override
A hook called right after setRelationFeature() is executed, but before updateUi() is called.
QgsRelationEditorWidget(const QVariantMap &config, QWidget *parent=nullptr)
Constructor.
QVariantMap config() const override
Returns the current configuration.
void setConfig(const QVariantMap &config) override
Defines the current configuration.
void beforeSetRelations(const QgsRelation &newRelation, const QgsRelation &newNmRelation) override
A hook called right before setRelations() is executed.
void afterSetRelations() override
A hook called right after setRelations() is executed, but before updateUi() is called.
void deleteSelectedFeatures()
Deletes the currently selected features.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
bool isValid
Definition: qgsrelation.h:50
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
int selectedFeatureCount() override
Returns the number of features that are selected in this layer.
const QgsFeatureIds & selectedFeatureIds() const override
Returns reference to identifiers of selected features.
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
void editingStarted()
Emitted when editing on this layer has started.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:798
QString qgsFlagValueToKeys(const T &value)
Returns the value for the given keys of a flag.
Definition: qgis.h:786
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28