void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
{
  if ( !value.isValid() || value.isNull() )
  {
    deleteForeignKey();
    return;
  }

  QgsFeature f;
  if ( !mReferencedLayer )
    return;

  // TODO: Rewrite using expression
  QgsFeatureIterator fit = mReferencedLayer->getFeatures( QgsFeatureRequest() );
  while ( fit.nextFeature( f ) )
  {
    if ( f.attribute( mFkeyFieldIdx ) == value )
    {
      break;
    }
  }

  if ( !f.isValid() )
  {
    deleteForeignKey();
    return;
  }

  mForeignKey = f.attribute( mFkeyFieldIdx );

  if ( mReadOnlySelector )
  {
    QgsExpression expr( mReferencedLayer->displayExpression() );
    QString title = expr.evaluate( &f ).toString();
    if ( expr.hasEvalError() )
    {
      title = f.attribute( mFkeyFieldIdx ).toString();
    }
    mLineEdit->setText( title );
    mFeatureId = f.id();
  }
  else
  {
    int i = mComboBox->findData( value );
    if ( i == -1 && mAllowNull )
    {
      mComboBox->setCurrentIndex( 0 );
    }
    else
    {
      mComboBox->setCurrentIndex( i );
    }
  }

  mRemoveFKButton->setEnabled( mIsEditable );
  highlightFeature( f );
  updateAttributeEditorFrame( f );
  emit foreignKeyChanged( foreignKey() );
}
void QgsRelationReferenceWidget::setForeignKey( const QVariant& value )
{
  if ( !value.isValid() || value.isNull() )
  {
    deleteForeignKey();
    return;
  }

  if ( !mReferencedLayer )
    return;

  QgsAttributes attrs = QgsAttributes( mReferencingLayer->fields().count() );
  attrs[mFkeyFieldIdx] = value;

  QgsFeatureRequest request = mRelation.getReferencedFeatureRequest( attrs );

  mReferencedLayer->getFeatures( request ).nextFeature( mFeature );

  if ( !mFeature.isValid() )
  {
    deleteForeignKey();
    return;
  }

  mForeignKey = mFeature.attribute( mFkeyFieldIdx );

  if ( mReadOnlySelector )
  {
    QgsExpression expr( mReferencedLayer->displayExpression() );
    QgsExpressionContext context;
    context << QgsExpressionContextUtils::globalScope()
    << QgsExpressionContextUtils::projectScope()
    << QgsExpressionContextUtils::layerScope( mReferencedLayer );
    context.setFeature( mFeature );
    QString title = expr.evaluate( &context ).toString();
    if ( expr.hasEvalError() )
    {
      title = mFeature.attribute( mFkeyFieldIdx ).toString();
    }
    mLineEdit->setText( title );
  }
  else
  {
    int i = mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole );
    if ( i == -1 && mAllowNull )
    {
      mComboBox->setCurrentIndex( 0 );
    }
    else
    {
      mComboBox->setCurrentIndex( i );
    }
  }

  mRemoveFKButton->setEnabled( mIsEditable );
  highlightFeature( mFeature );
  updateAttributeEditorFrame( mFeature );
  emit foreignKeyChanged( foreignKey() );
}
QgsRelationReferenceWidget::QgsRelationReferenceWidget( QWidget* parent )
    : QWidget( parent )
    , mEditorContext( QgsAttributeEditorContext() )
    , mCanvas( NULL )
    , mMessageBar( NULL )
    , mForeignKey( QVariant() )
    , mFkeyFieldIdx( -1 )
    , mAllowNull( true )
    , mHighlight( NULL )
    , mMapTool( NULL )
    , mMessageBarItem( NULL )
    , mRelationName( "" )
    , mReferencedAttributeForm( NULL )
    , mReferencedLayer( NULL )
    , mReferencingLayer( NULL )
    , mMasterModel( 0 )
    , mFilterModel( 0 )
    , mFeatureListModel( 0 )
    , mWindowWidget( NULL )
    , mShown( false )
    , mIsEditable( true )
    , mEmbedForm( false )
    , mReadOnlySelector( false )
    , mAllowMapIdentification( false )
    , mOrderByValue( false )
    , mOpenFormButtonVisible( true )
    , mChainFilters( false )
{
  mTopLayout = new QVBoxLayout( this );
  mTopLayout->setContentsMargins( 0, 0, 0, 0 );
  mTopLayout->setAlignment( Qt::AlignTop );
  setLayout( mTopLayout );

  QHBoxLayout* editLayout = new QHBoxLayout();
  editLayout->setContentsMargins( 0, 0, 0, 0 );
  editLayout->setSpacing( 2 );

  // Prepare the container and layout for the filter comboboxes
  mChooserContainer = new QWidget;
  editLayout->addWidget( mChooserContainer );
  QHBoxLayout* chooserLayout = new QHBoxLayout;
  chooserLayout->setContentsMargins( 0, 0, 0, 0 );
  mFilterLayout = new QHBoxLayout;
  mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
  mFilterContainer = new QWidget;
  mFilterContainer->setLayout( mFilterLayout );
  mChooserContainer->setLayout( chooserLayout );
  chooserLayout->addWidget( mFilterContainer );

  // combobox (for non-geometric relation)
  mComboBox = new QComboBox( this );
  mChooserContainer->layout()->addWidget( mComboBox );

  // read-only line edit
  mLineEdit = new QLineEdit( this );
  mLineEdit->setReadOnly( true );
  editLayout->addWidget( mLineEdit );

  // open form button
  mOpenFormButton = new QToolButton( this );
  mOpenFormButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) );
  mOpenFormButton->setText( tr( "Open related feature form" ) );
  editLayout->addWidget( mOpenFormButton );

  // highlight button
  mHighlightFeatureButton = new QToolButton( this );
  mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
  mHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionHighlightFeature.svg" ), tr( "Highlight feature" ), this );
  mScaleHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionScaleHighlightFeature.svg" ), tr( "Scale and highlight feature" ), this );
  mPanHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionPanHighlightFeature.svg" ), tr( "Pan and highlight feature" ), this );
  mHighlightFeatureButton->addAction( mHighlightFeatureAction );
  mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
  mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
  mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
  editLayout->addWidget( mHighlightFeatureButton );

  // map identification button
  mMapIdentificationButton = new QToolButton( this );
  mMapIdentificationButton->setIcon( QgsApplication::getThemeIcon( "/mActionMapIdentification.svg" ) );
  mMapIdentificationButton->setText( tr( "Select on map" ) );
  mMapIdentificationButton->setCheckable( true );
  editLayout->addWidget( mMapIdentificationButton );

  // remove foreign key button
  mRemoveFKButton = new QToolButton( this );
  mRemoveFKButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.svg" ) );
  mRemoveFKButton->setText( tr( "No selection" ) );
  editLayout->addWidget( mRemoveFKButton );

  // spacer
  editLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );

  // add line to top layout
  mTopLayout->addLayout( editLayout );

  // embed form
  mAttributeEditorFrame = new QgsCollapsibleGroupBox( this );
  mAttributeEditorLayout = new QVBoxLayout( mAttributeEditorFrame );
  mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
  mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
  mTopLayout->addWidget( mAttributeEditorFrame );

  // invalid label
  mInvalidLabel = new QLabel( tr( "The relation is not valid. Please make sure your relation definitions are ok." ) );
  mInvalidLabel->setWordWrap( true );
  QFont font = mInvalidLabel->font();
  font.setItalic( true );
  mInvalidLabel->setStyleSheet( "QLabel { color: red; } " );
  mInvalidLabel->setFont( font );
  mTopLayout->addWidget( mInvalidLabel );

  // default mode is combobox, no geometric relation and no embed form
  mLineEdit->hide();
  mMapIdentificationButton->hide();
  mHighlightFeatureButton->hide();
  mAttributeEditorFrame->hide();
  mInvalidLabel->hide();

  // connect buttons
  connect( mOpenFormButton, SIGNAL( clicked() ), this, SLOT( openForm() ) );
  connect( mHighlightFeatureButton, SIGNAL( triggered( QAction* ) ), this, SLOT( highlightActionTriggered( QAction* ) ) );
  connect( mMapIdentificationButton, SIGNAL( clicked() ), this, SLOT( mapIdentification() ) );
  connect( mRemoveFKButton, SIGNAL( clicked() ), this, SLOT( deleteForeignKey() ) );
}
void QgsRelationReferenceWidget::setForeignKey( const QVariant &value )
{
  if ( !value.isValid() )
  {
    return;
  }
  if ( value.isNull() )
  {
    deleteForeignKey();
    return;
  }

  if ( !mReferencedLayer )
    return;

  if ( mReadOnlySelector )
  {
    // Attributes from the referencing layer
    QgsAttributes attrs = QgsAttributes( mReferencingLayer->fields().count() );
    // Set the value on the foreign key field of the referencing record
    attrs[ mReferencingLayer->fields().lookupField( mRelation.fieldPairs().at( 0 ).first )] = value;

    QgsFeatureRequest request = mRelation.getReferencedFeatureRequest( attrs );

    mReferencedLayer->getFeatures( request ).nextFeature( mFeature );

    if ( !mFeature.isValid() )
    {
      return;
    }

    mForeignKey = mFeature.attribute( mReferencedFieldIdx );

    QgsExpression expr( mReferencedLayer->displayExpression() );
    QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mReferencedLayer ) );
    context.setFeature( mFeature );
    QString title = expr.evaluate( &context ).toString();
    if ( expr.hasEvalError() )
    {
      title = mFeature.attribute( mReferencedFieldIdx ).toString();
    }
    mLineEdit->setText( title );
  }
  else
  {
    mComboBox->setIdentifierValue( value );

    if ( mChainFilters )
    {
      QVariant nullValue = QgsApplication::nullRepresentation();

      QgsFeatureRequest request = mComboBox->currentFeatureRequest();

      mReferencedLayer->getFeatures( request ).nextFeature( mFeature );

      const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
      for ( int i = 0; i < count; i++ )
      {
        QVariant v = mFeature.attribute( mFilterFields[i] );
        QString f = v.isNull() ? nullValue.toString() : v.toString();
        mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
      }
    }
  }

  mRemoveFKButton->setEnabled( mIsEditable );
  highlightFeature( mFeature ); // TODO : make this async
  updateAttributeEditorFrame( mFeature );

  emitForeignKeyChanged( foreignKey() );
}