14 changes: 8 additions & 6 deletions src/app/qgsfieldsproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,9 @@ void QgsFieldsProperties::loadRelations()
if ( nmrel.fieldPairs().at( 0 ).referencingField() != relation.fieldPairs().at( 0 ).referencingField() )
nmCombo->addItem( QString( "%1 (%2)" ).arg( nmrel.referencedLayer()->name(), nmrel.fieldPairs().at( 0 ).referencedField() ), nmrel.id() );

QgsEditorWidgetConfig cfg = mLayer->editFormConfig().widgetConfig( relation.id() );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, relation.id() );

QVariant nmrelcfg = cfg.value( "nm-rel" );
const QVariant nmrelcfg = setup.config().value( "nm-rel" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not really any advantage in making these local variables const


int idx = nmCombo->findData( nmrelcfg.toString() );

Expand Down Expand Up @@ -940,6 +940,7 @@ void QgsFieldsProperties::apply()
for ( int i = 0; i < mFieldsList->rowCount(); i++ )
{
int idx = mFieldsList->item( i, attrIdCol )->text().toInt();
QString name = mLayer->fields().at( idx ).name();
FieldConfig cfg = configForRow( i );

editFormConfig.setReadOnly( i, !cfg.mEditable );
Expand All @@ -948,8 +949,8 @@ void QgsFieldsProperties::apply()
editFormConfig.setExpressionDescription( i, cfg.mConstraintDescription );
editFormConfig.setExpression( i, cfg.mConstraint );

editFormConfig.setWidgetType( idx, cfg.mEditorWidgetType );
editFormConfig.setWidgetConfig( idx, cfg.mEditorWidgetConfig );
editFormConfig.setWidgetType( name, cfg.mEditorWidgetType );
editFormConfig.setWidgetConfig( name, cfg.mEditorWidgetConfig );

if ( mFieldsList->item( i, attrWMSCol )->checkState() == Qt::Unchecked )
{
Expand Down Expand Up @@ -1030,8 +1031,9 @@ QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
mNotNull = layer->editFormConfig().notNull( idx );
mConstraint = layer->editFormConfig().expression( idx );
mConstraintDescription = layer->editFormConfig().expressionDescription( idx );
mEditorWidgetType = layer->editFormConfig().widgetType( idx );
mEditorWidgetConfig = layer->editFormConfig().widgetConfig( idx );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer, layer->fields().field( idx ).name() );
mEditorWidgetType = setup.type();
mEditorWidgetConfig = setup.config();
}

/*
Expand Down
20 changes: 11 additions & 9 deletions src/app/qgsidentifyresultsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat
if ( i >= fields.count() )
break;

if ( vlayer->editFormConfig().widgetType( i ) == "Hidden" )
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( vlayer, fields[i].name() );
if ( setup.type() == "Hidden" )
{
continue;
}
Expand All @@ -498,7 +499,7 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat

attrItem->setData( 1, Qt::UserRole, value );

value = representValue( vlayer, fields.at( i ).name(), attrs.at( i ) );
value = representValue( vlayer, setup, fields.at( i ).name(), attrs.at( i ) );
attrItem->setSortData( 1, value );
bool foundLinks = false;
QString links = QgsStringUtils::insertLinks( value, &foundLinks );
Expand Down Expand Up @@ -543,7 +544,8 @@ void QgsIdentifyResultsDialog::addFeature( QgsVectorLayer *vlayer, const QgsFeat
continue;

QString value = fields.at( i ).displayString( attrs.at( i ) );
QString value2 = representValue( vlayer, fields.at( i ).name(), value );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( vlayer, fields.at( i ).name() );
QString value2 = representValue( vlayer, setup, fields.at( i ).name(), value );

tblResults->setRowCount( j + 1 );

Expand Down Expand Up @@ -664,13 +666,12 @@ QgsIdentifyPlotCurve::~QgsIdentifyPlotCurve()
}
}

QString QgsIdentifyResultsDialog::representValue( QgsVectorLayer* vlayer, const QString& fieldName, const QVariant& value )
QString QgsIdentifyResultsDialog::representValue( QgsVectorLayer* vlayer, const QgsEditorWidgetSetup& setup, const QString& fieldName, const QVariant& value )
{
QVariant cache;
QMap<QString, QVariant>& layerCaches = mWidgetCaches[vlayer->id()];

QString widgetType = vlayer->editFormConfig().widgetType( fieldName );
QgsEditorWidgetFactory* factory = QgsEditorWidgetRegistry::instance()->factory( widgetType );
QgsEditorWidgetFactory* factory = QgsEditorWidgetRegistry::instance()->factory( setup.type() );

int idx = vlayer->fieldNameIndex( fieldName );

Expand All @@ -683,11 +684,11 @@ QString QgsIdentifyResultsDialog::representValue( QgsVectorLayer* vlayer, const
}
else
{
cache = factory->createCache( vlayer, idx, vlayer->editFormConfig().widgetConfig( fieldName ) );
cache = factory->createCache( vlayer, idx, setup.config() );
layerCaches.insert( fieldName, cache );
}

return factory->representValue( vlayer, idx, vlayer->editFormConfig().widgetConfig( fieldName ), cache, value );
return factory->representValue( vlayer, idx, setup.config(), cache, value );
}

void QgsIdentifyResultsDialog::addFeature( QgsRasterLayer *layer,
Expand Down Expand Up @@ -1502,7 +1503,8 @@ void QgsIdentifyResultsDialog::attributeValueChanged( QgsFeatureId fid, int idx,

if ( item->data( 0, Qt::UserRole + 1 ).toInt() == idx )
{
value = representValue( vlayer, fld.name(), val );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( vlayer, fld.name() );
value = representValue( vlayer, setup, fld.name(), val );

QgsTreeWidgetItem* treeItem = static_cast< QgsTreeWidgetItem* >( item );
treeItem->setSortData( 1, value );
Expand Down
3 changes: 2 additions & 1 deletion src/app/qgsidentifyresultsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class QgsHighlight;
class QgsMapCanvas;
class QgsDockWidget;
class QgsMapLayerAction;
class QgsEditorWidgetSetup;

class QwtPlotCurve;

Expand Down Expand Up @@ -217,7 +218,7 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
void mapLayerActionDestroyed();

private:
QString representValue( QgsVectorLayer* vlayer, const QString& fieldName, const QVariant& value );
QString representValue( QgsVectorLayer* vlayer, const QgsEditorWidgetSetup& setup, const QString& fieldName, const QVariant& value );

enum ItemDataRole
{
Expand Down
7 changes: 4 additions & 3 deletions src/app/qgsmergeattributesdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "qgsvectordataprovider.h"
#include "qgsattributeeditor.h"
#include "qgsstatisticalsummary.h"
#include "qgseditorwidgetregistry.h"

#include <limits>
#include <QComboBox>
Expand Down Expand Up @@ -116,16 +117,16 @@ void QgsMergeAttributesDialog::createTableWidgetContents()
mHiddenAttributes.clear();
for ( int idx = 0; idx < mFields.count(); ++idx )
{
if ( mVectorLayer->editFormConfig().widgetType( idx ) == "Hidden" ||
mVectorLayer->editFormConfig().widgetType( idx ) == "Immutable" )
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mVectorLayer, mFields.at( idx ).name() );
if ( setup.type() == "Hidden" || setup.type() == "Immutable" )
{
mHiddenAttributes.insert( idx );
continue;
}

mTableWidget->setColumnCount( col + 1 );

QComboBox *cb = createMergeComboBox( mFields.at( idx ) .type() );
QComboBox *cb = createMergeComboBox( mFields.at( idx ).type() );
if ( pkAttrList.contains( idx ) )
{
cb->setCurrentIndex( cb->findData( "skip" ) );
Expand Down
92 changes: 30 additions & 62 deletions src/core/qgseditformconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "qgseditformconfig.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
//#include "qgseditorwidgetregistry.h"

QgsAttributeEditorContainer::~QgsAttributeEditorContainer()
{
Expand All @@ -27,30 +28,14 @@ QgsEditFormConfig::QgsEditFormConfig()
{
}

QString QgsEditFormConfig::widgetType( int fieldIdx ) const
{
if ( fieldIdx < 0 || fieldIdx >= d->mFields.count() )
return "TextEdit";

return d->mEditorWidgetTypes.value( d->mFields.at( fieldIdx ).name(), "TextEdit" );
}

QString QgsEditFormConfig::widgetType( const QString& fieldName ) const
{
return d->mEditorWidgetTypes.value( fieldName, "TextEdit" );
}

QgsEditorWidgetConfig QgsEditFormConfig::widgetConfig( int fieldIdx ) const
{
if ( fieldIdx < 0 || fieldIdx >= d->mFields.count() )
return QgsEditorWidgetConfig();

return d->mWidgetConfigs.value( d->mFields.at( fieldIdx ).name() );
return d->mEditorWidgetTypes.value( fieldName );
}

QgsEditorWidgetConfig QgsEditFormConfig::widgetConfig( const QString& widgetName ) const
QgsEditorWidgetConfig QgsEditFormConfig::widgetConfig( const QString& fieldName ) const
{
return d->mWidgetConfigs.value( widgetName );
return d->mWidgetConfigs.value( fieldName );
}

void QgsEditFormConfig::setFields( const QgsFields& fields )
Expand Down Expand Up @@ -84,19 +69,9 @@ void QgsEditFormConfig::onRelationsLoaded()
}
}

void QgsEditFormConfig::setWidgetType( int attrIdx, const QString& widgetType )
void QgsEditFormConfig::setWidgetType( const QString& widgetName, const QString& widgetType )
{
if ( attrIdx >= 0 && attrIdx < d->mFields.count() )
d->mEditorWidgetTypes[ d->mFields.at( attrIdx ).name()] = widgetType;
}

void QgsEditFormConfig::setWidgetConfig( int attrIdx, const QgsEditorWidgetConfig& config )
{
if ( attrIdx >= 0 && attrIdx < d->mFields.count() )
{
d.detach();
d->mWidgetConfigs[ d->mFields.at( attrIdx ).name()] = config;
}
d->mEditorWidgetTypes[widgetName] = widgetType;
}

void QgsEditFormConfig::setWidgetConfig( const QString& widgetName, const QgsEditorWidgetConfig& config )
Expand All @@ -111,15 +86,6 @@ bool QgsEditFormConfig::removeWidgetConfig( const QString& widgetName )
return d->mWidgetConfigs.remove( widgetName ) != 0;
}

bool QgsEditFormConfig::removeWidgetConfig( int fieldIdx )
{
if ( fieldIdx < 0 || fieldIdx >= d->mFields.count() )
return false;

d.detach();
return d->mWidgetConfigs.remove( d->mFields.at( fieldIdx ).name() );
}

QgsEditFormConfig::QgsEditFormConfig( const QgsEditFormConfig& o )
: d( o.d )
{
Expand Down Expand Up @@ -446,38 +412,40 @@ void QgsEditFormConfig::readXml( const QDomNode& node )
}


//// TODO: MAKE THIS MORE GENERIC, SO INDIVIDUALL WIDGETS CAN NOT ONLY SAVE STRINGS
/// SEE QgsEditorWidgetFactory::writeConfig

QDomElement widgetsElem = node.namedItem( "widgets" ).toElement();

QDomNodeList widgetConfigsElems = widgetsElem.childNodes();

for ( int i = 0; i < widgetConfigsElems.size(); ++i )
{
QgsEditorWidgetConfig cfg;

QDomElement wdgElem = widgetConfigsElems.at( i ).toElement();

QDomElement cfgElem = wdgElem.namedItem( "config" ).toElement();

for ( int j = 0; j < cfgElem.attributes().size(); ++j )
{
QDomAttr attr = cfgElem.attributes().item( j ).toAttr();
cfg.insert( attr.name(), attr.value() );
}
const QDomElement wdgElem = widgetConfigsElems.at( i ).toElement();
const QDomElement cfgElem = wdgElem.namedItem( "config" ).toElement();
const QgsEditorWidgetConfig widgetConfig = parseEditorWidgetConfig( cfgElem );
setWidgetConfig( wdgElem.attribute( "name" ), widgetConfig );
}
}

QDomNodeList optionElements = cfgElem.elementsByTagName( "option" );
for ( int j = 0; j < optionElements.size(); ++j )
{
QString key = optionElements.at( j ).toElement().attribute( "key" );
QString value = optionElements.at( j ).toElement().attribute( "value" );
cfg.insert( key, value );
}
QgsEditorWidgetConfig QgsEditFormConfig::parseEditorWidgetConfig( const QDomElement& cfgElem )
{
QgsEditorWidgetConfig cfg;
//// TODO: MAKE THIS MORE GENERIC, SO INDIVIDUALL WIDGETS CAN NOT ONLY SAVE STRINGS
/// SEE QgsEditorWidgetFactory::writeConfig
for ( int j = 0; j < cfgElem.attributes().size(); ++j )
{
const QDomAttr attr = cfgElem.attributes().item( j ).toAttr();
cfg.insert( attr.name(), attr.value() );
}

setWidgetConfig( wdgElem.attribute( "name" ), cfg );
const QDomNodeList optionElements = cfgElem.elementsByTagName( "option" );
for ( int j = 0; j < optionElements.size(); ++j )
{
const QDomElement option = optionElements.at( j ).toElement();
const QString key = option.attribute( "key" );
const QString value = option.attribute( "value" );
cfg.insert( key, value );
}
//// END TODO
return cfg;
}

void QgsEditFormConfig::writeXml( QDomNode& node ) const
Expand Down
67 changes: 15 additions & 52 deletions src/core/qgseditformconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,46 +170,21 @@ class CORE_EXPORT QgsEditFormConfig
* <li>WebView (QgsWebViewWidgetWrapper)</li>
* </ul>
*
* @param fieldIdx Index of the field
* @param fieldName The name of the field
* @param widgetType Type id of the editor widget to use
*/
void setWidgetType( int fieldIdx, const QString& widgetType );

/**
* Get the id for the editor widget used to represent the field at the given index
*
* @param fieldIdx The index of the field
*
* @return The id for the editor widget or a NULL string if not applicable
*/
QString widgetType( int fieldIdx ) const;
void setWidgetType( const QString& fieldName, const QString& widgetType );

/**
* Get the id for the editor widget used to represent the field at the given index
* Don't use this directly. Prefere the use of QgsEditorWidgetRegistry::instance()->findBestType.
*
* @param fieldName The name of the field
*
* @return The id for the editor widget or a NULL string if not applicable
*/
QString widgetType( const QString& fieldName ) const;

/**
* Set the editor widget config for a field.
*
* Python: Will accept a map.
*
* Example:
* \code{.py}
* layer.setWidgetConfig( 1, { 'Layer': 'otherlayerid_1234', 'Key': 'Keyfield', 'Value': 'ValueField' } )
* \endcode
*
* @param attrIdx Index of the field
* @param config The config to set for this field
*
* @see setWidgetType() for a list of widgets and choose the widget to see the available options.
*/
void setWidgetConfig( int attrIdx, const QgsEditorWidgetConfig& config );

/**
* Set the editor widget config for a widget.
*
Expand All @@ -218,50 +193,33 @@ class CORE_EXPORT QgsEditFormConfig
* layer.setWidgetConfig( 'relation_id', { 'nm-rel': 'other_relation' } )
* \endcode
*
* @param widgetName The name of the widget or field to configure
* @param fieldName The name of the field to configure
* @param config The config to set for this field
*
* @see setWidgetType() for a list of widgets and choose the widget to see the available options.
*
* @note not available in python bindings
*/
void setWidgetConfig( const QString& widgetName, const QgsEditorWidgetConfig& config );

/**
* Get the configuration for the editor widget used to represent the field at the given index
*
* @param fieldIdx The index of the field
*
* @return The configuration for the editor widget or an empty config if the field does not exist
*/
QgsEditorWidgetConfig widgetConfig( int fieldIdx ) const;
void setWidgetConfig( const QString& fieldName, const QgsEditorWidgetConfig& config );

/**
* Get the configuration for the editor widget used to represent the field with the given name
* Don't use this directly. Prefere the use of QgsEditorWidgetRegistry::instance()->findBestConfig.
*
* @param widgetName The name of the widget. This can be a field name or the name of an additional widget.
* @param fieldName The name of the field.
*
* @return The configuration for the editor widget or an empty config if the field does not exist
*/
QgsEditorWidgetConfig widgetConfig( const QString& widgetName ) const;

/**
* Remove the configuration for the editor widget used to represent the field at the given index
*
* @param fieldIdx The index of the field
*
* @return true if successful, false if the field does not exist
*/
bool removeWidgetConfig( int fieldIdx );
QgsEditorWidgetConfig widgetConfig( const QString& fieldName ) const;

/**
* Remove the configuration for the editor widget used to represent the field with the given name
*
* @param widgetName The name of the widget. This can be a field name or the name of an additional widget.
* @param fieldName The name of the widget.
*
* @return true if successful, false if the field does not exist
*/
bool removeWidgetConfig( const QString& widgetName );
bool removeWidgetConfig( const QString& fieldName );

/**
* This returns true if the field is manually set to read only or if the field
Expand Down Expand Up @@ -411,6 +369,11 @@ class CORE_EXPORT QgsEditFormConfig
*/
explicit QgsEditFormConfig();

/**
* Parse the XML for the config of one editor widget.
*/
static QgsEditorWidgetConfig parseEditorWidgetConfig( const QDomElement& cfgElem );

private:

/**
Expand Down
54 changes: 54 additions & 0 deletions src/core/qgseditorwidgetsetup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/***************************************************************************
qgseditorwidgetsetup.h - Holder for the widget configuration.
--------------------------------------
Date : 01-Sep-2016
Copyright : (C) 2016 by Patrick Valsecchi
email : patrick.valsecchi at camptocamp.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSEDITORWIDGETSETUP_H
#define QGSEDITORWIDGETSETUP_H

#include "qgseditorwidgetconfig.h"

/** \ingroup core
* Holder for the widget type and its configuration for a field.
*
* @note added in QGIS 3.0
*/
class CORE_EXPORT QgsEditorWidgetSetup
{
public:
/**
* Constructor
*/
QgsEditorWidgetSetup( const QString& type, const QgsEditorWidgetConfig& config ) : mType( type ), mConfig( config ) {}
QgsEditorWidgetSetup() {}

/**
* @return the widget type to use
*/
QString type() const { return mType; }

/**
* @return the widget configuration to used
*/
QgsEditorWidgetConfig config() const { return mConfig; }

/**
* @return true if there is no widget configured.
*/
bool isNull() const { return mType.isEmpty(); }
private:
QString mType;
QgsEditorWidgetConfig mConfig;
};

#endif // QGSEDITORWIDGETSETUP_H
10 changes: 10 additions & 0 deletions src/core/qgsfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,16 @@ bool QgsField::convertCompatible( QVariant& v ) const
return true;
}

