41 #include <QHBoxLayout>
43 #include <QMessageBox>
44 #include <QPushButton>
45 #include <QTreeWidget>
57 if ( mRequest.acceptFeature( layer->
getFeature( fid ) ) )
58 mSelectedFeatureIds << fid;
63 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds()
const
67 return mSelectedFeatureIds;
70 int QgsFilteredSelectionManager::selectedFeatureCount()
72 return mSelectedFeatureIds.count();
75 void QgsFilteredSelectionManager::onSelectionChanged(
const QgsFeatureIds &selected,
const QgsFeatureIds &deselected,
bool clearAndSelect )
80 mSelectedFeatureIds.clear();
84 for (
const auto fid : deselected )
85 mSelectedFeatureIds.remove( fid );
88 for (
const auto fid : selected )
89 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
90 mSelectedFeatureIds << fid;
92 lselected.remove( fid );
94 emit selectionChanged( lselected, deselected, clearAndSelect );
102 , mShowFirstFeature( config.value( QStringLiteral(
"show_first_feature" ), true ).toBool() )
104 QVBoxLayout *rootLayout =
new QVBoxLayout(
this );
105 rootLayout->setContentsMargins( 0, 9, 0, 0 );
108 QHBoxLayout *buttonLayout =
new QHBoxLayout();
109 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
111 mToggleEditingButton =
new QToolButton(
this );
112 mToggleEditingButton->setObjectName( QStringLiteral(
"mToggleEditingButton" ) );
114 mToggleEditingButton->setText( tr(
"Toggle Editing" ) );
115 mToggleEditingButton->setEnabled(
false );
116 mToggleEditingButton->setCheckable(
true );
117 mToggleEditingButton->setToolTip( tr(
"Toggle editing mode for child layer" ) );
118 buttonLayout->addWidget( mToggleEditingButton );
120 mSaveEditsButton =
new QToolButton(
this );
122 mSaveEditsButton->setText( tr(
"Save Child Layer Edits" ) );
123 mSaveEditsButton->setToolTip( tr(
"Save child layer edits" ) );
124 mSaveEditsButton->setEnabled(
true );
125 buttonLayout->addWidget( mSaveEditsButton );
127 mAddFeatureGeometryButton =
new QToolButton(
this );
128 mAddFeatureGeometryButton->setObjectName( QStringLiteral(
"mAddFeatureGeometryButton" ) );
129 buttonLayout->addWidget( mAddFeatureGeometryButton );
131 mAddFeatureButton =
new QToolButton(
this );
133 mAddFeatureButton->setText( tr(
"Add Child Feature" ) );
134 mAddFeatureButton->setToolTip( tr(
"Add child feature" ) );
135 mAddFeatureButton->setObjectName( QStringLiteral(
"mAddFeatureButton" ) );
136 buttonLayout->addWidget( mAddFeatureButton );
138 mDuplicateFeatureButton =
new QToolButton(
this );
140 mDuplicateFeatureButton->setText( tr(
"Duplicate Child Feature(s)" ) );
141 mDuplicateFeatureButton->setToolTip( tr(
"Duplicate selected child feature(s)" ) );
142 mDuplicateFeatureButton->setObjectName( QStringLiteral(
"mDuplicateFeatureButton" ) );
143 buttonLayout->addWidget( mDuplicateFeatureButton );
145 mDeleteFeatureButton =
new QToolButton(
this );
147 mDeleteFeatureButton->setText( tr(
"Delete Child Feature(s)" ) );
148 mDeleteFeatureButton->setToolTip( tr(
"Delete selected child feature(s)" ) );
149 mDeleteFeatureButton->setObjectName( QStringLiteral(
"mDeleteFeatureButton" ) );
150 buttonLayout->addWidget( mDeleteFeatureButton );
152 mLinkFeatureButton =
new QToolButton(
this );
154 mLinkFeatureButton->setText( tr(
"Link Existing Feature(s)" ) );
155 mLinkFeatureButton->setToolTip( tr(
"Link existing child feature(s)" ) );
156 mLinkFeatureButton->setObjectName( QStringLiteral(
"mLinkFeatureButton" ) );
157 buttonLayout->addWidget( mLinkFeatureButton );
159 mUnlinkFeatureButton =
new QToolButton(
this );
161 mUnlinkFeatureButton->setText( tr(
"Unlink Feature(s)" ) );
162 mUnlinkFeatureButton->setToolTip( tr(
"Unlink selected child feature(s)" ) );
163 mUnlinkFeatureButton->setObjectName( QStringLiteral(
"mUnlinkFeatureButton" ) );
164 buttonLayout->addWidget( mUnlinkFeatureButton );
166 mZoomToFeatureButton =
new QToolButton(
this );
168 mZoomToFeatureButton->setText( tr(
"Zoom To Feature(s)" ) );
169 mZoomToFeatureButton->setToolTip( tr(
"Zoom to selected child feature(s)" ) );
170 mZoomToFeatureButton->setObjectName( QStringLiteral(
"mZoomToFeatureButton" ) );
171 buttonLayout->addWidget( mZoomToFeatureButton );
173 buttonLayout->addItem(
new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
175 mFormViewButton =
new QToolButton(
this );
176 mFormViewButton->setText( tr(
"Form View" ) );
177 mFormViewButton->setToolTip( tr(
"Switch to form view" ) );
179 mFormViewButton->setCheckable(
true );
181 buttonLayout->addWidget( mFormViewButton );
183 mTableViewButton =
new QToolButton(
this );
184 mTableViewButton->setText( tr(
"Table View" ) );
185 mTableViewButton->setToolTip( tr(
"Switch to table view" ) );
187 mTableViewButton->setCheckable(
true );
189 buttonLayout->addWidget( mTableViewButton );
191 mViewModeButtonGroup =
new QButtonGroup(
this );
195 mMultiEditInfoLabel =
new QLabel(
this );
196 buttonLayout->addWidget( mMultiEditInfoLabel );
199 rootLayout->addLayout( buttonLayout );
202 mStackedWidget =
new QStackedWidget(
this );
206 mDualView->
setView( mViewMode );
210 mMultiEditStackedWidgetPage =
new QWidget(
this );
212 QVBoxLayout *vBoxLayout =
new QVBoxLayout();
213 vBoxLayout->setContentsMargins( 0, 0, 0, 0 );
215 mMultiEditTreeWidget =
new QTreeWidget(
this );
216 mMultiEditTreeWidget->setHeaderHidden(
true );
217 mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
218 vBoxLayout->addWidget( mMultiEditTreeWidget );
220 mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
222 mStackedWidget->addWidget( mMultiEditStackedWidgetPage );
224 mStackedWidget->addWidget( mDualView );
226 rootLayout->addWidget( mStackedWidget );
228 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
229 connect( mViewModeButtonGroup,
static_cast<void ( QButtonGroup::* )(
int )
>( &QButtonGroup::buttonClicked ),
232 connect( mViewModeButtonGroup, &QButtonGroup::idClicked,
235 connect( mToggleEditingButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::toggleEditing );
237 connect( mAddFeatureButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::addFeature );
238 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked,
this, &QgsRelationEditorWidget::addFeatureGeometry );
244 connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged,
this, &QgsRelationEditorWidget::multiEditItemSelectionChanged );
249 setLayout( rootLayout );
263 mFeatureSelectionMgr =
new QgsFilteredSelectionManager( layer, request, mDualView );
273 text = tr(
"Add Point Child Feature" );
278 text = tr(
"Add Line Child Feature" );
283 text = tr(
"Add Polygon Child Feature" );
286 mAddFeatureGeometryButton->setIcon( icon );
287 mAddFeatureGeometryButton->setText( text );
288 mAddFeatureGeometryButton->setToolTip( text );
300 mMapToolDigitize->
setButton( mAddFeatureGeometryButton );
312 void QgsRelationEditorWidget::updateButtons()
314 bool toggleEditingButtonEnabled =
false;
316 bool canAddGeometry =
false;
317 bool canRemove =
false;
318 bool canEdit =
false;
319 bool canLink =
false;
320 bool canUnlink =
false;
321 bool spatial =
false;
345 const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->
selectedFeatureCount() :
false;
348 const bool multieditLinkedChildSelected = ! selectedChildFeatureIds().isEmpty();
350 canAddGeometry =
false;
352 canRemove = canRemove && multieditLinkedChildSelected;
356 canUnlink = canUnlink && multieditLinkedChildSelected;
360 canRemove = canRemove && selectionNotEmpty;
361 canUnlink = canUnlink && selectionNotEmpty;
364 mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
365 mAddFeatureButton->setEnabled( canAdd );
366 mAddFeatureGeometryButton->setEnabled( canAddGeometry );
367 mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
368 mLinkFeatureButton->setEnabled( canLink );
369 mDeleteFeatureButton->setEnabled( canRemove );
370 mUnlinkFeatureButton->setEnabled( canUnlink );
371 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
372 mToggleEditingButton->setChecked( canEdit );
373 mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );
377 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
378 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
379 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !
mLayerInSameTransactionGroup );
380 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
382 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
383 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
384 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) &&
mEditorContext.
mapCanvas() && spatial );
387 void QgsRelationEditorWidget::addFeature()
394 mMultiEditTreeWidget->blockSignals(
true );
395 mMultiEdit1NJustAddedIds = addedFeatures;
396 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
397 while ( *treeWidgetItemIterator )
399 if ( ( *treeWidgetItemIterator )->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() !=
static_cast<int>( MultiEditFeatureType::Child ) )
401 ++treeWidgetItemIterator;
405 if ( addedFeatures.contains( ( *treeWidgetItemIterator )->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt() ) )
406 ( *treeWidgetItemIterator )->setSelected(
true );
408 ++treeWidgetItemIterator;
410 mMultiEditTreeWidget->blockSignals(
false );
416 void QgsRelationEditorWidget::addFeatureGeometry()
420 QgsLogger::warning( tr(
"Adding a geometry feature is not supported in multiple edit mode" ) );
430 mMapToolDigitize->
setLayer( layer );
433 if ( window()->objectName() != QLatin1String(
"QgisApp" ) )
435 window()->setVisible(
false );
437 setMapTool( mMapToolDigitize );
447 const QString msg = tr(
"Digitize the geometry for the new feature on layer %1. Press <ESC> to cancel." )
448 .arg( layer->
name() );
450 lMainMessageBar->pushItem( mMessageBarItem );
454 void QgsRelationEditorWidget::onDigitizingCompleted(
const QgsFeature &feature )
461 void QgsRelationEditorWidget::multiEditItemSelectionChanged()
463 const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
467 if ( selectedItems.size() == 1
468 && mMultiEditPreviousSelectedItems.size() <= 1 )
470 if ( selectedItems.first()->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ==
static_cast<int>( MultiEditFeatureType::Child ) )
472 mMultiEditTreeWidget->blockSignals(
true );
474 const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
476 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
477 while ( *treeWidgetItemIterator )
479 if ( ( *treeWidgetItemIterator )->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() !=
static_cast<int>( MultiEditFeatureType::Child ) )
481 ++treeWidgetItemIterator;
485 const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
488 if ( featureIdSelectedItem == featureIdCurrentItem )
489 ( *treeWidgetItemIterator )->setSelected(
true );
493 if ( ! mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
496 if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
497 ( *treeWidgetItemIterator )->setSelected(
true );
500 ++treeWidgetItemIterator;
502 mMultiEditTreeWidget->blockSignals(
false );
505 mMultiEditPreviousSelectedItems = selectedItems;
509 void QgsRelationEditorWidget::toggleEditing(
bool state )
527 updateUiSingleEdit();
532 mButtonsVisibility = buttons;
539 if ( mLinkFeatureButton->isVisible() )
540 buttons |= Button::Link;
541 if ( mUnlinkFeatureButton->isVisible() )
542 buttons |= Button::Unlink;
543 if ( mSaveEditsButton->isVisible() )
544 buttons |= Button::SaveChildEdits;
545 if ( mAddFeatureButton->isVisible() )
546 buttons |= Button::AddChildFeature;
547 if ( mDuplicateFeatureButton->isVisible() )
548 buttons |= Button::DuplicateChildFeature;
549 if ( mDeleteFeatureButton->isVisible() )
550 buttons |= Button::DeleteChildFeature;
551 if ( mZoomToFeatureButton->isVisible() )
552 buttons |= Button::ZoomToChildFeature;
565 QAction *qAction =
nullptr;
568 connect( qAction, &QAction::triggered,
this, [
this, fid]() {
deleteFeature( fid ); } );
571 connect( qAction, &QAction::triggered,
this, [
this, fid]() {
unlinkFeature( fid ); } );
575 void QgsRelationEditorWidget::setMapTool(
QgsMapTool *mapTool )
580 mapCanvas->window()->raise();
581 mapCanvas->activateWindow();
582 mapCanvas->setFocus();
586 void QgsRelationEditorWidget::unsetMapTool()
597 QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds()
const
602 for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
604 if (
static_cast<MultiEditFeatureType
>( treeWidgetItem->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
607 featureIds.insert( treeWidgetItem->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
615 void QgsRelationEditorWidget::updateUiSingleEdit()
617 mFormViewButton->setVisible(
true );
618 mTableViewButton->setVisible(
true );
619 mMultiEditInfoLabel->setVisible(
false );
621 mStackedWidget->setCurrentWidget( mDualView );
634 filters << filter.prepend(
'(' ).append(
')' );
648 void QgsRelationEditorWidget::updateUiMultiEdit()
650 mFormViewButton->setVisible(
false );
651 mTableViewButton->setVisible(
false );
652 mMultiEditInfoLabel->setVisible(
true );
654 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage ) ;
656 QList<QTreeWidgetItem *> parentTreeWidgetItems;
659 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
661 mMultiEditTreeWidget->clear();
664 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent,
mRelation.
referencedLayer(), MultiEditFeatureType::Parent );
667 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
669 parentTreeWidgetItems.append( treeWidgetItem );
675 while ( featureIterator.
nextFeature( featureChild ) )
682 while ( featureIteratorFinalChild.
nextFeature( featureChildChild ) )
684 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild,
mNmRelation.
referencedLayer(), MultiEditFeatureType::Child );
686 treeWidgetItem->addChild( treeWidgetItemChild );
688 featureIdsMixedValues.insert( featureChildChild.
id() );
689 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.
id() );
694 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild,
mRelation.
referencingLayer(), MultiEditFeatureType::Child );
695 treeWidgetItem->addChild( treeWidgetItemChild );
697 featureIdsMixedValues.insert( featureChild.
id() );
701 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
702 treeWidgetItem->setExpanded(
true );
718 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
719 while ( iterator != featureIdsMixedValues.end() )
721 bool mixedValues =
false;
722 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
724 if ( ! multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
733 iterator = featureIdsMixedValues.erase( iterator );
742 if ( featureIdsMixedValues.isEmpty() )
745 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
746 mMultiEditInfoLabel->height() ) );
747 mMultiEditInfoLabel->setToolTip( tr(
"All features in selection have equal relations" ) );
752 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
753 mMultiEditInfoLabel->height() ) );
754 mMultiEditInfoLabel->setToolTip( tr(
"Some features in selection have different relations" ) );
757 QFont fontItalic = mMultiEditTreeWidget->font();
758 fontItalic.setItalic(
true );
759 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
761 for (
int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
763 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
764 const QgsFeatureId featureIdCurrentItem = childItem->data( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
765 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
766 childItem->setFont( 0, fontItalic );
772 QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem(
const QgsFeature &feature,
QgsVectorLayer *layer, MultiEditFeatureType type )
774 QTreeWidgetItem *treeWidgetItem =
new QTreeWidgetItem();
777 treeWidgetItem->setData( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureType ),
static_cast<int>( type ) );
778 treeWidgetItem->setData( 0,
static_cast<int>( MultiEditTreeWidgetRole::FeatureId ),
feature.
id() );
779 return treeWidgetItem;
782 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
784 if ( e->key() == Qt::Key_Escape )
786 window()->setVisible(
true );
788 window()->activateWindow();
793 void QgsRelationEditorWidget::mapToolDeactivated()
799 mMessageBarItem =
nullptr;
805 {
"show_first_feature", mShowFirstFeature}} );
810 mButtonsVisibility =
qgsFlagKeysToValue(
config.value( QStringLiteral(
"buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
811 mShowFirstFeature =
config.value( QStringLiteral(
"show_first_feature" ),
true ).toBool();
817 Q_UNUSED( newRelation );
818 Q_UNUSED( newFeature );
847 Q_UNUSED( newRelation );
848 Q_UNUSED( newNmRelation );
882 return mFeatureSelectionMgr;
887 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
903 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
930 QgsRelationEditorWidget::Buttons buttons;
931 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
932 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
933 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
934 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
935 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
936 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
937 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
942 {
"show_first_feature", mShowFirstFeature->isChecked()}
948 const QgsRelationEditorWidget::Buttons buttons =
qgsFlagKeysToValue(
config.value( QStringLiteral(
"buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
950 mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
951 mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
952 mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
953 mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
954 mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
955 mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
956 mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
957 mShowFirstFeature->setChecked(
config.value( QStringLiteral(
"show_first_feature" ),
true ).toBool() );
972 return QStringLiteral(
"relation_editor" );
977 return QObject::tr(
"Relation Editor" );