Skip to content

Commit

Permalink
[FEATURE] Joined fields are editable if the option is activated
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere committed Aug 28, 2017
1 parent e92f59c commit 477775a
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 19 deletions.
2 changes: 1 addition & 1 deletion python/core/qgsvectorlayer.sip
Expand Up @@ -905,7 +905,7 @@ Return the provider type for this layer
:rtype: QgsFeatureIterator
%End

QgsFeature getFeature( QgsFeatureId fid );
QgsFeature getFeature( QgsFeatureId fid ) const;
%Docstring
Query the layer for the feature with the given id.
If there is no such feature, the returned feature will be invalid.
Expand Down
9 changes: 9 additions & 0 deletions python/core/qgsvectorlayerjoinbuffer.sip
Expand Up @@ -117,6 +117,15 @@ Quick way to test if there is any join at all
:rtype: QgsFeature
%End

QgsFeature targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const;
%Docstring
Returns the targeted feature corresponding to the joined feature.
\param info the vector join information
\param feature the feature of the joined layer
.. versionadded:: 3.0
:rtype: QgsFeature
%End

QgsVectorLayerJoinBuffer *clone() const /Factory/;
%Docstring
.. versionadded:: 2.6
Expand Down
30 changes: 27 additions & 3 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -2258,10 +2258,34 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom )

bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
{
if ( !mEditBuffer || !mDataProvider )
return false;
if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin )
{
int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = mJoinBuffer->joinForFieldIndex( field, fields(), srcFieldIndex );
if ( info && info->joinLayer() && info->isEditable() )
{
QgsFeature feature = getFeature( fid );

return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue );
if ( !feature.isValid() )
return false;

QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature );

if ( joinFeature.isValid() )
return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
else
return false;
}
else
return false;
}
else
{
if ( !mEditBuffer || !mDataProvider )
return false;
else
return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue );
}
}

bool QgsVectorLayer::addAttribute( const QgsField &field )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayer.h
Expand Up @@ -875,7 +875,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* Query the layer for the feature with the given id.
* If there is no such feature, the returned feature will be invalid.
*/
inline QgsFeature getFeature( QgsFeatureId fid )
inline QgsFeature getFeature( QgsFeatureId fid ) const
{
QgsFeature feature;
getFeatures( QgsFeatureRequest( fid ) ).nextFeature( feature );
Expand Down
36 changes: 36 additions & 0 deletions src/core/qgsvectorlayercache.cpp
Expand Up @@ -18,6 +18,8 @@
#include "qgsvectorlayercache.h"
#include "qgscacheindex.h"
#include "qgscachedfeatureiterator.h"
#include "qgsvectorlayerjoininfo.h"
#include "qgsvectorlayerjoinbuffer.h"

QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
: QObject( parent )
Expand All @@ -37,6 +39,8 @@ QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize,
connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );

connectJoinedLayers();
}

QgsVectorLayerCache::~QgsVectorLayerCache()
Expand Down Expand Up @@ -232,6 +236,28 @@ void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field,
emit attributeValueChanged( fid, field, value );
}

void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
{
const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );

Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
{
if ( joinLayer == info.joinLayer() )
{
QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );

const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
const int fieldIndex = mLayer->fields().indexFromName( fieldName );

if ( feature.isValid() && fieldIndex != -1 )
{
onAttributeValueChanged( feature.id(), fieldIndex, value );
return;
}
}
}
}

void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
{
mCache.remove( fid );
Expand Down Expand Up @@ -425,3 +451,13 @@ bool QgsVectorLayerCache::checkInformationCovered( const QgsFeatureRequest &feat

return true;
}

void QgsVectorLayerCache::connectJoinedLayers() const
{
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
{
const QgsVectorLayer *vl = info.joinLayer();
if ( vl )
connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
}
}
3 changes: 3 additions & 0 deletions src/core/qgsvectorlayercache.h
Expand Up @@ -364,6 +364,7 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject

private slots:
void onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value );
void onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value );
void featureDeleted( QgsFeatureId fid );
void onFeatureAdded( QgsFeatureId fid );
void attributeAdded( int field );
Expand All @@ -374,6 +375,8 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject

private:

void connectJoinedLayers() const;

inline void cacheFeature( QgsFeature &feat )
{
QgsCachedFeature *cachedFeature = new QgsCachedFeature( feat, this );
Expand Down
20 changes: 20 additions & 0 deletions src/core/qgsvectorlayerjoinbuffer.cpp
Expand Up @@ -439,6 +439,26 @@ QgsFeature QgsVectorLayerJoinBuffer::joinedFeatureOf( const QgsVectorLayerJoinIn
return joinedFeature;
}

QgsFeature QgsVectorLayerJoinBuffer::targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const
{
QgsFeature targetedFeature;

if ( info->joinLayer() )
{
const QVariant targetValue = feature.attribute( info->joinFieldName() );
const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue );

QgsFeatureRequest request;
request.setFilterExpression( filter );
request.setLimit( 1 );

QgsFeatureIterator it = mLayer->getFeatures( request );
it.nextFeature( targetedFeature );
}

return targetedFeature;
}

QgsVectorLayerJoinBuffer *QgsVectorLayerJoinBuffer::clone() const
{
QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );
Expand Down
9 changes: 8 additions & 1 deletion src/core/qgsvectorlayerjoinbuffer.h
Expand Up @@ -87,7 +87,7 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject
/** Returns joins where the field of a target layer is considered as an id.
* \param field the field of a target layer
* \returns a list of vector joins
* \since QGIS3.0
* \since QGIS 3.0
*/
QList<const QgsVectorLayerJoinInfo *> joinsWhereFieldIsId( const QgsField &field ) const;

Expand All @@ -98,6 +98,13 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject
*/
QgsFeature joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const;

/** Returns the targeted feature corresponding to the joined feature.
* \param info the vector join information
* \param feature the feature of the joined layer
* \since QGIS 3.0
*/
QgsFeature targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const;

//! Create a copy of the join buffer
//! \since QGIS 2.6
QgsVectorLayerJoinBuffer *clone() const SIP_FACTORY;
Expand Down
17 changes: 16 additions & 1 deletion src/gui/attributetable/qgsattributetabledelegate.cpp
Expand Up @@ -30,6 +30,8 @@
#include "qgsvectordataprovider.h"
#include "qgsactionmanager.h"
#include "qgsgui.h"
#include "qgsvectorlayerjoininfo.h"
#include "qgsvectorlayerjoinbuffer.h"