void QgsField::setEditorWidgetSetup( const QgsEditorWidgetSetup& v )
{
d->editorWidgetSetup = v;
}

const QgsEditorWidgetSetup& QgsField::editorWidgetSetup() const
{
return d->editorWidgetSetup;
}

/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests in testqgsfield.cpp.
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgsfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class QgsFieldsPrivate;
* See details in QEP #17
****************************************************************************/

#include "qgseditorwidgetsetup.h"

/** \class QgsField
* \ingroup core
* Encapsulate a field in an attribute table or data source.
Expand Down Expand Up @@ -218,6 +220,20 @@ class CORE_EXPORT QgsField
return QVariant::fromValue( *this );
}

/**
* Set the editor widget setup for the field.
*
* @param v The value to set
*/
void setEditorWidgetSetup( const QgsEditorWidgetSetup& v );

/**
* Get the editor widget setup for the field.
*
* @return the value
*/
const QgsEditorWidgetSetup& editorWidgetSetup() const;

private:

QSharedDataPointer<QgsFieldPrivate> d;
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgsfield_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class QgsFieldPrivate : public QSharedData

//! Default value expression
QString defaultValueExpression;

QgsEditorWidgetSetup editorWidgetSetup;
};


Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayereditpassthrough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ bool QgsVectorLayerEditPassthrough::deleteAttribute( int attr )
if ( L->dataProvider()->deleteAttributes( QgsAttributeIds() << attr ) )
{
mModified = true;
L->editFormConfig().removeWidgetConfig( attr );
L->editFormConfig().removeWidgetConfig( L->fields().at( attr ).name() );
emit attributeDeleted( attr );
mModified = true;
return true;
Expand Down
7 changes: 4 additions & 3 deletions src/core/qgsvectorlayerundocommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@ QgsVectorLayerUndoCommandDeleteAttribute::QgsVectorLayerUndoCommandDeleteAttribu
QgsFields::FieldOrigin origin = fields.fieldOrigin( mFieldIndex );
mOriginIndex = fields.fieldOriginIndex( mFieldIndex );
mProviderField = ( origin == QgsFields::OriginProvider );
mOldEditorWidgetConfig = mBuffer->L->editFormConfig().widgetConfig( mFieldIndex );
mFieldName = fields.field( mFieldIndex ).name();
mOldEditorWidgetConfig = mBuffer->L->editFormConfig().widgetConfig( mFieldName );

if ( !mProviderField )
{
Expand Down Expand Up @@ -410,7 +411,7 @@ void QgsVectorLayerUndoCommandDeleteAttribute::undo()
}
}

