39 #include <QHBoxLayout>
41 #include <QMessageBox>
42 #include <QPushButton>
54 if ( mRequest.acceptFeature( layer->
getFeature( fid ) ) )
55 mSelectedFeatureIds << fid;
60 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds()
const
62 return mSelectedFeatureIds;
65 int QgsFilteredSelectionManager::selectedFeatureCount()
67 return mSelectedFeatureIds.count();
70 void QgsFilteredSelectionManager::onSelectionChanged(
const QgsFeatureIds &selected,
const QgsFeatureIds &deselected,
bool clearAndSelect )
75 mSelectedFeatureIds.clear();
79 for (
auto fid : deselected )
80 mSelectedFeatureIds.remove( fid );
83 for (
auto fid : selected )
84 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
85 mSelectedFeatureIds << fid;
87 lselected.remove( fid );
89 emit selectionChanged( lselected, deselected, clearAndSelect );
97 QVBoxLayout *topLayout =
new QVBoxLayout(
this );
98 topLayout->setContentsMargins( 0, 9, 0, 0 );
99 setLayout( topLayout );
102 QHBoxLayout *buttonLayout =
new QHBoxLayout();
103 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
105 mToggleEditingButton =
new QToolButton(
this );
106 mToggleEditingButton->setObjectName( QStringLiteral(
"mToggleEditingButton" ) );
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 );
114 mSaveEditsButton =
new QToolButton(
this );
116 mSaveEditsButton->setText( tr(
"Save Child Layer Edits" ) );
117 mSaveEditsButton->setToolTip( tr(
"Save child layer edits" ) );
118 mSaveEditsButton->setEnabled(
true );
119 buttonLayout->addWidget( mSaveEditsButton );
121 mAddFeatureGeometryButton =
new QToolButton(
this );
122 mAddFeatureGeometryButton->setObjectName( QStringLiteral(
"mAddFeatureGeometryButton" ) );
123 buttonLayout->addWidget( mAddFeatureGeometryButton );
125 mAddFeatureButton =
new QToolButton(
this );
127 mAddFeatureButton->setText( tr(
"Add Child Feature" ) );
128 mAddFeatureButton->setToolTip( tr(
"Add child feature" ) );
129 mAddFeatureButton->setObjectName( QStringLiteral(
"mAddFeatureButton" ) );
130 buttonLayout->addWidget( mAddFeatureButton );
132 mDuplicateFeatureButton =
new QToolButton(
this );
134 mDuplicateFeatureButton->setText( tr(
"Duplicate Child Feature" ) );
135 mDuplicateFeatureButton->setToolTip( tr(
"Duplicate child feature" ) );
136 mDuplicateFeatureButton->setObjectName( QStringLiteral(
"mDuplicateFeatureButton" ) );
137 buttonLayout->addWidget( mDuplicateFeatureButton );
139 mDeleteFeatureButton =
new QToolButton(
this );
141 mDeleteFeatureButton->setText( tr(
"Delete Child Feature" ) );
142 mDeleteFeatureButton->setToolTip( tr(
"Delete child feature" ) );
143 mDeleteFeatureButton->setObjectName( QStringLiteral(
"mDeleteFeatureButton" ) );
144 buttonLayout->addWidget( mDeleteFeatureButton );
146 mLinkFeatureButton =
new QToolButton(
this );
148 mLinkFeatureButton->setText( tr(
"Link Existing Features" ) );
149 mLinkFeatureButton->setToolTip( tr(
"Link existing child features" ) );
150 mLinkFeatureButton->setObjectName( QStringLiteral(
"mLinkFeatureButton" ) );
151 buttonLayout->addWidget( mLinkFeatureButton );
153 mUnlinkFeatureButton =
new QToolButton(
this );
155 mUnlinkFeatureButton->setText( tr(
"Unlink Feature" ) );
156 mUnlinkFeatureButton->setToolTip( tr(
"Unlink child feature" ) );
157 mUnlinkFeatureButton->setObjectName( QStringLiteral(
"mUnlinkFeatureButton" ) );
158 buttonLayout->addWidget( mUnlinkFeatureButton );
160 mZoomToFeatureButton =
new QToolButton(
this );
162 mZoomToFeatureButton->setText( tr(
"Zoom To Feature" ) );
163 mZoomToFeatureButton->setToolTip( tr(
"Zoom to child feature" ) );
164 mZoomToFeatureButton->setObjectName( QStringLiteral(
"mZoomToFeatureButton" ) );
165 buttonLayout->addWidget( mZoomToFeatureButton );
167 buttonLayout->addItem(
new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
169 mFormViewButton =
new QToolButton(
this );
170 mFormViewButton->setText( tr(
"Form View" ) );
171 mFormViewButton->setToolTip( tr(
"Switch to form view" ) );
173 mFormViewButton->setCheckable(
true );
175 buttonLayout->addWidget( mFormViewButton );
177 mTableViewButton =
new QToolButton(
this );
178 mTableViewButton->setText( tr(
"Table View" ) );
179 mTableViewButton->setToolTip( tr(
"Switch to table view" ) );
181 mTableViewButton->setCheckable(
true );
183 buttonLayout->addWidget( mTableViewButton );
185 mViewModeButtonGroup =
new QButtonGroup(
this );
190 topLayout->addLayout( buttonLayout );
192 mRelationLayout =
new QGridLayout();
193 mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
194 topLayout->addLayout( mRelationLayout );
197 mDualView->
setView( mViewMode );
199 mRelationLayout->addWidget( mDualView );
202 connect( mViewModeButtonGroup,
static_cast<void ( QButtonGroup::* )(
int )
>( &QButtonGroup::buttonClicked ),
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 );
228 mRelation = relation;
239 if ( canChangeAttributes && !lyr->
readOnly() )
241 mToggleEditingButton->setEnabled(
true );
246 mToggleEditingButton->setEnabled(
false );
249 setObjectName( QStringLiteral(
"referenced/" ) + mRelation.
name() );
266 mDualView->
init( layer, mEditorContext.
mapCanvas(), request, ctx );
267 mFeatureSelectionMgr =
new QgsFilteredSelectionManager( layer, request, mDualView );
277 text = tr(
"Add Point child Feature" );
282 text = tr(
"Add Line child Feature" );
287 text = tr(
"Add Polygon Feature" );
290 mAddFeatureGeometryButton->setIcon( icon );
291 mAddFeatureGeometryButton->setText( text );
292 mAddFeatureGeometryButton->setToolTip( text );
311 mRelation = relation;
312 mNmRelation = nmrelation;
317 mLayerInSameTransactionGroup =
false;
320 for (
auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it )
327 mLayerInSameTransactionGroup =
true;
333 mLayerInSameTransactionGroup =
true;
351 if ( canChangeAttributes && !lyr->
readOnly() )
353 mToggleEditingButton->setEnabled(
true );
357 mToggleEditingButton->setEnabled(
false );
362 setObjectName( QStringLiteral(
"referenced/" ) + mRelation.
name() );
369 mEditorContext = context;
374 mMapToolDigitize->
setButton( mAddFeatureGeometryButton );
382 return mEditorContext;
387 return mFeatureSelectionMgr;
406 void QgsRelationEditorWidget::updateButtons()
408 bool editable =
false;
409 bool linkable =
false;
410 bool spatial =
false;
411 bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->
selectedFeatureCount() :
false;
426 mAddFeatureButton->setEnabled( editable );
427 mAddFeatureGeometryButton->setEnabled( editable );
428 mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
429 mLinkFeatureButton->setEnabled( linkable );
430 mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
431 mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
432 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
433 mToggleEditingButton->setChecked( editable );
434 mSaveEditsButton->setEnabled( editable );
436 mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
437 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Link ) );
438 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Unlink ) );
439 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
440 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) );
441 mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) && mEditorContext.
mapCanvas() && mEditorContext.
cadDockWidget() && spatial );
442 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature ) );
443 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature ) );
444 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature ) && mEditorContext.
mapCanvas() && spatial );
447 void QgsRelationEditorWidget::addFeatureGeometry()
455 mMapToolDigitize->
setLayer( layer );
458 window()->setVisible(
false );
459 setMapTool( mMapToolDigitize );
468 QString title = tr(
"Create child feature for parent %1 \"%2\"" ).arg( mRelation.
referencedLayer()->
name(), displayString );
469 QString msg = tr(
"Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." )
470 .arg( layer->
name() );
472 lMainMessageBar->pushItem( mMessageBarItem );
477 void QgsRelationEditorWidget::addFeature(
const QgsGeometry &geometry )
497 const auto constFieldPairs = mRelation.
fieldPairs();
500 int index = fields.
indexOf( fieldPair.first );
501 linkAttributes.insert( index, mFeature.
attribute( fieldPair.second ) );
504 const auto constNmFieldPairs = mNmRelation.
fieldPairs();
507 int index = fields.
indexOf( fieldPair.first );
508 linkAttributes.insert( index, f.
attribute( fieldPair.second ) );
521 const auto constFieldPairs = mRelation.
fieldPairs();
524 keyAttrs.insert( fields.
indexFromName( fieldPair.referencingField() ), mFeature.
attribute( fieldPair.referencedField() ) );
531 void QgsRelationEditorWidget::onDigitizingCompleted(
const QgsFeature &feature )
538 void QgsRelationEditorWidget::linkFeature()
547 QgsFeatureSelectionDlg *selectionDlg =
new QgsFeatureSelectionDlg( layer, mEditorContext,
this );
548 selectionDlg->setAttribute( Qt::WA_DeleteOnClose );
551 selectionDlg->setWindowTitle( tr(
"Link existing child features for parent %1 \"%2\"" ).arg( mRelation.
referencedLayer()->
name(), displayString ) );
553 connect( selectionDlg, &QDialog::accepted,
this, &QgsRelationEditorWidget::onLinkFeatureDlgAccepted );
554 selectionDlg->show();
557 void QgsRelationEditorWidget::onLinkFeatureDlgAccepted()
559 QgsFeatureSelectionDlg *selectionDlg = qobject_cast<QgsFeatureSelectionDlg *>( sender() );
564 .setFilterFids( selectionDlg->selectedFeatures() )
578 const auto constFieldPairs = mRelation.
fieldPairs();
581 int index = fields.
indexOf( fieldPair.first );
582 linkAttributes.insert( index, mFeature.
attribute( fieldPair.second ) );
587 const auto constFieldPairs = mNmRelation.
fieldPairs();
590 int index = fields.
indexOf( fieldPair.first );
591 linkAttributes.insert( index, relatedFeature.
attribute( fieldPair.second ) );
595 newFeatures << linkFeature;
600 const auto constNewFeatures = newFeatures;
601 for (
const QgsFeature &f : constNewFeatures )
607 QMap<int, QVariant> keys;
608 const auto constFieldPairs = mRelation.
fieldPairs();
612 QVariant val = mFeature.
attribute( fieldPair.referencedField() );
613 keys.insert( idx, val );
616 const auto constSelectedFeatures = selectionDlg->selectedFeatures();
619 QMapIterator<int, QVariant> it( keys );
620 while ( it.hasNext() )
631 void QgsRelationEditorWidget::duplicateFeature()
644 void QgsRelationEditorWidget::deleteFeature(
const QgsFeatureId featureid )
649 void QgsRelationEditorWidget::deleteSelectedFeatures()
652 deleteFeatures( selectedFids );
655 void QgsRelationEditorWidget::deleteFeatures(
const QgsFeatureIds &featureids )
657 bool deleteFeatures =
true;
676 QStringList deletedFeaturesPks;
687 QString linkingFeaturesRequestExpression;
688 if ( !deletedFeaturesPks.empty() )
695 int relatedLinkingFeaturesCount = 0;
698 relatedLinkingFeaturesCount++;
701 if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
703 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 );
704 messageBox.addButton( QMessageBox::Cancel );
705 QAbstractButton *deleteButton = messageBox.addButton( tr(
"Delete" ), QMessageBox::AcceptRole );
708 if ( messageBox.clickedButton() != deleteButton )
709 deleteFeatures =
false;
711 else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
713 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 );
714 messageBox.addButton( QMessageBox::Cancel );
715 QAbstractButton *deleteButton = messageBox.addButton( tr(
"Delete" ), QMessageBox::AcceptRole );
718 if ( messageBox.clickedButton() != deleteButton )
719 deleteFeatures =
false;
731 QString childrenInfo;
732 int childrenCount = 0;
733 const auto infoContextLayers = infoContext.
layers();
737 childrenInfo += ( tr(
"%1 feature(s) on layer \"%2\", " ).arg( infoContext.
duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
741 int res = QMessageBox::question(
this, tr(
"Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ),
742 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 ),
743 QMessageBox::Yes | QMessageBox::No );
744 if ( res != QMessageBox::Yes )
745 deleteFeatures =
false;
748 if ( deleteFeatures )
752 const auto contextLayers = context.handledLayers();
753 if ( contextLayers.size() > 1 )
755 int deletedCount = 0;
756 QString feedbackMessage;
759 feedbackMessage += tr(
"%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() );
760 deletedCount += context.handledFeatures( contextLayer ).size();
769 void QgsRelationEditorWidget::unlinkFeature(
const QgsFeatureId featureid )
774 void QgsRelationEditorWidget::unlinkSelectedFeatures()
779 void QgsRelationEditorWidget::zoomToSelectedFeatures()
791 void QgsRelationEditorWidget::unlinkFeatures(
const QgsFeatureIds &featureids )
797 .setFilterFids( featureids )
809 QString filter = QStringLiteral(
"(%1) AND (%2)" ).arg(
811 filters.join( QLatin1String(
" OR " ) ) );
815 .setFilterExpression( filter ) );
831 QMap<int, QgsField> keyFields;
832 const auto constFieldPairs = mRelation.
fieldPairs();
838 QgsDebugMsg( QStringLiteral(
"referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
842 keyFields.insert( idx, fld );
845 const auto constFeatureids = featureids;
848 QMapIterator<int, QgsField> it( keyFields );
849 while ( it.hasNext() )
858 void QgsRelationEditorWidget::toggleEditing(
bool state )
876 void QgsRelationEditorWidget::saveEdits()
883 void QgsRelationEditorWidget::onCollapsedStateChanged(
bool collapsed )
892 void QgsRelationEditorWidget::updateUi()
913 filters << filter.prepend(
'(' ).append(
')' );
931 return mLinkFeatureButton->isVisible();
941 return mUnlinkFeatureButton->isVisible();
946 mSaveEditsButton->setVisible( showChildEdits );
951 return mSaveEditsButton->isVisible();
956 mButtonsVisibility = buttons;
962 QgsAttributeEditorRelation::Buttons buttons;
963 if ( mLinkFeatureButton->isVisible() )
964 buttons |= QgsAttributeEditorRelation::Button::Link;
965 if ( mUnlinkFeatureButton->isVisible() )
966 buttons |= QgsAttributeEditorRelation::Button::Unlink;
967 if ( mSaveEditsButton->isVisible() )
968 buttons |= QgsAttributeEditorRelation::Button::SaveChildEdits;
969 if ( mAddFeatureButton->isVisible() )
970 buttons |= QgsAttributeEditorRelation::Button::AddChildFeature;
971 if ( mDuplicateFeatureButton->isVisible() )
972 buttons |= QgsAttributeEditorRelation::Button::DuplicateChildFeature;
973 if ( mDeleteFeatureButton->isVisible() )
974 buttons |= QgsAttributeEditorRelation::Button::DeleteChildFeature;
975 if ( mZoomToFeatureButton->isVisible() )
976 buttons |= QgsAttributeEditorRelation::Button::ZoomToChildFeature;
987 return mForceSuppressFormPopup;
997 return mNmRelationId;
1038 QAction *qAction =
nullptr;
1041 connect( qAction, &QAction::triggered,
this, [
this, fid]() { deleteFeature( fid ); } );
1044 connect( qAction, &QAction::triggered,
this, [
this, fid]() { unlinkFeature( fid ); } );
1048 void QgsRelationEditorWidget::setMapTool(
QgsMapTool *mapTool )
1053 mapCanvas->window()->raise();
1054 mapCanvas->activateWindow();
1055 mapCanvas->setFocus();
1059 void QgsRelationEditorWidget::unsetMapTool()
1070 void QgsRelationEditorWidget::updateTitle()
1072 if ( mShowLabel && !mLabel.isEmpty() )
1076 else if ( mShowLabel && mRelation.
isValid() )
1078 setTitle( mRelation.
name() );
1082 setTitle( QString() );
1091 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
1093 if ( e->key() == Qt::Key_Escape )
1099 void QgsRelationEditorWidget::mapToolDeactivated()
1101 window()->setVisible(
true );
1103 window()->activateWindow();
1109 mMessageBarItem =
nullptr;