QgsVectorLayer *QgsAttributeTableDelegate::layer( const QAbstractItemModel *model )
{
Expand Down Expand Up @@ -73,7 +75,20 @@ QWidget *QgsAttributeTableDelegate::createEditor( QWidget *parent, const QStyleO
w->setAutoFillBackground( true );
w->setFocusPolicy( Qt::StrongFocus ); // to make sure QMouseEvents are propagated to the editor widget

eww->setEnabled( !vl->editFormConfig().readOnly( fieldIdx ) );
const int fieldOrigin = vl->fields().fieldOrigin( fieldIdx );
bool readOnly = true;
if ( fieldOrigin == QgsFields::OriginJoin )
{
int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = vl->joinBuffer()->joinForFieldIndex( fieldIdx, vl->fields(), srcFieldIndex );

if ( info && info->isEditable() )
readOnly = info->joinLayer()->editFormConfig().readOnly( srcFieldIndex );
}
else
readOnly = vl->editFormConfig().readOnly( fieldIdx );

eww->setEnabled( !readOnly );

return w;
}
Expand Down
28 changes: 24 additions & 4 deletions src/gui/attributetable/qgsattributetablemodel.cpp
Expand Up @@ -35,6 +35,8 @@
#include "qgsfieldformatterregistry.h"
#include "qgsgui.h"
#include "qgsexpressionnodeimpl.h"
#include "qgsvectorlayerjoininfo.h"
#include "qgsvectorlayerjoinbuffer.h"

#include <QVariant>

Expand Down Expand Up @@ -740,15 +742,33 @@ Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const

Qt::ItemFlags flags = QAbstractItemModel::flags( index );

if ( layer()->isEditable() &&
!layer()->editFormConfig().readOnly( mAttributes[index.column()] ) &&
( ( layer()->dataProvider() && layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) ||
FID_IS_NEW( rowToId( index.row() ) ) ) )
bool editable = false;
const int fieldIndex = mAttributes[index.column()];
const QgsFeatureId fid = rowToId( index.row() );
if ( layer()->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
{
int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = layer()->joinBuffer()->joinForFieldIndex( fieldIndex, layer()->fields(), srcFieldIndex );

if ( info && info->isEditable() )
editable = fieldIsEditable( *info->joinLayer(), srcFieldIndex, fid );
}
else
editable = fieldIsEditable( *layer(), fieldIndex, fid );

if ( editable )
flags |= Qt::ItemIsEditable;

return flags;
}

bool QgsAttributeTableModel::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const
{
return ( layer.isEditable() &&
!layer.editFormConfig().readOnly( fieldIndex ) &&
( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ) );
}

void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
{
mFeat.setId( std::numeric_limits<int>::min() );
Expand Down
2 changes: 2 additions & 0 deletions src/gui/attributetable/qgsattributetablemodel.h
Expand Up @@ -357,6 +357,8 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
*/
virtual bool loadFeatureAtId( QgsFeatureId fid ) const;

bool fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const;

QgsFeatureRequest mFeatureRequest;

//! The currently cached column
Expand Down
36 changes: 28 additions & 8 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -298,8 +298,7 @@ bool QgsAttributeForm::saveEdits()
// be careful- sometimes two null qvariants will be reported as not equal!! (e.g., different types)
bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
|| ( dstVar.isNull() != srcVar.isNull() );
if ( changed && srcVar.isValid()
&& !mLayer->editFormConfig().readOnly( eww->fieldIdx() ) )
if ( changed && srcVar.isValid() && fieldIsEditable( eww->fieldIdx() ) )
{
dst[eww->fieldIdx()] = srcVar;

Expand Down Expand Up @@ -344,7 +343,7 @@ bool QgsAttributeForm::saveEdits()
{
if ( ( dst.at( i ) == src.at( i ) && dst.at( i ).isNull() == src.at( i ).isNull() ) // If field is not changed...
|| !dst.at( i ).isValid() // or the widget returns invalid (== do not change)
|| mLayer->editFormConfig().readOnly( i ) ) // or the field cannot be edited ...
|| !fieldIsEditable( i ) ) // or the field cannot be edited ...
{
continue;
}
Expand Down Expand Up @@ -1019,15 +1018,12 @@ void QgsAttributeForm::synchronizeEnabledState()

Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
{
bool fieldEditable = true;
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( eww )
{
fieldEditable = !mLayer->editFormConfig().readOnly( eww->fieldIdx() ) &&
( ( mLayer->dataProvider() && layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) ||
FID_IS_NEW( mFeature.id() ) );
bool enabled = isEditable && fieldIsEditable( eww->fieldIdx() );
ww->setEnabled( enabled );
}
ww->setEnabled( isEditable && fieldEditable );
}

// push a message and disable the OK button if constraints are invalid
Expand Down Expand Up @@ -2019,3 +2015,27 @@ void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww )
}
}
}

bool QgsAttributeForm::fieldIsEditable( int fieldIndex ) const
{
bool editable = false;

if ( mLayer->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
{
int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( fieldIndex, mLayer->fields(), srcFieldIndex );

if ( info && info->isEditable() && info->joinLayer()->isEditable() )
editable = fieldIsEditable( *( info->joinLayer() ), srcFieldIndex, mFeature.id() );
}
else
editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.id() );

return editable;
}

bool QgsAttributeForm::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const
{
return !layer.editFormConfig().readOnly( fieldIndex ) &&
( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) );
}
4 changes: 4 additions & 0 deletions src/gui/qgsattributeform.h
Expand Up @@ -276,6 +276,10 @@ class GUI_EXPORT QgsAttributeForm : public QWidget

void updateJoinedFields( const QgsEditorWidgetWrapper &eww );

bool fieldIsEditable( int fieldIndex ) const;

bool fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const ;

struct WidgetInfo
{
WidgetInfo()
Expand Down

0 comments on commit 477775a

Please sign in to comment.