mBuffer->L->editFormConfig().setWidgetConfig( mFieldIndex, mOldEditorWidgetConfig );
mBuffer->L->editFormConfig().setWidgetConfig( mFieldName, mOldEditorWidgetConfig );

emit mBuffer->attributeAdded( mFieldIndex );
}
Expand All @@ -428,7 +429,7 @@ void QgsVectorLayerUndoCommandDeleteAttribute::redo()
mBuffer->mAddedAttributes.removeAt( mOriginIndex ); // removing temporary attribute
}

mBuffer->L->editFormConfig().removeWidgetConfig( mFieldIndex );
mBuffer->L->editFormConfig().removeWidgetConfig( mFieldName );
mBuffer->handleAttributeDeleted( mFieldIndex ); // update changed attributes + new features
mBuffer->updateLayerFields();
emit mBuffer->attributeDeleted( mFieldIndex );
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorlayerundocommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class CORE_EXPORT QgsVectorLayerUndoCommandDeleteAttribute : public QgsVectorLay

private:
int mFieldIndex;
QString mFieldName;
bool mProviderField;
int mOriginIndex;
QgsField mOldField;
Expand Down
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ SET(QGIS_GUI_SRCS
auth/qgsauthtrustedcasdialog.cpp

editorwidgets/core/qgseditorconfigwidget.cpp
editorwidgets/core/qgseditorwidgetautoconf.cpp
editorwidgets/core/qgseditorwidgetfactory.cpp
editorwidgets/core/qgseditorwidgetregistry.cpp
editorwidgets/core/qgseditorwidgetwrapper.cpp
Expand Down
4 changes: 1 addition & 3 deletions src/gui/attributetable/qgsattributetabledelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ QWidget* QgsAttributeTableDelegate::createEditor( QWidget *parent, const QStyleO

int fieldIdx = index.model()->data( index, QgsAttributeTableModel::FieldIndexRole ).toInt();

QString widgetType = vl->editFormConfig().widgetType( fieldIdx );
QgsEditorWidgetConfig cfg = vl->editFormConfig().widgetConfig( fieldIdx );
QgsAttributeEditorContext context( masterModel( index.model() )->editorContext(), QgsAttributeEditorContext::Popup );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, vl, fieldIdx, cfg, nullptr, parent, context );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( vl, fieldIdx, nullptr, parent, context );
QWidget* w = eww->widget();

w->setAutoFillBackground( true );
Expand Down
8 changes: 4 additions & 4 deletions src/gui/attributetable/qgsattributetablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,13 @@ void QgsAttributeTableModel::loadAttributes()

for ( int idx = 0; idx < fields.count(); ++idx )
{
const QString widgetType = layer()->editFormConfig().widgetType( idx );
QgsEditorWidgetFactory* widgetFactory = QgsEditorWidgetRegistry::instance()->factory( widgetType );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer(), fields[idx].name() );
QgsEditorWidgetFactory* widgetFactory = QgsEditorWidgetRegistry::instance()->factory( setup.type() );
if ( widgetFactory )
{
mWidgetFactories.append( widgetFactory );
mWidgetConfigs.append( layer()->editFormConfig().widgetConfig( idx ) );
mAttributeWidgetCaches.append( widgetFactory->createCache( layer(), idx, mWidgetConfigs.last() ) );
mWidgetConfigs.append( setup.config() );
mAttributeWidgetCaches.append( widgetFactory->createCache( layer(), idx, setup.config() ) );

attributes << idx;
}
Expand Down
3 changes: 2 additions & 1 deletion src/gui/attributetable/qgsdualview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "qgsvectordataprovider.h"
#include "qgsvectorlayercache.h"
#include "qgsorganizetablecolumnsdialog.h"
#include "qgseditorwidgetregistry.h"

#include <QClipboard>
#include <QDialog>
Expand Down Expand Up @@ -141,7 +142,7 @@ void QgsDualView::columnBoxInit()
if ( fieldIndex == -1 )
continue;

if ( mLayerCache->layer()->editFormConfig().widgetType( fieldIndex ) != "Hidden" )
if ( QgsEditorWidgetRegistry::instance()->findBest( mLayerCache->layer(), field.name() ).type() != "Hidden" )
{
QIcon icon = mLayerCache->layer()->fields().iconForField( fieldIndex );
QString text = field.name();
Expand Down
112 changes: 112 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetautoconf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/***************************************************************************
qgseditorwidgetautoconf.cpp
---------------------
begin : July 2016
copyright : (C) 2016 by Patrick Valsecchi
email : patrick.valsecchi at camptocamp.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgseditorwidgetautoconf.h"
#include "qgseditorwidgetregistry.h"

/** \ingroup gui
* Widget auto conf plugin that guesses what widget type to use in function of what the widgets support.
*
* @note not available in Python bindings
* @note added in QGIS 3.0
*/
class FromFactoriesPlugin: public QgsEditorWidgetAutoConfPlugin
{
public:
virtual QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName, int& score ) const override
{
int bestScore = 0;
QString bestType;
const QMap<QString, QgsEditorWidgetFactory*> factories = QgsEditorWidgetRegistry::instance()->factories();
for ( QMap<QString, QgsEditorWidgetFactory*>::const_iterator i = factories.begin(); i != factories.end(); ++i )
{
const int index = vl->fieldNameIndex( fieldName );
if ( index >= 0 )
{
const int score = i.value()->fieldScore( vl, index );
if ( score > bestScore )
{
bestType = i.key();
bestScore = score;
}
}
}
if ( bestScore > 0 )
{
score = 10;
return QgsEditorWidgetSetup( bestType, QgsEditorWidgetConfig() );
}
return QgsEditorWidgetSetup();
}
};


/** \ingroup gui
* Widget auto conf plugin that reads the widget setup to use from what the data provider says.
*
* @note not available in Python bindings
* @note added in QGIS 3.0
*/
class FromDbTablePlugin: public QgsEditorWidgetAutoConfPlugin
{
public:
virtual QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName, int& score ) const override
{
QgsField field = vl->fields().field( fieldName );
if ( !field.editorWidgetSetup().isNull() )
{
score = 20;
return field.editorWidgetSetup();
}
else
{
return QgsEditorWidgetSetup();
}
}
};

