39 #include <QHBoxLayout>
41 #include <QMessageBox>
42 #include <QPushButton>
51 if ( mRequest.acceptFeature( layer->
getFeature( fid ) ) )
52 mSelectedFeatureIds << fid;
57 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds()
const
59 return mSelectedFeatureIds;
62 int QgsFilteredSelectionManager::selectedFeatureCount()
64 return mSelectedFeatureIds.count();
67 void QgsFilteredSelectionManager::onSelectionChanged(
const QgsFeatureIds &selected,
const QgsFeatureIds &deselected,
bool clearAndSelect )
72 mSelectedFeatureIds.clear();
76 for (
auto fid : deselected )
77 mSelectedFeatureIds.remove( fid );
80 for (
auto fid : selected )
81 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
82 mSelectedFeatureIds << fid;
84 lselected.remove( fid );
86 emit selectionChanged( lselected, deselected, clearAndSelect );
94 QVBoxLayout *topLayout =
new QVBoxLayout(
this );
95 topLayout->setContentsMargins( 0, 9, 0, 0 );
96 setLayout( topLayout );
99 QHBoxLayout *buttonLayout =
new QHBoxLayout();
100 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
102 mToggleEditingButton =
new QToolButton(
this );
103 mToggleEditingButton->setObjectName( QStringLiteral(
"mToggleEditingButton" ) );
105 mToggleEditingButton->setText( tr(
"Toggle Editing" ) );
106 mToggleEditingButton->setEnabled(
false );
107 mToggleEditingButton->setCheckable(
true );
108 mToggleEditingButton->setToolTip( tr(
"Toggle editing mode for child layer" ) );
109 buttonLayout->addWidget( mToggleEditingButton );
111 mSaveEditsButton =
new QToolButton(
this );
113 mSaveEditsButton->setText( tr(
"Save Child Layer Edits" ) );
114 mSaveEditsButton->setToolTip( tr(
"Save child layer edits" ) );
115 mSaveEditsButton->setEnabled(
true );
116 buttonLayout->addWidget( mSaveEditsButton );
118 mAddFeatureGeometryButton =
new QToolButton(
this );
119 mAddFeatureGeometryButton->setObjectName( QStringLiteral(
"mAddFeatureGeometryButton" ) );
120 buttonLayout->addWidget( mAddFeatureGeometryButton );
122 mAddFeatureButton =
new QToolButton(
this );
124 mAddFeatureButton->setText( tr(
"Add Child Feature" ) );
125 mAddFeatureButton->setToolTip( tr(
"Add child feature" ) );
126 mAddFeatureButton->setObjectName( QStringLiteral(
"mAddFeatureButton" ) );
127 buttonLayout->addWidget( mAddFeatureButton );
129 mDuplicateFeatureButton =
new QToolButton(
this );
131 mDuplicateFeatureButton->setText( tr(
"Duplicate Child Feature" ) );
132 mDuplicateFeatureButton->setToolTip( tr(
"Duplicate child feature" ) );
133 mDuplicateFeatureButton->setObjectName( QStringLiteral(
"mDuplicateFeatureButton" ) );
134 buttonLayout->addWidget( mDuplicateFeatureButton );
136 mDeleteFeatureButton =
new QToolButton(
this );
138 mDeleteFeatureButton->setText( tr(
"Delete Child Feature" ) );
139 mDeleteFeatureButton->setToolTip( tr(
"Delete child feature" ) );
140 mDeleteFeatureButton->setObjectName( QStringLiteral(
"mDeleteFeatureButton" ) );
141 buttonLayout->addWidget( mDeleteFeatureButton );
143 mLinkFeatureButton =
new QToolButton(
this );
145 mLinkFeatureButton->setText( tr(
"Link Existing Features" ) );
146 mLinkFeatureButton->setToolTip( tr(
"Link existing child features" ) );
147 mLinkFeatureButton->setObjectName( QStringLiteral(
"mLinkFeatureButton" ) );
148 buttonLayout->addWidget( mLinkFeatureButton );
150 mUnlinkFeatureButton =
new QToolButton(
this );
152 mUnlinkFeatureButton->setText( tr(
"Unlink Feature" ) );
153 mUnlinkFeatureButton->setToolTip( tr(
"Unlink child feature" ) );
154 mUnlinkFeatureButton->setObjectName( QStringLiteral(
"mUnlinkFeatureButton" ) );
155 buttonLayout->addWidget( mUnlinkFeatureButton );
157 mZoomToFeatureButton =
new QToolButton(
this );
159 mZoomToFeatureButton->setText( tr(
"Zoom To Feature" ) );
160 mZoomToFeatureButton->setToolTip( tr(
"Zoom to child feature" ) );
161 mZoomToFeatureButton->setObjectName( QStringLiteral(
"mZoomToFeatureButton" ) );
162 buttonLayout->addWidget( mZoomToFeatureButton );
164 buttonLayout->addItem(
new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
166 mFormViewButton =
new QToolButton(
this );
167 mFormViewButton->setText( tr(
"Form View" ) );
168 mFormViewButton->setToolTip( tr(
"Switch to form view" ) );
170 mFormViewButton->setCheckable(
true );
172 buttonLayout->addWidget( mFormViewButton );
174 mTableViewButton =
new QToolButton(
this );
175 mTableViewButton->setText( tr(
"Table View" ) );
176 mTableViewButton->setToolTip( tr(
"Switch to table view" ) );
178 mTableViewButton->setCheckable(
true );
180 buttonLayout->addWidget( mTableViewButton );
182 mViewModeButtonGroup =
new QButtonGroup(
this );
187 topLayout->addLayout( buttonLayout );
189 mRelationLayout =
new QGridLayout();
190 mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
191 topLayout->addLayout( mRelationLayout );
194 mDualView->
setView( mViewMode );
196 mRelationLayout->addWidget( mDualView );
199 connect( mViewModeButtonGroup,
static_cast<void ( QButtonGroup::* )(
int )
>( &QButtonGroup::buttonClicked ),
201 connect( mToggleEditingButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::toggleEditing );
202 connect( mSaveEditsButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::saveEdits );
203 connect( mAddFeatureButton, &QAbstractButton::clicked,
this, [
this]() { addFeature(); } );
204 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::addFeatureGeometry );
205 connect( mDuplicateFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::duplicateFeature );
206 connect( mDeleteFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::deleteSelectedFeatures );
207 connect( mLinkFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::linkFeature );
208 connect( mUnlinkFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
209 connect( mZoomToFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
225 mRelation = relation;
236 if ( canChangeAttributes && !lyr->
readOnly() )
238 mToggleEditingButton->setEnabled(
true );
243 mToggleEditingButton->setEnabled(
false );
246 setObjectName( QStringLiteral(
"referenced/" ) + mRelation.
name() );
263 mDualView->
init( layer, mEditorContext.
mapCanvas(), request, ctx );
264 mFeatureSelectionMgr =
new QgsFilteredSelectionManager( layer, request, mDualView );
274 text = tr(
"Add Point child Feature" );
279 text = tr(
"Add Line child Feature" );
284 text = tr(
"Add Polygon Feature" );
289 mAddFeatureGeometryButton->setVisible(
false );
293 mAddFeatureGeometryButton->setIcon( icon );
294 mAddFeatureGeometryButton->setText( text );
295 mAddFeatureGeometryButton->setToolTip( text );
315 mRelation = relation;
316 mNmRelation = nmrelation;
321 mToggleEditingButton->setVisible(
true );
324 for (
auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it )
328 mToggleEditingButton->setVisible(
false );
329 mSaveEditsButton->setVisible(
false );
347 if ( canChangeAttributes && !lyr->
readOnly() )
349 mToggleEditingButton->setEnabled(
true );
354 mToggleEditingButton->setEnabled(
false );
362 setObjectName( QStringLiteral(
"referenced/" ) + mRelation.
name() );
369 mEditorContext = context;
374 mMapToolDigitize->
setButton( mAddFeatureGeometryButton );
380 return mEditorContext;
385 return mFeatureSelectionMgr;
404 void QgsRelationEditorWidget::updateButtons()
406 bool editable =
false;
407 bool linkable =
false;
408 bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->
selectedFeatureCount() :
false;
421 mAddFeatureButton->setEnabled( editable );
422 mAddFeatureGeometryButton->setEnabled( editable );
423 mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
424 mLinkFeatureButton->setEnabled( linkable );
425 mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
426 mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
428 mZoomToFeatureButton->setVisible(
444 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
446 mToggleEditingButton->setChecked( editable );
447 mSaveEditsButton->setEnabled( editable );
450 void QgsRelationEditorWidget::addFeatureGeometry()
458 mMapToolDigitize->
setLayer( layer );
461 window()->setVisible(
false );
462 setMapTool( mMapToolDigitize );
471 QString title = tr(
"Create child feature for parent %1 \"%2\"" ).arg( mRelation.
referencedLayer()->
name(), displayString );
472 QString msg = tr(
"Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." )
473 .arg( layer->
name() );
480 void QgsRelationEditorWidget::addFeature(
const QgsGeometry &geometry )
500 const auto constFieldPairs = mRelation.
fieldPairs();
503 int index = fields.
indexOf( fieldPair.first );
504 linkAttributes.insert( index, mFeature.
attribute( fieldPair.second ) );
507 const auto constNmFieldPairs = mNmRelation.
fieldPairs();
510 int index = fields.
indexOf( fieldPair.first );
511 linkAttributes.insert( index, f.
attribute( fieldPair.second ) );
524 const auto constFieldPairs = mRelation.
fieldPairs();
527 keyAttrs.insert( fields.
indexFromName( fieldPair.referencingField() ), mFeature.
attribute( fieldPair.referencedField() ) );
534 void QgsRelationEditorWidget::onDigitizingCompleted(
const QgsFeature &feature )
541 void QgsRelationEditorWidget::linkFeature()
550 QgsFeatureSelectionDlg *selectionDlg =
new QgsFeatureSelectionDlg( layer, mEditorContext,
this );
551 selectionDlg->setAttribute( Qt::WA_DeleteOnClose );
554 selectionDlg->setWindowTitle( tr(
"Link existing child features for parent %1 \"%2\"" ).arg( mRelation.
referencedLayer()->
name(), displayString ) );
556 connect( selectionDlg, &QDialog::accepted,
this, &QgsRelationEditorWidget::onLinkFeatureDlgAccepted );
557 selectionDlg->show();
560 void QgsRelationEditorWidget::onLinkFeatureDlgAccepted()
562 QgsFeatureSelectionDlg *selectionDlg = qobject_cast<QgsFeatureSelectionDlg *>( sender() );
567 .setFilterFids( selectionDlg->selectedFeatures() )
581 const auto constFieldPairs = mRelation.
fieldPairs();
584 int index = fields.
indexOf( fieldPair.first );
585 linkAttributes.insert( index, mFeature.
attribute( fieldPair.second ) );
590 const auto constFieldPairs = mNmRelation.
fieldPairs();
593 int index = fields.
indexOf( fieldPair.first );
594 linkAttributes.insert( index, relatedFeature.
attribute( fieldPair.second ) );
598 newFeatures << linkFeature;
603 const auto constNewFeatures = newFeatures;
604 for (
const QgsFeature &f : constNewFeatures )
610 QMap<int, QVariant> keys;
611 const auto constFieldPairs = mRelation.
fieldPairs();
615 QVariant val = mFeature.
attribute( fieldPair.referencedField() );
616 keys.insert( idx, val );
619 const auto constSelectedFeatures = selectionDlg->selectedFeatures();
622 QMapIterator<int, QVariant> it( keys );
623 while ( it.hasNext() )
634 void QgsRelationEditorWidget::duplicateFeature()
647 void QgsRelationEditorWidget::deleteFeature(
const QgsFeatureId featureid )
652 void QgsRelationEditorWidget::deleteSelectedFeatures()
655 deleteFeatures( selectedFids );
658 void QgsRelationEditorWidget::deleteFeatures(
const QgsFeatureIds &featureids )
660 bool deleteFeatures =
true;
679 QStringList deletedFeaturesPks;
690 QString linkingFeaturesRequestExpression;
691 if ( !deletedFeaturesPks.empty() )
698 int relatedLinkingFeaturesCount = 0;
701 relatedLinkingFeaturesCount++;
704 if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
706 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 );
707 messageBox.addButton( QMessageBox::Cancel );
708 QAbstractButton *deleteButton = messageBox.addButton( tr(
"Delete" ), QMessageBox::AcceptRole );
711 if ( messageBox.clickedButton() != deleteButton )
712 deleteFeatures =
false;
714 else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
716 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 );
717 messageBox.addButton( QMessageBox::Cancel );
718 QAbstractButton *deleteButton = messageBox.addButton( tr(
"Delete" ), QMessageBox::AcceptRole );
721 if ( messageBox.clickedButton() != deleteButton )
722 deleteFeatures =
false;
734 QString childrenInfo;
735 int childrenCount = 0;
736 const auto infoContextLayers = infoContext.
layers();
740 childrenInfo += ( tr(
"%1 feature(s) on layer \"%2\", " ).arg( infoContext.
duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
744 int res = QMessageBox::question(
this, tr(
"Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ),
745 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 ),
746 QMessageBox::Yes | QMessageBox::No );
747 if ( res != QMessageBox::Yes )
748 deleteFeatures =
false;
751 if ( deleteFeatures )
755 const auto contextLayers = context.handledLayers();
756 if ( contextLayers.size() > 1 )
758 int deletedCount = 0;
759 QString feedbackMessage;
762 feedbackMessage += tr(
"%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() );
763 deletedCount += context.handledFeatures( contextLayer ).size();
772 void QgsRelationEditorWidget::unlinkFeature(
const QgsFeatureId featureid )
777 void QgsRelationEditorWidget::unlinkSelectedFeatures()
782 void QgsRelationEditorWidget::zoomToSelectedFeatures()
794 void QgsRelationEditorWidget::unlinkFeatures(
const QgsFeatureIds &featureids )
800 .setFilterFids( featureids )
812 QString filter = QStringLiteral(
"(%1) AND (%2)" ).arg(
814 filters.join( QStringLiteral(
" OR " ) ) );
818 .setFilterExpression( filter ) );
834 QMap<int, QgsField> keyFields;
835 const auto constFieldPairs = mRelation.
fieldPairs();
841 QgsDebugMsg( QStringLiteral(
"referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
845 keyFields.insert( idx, fld );
848 const auto constFeatureids = featureids;
851 QMapIterator<int, QgsField> it( keyFields );
852 while ( it.hasNext() )
861 void QgsRelationEditorWidget::toggleEditing(
bool state )
877 void QgsRelationEditorWidget::saveEdits()
884 void QgsRelationEditorWidget::onCollapsedStateChanged(
bool collapsed )
893 void QgsRelationEditorWidget::updateUi()
914 filters << filter.prepend(
'(' ).append(
')' );
932 return mLinkFeatureButton->isVisible();
942 return mUnlinkFeatureButton->isVisible();
947 mSaveEditsButton->setVisible( showChildEdits );
952 return mSaveEditsButton->isVisible();
981 QAction *qAction =
nullptr;
984 connect( qAction, &QAction::triggered,
this, [
this, fid]() { deleteFeature( fid ); } );
987 connect( qAction, &QAction::triggered,
this, [
this, fid]() { unlinkFeature( fid ); } );
991 void QgsRelationEditorWidget::setMapTool(
QgsMapTool *mapTool )
996 mapCanvas->window()->raise();
997 mapCanvas->activateWindow();
998 mapCanvas->setFocus();
1002 void QgsRelationEditorWidget::unsetMapTool()
1013 void QgsRelationEditorWidget::updateTitle()
1015 if ( mShowLabel && mRelation.
isValid() )
1016 setTitle( mRelation.
name() );
1018 setTitle( QString() );
1026 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
1028 if ( e->key() == Qt::Key_Escape )
1034 void QgsRelationEditorWidget::mapToolDeactivated()
1036 window()->setVisible(
true );
1038 window()->activateWindow();
1044 mMessageBarItem =
nullptr;