18 #include <QPushButton>
20 #include <QHBoxLayout>
55 for (
int i = 0; i < list.size(); ++i )
57 if ( !list.at( i ).isNull() )
67 mTopLayout =
new QVBoxLayout(
this );
68 mTopLayout->setContentsMargins( 0, 0, 0, 0 );
70 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
72 setLayout( mTopLayout );
74 QHBoxLayout *editLayout =
new QHBoxLayout();
75 editLayout->setContentsMargins( 0, 0, 0, 0 );
76 editLayout->setSpacing( 2 );
79 mChooserContainer =
new QWidget;
80 editLayout->addWidget( mChooserContainer );
81 QHBoxLayout *chooserLayout =
new QHBoxLayout;
82 chooserLayout->setContentsMargins( 0, 0, 0, 0 );
83 mFilterLayout =
new QHBoxLayout;
84 mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
85 mFilterContainer =
new QWidget;
86 mFilterContainer->setLayout( mFilterLayout );
87 mChooserContainer->setLayout( chooserLayout );
88 chooserLayout->addWidget( mFilterContainer );
91 mChooserContainer->layout()->addWidget( mComboBox );
94 mOpenFormButton =
new QToolButton();
96 mOpenFormButton->setText( tr(
"Open Related Feature Form" ) );
97 editLayout->addWidget( mOpenFormButton );
99 mAddEntryButton =
new QToolButton();
101 mAddEntryButton->setText( tr(
"Add New Entry" ) );
102 editLayout->addWidget( mAddEntryButton );
105 mHighlightFeatureButton =
new QToolButton(
this );
106 mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
107 mHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionHighlightFeature.svg" ) ), tr(
"Highlight feature" ),
this );
108 mScaleHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionScaleHighlightFeature.svg" ) ), tr(
"Scale and highlight feature" ),
this );
109 mPanHighlightFeatureAction =
new QAction(
QgsApplication::getThemeIcon( QStringLiteral(
"/mActionPanHighlightFeature.svg" ) ), tr(
"Pan and highlight feature" ),
this );
110 mHighlightFeatureButton->addAction( mHighlightFeatureAction );
111 mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
112 mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
113 mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
114 editLayout->addWidget( mHighlightFeatureButton );
117 mMapIdentificationButton =
new QToolButton(
this );
119 mMapIdentificationButton->setText( tr(
"Select on Map" ) );
120 mMapIdentificationButton->setCheckable(
true );
121 editLayout->addWidget( mMapIdentificationButton );
124 mRemoveFKButton =
new QToolButton(
this );
126 mRemoveFKButton->setText( tr(
"No Selection" ) );
127 editLayout->addWidget( mRemoveFKButton );
130 mTopLayout->addLayout( editLayout );
134 mAttributeEditorLayout =
new QVBoxLayout( mAttributeEditorFrame );
135 mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
136 mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
137 mTopLayout->addWidget( mAttributeEditorFrame );
140 mInvalidLabel =
new QLabel( tr(
"The relation is not valid. Please make sure your relation definitions are OK." ) );
141 mInvalidLabel->setWordWrap(
true );
142 QFont font = mInvalidLabel->font();
143 font.setItalic(
true );
144 mInvalidLabel->setStyleSheet( QStringLiteral(
"QLabel { color: red; } " ) );
145 mInvalidLabel->setFont( font );
146 mTopLayout->addWidget( mInvalidLabel );
149 mMapIdentificationButton->hide();
150 mHighlightFeatureButton->hide();
151 mAttributeEditorFrame->hide();
152 mInvalidLabel->hide();
153 mAddEntryButton->hide();
157 connect( mHighlightFeatureButton, &QToolButton::triggered,
this, &QgsRelationReferenceWidget::highlightActionTriggered );
160 connect( mAddEntryButton, &QAbstractButton::clicked,
this, &QgsRelationReferenceWidget::addEntry );
161 connect( mComboBox, &QComboBox::editTextChanged,
this, &QgsRelationReferenceWidget::updateAddEntryButton );
172 mAllowNull = allowNullValue;
173 mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
181 mInvalidLabel->hide();
190 mReferencedFields << fieldPair.referencedField();
199 mAttributeEditorFrame->setObjectName( QStringLiteral(
"referencing/" ) +
relation.
name() );
204 mAttributeEditorFrame->setTitle( mReferencedLayer->
name() );
206 mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
211 updateAddEntryButton();
215 mInvalidLabel->show();
218 if ( mShown && isVisible() )
231 mFilterContainer->setEnabled( editable );
232 mComboBox->setEnabled( editable && !mReadOnlySelector );
233 mComboBox->setEditable(
true );
234 mMapIdentificationButton->setEnabled( editable );
235 mRemoveFKButton->setEnabled( editable );
236 mIsEditable = editable;
246 if ( values.isEmpty() )
256 if ( !mReferencedLayer )
261 if ( mEmbedForm || mChainFilters )
269 const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
270 for (
int i = 0; i < count; i++ )
272 QVariant v = mFeature.
attribute( mFilterFields[i] );
273 QString f = v.isNull() ? nullValue.toString() : v.toString();
274 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
278 mRemoveFKButton->setEnabled( mIsEditable );
279 highlightFeature( mFeature );
280 updateAttributeEditorFrame( mFeature );
288 if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
290 QComboBox *cb = mFilterComboBoxes.first();
291 cb->setCurrentIndex( 0 );
292 disableChainedComboBoxes( cb );
296 mRemoveFKButton->setEnabled(
false );
305 if ( mReferencedLayer )
315 mRemoveFKButton->setEnabled(
false );
322 if ( fkeys.isEmpty() )
323 return QVariant( QVariant::Int );
325 return fkeys.at( 0 );
335 mEditorContext = context;
337 mMessageBar = messageBar;
340 mMapToolIdentify->
setButton( mMapIdentificationButton );
345 mMapToolDigitize->
setButton( mAddEntryButton );
346 updateAddEntryButton();
354 setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
355 mTopLayout->setAlignment( Qt::AlignTop );
358 mAttributeEditorFrame->setVisible( display );
359 mEmbedForm = display;
364 mComboBox->setEnabled( !readOnly );
365 mRemoveFKButton->setVisible( mAllowNull && readOnly );
366 mReadOnlySelector = readOnly;
383 mFilterFields = filterFields;
399 mFilterExpression = expression;
413 if ( mReferencedLayer )
415 QApplication::setOverrideCursor( Qt::WaitCursor );
417 QSet<QString> requestedAttrs;
419 if ( !mFilterFields.isEmpty() )
421 for (
const QString &fieldName : std::as_const( mFilterFields ) )
428 QComboBox *cb =
new QComboBox();
429 cb->setProperty(
"Field", fieldName );
431 mFilterComboBoxes << cb;
432 QVariantList uniqueValues = qgis::setToList( mReferencedLayer->
uniqueValues( idx ) );
435 cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->
fields().
at( idx ).
type() ) );
438 const auto constUniqueValues = uniqueValues;
439 for (
const QVariant &v : constUniqueValues )
441 cb->addItem( v.toString(), v );
444 connect( cb,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsRelationReferenceWidget::filterChanged );
447 requestedAttrs << fieldName;
449 mFilterLayout->addWidget( cb );
459 : mReferencedLayer->
getFeatures( mFilterExpression );
462 const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
463 for (
int i = 0; i < count - 1; i++ )
465 QVariant cv = ft.
attribute( mFilterFields.at( i ) );
466 QVariant nv = ft.
attribute( mFilterFields.at( i + 1 ) );
467 QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
468 QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
469 mFilterCache[mFilterFields[i]][cf] << nf;
473 if ( !mFilterComboBoxes.isEmpty() )
475 QComboBox *cb = mFilterComboBoxes.first();
476 cb->setCurrentIndex( 0 );
477 disableChainedComboBoxes( cb );
483 mFilterContainer->hide();
491 if ( ! mFilterExpression.isEmpty() )
496 if ( mChainFilters && mFeature.
isValid() )
498 for (
int i = 0; i < mFilterFields.size(); i++ )
500 QVariant v = mFeature.
attribute( mFilterFields[i] );
501 QString f = v.isNull() ? nullValue.toString() : v.toString();
502 mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
509 QApplication::restoreOverrideCursor();
515 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
517 if ( action == mHighlightFeatureAction )
521 else if ( action == mScaleHighlightFeatureAction )
525 else if ( action == mPanHighlightFeatureAction )
540 attributeDialog->
show();
543 void QgsRelationReferenceWidget::highlightFeature(
QgsFeature f, CanvasExtent canvasExtent )
563 if ( canvasExtent ==
Scale )
576 else if ( canvasExtent ==
Pan )
586 mHighlight =
new QgsHighlight( mCanvas, f, mReferencedLayer );
590 QTimer *timer =
new QTimer(
this );
591 timer->setSingleShot(
true );
592 connect( timer, &QTimer::timeout,
this, &QgsRelationReferenceWidget::deleteHighlight );
593 timer->start( 3000 );
596 void QgsRelationReferenceWidget::deleteHighlight()
603 mHighlight =
nullptr;
608 if ( !mAllowMapIdentification || !mReferencedLayer )
617 mMapToolIdentify->
setLayer( mReferencedLayer );
618 setMapTool( mMapToolIdentify );
624 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
625 QString msg = tr(
"Identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->
name() );
627 mMessageBar->
pushItem( mMessageBarItem );
631 void QgsRelationReferenceWidget::comboReferenceChanged()
634 highlightFeature( mFeature );
635 updateAttributeEditorFrame( mFeature );
640 void QgsRelationReferenceWidget::updateAttributeEditorFrame(
const QgsFeature &feature )
642 mOpenFormButton->setEnabled( feature.
isValid() );
644 if ( mAttributeEditorFrame && mReferencedAttributeForm )
646 mReferencedAttributeForm->
setFeature( feature );
652 return mAllowAddFeatures;
658 updateAddEntryButton();
666 void QgsRelationReferenceWidget::featureIdentified(
const QgsFeature &feature )
671 mRemoveFKButton->setEnabled( mIsEditable );
672 highlightFeature( feature );
673 updateAttributeEditorFrame( feature );
679 void QgsRelationReferenceWidget::setMapTool(
QgsMapTool *mapTool )
681 mCurrentMapTool = mapTool;
684 mWindowWidget = window();
686 mCanvas->window()->raise();
687 mCanvas->activateWindow();
692 void QgsRelationReferenceWidget::unsetMapTool()
695 if ( mCurrentMapTool )
700 if ( mCurrentMapTool == mMapToolDigitize )
712 void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
714 if ( e->key() == Qt::Key_Escape )
720 void QgsRelationReferenceWidget::mapToolDeactivated()
724 mWindowWidget->raise();
725 mWindowWidget->activateWindow();
728 if ( mMessageBar && mMessageBarItem )
730 mMessageBar->
popWidget( mMessageBarItem );
732 mMessageBarItem =
nullptr;
735 void QgsRelationReferenceWidget::filterChanged()
739 QMap<QString, QString> filters;
742 QComboBox *scb = qobject_cast<QComboBox *>( sender() );
756 disableChainedComboBoxes( scb );
759 const auto constMFilterComboBoxes = mFilterComboBoxes;
760 for ( QComboBox *cb : constMFilterComboBoxes )
762 if ( cb->currentIndex() != 0 )
764 const QString fieldName = cb->property(
"Field" ).toString();
766 if ( cb->currentText() == nullValue.toString() )
768 filters[fieldName] = QStringLiteral(
"\"%1\" IS NULL" ).arg( fieldName );
780 QComboBox *ccb =
nullptr;
781 const auto constMFilterComboBoxes = mFilterComboBoxes;
782 for ( QComboBox *cb : constMFilterComboBoxes )
792 if ( ccb->currentIndex() != 0 )
794 const QString fieldName = cb->property(
"Field" ).toString();
796 cb->blockSignals(
true );
798 cb->addItem( cb->property(
"FieldAlias" ).toString() );
803 const auto txts { mFilterCache[ccb->property(
"Field" ).toString()][ccb->currentText()] };
804 for (
const QString &txt : txts )
806 QMap<QString, QString> filtersAttrs = filters;
812 expression += QLatin1String(
" AND " );
814 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ( " );
815 expression += filtersAttrs.values().join( QLatin1String(
" AND " ) );
816 expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral(
" ) " );
823 while ( it.nextFeature( f ) )
825 if ( !featureIds.contains( f.
id() ) )
826 featureIds << f.
id();
837 cb->addItems( texts );
839 cb->setEnabled(
true );
840 cb->blockSignals(
false );
850 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ( " );
852 filterExpression += filters.isEmpty() ? QString() : QStringLiteral(
" ) " );
857 void QgsRelationReferenceWidget::addEntry()
859 if ( !mReferencedLayer )
876 mMapToolDigitize->
setLayer( mReferencedLayer );
877 setMapTool( mMapToolDigitize );
884 QString title = tr(
"Relation %1 for %2." ).arg( mRelation.
name(), mReferencingLayer->
name() );
887 QString msg = tr(
"Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press <ESC> to cancel." )
888 .arg( mReferencingLayer->
name(), displayString, mReferencedLayer->
name() );
890 mMessageBar->
pushItem( mMessageBarItem );
895 void QgsRelationReferenceWidget::entryAdded(
const QgsFeature &feat )
901 if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
905 if ( fieldIdx != -1 )
907 attributes.insert( fieldIdx, mComboBox->currentText() );
914 for (
const QString &fieldName : std::as_const( mReferencedFields ) )
919 mAddEntryButton->setEnabled(
false );
925 void QgsRelationReferenceWidget::updateAddEntryButton()
927 mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
928 mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->
isEditable() );
931 void QgsRelationReferenceWidget::disableChainedComboBoxes(
const QComboBox *scb )
933 QComboBox *ccb =
nullptr;
934 const auto constMFilterComboBoxes = mFilterComboBoxes;
935 for ( QComboBox *cb : constMFilterComboBoxes )
947 cb->setCurrentIndex( 0 );
948 if ( ccb->currentIndex() == 0 )
950 cb->setEnabled(
false );
957 void QgsRelationReferenceWidget::emitForeignKeysChanged(
const QVariantList &foreignKeys,
bool force )
971 return mReferencedLayerName;
976 mReferencedLayerName = relationLayerName;
981 return mReferencedLayerId;
986 mReferencedLayerId = relationLayerId;
991 return mReferencedLayerProviderKey;
996 mReferencedLayerProviderKey = relationProviderKey;
1001 return mReferencedLayerDataSource;
1007 mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1012 mFormFeature = formFeature;