///@cond PRIVATE
QgsEditorWidgetAutoConf::QgsEditorWidgetAutoConf()
{
registerPlugin( new FromFactoriesPlugin() );
registerPlugin( new FromDbTablePlugin() );
}

QgsEditorWidgetSetup QgsEditorWidgetAutoConf::editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName ) const
{
QgsEditorWidgetSetup result( "TextEdit", QgsEditorWidgetConfig() );

if ( vl->fields().indexFromName( fieldName ) >= 0 )
{
int bestScore = 0;
Q_FOREACH ( QSharedPointer<QgsEditorWidgetAutoConfPlugin> cur, plugins )
{
int score = 0;
const QgsEditorWidgetSetup curResult = cur->editorWidgetSetup( vl, fieldName, score );
if ( score > bestScore )
{
result = curResult;
bestScore = score;
}
}
}

return result;
}

void QgsEditorWidgetAutoConf::registerPlugin( QgsEditorWidgetAutoConfPlugin* plugin )
{
plugins.append( QSharedPointer<QgsEditorWidgetAutoConfPlugin>( plugin ) );
}
///@endcond
88 changes: 88 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetautoconf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/***************************************************************************
qgseditorwidgetautoconf.h
---------------------
begin : July 2016
copyright : (C) 2016 by Patrick Valsecchi
email : patrick.valsecchi at camptocamp.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSEDITORWIDGETAUTOCONF_H
#define QGSEDITORWIDGETAUTOCONF_H

#include <QList>
#include <QSharedPointer>

class QgsVectorLayer;
class QgsEditorWidgetSetup;

/** \ingroup gui
* Base class for plugins allowing to pick automatically a widget type for editing fields.
*
* @note added in QGIS 3.0
*/
class GUI_EXPORT QgsEditorWidgetAutoConfPlugin
{
public:
/**
* Typical scores are:
* * 0: no matching type found.
* * 10: a widget has been guessed from the type of field.
* * 20: a widget has been determined from an external configuration (for example a database table)
*
* @param vl The vector layer for which this widget will be created
* @param fieldName The field name on the specified layer for which this widget will be created
* @param score Where the score is returned (default to 0)
*
* @return and integer value rating how good is the setup provided by this plugin.
*/
virtual QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName, int& score ) const = 0;

};


///@cond PRIVATE
/** \ingroup gui
* Class that allows to register plugins to pick automatically a widget type for editing fields.
* This class has only one instance, owned by the QgsEditorWidgetRegistry singleton
*
* The plugins are instances of QgsEditorWidgetAutoConfPlugin.
* @note added in QGIS 3.0
* @note not available in Python bindings
*/
class GUI_EXPORT QgsEditorWidgetAutoConf
{
public:
/**
* Register the default plugins.
*/
QgsEditorWidgetAutoConf();

/**
* Iterate over the plugins and return the setup of the plugin returning the highest score.
*
* @param vl The vector layer for which this widget will be created
* @param fieldName The field name on the specified layer for which this widget will be created
*
* @return The best widget setup that was found
*/
QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName ) const;

/**
* Register a new plugin.
*
* @param plugin The plugin (ownership is transfered)
*/
void registerPlugin( QgsEditorWidgetAutoConfPlugin* plugin );

private:
QList<QSharedPointer<QgsEditorWidgetAutoConfPlugin> > plugins;
};
///@endcond

#endif // QGSEDITORWIDGETAUTOCONF_H
4 changes: 2 additions & 2 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ QgsEditorWidgetConfig QgsEditorWidgetFactory::readConfig( const QDomElement& con
return QgsEditorWidgetConfig();
}

bool QgsEditorWidgetFactory::isFieldSupported( QgsVectorLayer* vl, int fieldIdx )
unsigned int QgsEditorWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
Q_UNUSED( vl )
Q_UNUSED( fieldIdx )
return true;
return 5;
}

18 changes: 12 additions & 6 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ class GUI_EXPORT QgsEditorWidgetFactory
* @param fieldIdx The field index
* @return True if the type is supported for this field
*
* @see isFieldSupported( QgsVectorLayer* vl, ind fieldIdx )
* @see fieldScore( const QgsVectorLayer* vl, ind fieldIdx )
*/
inline bool supportsField( QgsVectorLayer* vl, int fieldIdx ) { return isFieldSupported( vl, fieldIdx ); }
inline bool supportsField( const QgsVectorLayer* vl, int fieldIdx ) { return fieldScore( vl, fieldIdx ) > 0; }

/**
* Returns a list of widget types which this editor widget supports.
Expand Down Expand Up @@ -187,19 +187,25 @@ class GUI_EXPORT QgsEditorWidgetFactory
*/
virtual QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx );

private:
/**
* This method allows disabling this editor widget type for a certain field.
* By default, it returns true for all fields.
* By default, it returns 5 for every fields.
* Reimplement this if you only support certain fields.
*
* Typical return values are:
* * 0: not supported
* * 5: maybe support (for example, Datetime support strings depending on their content)
* * 10: basic support (this is what returns TextEdit for example, since it supports everything in a crude way)
* * 20: specialised support
*
* @param vl
* @param fieldIdx
* @return True if the field is supported.
* @return 0 if the field is not supported or a bigger number if it can (the widget with the biggest number will be
* taken by default). The default implementation returns 5..
*
* @see supportsField( QgsVectorLayer* vl, fieldIdx )
*/
virtual bool isFieldSupported( QgsVectorLayer* vl, int fieldIdx );
virtual unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const;

