1 /***************************************************************************
2 qgsrelationeditor.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
16 #include "qgsrelationeditorwidget.h"
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"
25 #include "qgsgenericfeatureselectionmanager.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"
33 #include "qgsvectorlayerselectionmanager.h"
34 #include "qgsmaptooldigitizefeature.h"
35 #include "qgsexpressioncontextutils.h"
36 #include "qgsmessagebar.h"
37 #include "qgsmessagebaritem.h"
38
39 #include <QHBoxLayout>
40 #include <QLabel>
41 #include <QMessageBox>
42 #include <QPushButton>
43
44 /// @cond PRIVATE
45 ///
QgsFilteredSelectionManager(QgsVectorLayer * layer,const QgsFeatureRequest & request,QObject * parent)46 QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
47 : QgsVectorLayerSelectionManager( layer, parent )
48 , mRequest( request )
49 {
50 if ( ! layer )
51 return;
52
53 for ( auto fid : layer->selectedFeatureIds() )
54 if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
55 mSelectedFeatureIds << fid;
56
57 connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
58 }
59
selectedFeatureIds() const60 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
61 {
62 return mSelectedFeatureIds;
63 }
64
selectedFeatureCount()65 int QgsFilteredSelectionManager::selectedFeatureCount()
66 {
67 return mSelectedFeatureIds.count();
68 }
69
onSelectionChanged(const QgsFeatureIds & selected,const QgsFeatureIds & deselected,bool clearAndSelect)70 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
71 {
72 QgsFeatureIds lselected = selected;
73 if ( clearAndSelect )
74 {
75 mSelectedFeatureIds.clear();
76 }
77 else
78 {
79 for ( auto fid : deselected )
80 mSelectedFeatureIds.remove( fid );
81 }
82
83 for ( auto fid : selected )
84 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
85 mSelectedFeatureIds << fid;
86 else
87 lselected.remove( fid );
88
89 emit selectionChanged( lselected, deselected, clearAndSelect );
90 }
91
92 /// @endcond
93
QgsRelationEditorWidget(QWidget * parent)94 QgsRelationEditorWidget::QgsRelationEditorWidget( QWidget *parent )
95 : QgsCollapsibleGroupBox( parent )
96 {
97 QVBoxLayout *topLayout = new QVBoxLayout( this );
98 topLayout->setContentsMargins( 0, 9, 0, 0 );
99 setLayout( topLayout );
100
101 // buttons
102 QHBoxLayout *buttonLayout = new QHBoxLayout();
103 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
104 // toggle editing
105 mToggleEditingButton = new QToolButton( this );
106 mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
107 mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
108 mToggleEditingButton->setText( tr( "Toggle Editing" ) );
109 mToggleEditingButton->setEnabled( false );
110 mToggleEditingButton->setCheckable( true );
111 mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
112 buttonLayout->addWidget( mToggleEditingButton );
113 // save Edits
114 mSaveEditsButton = new QToolButton( this );
115 mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
116 mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
117 mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
118 mSaveEditsButton->setEnabled( true );
119 buttonLayout->addWidget( mSaveEditsButton );
120 // add feature with geometry
121 mAddFeatureGeometryButton = new QToolButton( this );
122 mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
123 buttonLayout->addWidget( mAddFeatureGeometryButton );
124 // add feature
125 mAddFeatureButton = new QToolButton( this );
126 mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
127 mAddFeatureButton->setText( tr( "Add Child Feature" ) );
128 mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
129 mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
130 buttonLayout->addWidget( mAddFeatureButton );
131 // duplicate feature
132 mDuplicateFeatureButton = new QToolButton( this );
133 mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
134 mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) );
135 mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature" ) );
136 mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
137 buttonLayout->addWidget( mDuplicateFeatureButton );
138 // delete feature
139 mDeleteFeatureButton = new QToolButton( this );
140 mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
141 mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) );
142 mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature" ) );
143 mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
144 buttonLayout->addWidget( mDeleteFeatureButton );
145 // link feature
146 mLinkFeatureButton = new QToolButton( this );
147 mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
148 mLinkFeatureButton->setText( tr( "Link Existing Features" ) );
149 mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
150 mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
151 buttonLayout->addWidget( mLinkFeatureButton );
152 // unlink feature
153 mUnlinkFeatureButton = new QToolButton( this );
154 mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
155 mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) );
156 mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature" ) );
157 mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
158 buttonLayout->addWidget( mUnlinkFeatureButton );
159 // zoom to linked feature
160 mZoomToFeatureButton = new QToolButton( this );
161 mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
162 mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) );
163 mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature" ) );
164 mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
165 buttonLayout->addWidget( mZoomToFeatureButton );
166 // spacer
167 buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
168 // form view
169 mFormViewButton = new QToolButton( this );
170 mFormViewButton->setText( tr( "Form View" ) );
171 mFormViewButton->setToolTip( tr( "Switch to form view" ) );
172 mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
173 mFormViewButton->setCheckable( true );
174 mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
175 buttonLayout->addWidget( mFormViewButton );
176 // table view
177 mTableViewButton = new QToolButton( this );
178 mTableViewButton->setText( tr( "Table View" ) );
179 mTableViewButton->setToolTip( tr( "Switch to table view" ) );
180 mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
181 mTableViewButton->setCheckable( true );
182 mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
183 buttonLayout->addWidget( mTableViewButton );
184 // button group
185 mViewModeButtonGroup = new QButtonGroup( this );
186 mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
187 mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
188
189 // add buttons layout
190 topLayout->addLayout( buttonLayout );
191
192 mRelationLayout = new QGridLayout();
193 mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
194 topLayout->addLayout( mRelationLayout );
195
196 mDualView = new QgsDualView( this );
197 mDualView->setView( mViewMode );
198
199 mRelationLayout->addWidget( mDualView );
200
201 connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged );
202 connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
203 this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
204 connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
205 connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
206 connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } );
207 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
208 connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateFeature );
209 connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
210 connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
211 connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
212 connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
213
214 connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
215
216 // Set initial state for add/remove etc. buttons
217 updateButtons();
218 }
219
setRelationFeature(const QgsRelation & relation,const QgsFeature & feature)220 void QgsRelationEditorWidget::setRelationFeature( const QgsRelation &relation, const QgsFeature &feature )
221 {
222 if ( mRelation.isValid() )
223 {
224 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
225 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
226 }
227
228 mRelation = relation;
229 mFeature = feature;
230
231 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
232 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
233
234 updateTitle();
235
236 updateButtons();
237
238 setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
239
240 // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
241 // If it is already initialized, it has been set visible before and the currently shown feature is changing
242 // and the widget needs updating
243
244 if ( mVisible )
245 {
246 QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
247 initDualView( mRelation.referencingLayer(), myRequest );
248 }
249 }
250
initDualView(QgsVectorLayer * layer,const QgsFeatureRequest & request)251 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
252 {
253 QgsAttributeEditorContext ctx { mEditorContext };
254 ctx.setParentFormFeature( mFeature );
255 mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx );
256 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
257 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
258
259 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
260
261 QIcon icon;
262 QString text;
263 if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
264 {
265 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
266 text = tr( "Add Point child Feature" );
267 }
268 else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
269 {
270 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
271 text = tr( "Add Line child Feature" );
272 }
273 else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
274 {
275 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
276 text = tr( "Add Polygon Feature" );
277 }
278
279 mAddFeatureGeometryButton->setIcon( icon );
280 mAddFeatureGeometryButton->setText( text );
281 mAddFeatureGeometryButton->setToolTip( text );
282
283 updateButtons();
284 }
285
setRelations(const QgsRelation & relation,const QgsRelation & nmrelation)286 void QgsRelationEditorWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation )
287 {
288 if ( mRelation.isValid() )
289 {
290 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
291 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
292 }
293
294 if ( mNmRelation.isValid() )
295 {
296 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
297 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
298 }
299
300 mRelation = relation;
301 mNmRelation = nmrelation;
302
303 if ( !mRelation.isValid() )
304 return;
305
306 mLayerInSameTransactionGroup = false;
307
308 const auto transactionGroups = QgsProject::instance()->transactionGroups();
309 for ( auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it )
310 {
311 if ( mNmRelation.isValid() )
312 {
313 if ( it.value()->layers().contains( mRelation.referencedLayer() ) &&
314 it.value()->layers().contains( mRelation.referencingLayer() ) &&
315 it.value()->layers().contains( mNmRelation.referencedLayer() ) )
316 mLayerInSameTransactionGroup = true;
317 }
318 else
319 {
320 if ( it.value()->layers().contains( mRelation.referencedLayer() ) &&
321 it.value()->layers().contains( mRelation.referencingLayer() ) )
322 mLayerInSameTransactionGroup = true;
323 }
324 }
325
326 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
327 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
328
329 if ( mNmRelation.isValid() )
330 {
331 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
332 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
333 }
334
335 updateTitle();
336
337 updateButtons();
338
339 setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
340
341 updateUi();
342 }
343
setEditorContext(const QgsAttributeEditorContext & context)344 void QgsRelationEditorWidget::setEditorContext( const QgsAttributeEditorContext &context )
345 {
346 mEditorContext = context;
347
348 if ( context.mapCanvas() && context.cadDockWidget() )
349 {
350 mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
351 mMapToolDigitize->setButton( mAddFeatureGeometryButton );
352 }
353
354 updateButtons();
355 }
356
editorContext() const357 QgsAttributeEditorContext QgsRelationEditorWidget::editorContext() const
358 {
359 return mEditorContext;
360 }
361
featureSelectionManager()362 QgsIFeatureSelectionManager *QgsRelationEditorWidget::featureSelectionManager()
363 {
364 return mFeatureSelectionMgr;
365 }
366
setViewMode(QgsDualView::ViewMode mode)367 void QgsRelationEditorWidget::setViewMode( QgsDualView::ViewMode mode )
368 {
369 mDualView->setView( mode );
370 mViewMode = mode;
371 }
372
setFeature(const QgsFeature & feature,bool update)373 void QgsRelationEditorWidget::setFeature( const QgsFeature &feature, bool update )
374 {
375 mFeature = feature;
376
377 mEditorContext.setFormFeature( feature );
378
379 if ( update )
380 updateUi();
381 }
382
updateButtons()383 void QgsRelationEditorWidget::updateButtons()
384 {
385 bool toggleEditingButtonEnabled = false;
386 bool editable = false;
387 bool linkable = false;
388 bool spatial = false;
389 bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
390
391 if ( mRelation.isValid() )
392 {
393 bool canSupportEditing = mRelation.referencingLayer()->dataProvider()->capabilities() & QgsVectorDataProvider::EditingCapabilities;
394 canSupportEditing &= !mRelation.referencingLayer()->readOnly();
395 toggleEditingButtonEnabled = canSupportEditing;
396 editable = mRelation.referencingLayer()->isEditable();
397 linkable = mRelation.referencingLayer()->isEditable();
398 spatial = mRelation.referencingLayer()->isSpatial();
399 }
400
401 if ( mNmRelation.isValid() )
402 {
403 bool canSupportEditing = mNmRelation.referencedLayer()->dataProvider()->capabilities() & QgsVectorDataProvider::EditingCapabilities;
404 canSupportEditing &= !mNmRelation.referencedLayer()->readOnly();
405 toggleEditingButtonEnabled |= canSupportEditing;
406 editable = mNmRelation.referencedLayer()->isEditable();
407 spatial = mNmRelation.referencedLayer()->isSpatial();
408 }
409
410 mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
411 mAddFeatureButton->setEnabled( editable );
412 mAddFeatureGeometryButton->setEnabled( editable );
413 mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
414 mLinkFeatureButton->setEnabled( linkable );
415 mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
416 mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
417 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
418 mToggleEditingButton->setChecked( editable );
419 mSaveEditsButton->setEnabled( editable || linkable );
420
421 mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
422 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Link ) );
423 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Unlink ) );
424 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
425 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) );
426 mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
427 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature ) );
428 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature ) );
429 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
430 }
431
addFeatureGeometry()432 void QgsRelationEditorWidget::addFeatureGeometry()
433 {
434 QgsVectorLayer *layer = nullptr;
435 if ( mNmRelation.isValid() )
436 layer = mNmRelation.referencedLayer();
437 else
438 layer = mRelation.referencingLayer();
439
440 mMapToolDigitize->setLayer( layer );
441
442 // window is always on top, so we hide it to digitize without seeing it
443 window()->setVisible( false );
444 setMapTool( mMapToolDigitize );
445
446 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
447 connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
448
449 if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
450 {
451 QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );
452
453 QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
454 QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." )
455 .arg( layer->name() );
456 mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
457 lMainMessageBar->pushItem( mMessageBarItem );
458 }
459
460 }
461
addFeature(const QgsGeometry & geometry)462 void QgsRelationEditorWidget::addFeature( const QgsGeometry &geometry )
463 {
464 QgsAttributeMap keyAttrs;
465
466 const QgsVectorLayerTools *vlTools = mEditorContext.vectorLayerTools();
467
468 if ( mNmRelation.isValid() )
469 {
470 // n:m Relation: first let the user create a new feature on the other table
471 // and autocreate a new linking feature.
472 QgsFeature f;
473 if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), geometry, &f ) )
474 {
475 // Fields of the linking table
476 const QgsFields fields = mRelation.referencingLayer()->fields();
477
478 // Expression context for the linking table
479 QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext();
480
481 QgsAttributeMap linkAttributes;
482 const auto constFieldPairs = mRelation.fieldPairs();
483 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
484 {
485 int index = fields.indexOf( fieldPair.first );
486 linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
487 }
488
489 const auto constNmFieldPairs = mNmRelation.fieldPairs();
490 for ( const QgsRelation::FieldPair &fieldPair : constNmFieldPairs )
491 {
492 int index = fields.indexOf( fieldPair.first );
493 linkAttributes.insert( index, f.attribute( fieldPair.second ) );
494 }
495 QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
496
497 mRelation.referencingLayer()->addFeature( linkFeature );
498
499 updateUi();
500 }
501 }
502 else
503 {
504 QgsFields fields = mRelation.referencingLayer()->fields();
505
506 const auto constFieldPairs = mRelation.fieldPairs();
507 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
508 {
509 keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
510 }
511
512 vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry );
513 }
514 }
515
onDigitizingCompleted(const QgsFeature & feature)516 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
517 {
518 addFeature( feature.geometry() );
519
520 unsetMapTool();
521 }
522
linkFeature()523 void QgsRelationEditorWidget::linkFeature()
524 {
525 QgsVectorLayer *layer = nullptr;
526
527 if ( mNmRelation.isValid() )
528 layer = mNmRelation.referencedLayer();
529 else
530 layer = mRelation.referencingLayer();
531
532 QgsFeatureSelectionDlg *selectionDlg = new QgsFeatureSelectionDlg( layer, mEditorContext, this );
533 selectionDlg->setAttribute( Qt::WA_DeleteOnClose );
534
535 const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), mFeature );
536 selectionDlg->setWindowTitle( tr( "Link existing child features for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ) );
537
538 connect( selectionDlg, &QDialog::accepted, this, &QgsRelationEditorWidget::onLinkFeatureDlgAccepted );
539 selectionDlg->show();
540 }
541
onLinkFeatureDlgAccepted()542 void QgsRelationEditorWidget::onLinkFeatureDlgAccepted()
543 {
544 QgsFeatureSelectionDlg *selectionDlg = qobject_cast<QgsFeatureSelectionDlg *>( sender() );
545 if ( mNmRelation.isValid() )
546 {
547 QgsFeatureIterator it = mNmRelation.referencedLayer()->getFeatures(
548 QgsFeatureRequest()
549 .setFilterFids( selectionDlg->selectedFeatures() )
550 .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
551
552 QgsFeature relatedFeature;
553
554 QgsFeatureList newFeatures;
555
556 // Fields of the linking table
557 const QgsFields fields = mRelation.referencingLayer()->fields();
558
559 // Expression context for the linking table
560 QgsExpressionContext context = mRelation.referencingLayer()->createExpressionContext();
561
562 QgsAttributeMap linkAttributes;
563 const auto constFieldPairs = mRelation.fieldPairs();
564 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
565 {
566 int index = fields.indexOf( fieldPair.first );
567 linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
568 }
569
570 while ( it.nextFeature( relatedFeature ) )
571 {
572 const auto constFieldPairs = mNmRelation.fieldPairs();
573 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
574 {
575 int index = fields.indexOf( fieldPair.first );
576 linkAttributes.insert( index, relatedFeature.attribute( fieldPair.second ) );
577 }
578 const QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
579
580 newFeatures << linkFeature;
581 }
582
583 mRelation.referencingLayer()->addFeatures( newFeatures );
584 QgsFeatureIds ids;
585 const auto constNewFeatures = newFeatures;
586 for ( const QgsFeature &f : constNewFeatures )
587 ids << f.id();
588 mRelation.referencingLayer()->selectByIds( ids );
589 }
590 else
591 {
592 QMap<int, QVariant> keys;
593 const auto constFieldPairs = mRelation.fieldPairs();
594 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
595 {
596 int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
597 QVariant val = mFeature.attribute( fieldPair.referencedField() );
598 keys.insert( idx, val );
599 }
600
601 const auto constSelectedFeatures = selectionDlg->selectedFeatures();
602 for ( QgsFeatureId fid : constSelectedFeatures )
603 {
604 QMapIterator<int, QVariant> it( keys );
605 while ( it.hasNext() )
606 {
607 it.next();
608 mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
609 }
610 }
611 }
612
613 updateUi();
614 }
615
duplicateFeature()616 void QgsRelationEditorWidget::duplicateFeature()
617 {
618 QgsVectorLayer *layer = mRelation.referencingLayer();
619
620 QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) );
621 QgsFeature f;
622 while ( fit.nextFeature( f ) )
623 {
624 QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext;
625 QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext );
626 }
627 }
628
deleteFeature(const QgsFeatureId featureid)629 void QgsRelationEditorWidget::deleteFeature( const QgsFeatureId featureid )
630 {
631 deleteFeatures( QgsFeatureIds() << featureid );
632 }
633
deleteSelectedFeatures()634 void QgsRelationEditorWidget::deleteSelectedFeatures()
635 {
636 QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
637 deleteFeatures( selectedFids );
638 }
639
deleteFeatures(const QgsFeatureIds & featureids)640 void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids )
641 {
642 bool deleteFeatures = true;
643
644 QgsVectorLayer *layer;
645 if ( mNmRelation.isValid() )
646 {
647 layer = mNmRelation.referencedLayer();
648
649 // When deleting a linked feature within an N:M relation,
650 // check if the feature is linked to more than just one feature.
651 // In case it is linked more than just once, ask the user for confirmation
652 // as it is likely he was not aware of the implications and might delete
653 // there may be several linking entries deleted along.
654
655 QgsFeatureRequest deletedFeaturesRequest;
656 deletedFeaturesRequest.setFilterFids( featureids );
657 deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
658 deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() );
659
660 QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest );
661 QStringList deletedFeaturesPks;
662 QgsFeature feature;
663 while ( deletedFeatures.nextFeature( feature ) )
664 {
665 deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) );
666 }
667
668 QgsFeatureRequest linkingFeaturesRequest;
669 linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
670 linkingFeaturesRequest.setNoAttributes();
671
672 QString linkingFeaturesRequestExpression;
673 if ( !deletedFeaturesPks.empty() )
674 {
675 linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) );
676 linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression );
677
678 QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest );
679
680 int relatedLinkingFeaturesCount = 0;
681 while ( relatedLinkingFeatures.nextFeature( feature ) )
682 {
683 relatedLinkingFeaturesCount++;
684 }
685
686 if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
687 {
688 QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entry?" ), tr( "The entry on %1 is still linked to %2 features on %3. Do you want to delete it?" ).arg( mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
689 messageBox.addButton( QMessageBox::Cancel );
690 QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
691
692 messageBox.exec();
693 if ( messageBox.clickedButton() != deleteButton )
694 deleteFeatures = false;
695 }
696 else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
697 {
698 QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entries?" ), tr( "The %1 entries on %2 are still linked to %3 features on %4. Do you want to delete them?" ).arg( QString::number( deletedFeaturesPks.size() ), mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
699 messageBox.addButton( QMessageBox::Cancel );
700 QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
701
702 messageBox.exec();
703 if ( messageBox.clickedButton() != deleteButton )
704 deleteFeatures = false;
705 }
706 }
707 }
708 else
709 {
710 layer = mRelation.referencingLayer();
711 }
712
713 QgsVectorLayerUtils::QgsDuplicateFeatureContext infoContext;
714 if ( QgsVectorLayerUtils::impactsCascadeFeatures( layer, featureids, QgsProject::instance(), infoContext ) )
715 {
716 QString childrenInfo;
717 int childrenCount = 0;
718 const auto infoContextLayers = infoContext.layers();
719 for ( QgsVectorLayer *chl : infoContextLayers )
720 {
721 childrenCount += infoContext.duplicatedFeatures( chl ).size();
722 childrenInfo += ( tr( "%1 feature(s) on layer \"%2\", " ).arg( infoContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
723 }
724
725 // for extra safety to make sure we know that the delete can have impact on children and joins
726 int res = QMessageBox::question( this, tr( "Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ),
727 tr( "Delete %1 feature(s) on layer \"%2\", %3 as well\nand all of its other descendants.\nDelete these features?" ).arg( featureids.count() ).arg( layer->name() ).arg( childrenInfo ),
728 QMessageBox::Yes | QMessageBox::No );
729 if ( res != QMessageBox::Yes )
730 deleteFeatures = false;
731 }
732
733 if ( deleteFeatures )
734 {
735 QgsVectorLayer::DeleteContext context( true, QgsProject::instance() );
736 layer->deleteFeatures( featureids, &context );
737 const auto contextLayers = context.handledLayers();
738 if ( contextLayers.size() > 1 )
739 {
740 int deletedCount = 0;
741 QString feedbackMessage;
742 for ( QgsVectorLayer *contextLayer : contextLayers )
743 {
744 feedbackMessage += tr( "%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() );
745 deletedCount += context.handledFeatures( contextLayer ).size();
746 }
747 mEditorContext.mainMessageBar()->pushMessage( tr( "%1 features deleted: %2" ).arg( deletedCount ).arg( feedbackMessage ), Qgis::Success );
748 }
749
750 updateUi();
751 }
752 }
753
unlinkFeature(const QgsFeatureId featureid)754 void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid )
755 {
756 unlinkFeatures( QgsFeatureIds() << featureid );
757 }
758
unlinkSelectedFeatures()759 void QgsRelationEditorWidget::unlinkSelectedFeatures()
760 {
761 unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
762 }
763
zoomToSelectedFeatures()764 void QgsRelationEditorWidget::zoomToSelectedFeatures()
765 {
766 QgsMapCanvas *c = mEditorContext.mapCanvas();
767 if ( !c )
768 return;
769
770 c->zoomToFeatureIds(
771 mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(),
772 mFeatureSelectionMgr->selectedFeatureIds()
773 );
774 }
775
unlinkFeatures(const QgsFeatureIds & featureids)776 void QgsRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &featureids )
777 {
778 if ( mNmRelation.isValid() )
779 {
780 QgsFeatureIterator selectedIterator = mNmRelation.referencedLayer()->getFeatures(
781 QgsFeatureRequest()
782 .setFilterFids( featureids )
783 .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
784
785 QgsFeature f;
786
787 QStringList filters;
788
789 while ( selectedIterator.nextFeature( f ) )
790 {
791 filters << '(' + mNmRelation.getRelatedFeaturesRequest( f ).filterExpression()->expression() + ')';
792 }
793
794 QString filter = QStringLiteral( "(%1) AND (%2)" ).arg(
795 mRelation.getRelatedFeaturesRequest( mFeature ).filterExpression()->expression(),
796 filters.join( QLatin1String( " OR " ) ) );
797
798 QgsFeatureIterator linkedIterator = mRelation.referencingLayer()->getFeatures( QgsFeatureRequest()
799 .setNoAttributes()
800 .setFilterExpression( filter ) );
801
802 QgsFeatureIds fids;
803
804 while ( linkedIterator.nextFeature( f ) )
805 {
806 fids << f.id();
807 QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 );
808 }
809
810 mRelation.referencingLayer()->deleteFeatures( fids );
811
812 updateUi();
813 }
814 else
815 {
816 QMap<int, QgsField> keyFields;
817 const auto constFieldPairs = mRelation.fieldPairs();
818 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
819 {
820 int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
821 if ( idx < 0 )
822 {
823 QgsDebugMsg( QStringLiteral( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
824 return;
825 }
826 QgsField fld = mRelation.referencingLayer()->fields().at( idx );
827 keyFields.insert( idx, fld );
828 }
829
830 const auto constFeatureids = featureids;
831 for ( QgsFeatureId fid : constFeatureids )
832 {
833 QMapIterator<int, QgsField> it( keyFields );
834 while ( it.hasNext() )
835 {
836 it.next();
837 mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
838 }
839 }
840 }
841 }
842
toggleEditing(bool state)843 void QgsRelationEditorWidget::toggleEditing( bool state )
844 {
845 if ( state )
846 {
847 mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
848 if ( mNmRelation.isValid() )
849 mEditorContext.vectorLayerTools()->startEditing( mNmRelation.referencedLayer() );
850 }
851 else
852 {
853 mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
854 if ( mNmRelation.isValid() )
855 mEditorContext.vectorLayerTools()->stopEditing( mNmRelation.referencedLayer() );
856 }
857
858 updateButtons();
859 }
860
saveEdits()861 void QgsRelationEditorWidget::saveEdits()
862 {
863 mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
864 if ( mNmRelation.isValid() )
865 mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() );
866 }
867
onCollapsedStateChanged(bool collapsed)868 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
869 {
870 if ( !collapsed )
871 {
872 mVisible = true;
873 updateUi();
874 }
875 }
876
updateUi()877 void QgsRelationEditorWidget::updateUi()
878 {
879 // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
880 // If it is already initialized, it has been set visible before and the currently shown feature is changing
881 // and the widget needs updating
882
883 if ( mVisible )
884 {
885 QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
886
887 if ( mNmRelation.isValid() )
888 {
889 QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( myRequest );
890
891 QgsFeature fet;
892
893 QStringList filters;
894
895 while ( it.nextFeature( fet ) )
896 {
897 QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
898 filters << filter.prepend( '(' ).append( ')' );
899 }
900
901 QgsFeatureRequest nmRequest;
902
903 nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
904
905 initDualView( mNmRelation.referencedLayer(), nmRequest );
906 }
907 else if ( mRelation.referencingLayer() )
908 {
909 initDualView( mRelation.referencingLayer(), myRequest );
910 }
911 }
912 }
913
showLinkButton() const914 bool QgsRelationEditorWidget::showLinkButton() const
915 {
916 return mLinkFeatureButton->isVisible();
917 }
918
setShowLinkButton(bool showLinkButton)919 void QgsRelationEditorWidget::setShowLinkButton( bool showLinkButton )
920 {
921 mLinkFeatureButton->setVisible( showLinkButton );
922 }
923
showUnlinkButton() const924 bool QgsRelationEditorWidget::showUnlinkButton() const
925 {
926 return mUnlinkFeatureButton->isVisible();
927 }
928
setShowSaveChildEditsButton(bool showChildEdits)929 void QgsRelationEditorWidget::setShowSaveChildEditsButton( bool showChildEdits )
930 {
931 mSaveEditsButton->setVisible( showChildEdits );
932 }
933
showSaveChildEditsButton() const934 bool QgsRelationEditorWidget::showSaveChildEditsButton() const
935 {
936 return mSaveEditsButton->isVisible();
937 }
938
setVisibleButtons(const QgsAttributeEditorRelation::Buttons & buttons)939 void QgsRelationEditorWidget::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons )
940 {
941 mButtonsVisibility = buttons;
942 updateButtons();
943 }
944
visibleButtons() const945 QgsAttributeEditorRelation::Buttons QgsRelationEditorWidget::visibleButtons() const
946 {
947 QgsAttributeEditorRelation::Buttons buttons;
948 if ( mLinkFeatureButton->isVisible() )
949 buttons |= QgsAttributeEditorRelation::Button::Link;
950 if ( mUnlinkFeatureButton->isVisible() )
951 buttons |= QgsAttributeEditorRelation::Button::Unlink;
952 if ( mSaveEditsButton->isVisible() )
953 buttons |= QgsAttributeEditorRelation::Button::SaveChildEdits;
954 if ( mAddFeatureButton->isVisible() )
955 buttons |= QgsAttributeEditorRelation::Button::AddChildFeature;
956 if ( mDuplicateFeatureButton->isVisible() )
957 buttons |= QgsAttributeEditorRelation::Button::DuplicateChildFeature;
958 if ( mDeleteFeatureButton->isVisible() )
959 buttons |= QgsAttributeEditorRelation::Button::DeleteChildFeature;
960 if ( mZoomToFeatureButton->isVisible() )
961 buttons |= QgsAttributeEditorRelation::Button::ZoomToChildFeature;
962 return buttons;
963 }
964
setForceSuppressFormPopup(bool forceSuppressFormPopup)965 void QgsRelationEditorWidget::setForceSuppressFormPopup( bool forceSuppressFormPopup )
966 {
967 mForceSuppressFormPopup = forceSuppressFormPopup;
968 }
969
forceSuppressFormPopup() const970 bool QgsRelationEditorWidget::forceSuppressFormPopup() const
971 {
972 return mForceSuppressFormPopup;
973 }
974
setNmRelationId(const QVariant & nmRelationId)975 void QgsRelationEditorWidget::setNmRelationId( const QVariant &nmRelationId )
976 {
977 mNmRelationId = nmRelationId;
978 }
979
nmRelationId() const980 QVariant QgsRelationEditorWidget::nmRelationId() const
981 {
982 return mNmRelationId;
983 }
984
label() const985 QString QgsRelationEditorWidget::label() const
986 {
987 return mLabel;
988 }
989
setLabel(const QString & label)990 void QgsRelationEditorWidget::setLabel( const QString &label )
991 {
992 mLabel = label;
993
994 updateTitle();
995 }
996
setShowUnlinkButton(bool showUnlinkButton)997 void QgsRelationEditorWidget::setShowUnlinkButton( bool showUnlinkButton )
998 {
999 mUnlinkFeatureButton->setVisible( showUnlinkButton );
1000 }
1001
parentFormValueChanged(const QString & attribute,const QVariant & newValue)1002 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
1003 {
1004 mDualView->parentFormValueChanged( attribute, newValue );
1005 }
1006
showLabel() const1007 bool QgsRelationEditorWidget::showLabel() const
1008 {
1009 return mShowLabel;
1010 }
1011
setShowLabel(bool showLabel)1012 void QgsRelationEditorWidget::setShowLabel( bool showLabel )
1013 {
1014 mShowLabel = showLabel;
1015
1016 updateTitle();
1017 }
1018
showContextMenu(QgsActionMenu * menu,const QgsFeatureId fid)1019 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
1020 {
1021 if ( mRelation.referencingLayer()->isEditable() )
1022 {
1023 QAction *qAction = nullptr;
1024
1025 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
1026 connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
1027
1028 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
1029 connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
1030 }
1031 }
1032
setMapTool(QgsMapTool * mapTool)1033 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
1034 {
1035 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
1036
1037 mapCanvas->setMapTool( mapTool );
1038 mapCanvas->window()->raise();
1039 mapCanvas->activateWindow();
1040 mapCanvas->setFocus();
1041 connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
1042 }
1043
unsetMapTool()1044 void QgsRelationEditorWidget::unsetMapTool()
1045 {
1046 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
1047
1048 // this will call mapToolDeactivated
1049 mapCanvas->unsetMapTool( mMapToolDigitize );
1050
1051 disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
1052 disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
1053 }
1054
updateTitle()1055 void QgsRelationEditorWidget::updateTitle()
1056 {
1057 if ( mShowLabel && !mLabel.isEmpty() )
1058 {
1059 setTitle( mLabel );
1060 }
1061 else if ( mShowLabel && mRelation.isValid() )
1062 {
1063 setTitle( mRelation.name() );
1064 }
1065 else
1066 {
1067 setTitle( QString() );
1068 }
1069 }
1070
feature() const1071 QgsFeature QgsRelationEditorWidget::feature() const
1072 {
1073 return mFeature;
1074 }
1075
onKeyPressed(QKeyEvent * e)1076 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
1077 {
1078 if ( e->key() == Qt::Key_Escape )
1079 {
1080 unsetMapTool();
1081 }
1082 }
1083
mapToolDeactivated()1084 void QgsRelationEditorWidget::mapToolDeactivated()
1085 {
1086 window()->setVisible( true );
1087 window()->raise();
1088 window()->activateWindow();
1089
1090 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
1091 {
1092 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
1093 }
1094 mMessageBarItem = nullptr;
1095 }
1096