private:
QString mName;
Expand Down
34 changes: 28 additions & 6 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ QgsEditorWidgetRegistry* QgsEditorWidgetRegistry::instance()
void QgsEditorWidgetRegistry::initEditors( QgsMapCanvas *mapCanvas, QgsMessageBar *messageBar )
{
QgsEditorWidgetRegistry *reg = instance();
reg->registerWidget( "TextEdit", new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
reg->registerWidget( "Classification", new QgsClassificationWidgetWrapperFactory( tr( "Classification" ) ) );
reg->registerWidget( "Range", new QgsRangeWidgetFactory( tr( "Range" ) ) );
reg->registerWidget( "UniqueValues", new QgsUniqueValueWidgetFactory( tr( "Unique Values" ) ) );
Expand All @@ -62,7 +63,6 @@ void QgsEditorWidgetRegistry::initEditors( QgsMapCanvas *mapCanvas, QgsMessageBa
reg->registerWidget( "Enumeration", new QgsEnumerationWidgetFactory( tr( "Enumeration" ) ) );
reg->registerWidget( "Hidden", new QgsHiddenWidgetFactory( tr( "Hidden" ) ) );
reg->registerWidget( "CheckBox", new QgsCheckboxWidgetFactory( tr( "Check Box" ) ) );
reg->registerWidget( "TextEdit", new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
reg->registerWidget( "ValueRelation", new QgsValueRelationWidgetFactory( tr( "Value Relation" ) ) );
reg->registerWidget( "UuidGenerator", new QgsUuidWidgetFactory( tr( "Uuid Generator" ) ) );
reg->registerWidget( "Photo", new QgsPhotoWidgetFactory( tr( "Photo" ) ) );
Expand All @@ -89,6 +89,23 @@ QgsEditorWidgetRegistry::~QgsEditorWidgetRegistry()
qDeleteAll( mWidgetFactories );
}

QgsEditorWidgetSetup QgsEditorWidgetRegistry::findBest( const QgsVectorLayer* vl, const QString& fieldName ) const
{
const QString fromConfig = vl->editFormConfig().widgetType( fieldName );
if ( !fromConfig.isNull() )
{
return QgsEditorWidgetSetup( fromConfig, vl->editFormConfig().widgetConfig( fieldName ) );
}
return mAutoConf.editorWidgetSetup( vl, fieldName );
}

QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent, const QgsAttributeEditorContext &context )
{
const QString fieldName = vl->fields().field( fieldIdx ).name();
const QgsEditorWidgetSetup setup = findBest( vl, fieldName );
return create( setup.type(), vl, fieldIdx, setup.config(), editor, parent, context );
}

QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* editor, QWidget* parent, const QgsAttributeEditorContext &context )
{
if ( mWidgetFactories.contains( widgetId ) )
Expand Down Expand Up @@ -232,7 +249,7 @@ void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomEle

if ( mWidgetFactories.contains( ewv2Type ) )
{
formConfig.setWidgetType( idx, ewv2Type );
formConfig.setWidgetType( name, ewv2Type );
QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();

if ( !ewv2CfgElem.isNull() )
Expand All @@ -246,7 +263,7 @@ void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomEle
formConfig.setExpression( idx, ewv2CfgElem.attribute( "constraint", QString() ) );
formConfig.setExpressionDescription( idx, ewv2CfgElem.attribute( "constraintDescription", QString() ) );

formConfig.setWidgetConfig( idx, cfg );
formConfig.setWidgetConfig( name, cfg );
}
else
{
Expand Down Expand Up @@ -275,8 +292,13 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
QgsFields fields = vectorLayer->fields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
QgsField field = fields.at( idx );
const QString& widgetType = vectorLayer->editFormConfig().widgetType( idx );
const QgsField field = fields.at( idx );
const QString& widgetType = vectorLayer->editFormConfig().widgetType( field.name() );
if ( widgetType.isNull() )
{
// Don't save widget config if it is not manually edited
continue;
}
if ( !mWidgetFactories.contains( widgetType ) )
{
QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
Expand All @@ -297,7 +319,7 @@ void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement&
ewv2CfgElem.setAttribute( "constraint", vectorLayer->editFormConfig().expression( idx ) );
ewv2CfgElem.setAttribute( "constraintDescription", vectorLayer->editFormConfig().expressionDescription( idx ) );

mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig().widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );
mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig().widgetConfig( field.name() ), ewv2CfgElem, doc, vectorLayer, idx );

editTypeElement.appendChild( ewv2CfgElem );
}
Expand Down
37 changes: 37 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "qgseditorwidgetconfig.h"
#include "qgseditorwidgetfactory.h"
#include "qgsattributeeditorcontext.h"
#include "qgseditorwidgetautoconf.h"

class QgsMapLayer;
class QDomNode;
Expand Down Expand Up @@ -67,6 +68,16 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
*/
~QgsEditorWidgetRegistry();

/**
* Find the best editor widget and its configuration for a given field.
*
* @param vl The vector layer for which this widget will be created
* @param fieldName The field name on the specified layer for which this widget will be created
*
* @return The id of the widget type to use and its config
*/
QgsEditorWidgetSetup findBest( const QgsVectorLayer* vl, const QString& fieldName ) const;

/**
* Create an attribute editor widget wrapper of a given type for a given field.
* The editor may be NULL if you want the widget wrapper to create a default widget.
Expand All @@ -89,6 +100,24 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
QWidget* parent,
const QgsAttributeEditorContext& context = QgsAttributeEditorContext() );

/**
* Create an attribute editor widget wrapper of the best type for a given field.
* The editor may be NULL if you want the widget wrapper to create a default widget.
*
* @param vl The vector layer for which this widget will be created
* @param fieldIdx The field index on the specified layer for which this widget will be created
* @param editor An editor widget which will be used instead of an autocreated widget
* @param parent The parent which will be used for the created wrapper and the created widget
* @param context The editor context (not available in python bindings)
*
* @return A new widget wrapper
*/
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl,
int fieldIdx,
QWidget* editor,
QWidget* parent,
const QgsAttributeEditorContext& context = QgsAttributeEditorContext() );

QgsSearchWidgetWrapper* createSearchWidget( const QString& widgetId,
QgsVectorLayer* vl,
int fieldIdx,
Expand Down Expand Up @@ -141,6 +170,13 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
*/
bool registerWidget( const QString& widgetId, QgsEditorWidgetFactory* widgetFactory );

/**
* Register a new auto-conf plugin.
*
* @param plugin The plugin (ownership is transfered)
*/
void registerAutoConfPlugin( QgsEditorWidgetAutoConfPlugin* plugin ) { mAutoConf.registerPlugin( plugin ); }

protected:
QgsEditorWidgetRegistry();

Expand Down Expand Up @@ -199,6 +235,7 @@ class GUI_EXPORT QgsEditorWidgetRegistry : public QObject

QMap<QString, QgsEditorWidgetFactory*> mWidgetFactories;
QMap<const char*, QPair<int, QString> > mFactoriesByType;
QgsEditorWidgetAutoConf mAutoConf;
};

#endif // QGSEDITORWIDGETREGISTRY_H
6 changes: 6 additions & 0 deletions src/gui/editorwidgets/qgscheckboxwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,9 @@ QMap<const char*, int> QgsCheckboxWidgetFactory::supportedWidgetTypes()
map.insert( QGroupBox::staticMetaObject.className(), 10 );
return map;
}

unsigned int QgsCheckboxWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
const QVariant::Type type = vl->fields().field( fieldIdx ).type();
return type == QVariant::Bool ? 20 : 5;
}
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgscheckboxwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class GUI_EXPORT QgsCheckboxWidgetFactory : public QgsEditorWidgetFactory
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
QMap<const char*, int> supportedWidgetTypes() override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
};

#endif // QGSCHECKBOXWIDGETFACTORY_H
14 changes: 14 additions & 0 deletions src/gui/editorwidgets/qgscolorwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@ QgsEditorConfigWidget* QgsColorWidgetFactory::configWidget( QgsVectorLayer* vl,
{
return new QgsDummyConfigDlg( vl, fieldIdx, parent, QObject::tr( "Field contains a color." ) );
}

unsigned int QgsColorWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
const QgsField field = vl->fields().field( fieldIdx );
const QVariant::Type type = field.type();
if ( type == QVariant::Color )
{
return 20;
}
else
{
return 5;
}
}
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgscolorwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GUI_EXPORT QgsColorWidgetFactory : public QgsEditorWidgetFactory
public:
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const override;
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
};

#endif // QGSCOLORWIDGETFACTORY_H
15 changes: 15 additions & 0 deletions src/gui/editorwidgets/qgsdatetimeeditfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,18 @@ QMap<const char*, int> QgsDateTimeEditFactory::supportedWidgetTypes()
map.insert( QgsDateTimeEdit::staticMetaObject.className(), 10 );
return map;
}

unsigned int QgsDateTimeEditFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
const QgsField field = vl->fields().field( fieldIdx );
const QVariant::Type type = field.type();
const QgsEditorWidgetConfig config = vl->editFormConfig().widgetConfig( field.name() );
if ( type == QVariant::DateTime || config.contains( "field_format" ) )
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this also consider Date and Time field types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a feeling that QgsDateTimeEditWrapper is wired to support QDate or QTime. It looks to me it supports only QString and QDateTime.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - it definitely supports date & time type fields. It just needs to make sure the format settings are set correctly to match the field type.

{
return 20;
}
else
{
return 5;
}
}
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsdatetimeeditfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class GUI_EXPORT QgsDateTimeEditFactory : public QgsEditorWidgetFactory
QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;
Qt::AlignmentFlag alignmentFlag( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config ) const override;
virtual QMap<const char*, int> supportedWidgetTypes() override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
};

#endif // QGSDATETIMEEDITFACTORY_H
6 changes: 3 additions & 3 deletions src/gui/editorwidgets/qgsenumerationwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ QgsEditorConfigWidget* QgsEnumerationWidgetFactory::configWidget( QgsVectorLayer
}


bool QgsEnumerationWidgetFactory::isFieldSupported( QgsVectorLayer* vl, int fieldIdx )
unsigned int QgsEnumerationWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
QStringList list;
vl->dataProvider()->enumValues( fieldIdx, list );
if ( !list.isEmpty() )
return true;
return 20;
else
return false;
return 0;
}
3 changes: 1 addition & 2 deletions src/gui/editorwidgets/qgsenumerationwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ class GUI_EXPORT QgsEnumerationWidgetFactory : public QgsEditorWidgetFactory
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const override;
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;

private:
bool isFieldSupported( QgsVectorLayer* vl, int fieldIdx ) override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
};

#endif // QGSENUMERATIONWIDGETFACTORY_H
6 changes: 3 additions & 3 deletions src/gui/editorwidgets/qgsexternalresourcewidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ QgsEditorWidgetConfig QgsExternalResourceWidgetFactory::readConfig( const QDomEl
return cfg;
}

bool QgsExternalResourceWidgetFactory::isFieldSupported( QgsVectorLayer* vl, int fieldIdx )
unsigned int QgsExternalResourceWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
if ( vl->fields().at( fieldIdx ).type() == QVariant::String )
return true;
return 5;

return false;
return 0;
}
4 changes: 1 addition & 3 deletions src/gui/editorwidgets/qgsexternalresourcewidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,11 @@ class GUI_EXPORT QgsExternalResourceWidgetFactory : public QgsEditorWidgetFactor
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const override;
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;

// QgsEditorWidgetFactory interface
public:
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;

private:
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
bool isFieldSupported( QgsVectorLayer* vl, int fieldIdx ) override;
};

#endif // QGSEXTERNALRESOURCEWIDGETFACTORY_H
13 changes: 2 additions & 11 deletions src/gui/editorwidgets/qgsrangewidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,9 @@ void QgsRangeWidgetFactory::writeConfig( const QgsEditorWidgetConfig& config, QD
}
}

bool QgsRangeWidgetFactory::isFieldSupported( QgsVectorLayer* vl, int fieldIdx )
unsigned int QgsRangeWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
switch ( vl->fields().at( fieldIdx ).type() )
{
case QVariant::LongLong:
case QVariant::Double:
case QVariant::Int:
return true;

default:
return false;
}
return vl->fields().at( fieldIdx ).isNumeric() ? 20 : 0;
}

QMap<const char*, int> QgsRangeWidgetFactory::supportedWidgetTypes()
Expand Down
2 changes: 1 addition & 1 deletion src/gui/editorwidgets/qgsrangewidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class GUI_EXPORT QgsRangeWidgetFactory : public QgsEditorWidgetFactory
virtual QMap<const char*, int> supportedWidgetTypes() override;

private:
virtual bool isFieldSupported( QgsVectorLayer *vl, int fieldIdx ) override;
virtual unsigned int fieldScore( const QgsVectorLayer *vl, int fieldIdx ) const override;
};

#endif // QGSRANGEWIDGETFACTORY_H
7 changes: 7 additions & 0 deletions src/gui/editorwidgets/qgstexteditwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,10 @@ QgsEditorWidgetConfig QgsTextEditWidgetFactory::readConfig( const QDomElement& c

return cfg;
}

unsigned int QgsTextEditWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
Q_UNUSED( vl )
Q_UNUSED( fieldIdx )
return 10;
}
3 changes: 1 addition & 2 deletions src/gui/editorwidgets/qgstexteditwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ class GUI_EXPORT QgsTextEditWidgetFactory : public QgsEditorWidgetFactory
QgsSearchWidgetWrapper* createSearchWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;

// QgsEditorWidgetFactory interface
public:
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;

private:
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
Expand Down
6 changes: 6 additions & 0 deletions src/gui/editorwidgets/qgsuuidwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ QgsEditorConfigWidget* QgsUuidWidgetFactory::configWidget( QgsVectorLayer* vl, i
{
return new QgsDummyConfigDlg( vl, fieldIdx, parent, QObject::tr( "Read-only field that generates a UUID if empty." ) );
}

unsigned int QgsUuidWidgetFactory::fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const
{
const QVariant::Type type = vl->fields().field( fieldIdx ).type();
return type == QVariant::String ? 5 : 0;
}
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsuuidwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GUI_EXPORT QgsUuidWidgetFactory : public QgsEditorWidgetFactory
public:
QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const override;
QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const override;
unsigned int fieldScore( const QgsVectorLayer* vl, int fieldIdx ) const override;
};

#endif // QGSUUIDWIDGETFACTORY_H
5 changes: 1 addition & 4 deletions src/gui/qgsattributeeditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed

QWidget* QgsAttributeEditor::createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant& value, QgsAttributeEditorContext& context )
{
QString widgetType = vl->editFormConfig().widgetType( idx );
QgsEditorWidgetConfig cfg = vl->editFormConfig().widgetConfig( idx );

QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, vl, idx, cfg, editor, parent, context );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( vl, idx, editor, parent, context );

if ( eww )
{
Expand Down
33 changes: 15 additions & 18 deletions src/gui/qgsattributeform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,31 +1216,30 @@ void QgsAttributeForm::init()
//show attribute alias if available
QString fieldName = mLayer->attributeDisplayName( idx );

const QString widgetType = mLayer->editFormConfig().widgetType( idx );
const QgsEditorWidgetSetup widgetSetup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, field.name() );

if ( widgetType == "Hidden" )
if ( widgetSetup.type() == "Hidden" )
continue;

const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig().widgetConfig( idx );
bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );

// This will also create the widget
QLabel *l = new QLabel( fieldName );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, nullptr, this, mContext );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetSetup.type(), mLayer, idx, widgetSetup.config(), nullptr, this, mContext );

QWidget* w = nullptr;
if ( eww )
{
QgsAttributeFormEditorWidget* formWidget = new QgsAttributeFormEditorWidget( eww, this );
w = formWidget;
mFormEditorWidgets.insert( idx, formWidget );
formWidget->createSearchWidgetWrappers( widgetType, idx, widgetConfig, mContext );
formWidget->createSearchWidgetWrappers( widgetSetup.type(), idx, widgetSetup.config(), mContext );

l->setBuddy( eww->widget() );
}
else
{
w = new QLabel( QString( "<p style=\"color: red; font-style: italic;\">Failed to create widget with type '%1'</p>" ).arg( widgetType ) );
w = new QLabel( QString( "<p style=\"color: red; font-style: italic;\">Failed to create widget with type '%1'</p>" ).arg( widgetSetup.type() ) );
}


Expand All @@ -1265,8 +1264,8 @@ void QgsAttributeForm::init()
Q_FOREACH ( const QgsRelation& rel, QgsProject::instance()->relationManager()->referencedRelations( mLayer ) )
{
QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, rel, nullptr, this );
QgsEditorWidgetConfig cfg = mLayer->editFormConfig().widgetConfig( rel.id() );
rww->setConfig( cfg );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, rel.id() );
rww->setConfig( setup.config() );
rww->setContext( mContext );
gridLayout->addWidget( rww->widget(), row++, 0, 1, 2 );
mWidgets.append( rww );
Expand Down Expand Up @@ -1506,14 +1505,13 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
int fldIdx = vl->fieldNameIndex( fieldDef->name() );
if ( fldIdx < vl->fields().count() && fldIdx >= 0 )
{
const QString widgetType = mLayer->editFormConfig().widgetType( fldIdx );
const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig().widgetConfig( fldIdx );
const QgsEditorWidgetSetup widgetSetup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, fieldDef->name() );

QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, fldIdx, widgetConfig, nullptr, this, mContext );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetSetup.type(), mLayer, fldIdx, widgetSetup.config(), nullptr, this, mContext );
QgsAttributeFormEditorWidget* w = new QgsAttributeFormEditorWidget( eww, this );
mFormEditorWidgets.insert( fldIdx, w );

w->createSearchWidgetWrappers( widgetType, fldIdx, widgetConfig, mContext );
w->createSearchWidgetWrappers( widgetSetup.type(), fldIdx, widgetSetup.config(), mContext );

newWidgetInfo.widget = w;
addWidgetWrapper( eww );
Expand All @@ -1533,8 +1531,8 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
const QgsAttributeEditorRelation* relDef = dynamic_cast<const QgsAttributeEditorRelation*>( widgetDef );

QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relDef->relation(), nullptr, this );
QgsEditorWidgetConfig cfg = mLayer->editFormConfig().widgetConfig( relDef->relation().id() );
rww->setConfig( cfg );
const QgsEditorWidgetSetup widgetSetup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, relDef->relation().id() );
rww->setConfig( widgetSetup.config() );
rww->setContext( context );
newWidgetInfo.widget = rww->widget();
rww->setShowLabel( relDef->showLabel() );
Expand Down Expand Up @@ -1688,7 +1686,8 @@ void QgsAttributeForm::createWrappers()
if ( relation.isValid() )
{
QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relation, myWidget, this );
rww->setConfig( mLayer->editFormConfig().widgetConfig( relation.id() ) );
const QgsEditorWidgetSetup widgetSetup = QgsEditorWidgetRegistry::instance()->findBest( mLayer, relation.id() );
rww->setConfig( widgetSetup.config() );
rww->setContext( mContext );
rww->widget(); // Will initialize the widget
mWidgets.append( rww );
Expand All @@ -1700,11 +1699,9 @@ void QgsAttributeForm::createWrappers()
{
if ( field.name() == myWidget->objectName() )
{
const QString widgetType = mLayer->editFormConfig().widgetType( field.name() );
const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig().widgetConfig( field.name() );
int idx = mLayer->fieldNameIndex( field.name() );

QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, myWidget, this, mContext );
QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( mLayer, idx, myWidget, this, mContext );
addWidgetWrapper( eww );
}
}
Expand Down
1 change: 0 additions & 1 deletion src/providers/postgres/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

########################################################
# Files

Expand Down
2 changes: 2 additions & 0 deletions src/providers/postgres/qgspostgresconn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,7 @@ QString QgsPostgresConn::fieldExpression( const QgsField &fld, QString expr )
}
else if ( type.startsWith( '_' ) )
{
//TODO: add native support for arrays
return QString( "array_out(%1)::text" ).arg( expr );
}
else if ( type == "bool" )
Expand All @@ -1303,6 +1304,7 @@ QString QgsPostgresConn::fieldExpression( const QgsField &fld, QString expr )
{
return QString( "st_astext(%1)" ).arg( expr );
}
//TODO: add support for hstore
else
{
return expr + "::text";
Expand Down
90 changes: 68 additions & 22 deletions src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <qgsmessagelog.h>
#include <qgsrectangle.h>
#include <qgscoordinatereferencesystem.h>
#include <qgseditformconfig.h>

#include <QMessageBox>

Expand All @@ -38,6 +39,7 @@

const QString POSTGRES_KEY = "postgres";
const QString POSTGRES_DESCRIPTION = "PostgreSQL/PostGIS data provider";
static const QString EDITOR_WIDGET_STYLES_TABLE = "qgis_editor_widget_styles";

inline qint64 PKINT2FID( qint32 x )
{
Expand All @@ -49,6 +51,12 @@ inline qint32 FID2PKINT( qint64 x )
return QgsPostgresUtils::fid_to_int32pk( x );
}

static bool tableExists( QgsPostgresConn& conn, const QString& name )
{
QgsPostgresResult res( conn.PQexec( "SELECT COUNT(*) FROM information_schema.tables WHERE table_name=" + QgsPostgresConn::quotedValue( name ) ) );
return res.PQgetvalue( 0, 0 ).toInt() > 0;
}

QgsPostgresPrimaryKeyType
QgsPostgresProvider::pkType( const QgsField& f ) const
{
Expand Down Expand Up @@ -1053,9 +1061,48 @@ bool QgsPostgresProvider::loadFields()
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment ) );
}

setEditorWidgets();

return true;
}

void QgsPostgresProvider::setEditorWidgets()
{
if ( tableExists( *connectionRO(), EDITOR_WIDGET_STYLES_TABLE ) )
{
for ( int i = 0; i < mAttributeFields.count(); ++i )
{
// CREATE TABLE qgis_editor_widget_styles (schema_name TEXT NOT NULL, table_name TEXT NOT NULL, field_name TEXT NOT NULL,
// type TEXT NOT NULL, config TEXT,
// PRIMARY KEY(schema_name, table_name, field_name));
QgsField& field = mAttributeFields[i];
const QString sql = QString( "SELECT type, config FROM %1 WHERE schema_name = %2 and table_name = %3 and field_name = %4 LIMIT 1" ).
arg( EDITOR_WIDGET_STYLES_TABLE, quotedValue( mSchemaName ), quotedValue( mTableName ), quotedValue( field.name() ) );
QgsPostgresResult result( connectionRO()->PQexec( sql ) );
for ( int i = 0; i < result.PQntuples(); ++i )
{
const QString type = result.PQgetvalue( i, 0 );
QgsEditorWidgetConfig config;
if ( !result.PQgetisnull( i, 1 ) ) // Can be null and it's OK
{
const QString configTxt = result.PQgetvalue( i, 1 );
QDomDocument doc;
if ( doc.setContent( configTxt ) )
{
config = QgsEditFormConfig::parseEditorWidgetConfig( doc.documentElement() );
}
else
{
QgsMessageLog::logMessage( tr( "Cannot parse widget configuration for field %1.%2.%3\n" ).arg( mSchemaName, mTableName, field.name() ), tr( "PostGIS" ) );
}
}

field.setEditorWidgetSetup( QgsEditorWidgetSetup( type, config ) );
}
}
}
}

bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
{
QgsDebugMsg( "Checking for permissions on the relation" );
Expand Down Expand Up @@ -1937,6 +1984,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
.arg( delim,
quotedValue( v.toString() ) );
}
//TODO: convert arrays and hstore to native types
else
{
values += delim + quotedValue( v );
Expand Down Expand Up @@ -3965,24 +4013,23 @@ QGISEXTERN bool saveStyle( const QString& uri, const QString& qmlStyle, const QS
return false;
}

QgsPostgresResult res( conn->PQexec( "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='layer_styles'" ) );
if ( res.PQgetvalue( 0, 0 ).toInt() == 0 )
{
res = conn->PQexec( "CREATE TABLE layer_styles("
"id SERIAL PRIMARY KEY"
",f_table_catalog varchar"
",f_table_schema varchar"
",f_table_name varchar"
",f_geometry_column varchar"
",styleName varchar(30)"
",styleQML xml"
",styleSLD xml"
",useAsDefault boolean"
",description text"
",owner varchar(30)"
",ui xml"
",update_time timestamp DEFAULT CURRENT_TIMESTAMP"
")" );
if ( !tableExists( *conn, "layer_styles" ) )
{
QgsPostgresResult res( conn->PQexec( "CREATE TABLE layer_styles("
"id SERIAL PRIMARY KEY"
",f_table_catalog varchar"
",f_table_schema varchar"
",f_table_name varchar"
",f_geometry_column varchar"
",styleName varchar(30)"
",styleQML xml"
",styleSLD xml"
",useAsDefault boolean"
",description text"
",owner varchar(30)"
",ui xml"
",update_time timestamp DEFAULT CURRENT_TIMESTAMP"
")" ) );
if ( res.PQresultStatus() != PGRES_COMMAND_OK )
{
errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions (user=%1). Please contact your database admin" ).arg( dsUri.username() );
Expand Down Expand Up @@ -4036,7 +4083,7 @@ QGISEXTERN bool saveStyle( const QString& uri, const QString& qmlStyle, const QS
.arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) )
.arg( QgsPostgresConn::quotedValue( styleName.isEmpty() ? dsUri.table() : styleName ) );

res = conn->PQexec( checkQuery );
QgsPostgresResult res( conn->PQexec( checkQuery ) );
if ( res.PQntuples() > 0 )
{
if ( QMessageBox::question( nullptr, QObject::tr( "Save style in database" ),
Expand Down Expand Up @@ -4111,8 +4158,7 @@ QGISEXTERN QString loadStyle( const QString& uri, QString& errCause )
return "";
}

QgsPostgresResult result( conn->PQexec( "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='layer_styles'" ) );
if ( result.PQgetvalue( 0, 0 ).toInt() == 0 )
if ( !tableExists( *conn, "layer_styles" ) )
{
return "";
}
Expand All @@ -4130,7 +4176,7 @@ QGISEXTERN QString loadStyle( const QString& uri, QString& errCause )
.arg( QgsPostgresConn::quotedValue( dsUri.table() ) )
.arg( QgsPostgresConn::quotedValue( dsUri.geometryColumn() ) );

result = conn->PQexec( selectQmlQuery );
QgsPostgresResult result( conn->PQexec( selectQmlQuery ) );

QString style = result.PQntuples() == 1 ? result.PQgetvalue( 0, 0 ) : "";
conn->unref();
Expand Down
4 changes: 4 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
bool loadFields();

/** Set the default widget type for the fields
*/
void setEditorWidgets();

/** Convert a QgsField to work with PG */
static bool convertField( QgsField &field, const QMap<QString, QVariant> *options = nullptr );

Expand Down
7 changes: 4 additions & 3 deletions src/server/qgsserverprojectparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ void QgsServerProjectParser::addLayerProjectSettings( QDomElement& layerElem, QD
}

//edit type to text
attributeElem.setAttribute( "editType", vLayer->editFormConfig().widgetType( idx ) );
attributeElem.setAttribute( "editType", vLayer->editFormConfig().widgetType( field.name() ) );
attributeElem.setAttribute( "comment", field.comment() );
attributeElem.setAttribute( "length", field.length() );
attributeElem.setAttribute( "precision", field.precision() );
Expand Down Expand Up @@ -1555,10 +1555,11 @@ void QgsServerProjectParser::addValueRelationLayersForLayer( const QgsVectorLaye

for ( int idx = 0; idx < vl->pendingFields().size(); idx++ )
{
if ( vl->editFormConfig().widgetType( idx ) != "ValueRelation" )
const QString name = vl->pendingFields().field( idx ).name();
if ( vl->editFormConfig().widgetType( name ) != "ValueRelation" )
continue;

QgsEditorWidgetConfig cfg( vl->editFormConfig().widgetConfig( idx ) );
QgsEditorWidgetConfig cfg( vl->editFormConfig().widgetConfig( name ) );
if ( !cfg.contains( "Layer" ) )
continue;

Expand Down
8 changes: 4 additions & 4 deletions src/server/qgswmsserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3334,11 +3334,11 @@ QDomElement QgsWmsServer::createFeatureGML(

QString QgsWmsServer::replaceValueMapAndRelation( QgsVectorLayer* vl, int idx, const QString& attributeVal )
{
if ( QgsEditorWidgetFactory *factory = QgsEditorWidgetRegistry::instance()->factory( vl->editFormConfig().widgetType( idx ) ) )
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( vl, vl->fields().field( idx ).name() );
if ( QgsEditorWidgetFactory *factory = QgsEditorWidgetRegistry::instance()->factory( setup.type() ) )
{
QgsEditorWidgetConfig cfg( vl->editFormConfig().widgetConfig( idx ) );
QString value( factory->representValue( vl, idx, cfg, QVariant(), attributeVal ) );
if ( cfg.value( "AllowMulti" ).toBool() && value.startsWith( "{" ) && value.endsWith( "}" ) )
QString value( factory->representValue( vl, idx, setup.config(), QVariant(), attributeVal ) );
if ( setup.config().value( "AllowMulti" ).toBool() && value.startsWith( "{" ) && value.endsWith( "}" ) )
{
value = value.mid( 1, value.size() - 2 );
}
Expand Down
13 changes: 13 additions & 0 deletions tests/src/core/testqgsfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class TestQgsField: public QObject
void convertCompatible();
void dataStream();
void displayName();
void editorWidgetSetup();

private:
};
Expand Down Expand Up @@ -398,5 +399,17 @@ void TestQgsField::displayName()
QCOMPARE( field.displayName(), QString( "name" ) );
}

void TestQgsField::editorWidgetSetup()
{
QgsField field;
QgsEditorWidgetConfig config;
config.insert( "a", "value_a" );
const QgsEditorWidgetSetup setup( "test", config );
field.setEditorWidgetSetup( setup );

QCOMPARE( field.editorWidgetSetup().type(), setup.type() );
QCOMPARE( field.editorWidgetSetup().config(), setup.config() );
}

QTEST_MAIN( TestQgsField )
#include "testqgsfield.moc"
1 change: 1 addition & 0 deletions tests/src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,5 @@ ADD_QGIS_TEST(rubberbandtest testqgsrubberband.cpp)
ADD_QGIS_TEST(scalecombobox testqgsscalecombobox.cpp)
ADD_QGIS_TEST(spinbox testqgsspinbox.cpp)
ADD_QGIS_TEST(sqlcomposerdialog testqgssqlcomposerdialog.cpp)
ADD_QGIS_TEST(editorwidgetregistrytest testqgseditorwidgetregistry.cpp)

1 change: 1 addition & 0 deletions tests/src/gui/testqgsattributeform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void TestQgsAttributeForm::testFieldConstraint()
// make a temporary vector layer
QString def = "Point?field=col0:integer";
QgsVectorLayer* layer = new QgsVectorLayer( def, "test", "memory" );
layer->editFormConfig().setWidgetType( "col0", "TextEdit" );

// add a feature to the vector layer
QgsFeature ft( layer->dataProvider()->fields(), 1 );
Expand Down
119 changes: 119 additions & 0 deletions tests/src/gui/testqgseditorwidgetregistry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/***************************************************************************
testqgseditorwidgetregistry.cpp
---------------------
begin : July 2016
copyright : (C) 2016 by Patrick Valsecchi
email : patrick.valsecchi at camptocamp.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest/QtTest>

#include "qgseditorwidgetregistry.h"
#include "qgseditorwidgetautoconf.h"


class TestQgsEditorWidgetRegistry: public QObject
{
Q_OBJECT

class DummyPlugin: public QgsEditorWidgetAutoConfPlugin
{
public:
QgsEditorWidgetSetup editorWidgetSetup( const QgsVectorLayer* vl, const QString& fieldName, int& score ) const override
{
Q_UNUSED( vl )
if ( fieldName == "special" )
{
score = 100;
return QgsEditorWidgetSetup( "Special", QgsEditorWidgetConfig() );
}
score = 0;
return QgsEditorWidgetSetup();
}
};


private slots:
void initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
QgsEditorWidgetRegistry::initEditors();
QgsEditorWidgetRegistry::instance()->registerAutoConfPlugin( new DummyPlugin() );
}

void cleanupTestCase()
{
QgsApplication::exitQgis();
}

void stringType()
{
checkSimple( "string", "TextEdit" );
}

void datetimeType()
{
checkSimple( "datetime", "DateTime" );
}

void integerType()
{
checkSimple( "integer", "Range" );
}

void doubleType()
{
checkSimple( "double", "Range" );
}

void configuredType()
{
QgsVectorLayer vl( "LineString?crs=epsg:3111&field=pk:int&field=col1:string", "vl", "memory" );
QgsEditFormConfig formConfig = vl.editFormConfig();
formConfig.setWidgetType( "col1", "FooEdit" );
QgsEditorWidgetConfig config;
config["a"] = QVariant( 12 );
config["b"] = QVariant( "bar" );
formConfig.setWidgetConfig( "col1", config );
vl.setEditFormConfig( formConfig );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( &vl, "col1" );
QCOMPARE( setup.type(), QString( "FooEdit" ) );
QCOMPARE( setup.config(), config );
}

void wrongFieldName()
{
const QgsVectorLayer vl( "LineString?crs=epsg:3111&field=pk:int&field=col1:string", "vl", "memory" );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( &vl, "col2" );
// an unknown fields leads to a default setup with a TextEdit
QCOMPARE( setup.type(), QString( "TextEdit" ) );
QCOMPARE( setup.config().count(), 0 );
}

void typeFromPlugin()
{
const QgsVectorLayer vl( "LineString?crs=epsg:3111&field=pk:int&field=special:string", "vl", "memory" );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( &vl, "special" );
QCOMPARE( setup.type(), QString( "Special" ) );
}

private:

static void checkSimple( const QString& dataType, const QString& widgetType )
{
const QgsVectorLayer vl( "LineString?crs=epsg:3111&field=pk:int&field=col1:" + dataType, "vl", "memory" );
const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( &vl, "col1" );
QCOMPARE( setup.type(), widgetType );
QCOMPARE( setup.config().count(), 0 );
}
};

QTEST_MAIN( TestQgsEditorWidgetRegistry )
#include "testqgseditorwidgetregistry.moc"
23 changes: 23 additions & 0 deletions tests/src/python/test_provider_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
QgsTransactionGroup,
NULL
)
from qgis.gui import QgsEditorWidgetRegistry
from qgis.PyQt.QtCore import QSettings, QDate, QTime, QDateTime, QVariant
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
Expand All @@ -48,6 +49,7 @@ def setUpClass(cls):
cls.poly_vl = QgsVectorLayer(cls.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POLYGON table="qgis_test"."some_poly_data" (geom) sql=', 'test', 'postgres')
assert cls.poly_vl.isValid()
cls.poly_provider = cls.poly_vl.dataProvider()
QgsEditorWidgetRegistry.instance().initEditors()

@classmethod
def tearDownClass(cls):
Expand Down Expand Up @@ -312,6 +314,27 @@ def testRenameAttributes(self):
self.assertEqual(fet.fields()[1].name(), 'newname2')
self.assertEqual(fet.fields()[2].name(), 'another')

def testEditorWidgetTypes(self):
"""Test that editor widget types can be fetched from the qgis_editor_widget_styles table"""

vl = QgsVectorLayer('%s table="qgis_test"."widget_styles" sql=' % (self.dbconn), "widget_styles", "postgres")
self.assertTrue(vl.isValid())
fields = vl.dataProvider().fields()

setup1 = fields.field("fld1").editorWidgetSetup()
self.assertFalse(setup1.isNull())
self.assertEqual(setup1.type(), "FooEdit")
self.assertEqual(setup1.config(), {"param1": "value1", "param2": "2"})

best1 = QgsEditorWidgetRegistry.instance().findBest(vl, "fld1")
self.assertEqual(best1.type(), "FooEdit")
self.assertEqual(best1.config(), setup1.config())

self.assertTrue(fields.field("fld2").editorWidgetSetup().isNull())

best2 = QgsEditorWidgetRegistry.instance().findBest(vl, "fld2")
self.assertEqual(best2.type(), "TextEdit")


class TestPyQgsPostgresProviderCompoundKey(unittest.TestCase, ProviderTestCase):

Expand Down
1 change: 1 addition & 0 deletions tests/testdata/provider/testdata_pg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ SCRIPTS="
tests/testdata/provider/testdata_pg_vectorjoin.sql
"

dropdb qgis_test 2> /dev/null || true
createdb qgis_test || exit 1
for f in ${SCRIPTS}; do
psql -f $f qgis_test --set ON_ERROR_STOP=1 || exit 1
Expand Down
24 changes: 24 additions & 0 deletions tests/testdata/provider/testdata_pg.sql
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,27 @@ CREATE TABLE qgis_test.rename_table
);

INSERT INTO qgis_test.rename_table (field1,field2) VALUES ('a','b');


--------------------------------------
-- Table for editor widget types
--

CREATE TABLE qgis_editor_widget_styles
(
schema_name TEXT NOT NULL,
table_name TEXT NOT NULL,
field_name TEXT NOT NULL,
type TEXT NOT NULL,
config TEXT,
PRIMARY KEY(table_name, field_name)
);

CREATE TABLE qgis_test.widget_styles(
id int PRIMARY KEY,
fld1 TEXT,
fld2 TEXT
);

INSERT INTO qgis_editor_widget_styles VALUES
('qgis_test', 'widget_styles', 'fld1', 'FooEdit', '<config><option key="param1" value="value1"/><option key="param2" value="2"/></config>');